mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #4291 from jlowdermilk/delete-kubecfg
Remove kubecfg, cleanup a few stray references.
This commit is contained in:
		@@ -478,7 +478,6 @@ function kube::release::package_tarballs() {
 | 
				
			|||||||
  # Clean out any old releases
 | 
					  # Clean out any old releases
 | 
				
			||||||
  rm -rf "${RELEASE_DIR}"
 | 
					  rm -rf "${RELEASE_DIR}"
 | 
				
			||||||
  mkdir -p "${RELEASE_DIR}"
 | 
					  mkdir -p "${RELEASE_DIR}"
 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  kube::release::package_client_tarballs
 | 
					  kube::release::package_client_tarballs
 | 
				
			||||||
  kube::release::package_server_tarballs
 | 
					  kube::release::package_server_tarballs
 | 
				
			||||||
  kube::release::package_salt_tarball
 | 
					  kube::release::package_salt_tarball
 | 
				
			||||||
@@ -489,7 +488,7 @@ function kube::release::package_tarballs() {
 | 
				
			|||||||
# Package up all of the cross compiled clients.  Over time this should grow into
 | 
					# Package up all of the cross compiled clients.  Over time this should grow into
 | 
				
			||||||
# a full SDK
 | 
					# a full SDK
 | 
				
			||||||
function kube::release::package_client_tarballs() {
 | 
					function kube::release::package_client_tarballs() {
 | 
				
			||||||
   # Find all of the built kubecfg binaries
 | 
					   # Find all of the built client binaries
 | 
				
			||||||
  local platform platforms
 | 
					  local platform platforms
 | 
				
			||||||
  platforms=($(cd "${LOCAL_OUTPUT_BINPATH}" ; echo */*))
 | 
					  platforms=($(cd "${LOCAL_OUTPUT_BINPATH}" ; echo */*))
 | 
				
			||||||
  for platform in "${platforms[@]}" ; do
 | 
					  for platform in "${platforms[@]}" ; do
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,109 +0,0 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
# you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
# You may obtain a copy of the License at
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
# See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
# limitations under the License.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set -o errexit
 | 
					 | 
				
			||||||
set -o nounset
 | 
					 | 
				
			||||||
set -o pipefail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
 | 
					 | 
				
			||||||
source "${KUBE_ROOT}/cluster/kube-env.sh"
 | 
					 | 
				
			||||||
source "${KUBE_ROOT}/cluster/${KUBERNETES_PROVIDER}/util.sh"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Detect the OS name/arch so that we can find our binary
 | 
					 | 
				
			||||||
case "$(uname -s)" in
 | 
					 | 
				
			||||||
  Darwin)
 | 
					 | 
				
			||||||
    host_os=darwin
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  Linux)
 | 
					 | 
				
			||||||
    host_os=linux
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  *)
 | 
					 | 
				
			||||||
    echo "Unsupported host OS.  Must be Linux or Mac OS X." >&2
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
esac
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
case "$(uname -m)" in
 | 
					 | 
				
			||||||
  x86_64*)
 | 
					 | 
				
			||||||
    host_arch=amd64
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  i?86_64*)
 | 
					 | 
				
			||||||
    host_arch=amd64
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  amd64*)
 | 
					 | 
				
			||||||
    host_arch=amd64
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  arm*)
 | 
					 | 
				
			||||||
    host_arch=arm
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  i?86*)
 | 
					 | 
				
			||||||
    host_arch=x86
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
  *)
 | 
					 | 
				
			||||||
    echo "Unsupported host arch. Must be x86_64, 386 or arm." >&2
 | 
					 | 
				
			||||||
    exit 1
 | 
					 | 
				
			||||||
    ;;
 | 
					 | 
				
			||||||
esac
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Gather up the list of likely places and use ls to find the latest one.
 | 
					 | 
				
			||||||
locations=(
 | 
					 | 
				
			||||||
  "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/kubecfg"
 | 
					 | 
				
			||||||
  "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/kubecfg"
 | 
					 | 
				
			||||||
  "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/kubecfg"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
kubecfg=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if [[ ! -x "$kubecfg" ]]; then
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    echo "It looks as if you don't have a compiled kubecfg binary."
 | 
					 | 
				
			||||||
    echo
 | 
					 | 
				
			||||||
    echo "If you are running from a clone of the git repo, please run"
 | 
					 | 
				
			||||||
    echo "'./build/run.sh hack/build-cross.sh'. Note that this requires having"
 | 
					 | 
				
			||||||
    echo "Docker installed."
 | 
					 | 
				
			||||||
    echo
 | 
					 | 
				
			||||||
    echo "If you are running from a binary release tarball, something is wrong. "
 | 
					 | 
				
			||||||
    echo "Look at http://kubernetes.io/ for information on how to contact the "
 | 
					 | 
				
			||||||
    echo "development team for help."
 | 
					 | 
				
			||||||
  } >&2
 | 
					 | 
				
			||||||
  exit 1
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# When we are using vagrant it has hard coded auth.  We repeat that here so that
 | 
					 | 
				
			||||||
# we don't clobber auth that might be used for a publicly facing cluster.
 | 
					 | 
				
			||||||
if [[ "$KUBERNETES_PROVIDER" == "vagrant" ]]; then
 | 
					 | 
				
			||||||
  auth_config=(
 | 
					 | 
				
			||||||
    "-auth" "$HOME/.kubernetes_vagrant_auth"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
elif [[ "${KUBERNETES_PROVIDER}" == "gke" ]]; then
 | 
					 | 
				
			||||||
  # While KUBECFG calls remain, we manually pass the auth and cert info.
 | 
					 | 
				
			||||||
  detect-project &> /dev/null
 | 
					 | 
				
			||||||
  cluster_config_dir="${GCLOUD_CONFIG_DIR}/${PROJECT}.${ZONE}.${CLUSTER_NAME}"
 | 
					 | 
				
			||||||
  auth_config=(
 | 
					 | 
				
			||||||
    "-certificate_authority=${cluster_config_dir}/ca.crt"
 | 
					 | 
				
			||||||
    "-client_certificate=${cluster_config_dir}/kubecfg.crt"
 | 
					 | 
				
			||||||
    "-client_key=${cluster_config_dir}/kubecfg.key"
 | 
					 | 
				
			||||||
    "-auth=${cluster_config_dir}/kubernetes_auth"
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
  auth_config=()
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
detect-master > /dev/null
 | 
					 | 
				
			||||||
if [[ -n "${KUBE_MASTER_IP-}" && -z "${KUBERNETES_MASTER-}" ]]; then
 | 
					 | 
				
			||||||
  export KUBERNETES_MASTER=https://${KUBE_MASTER_IP}
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"${kubecfg}" "${auth_config[@]:+${auth_config[@]}}" "${@}"
 | 
					 | 
				
			||||||
@@ -1,529 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"flag"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/version/verflag"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/golang/glog"
 | 
					 | 
				
			||||||
	"github.com/skratchdot/open-golang/open"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	serverVersion = verflag.Version("server_version", verflag.VersionFalse, "Print the server's version information and quit")
 | 
					 | 
				
			||||||
	preventSkew   = flag.Bool("expect_version_match", false, "Fail if server's version doesn't match own version.")
 | 
					 | 
				
			||||||
	config        = flag.String("c", "", "Path or URL to the config file, or '-' to read from STDIN")
 | 
					 | 
				
			||||||
	selector      = flag.String("l", "", "Selector (label query) to use for listing")
 | 
					 | 
				
			||||||
	fieldSelector = flag.String("fields", "", "Selector (field query) to use for listing")
 | 
					 | 
				
			||||||
	updatePeriod  = flag.Duration("u", 60*time.Second, "Update interval period")
 | 
					 | 
				
			||||||
	portSpec      = flag.String("p", "", "The port spec, comma-separated list of <external>:<internal>,...")
 | 
					 | 
				
			||||||
	servicePort   = flag.Int("s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'")
 | 
					 | 
				
			||||||
	authConfig    = flag.String("auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file.  If missing, prompt the user.  Only used if doing https.")
 | 
					 | 
				
			||||||
	json          = flag.Bool("json", false, "If true, print raw JSON for responses")
 | 
					 | 
				
			||||||
	yaml          = flag.Bool("yaml", false, "If true, print raw YAML for responses")
 | 
					 | 
				
			||||||
	verbose       = flag.Bool("verbose", false, "If true, print extra information")
 | 
					 | 
				
			||||||
	validate      = flag.Bool("validate", false, "If true, try to validate the passed in object using a swagger schema on the api server")
 | 
					 | 
				
			||||||
	proxy         = flag.Bool("proxy", false, "If true, run a proxy to the api server")
 | 
					 | 
				
			||||||
	www           = flag.String("www", "", "If -proxy is true, use this directory to serve static files")
 | 
					 | 
				
			||||||
	templateFile  = flag.String("template_file", "", "If present, load this file as a golang template and use it for output printing")
 | 
					 | 
				
			||||||
	templateStr   = flag.String("template", "", "If present, parse this string as a golang template and use it for output printing")
 | 
					 | 
				
			||||||
	imageName     = flag.String("image", "", "Image used when updating a replicationController.  Will apply to the first container in the pod template.")
 | 
					 | 
				
			||||||
	clientConfig  = &client.Config{}
 | 
					 | 
				
			||||||
	openBrowser   = flag.Bool("open_browser", true, "If true, and -proxy is specified, open a browser pointed at the Kubernetes UX. Default true.")
 | 
					 | 
				
			||||||
	ns            = flag.String("ns", "", "If present, the namespace scope for this request.")
 | 
					 | 
				
			||||||
	nsFile        = flag.String("ns_file", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace file")
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	flag.StringVar(&clientConfig.Host, "h", "", "The host to connect to.")
 | 
					 | 
				
			||||||
	flag.StringVar(&clientConfig.Version, "api_version", latest.Version, "The version of the API to use against this server.")
 | 
					 | 
				
			||||||
	flag.StringVar(&clientConfig.CAFile, "certificate_authority", "", "Path to a cert. file for the certificate authority")
 | 
					 | 
				
			||||||
	flag.StringVar(&clientConfig.CertFile, "client_certificate", "", "Path to a client certificate for TLS.")
 | 
					 | 
				
			||||||
	flag.StringVar(&clientConfig.KeyFile, "client_key", "", "Path to a client key file for TLS.")
 | 
					 | 
				
			||||||
	flag.BoolVar(&clientConfig.Insecure, "insecure_skip_tls_verify", clientConfig.Insecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var parser = kubecfg.NewParser(map[string]runtime.Object{
 | 
					 | 
				
			||||||
	"pods":                   &api.Pod{},
 | 
					 | 
				
			||||||
	"services":               &api.Service{},
 | 
					 | 
				
			||||||
	"replicationControllers": &api.ReplicationController{},
 | 
					 | 
				
			||||||
	"minions":                &api.Node{},
 | 
					 | 
				
			||||||
	"nodes":                  &api.Node{},
 | 
					 | 
				
			||||||
	"events":                 &api.Event{},
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func usage() {
 | 
					 | 
				
			||||||
	fmt.Fprintf(os.Stderr, `Usage: kubecfg -h [-c config/file.json|url|-] <method>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Kubernetes REST API:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kubecfg [OPTIONS] get|list|create|delete|update <%s>[/<id>]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Manage replication controllers:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kubecfg [OPTIONS] stop|rm <controller>
 | 
					 | 
				
			||||||
  kubecfg [OPTIONS] [-u <time>] [-image <image>] rollingupdate <controller>
 | 
					 | 
				
			||||||
  kubecfg [OPTIONS] resize <controller> <replicas>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Launch a simple ReplicationController with a single container based
 | 
					 | 
				
			||||||
on the given image:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kubecfg [OPTIONS] [-p <port spec>] run <image> <replicas> <controller>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Manage namespace:
 | 
					 | 
				
			||||||
	kubecfg [OPTIONS] ns [<namespace>]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Options:
 | 
					 | 
				
			||||||
`, prettyWireStorage())
 | 
					 | 
				
			||||||
	flag.PrintDefaults()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func prettyWireStorage() string {
 | 
					 | 
				
			||||||
	types := parser.SupportedWireStorage()
 | 
					 | 
				
			||||||
	sort.Strings(types)
 | 
					 | 
				
			||||||
	return strings.Join(types, "|")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// readConfigData reads the bytes from the specified filesytem or network location associated with the *config flag
 | 
					 | 
				
			||||||
func readConfigData() []byte {
 | 
					 | 
				
			||||||
	// read from STDIN
 | 
					 | 
				
			||||||
	if *config == "-" {
 | 
					 | 
				
			||||||
		data, err := ioutil.ReadAll(os.Stdin)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Unable to read from STDIN: %v\n", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return data
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// we look for http:// or https:// to determine if valid URL, otherwise do normal file IO
 | 
					 | 
				
			||||||
	if strings.Index(*config, "http://") == 0 || strings.Index(*config, "https://") == 0 {
 | 
					 | 
				
			||||||
		resp, err := http.Get(*config)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Unable to access URL %v: %v\n", *config, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		defer resp.Body.Close()
 | 
					 | 
				
			||||||
		if resp.StatusCode != 200 {
 | 
					 | 
				
			||||||
			glog.Fatalf("Unable to read URL, server reported %d %s", resp.StatusCode, resp.Status)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		data, err := ioutil.ReadAll(resp.Body)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Unable to read URL %v: %v\n", *config, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return data
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data, err := ioutil.ReadFile(*config)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Unable to read %v: %v\n", *config, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return data
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// readConfig reads and parses pod, replicationController, and service
 | 
					 | 
				
			||||||
// configuration files. If any errors log and exit non-zero.
 | 
					 | 
				
			||||||
func readConfig(storage string, c *client.Client) []byte {
 | 
					 | 
				
			||||||
	serverCodec := c.RESTClient.Codec
 | 
					 | 
				
			||||||
	if len(*config) == 0 {
 | 
					 | 
				
			||||||
		glog.Fatal("Need config file (-c)")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dataInput := readConfigData()
 | 
					 | 
				
			||||||
	if *validate {
 | 
					 | 
				
			||||||
		err := kubecfg.ValidateObject(dataInput, c)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error validating %v as an object for %v: %v\n", *config, storage, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data, err := parser.ToWireFormat(dataInput, storage, latest.Codec, serverCodec)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Error parsing %v as an object for %v: %v\n", *config, storage, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if *verbose {
 | 
					 | 
				
			||||||
		glog.Infof("Parsed config file successfully; sending:\n%v\n", string(data))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return data
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	flag.Usage = func() {
 | 
					 | 
				
			||||||
		usage()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flag.Parse()
 | 
					 | 
				
			||||||
	util.InitLogs()
 | 
					 | 
				
			||||||
	defer util.FlushLogs()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	verflag.PrintAndExitIfRequested()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Initialize the client
 | 
					 | 
				
			||||||
	if clientConfig.Host == "" {
 | 
					 | 
				
			||||||
		clientConfig.Host = os.Getenv("KUBERNETES_MASTER")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Load namespace information for requests
 | 
					 | 
				
			||||||
	// Check if the namespace was overriden by the -ns argument
 | 
					 | 
				
			||||||
	ctx := api.NewDefaultContext()
 | 
					 | 
				
			||||||
	if len(*ns) > 0 {
 | 
					 | 
				
			||||||
		ctx = api.WithNamespace(ctx, *ns)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		nsInfo, err := kubecfg.LoadNamespaceInfo(*nsFile)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error loading current namespace: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		ctx = api.WithNamespace(ctx, nsInfo.Namespace)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if clientConfig.Host == "" {
 | 
					 | 
				
			||||||
		// TODO: eventually apiserver should start on 443 and be secure by default
 | 
					 | 
				
			||||||
		// TODO: don't specify http or https in Host, and infer that from auth options.
 | 
					 | 
				
			||||||
		clientConfig.Host = "http://localhost:8080"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if client.IsConfigTransportTLS(*clientConfig) {
 | 
					 | 
				
			||||||
		auth, err := kubecfg.LoadClientAuthInfoOrPrompt(*authConfig, os.Stdin)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error loading auth: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		clientConfig.Username = auth.User
 | 
					 | 
				
			||||||
		clientConfig.Password = auth.Password
 | 
					 | 
				
			||||||
		if auth.CAFile != "" {
 | 
					 | 
				
			||||||
			clientConfig.CAFile = auth.CAFile
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if auth.CertFile != "" {
 | 
					 | 
				
			||||||
			clientConfig.CertFile = auth.CertFile
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if auth.KeyFile != "" {
 | 
					 | 
				
			||||||
			clientConfig.KeyFile = auth.KeyFile
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if auth.BearerToken != "" {
 | 
					 | 
				
			||||||
			clientConfig.BearerToken = auth.BearerToken
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if auth.Insecure != nil {
 | 
					 | 
				
			||||||
			clientConfig.Insecure = *auth.Insecure
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	kubeClient, err := client.New(clientConfig)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Can't configure client: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if *serverVersion != verflag.VersionFalse {
 | 
					 | 
				
			||||||
		got, err := kubeClient.ServerVersion()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Printf("Couldn't read version from server: %v\n", err)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if *serverVersion == verflag.VersionRaw {
 | 
					 | 
				
			||||||
			fmt.Printf("%#v\n", *got)
 | 
					 | 
				
			||||||
			os.Exit(0)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			fmt.Printf("Server: Kubernetes %s\n", got)
 | 
					 | 
				
			||||||
			os.Exit(0)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if *preventSkew {
 | 
					 | 
				
			||||||
		got, err := kubeClient.ServerVersion()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Printf("Couldn't read version from server: %v\n", err)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if c, s := version.Get(), *got; !reflect.DeepEqual(c, s) {
 | 
					 | 
				
			||||||
			fmt.Printf("Server version (%#v) differs from client version (%#v)!\n", s, c)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if *proxy {
 | 
					 | 
				
			||||||
		glog.Info("Starting to serve on localhost:8001")
 | 
					 | 
				
			||||||
		if *openBrowser {
 | 
					 | 
				
			||||||
			go func() {
 | 
					 | 
				
			||||||
				time.Sleep(2 * time.Second)
 | 
					 | 
				
			||||||
				open.Start("http://localhost:8001/static/")
 | 
					 | 
				
			||||||
			}()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		server, err := kubecfg.NewProxyServer(*www, clientConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error creating proxy server: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		glog.Fatal(server.Serve())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(flag.Args()) < 1 {
 | 
					 | 
				
			||||||
		usage()
 | 
					 | 
				
			||||||
		os.Exit(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	method := flag.Arg(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchFound := executeAPIRequest(ctx, method, kubeClient) || executeControllerRequest(ctx, method, kubeClient) || executeNamespaceRequest(method, kubeClient)
 | 
					 | 
				
			||||||
	if matchFound == false {
 | 
					 | 
				
			||||||
		glog.Fatalf("Unknown command %s", method)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// storagePathFromArg normalizes a path and breaks out the first segment if available
 | 
					 | 
				
			||||||
func storagePathFromArg(arg string) (storage, path string, hasSuffix bool) {
 | 
					 | 
				
			||||||
	path = strings.Trim(arg, "/")
 | 
					 | 
				
			||||||
	segments := strings.SplitN(path, "/", 2)
 | 
					 | 
				
			||||||
	storage = segments[0]
 | 
					 | 
				
			||||||
	if len(segments) > 1 && segments[1] != "" {
 | 
					 | 
				
			||||||
		hasSuffix = true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return storage, path, hasSuffix
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//checkStorage returns true if the provided storage is valid
 | 
					 | 
				
			||||||
func checkStorage(storage string) bool {
 | 
					 | 
				
			||||||
	for _, allowed := range parser.SupportedWireStorage() {
 | 
					 | 
				
			||||||
		if allowed == storage {
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getPrinter() kubecfg.ResourcePrinter {
 | 
					 | 
				
			||||||
	var printer kubecfg.ResourcePrinter
 | 
					 | 
				
			||||||
	switch {
 | 
					 | 
				
			||||||
	case *json:
 | 
					 | 
				
			||||||
		printer = &kubecfg.IdentityPrinter{}
 | 
					 | 
				
			||||||
	case *yaml:
 | 
					 | 
				
			||||||
		printer = &kubecfg.YAMLPrinter{}
 | 
					 | 
				
			||||||
	case len(*templateFile) > 0 || len(*templateStr) > 0:
 | 
					 | 
				
			||||||
		var data []byte
 | 
					 | 
				
			||||||
		if len(*templateFile) > 0 {
 | 
					 | 
				
			||||||
			var err error
 | 
					 | 
				
			||||||
			data, err = ioutil.ReadFile(*templateFile)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Fatalf("Error reading template %s, %v\n", *templateFile, err)
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			data = []byte(*templateStr)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		printer, err = kubecfg.NewTemplatePrinter(data)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error '%v' parsing template:\n'%s'", err, string(data))
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		printer = humanReadablePrinter()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return printer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func executeAPIRequest(ctx api.Context, method string, c *client.Client) bool {
 | 
					 | 
				
			||||||
	storage, path, hasSuffix := storagePathFromArg(flag.Arg(1))
 | 
					 | 
				
			||||||
	validStorage := checkStorage(storage)
 | 
					 | 
				
			||||||
	verb := ""
 | 
					 | 
				
			||||||
	setBody := false
 | 
					 | 
				
			||||||
	var version string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	printer := getPrinter()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch method {
 | 
					 | 
				
			||||||
	case "get":
 | 
					 | 
				
			||||||
		verb = "GET"
 | 
					 | 
				
			||||||
		if !validStorage || !hasSuffix {
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>[/<id>]", method, prettyWireStorage())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "list":
 | 
					 | 
				
			||||||
		verb = "GET"
 | 
					 | 
				
			||||||
		if !validStorage || hasSuffix {
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>", method, prettyWireStorage())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "delete":
 | 
					 | 
				
			||||||
		verb = "DELETE"
 | 
					 | 
				
			||||||
		if !validStorage || !hasSuffix {
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>/<id>", method, prettyWireStorage())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "create":
 | 
					 | 
				
			||||||
		verb = "POST"
 | 
					 | 
				
			||||||
		setBody = true
 | 
					 | 
				
			||||||
		if !validStorage || hasSuffix {
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>", method, prettyWireStorage())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "update":
 | 
					 | 
				
			||||||
		obj, err := c.Verb("GET").Namespace(api.NamespaceValue(ctx)).Suffix(path).Do().Get()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("error obtaining resource version for update: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		meta, err := meta.Accessor(obj)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("error finding json base for update: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		version = meta.ResourceVersion()
 | 
					 | 
				
			||||||
		verb = "PUT"
 | 
					 | 
				
			||||||
		setBody = true
 | 
					 | 
				
			||||||
		if !validStorage || !hasSuffix {
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>/<id>", method, prettyWireStorage())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case "print":
 | 
					 | 
				
			||||||
		data := readConfig(storage, c)
 | 
					 | 
				
			||||||
		obj, err := latest.Codec.Decode(data)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("error setting resource version: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		printer.PrintObj(obj, os.Stdout)
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	r := c.Verb(verb).Namespace(api.NamespaceValue(ctx)).Suffix(path)
 | 
					 | 
				
			||||||
	if len(*selector) > 0 {
 | 
					 | 
				
			||||||
		r.ParseSelectorParam("labels", *selector)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(*fieldSelector) > 0 {
 | 
					 | 
				
			||||||
		r.ParseSelectorParam("fields", *fieldSelector)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if setBody {
 | 
					 | 
				
			||||||
		if len(version) > 0 {
 | 
					 | 
				
			||||||
			data := readConfig(storage, c)
 | 
					 | 
				
			||||||
			obj, err := latest.Codec.Decode(data)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Fatalf("error setting resource version: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			jsonBase, err := meta.Accessor(obj)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Fatalf("error setting resource version: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			jsonBase.SetResourceVersion(version)
 | 
					 | 
				
			||||||
			data, err = c.RESTClient.Codec.Encode(obj)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Fatalf("error setting resource version: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			r.Body(data)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			r.Body(readConfig(storage, c))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	result := r.Do()
 | 
					 | 
				
			||||||
	obj, err := result.Get()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Got request error: %v\n", err)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err = printer.PrintObj(obj, os.Stdout); err != nil {
 | 
					 | 
				
			||||||
		body, _ := result.Raw()
 | 
					 | 
				
			||||||
		glog.Fatalf("Failed to print: %v\nRaw received object:\n%#v\n\nBody received: %v", err, obj, string(body))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Print("\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func executeControllerRequest(ctx api.Context, method string, c *client.Client) bool {
 | 
					 | 
				
			||||||
	parseController := func() string {
 | 
					 | 
				
			||||||
		if len(flag.Args()) != 2 {
 | 
					 | 
				
			||||||
			glog.Fatal("usage: kubecfg [OPTIONS] stop|rm|rollingupdate <controller>")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return flag.Arg(1)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	switch method {
 | 
					 | 
				
			||||||
	case "stop":
 | 
					 | 
				
			||||||
		err = kubecfg.StopController(ctx, parseController(), c)
 | 
					 | 
				
			||||||
	case "rm":
 | 
					 | 
				
			||||||
		err = kubecfg.DeleteController(ctx, parseController(), c)
 | 
					 | 
				
			||||||
	case "rollingupdate":
 | 
					 | 
				
			||||||
		err = kubecfg.Update(ctx, parseController(), c, *updatePeriod, *imageName)
 | 
					 | 
				
			||||||
	case "run":
 | 
					 | 
				
			||||||
		if len(flag.Args()) != 4 {
 | 
					 | 
				
			||||||
			glog.Fatal("usage: kubecfg [OPTIONS] run <image> <replicas> <controller>")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		image := flag.Arg(1)
 | 
					 | 
				
			||||||
		replicas, err2 := strconv.Atoi(flag.Arg(2))
 | 
					 | 
				
			||||||
		if err2 != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error parsing replicas: %v", err2)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		name := flag.Arg(3)
 | 
					 | 
				
			||||||
		err = kubecfg.RunController(ctx, image, name, replicas, c, *portSpec, *servicePort)
 | 
					 | 
				
			||||||
	case "resize":
 | 
					 | 
				
			||||||
		args := flag.Args()
 | 
					 | 
				
			||||||
		if len(args) < 3 {
 | 
					 | 
				
			||||||
			glog.Fatal("usage: kubecfg resize <controller> <replicas>")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		name := args[1]
 | 
					 | 
				
			||||||
		replicas, err2 := strconv.Atoi(args[2])
 | 
					 | 
				
			||||||
		if err2 != nil {
 | 
					 | 
				
			||||||
			glog.Fatalf("Error parsing replicas: %v", err2)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err = kubecfg.ResizeController(ctx, name, replicas, c)
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// executeNamespaceRequest handles client operations for namespaces
 | 
					 | 
				
			||||||
func executeNamespaceRequest(method string, c *client.Client) bool {
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	var ns *kubecfg.NamespaceInfo
 | 
					 | 
				
			||||||
	switch method {
 | 
					 | 
				
			||||||
	case "ns":
 | 
					 | 
				
			||||||
		args := flag.Args()
 | 
					 | 
				
			||||||
		switch len(args) {
 | 
					 | 
				
			||||||
		case 1:
 | 
					 | 
				
			||||||
			ns, err = kubecfg.LoadNamespaceInfo(*nsFile)
 | 
					 | 
				
			||||||
		case 2:
 | 
					 | 
				
			||||||
			ns = &kubecfg.NamespaceInfo{Namespace: args[1]}
 | 
					 | 
				
			||||||
			err = kubecfg.SaveNamespaceInfo(*nsFile, ns)
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			glog.Fatalf("usage: kubecfg ns [<namespace>]")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		glog.Fatalf("Error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Printf("Using namespace %s\n", ns.Namespace)
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func humanReadablePrinter() *kubecfg.HumanReadablePrinter {
 | 
					 | 
				
			||||||
	printer := kubecfg.NewHumanReadablePrinter()
 | 
					 | 
				
			||||||
	// Add Handler calls here to support additional types
 | 
					 | 
				
			||||||
	return printer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,199 +0,0 @@
 | 
				
			|||||||
#!bash
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# bash completion file for core kubecfg commands
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This script provides completion of non replication controller options
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# To enable the completions either:
 | 
					 | 
				
			||||||
#  - place this file in /etc/bash_completion.d
 | 
					 | 
				
			||||||
#  or
 | 
					 | 
				
			||||||
#  - copy this file and add the line below to your .bashrc after
 | 
					 | 
				
			||||||
#    bash completion features are loaded
 | 
					 | 
				
			||||||
#     . kubecfg
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Note:
 | 
					 | 
				
			||||||
# Currently, the completions will not work if the kube-apiserver daemon is not
 | 
					 | 
				
			||||||
# running on localhost on the standard port 8080
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__contains_word () {
 | 
					 | 
				
			||||||
    local w word=$1; shift
 | 
					 | 
				
			||||||
    for w in "$@"; do
 | 
					 | 
				
			||||||
        [[ $w = "$word" ]] && return
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This should be provided by the bash-completions, but give a really simple
 | 
					 | 
				
			||||||
# stoopid version just in case.  It works most of the time.
 | 
					 | 
				
			||||||
if ! declare -F _get_comp_words_by_ref >/dev/null 2>&1; then
 | 
					 | 
				
			||||||
_get_comp_words_by_ref ()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    while [ $# -gt 0 ]; do
 | 
					 | 
				
			||||||
        case "$1" in
 | 
					 | 
				
			||||||
        cur)
 | 
					 | 
				
			||||||
            cur=${COMP_WORDS[COMP_CWORD]}
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        prev)
 | 
					 | 
				
			||||||
            prev=${COMP_WORDS[COMP_CWORD-1]}
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        words)
 | 
					 | 
				
			||||||
            words=("${COMP_WORDS[@]}")
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        cword)
 | 
					 | 
				
			||||||
            cword=$COMP_CWORD
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        -n)
 | 
					 | 
				
			||||||
            shift # we don't handle excludes
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        esac
 | 
					 | 
				
			||||||
        shift
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__has_service() {
 | 
					 | 
				
			||||||
    local i
 | 
					 | 
				
			||||||
    for ((i=0; i < cword; i++)); do
 | 
					 | 
				
			||||||
        local word=${words[i]}
 | 
					 | 
				
			||||||
        # strip everything after a / so things like pods/[id] match
 | 
					 | 
				
			||||||
        word=${word%%/*}
 | 
					 | 
				
			||||||
        if __contains_word "${word}" "${services[@]}" &&
 | 
					 | 
				
			||||||
           ! __contains_word "${words[i-1]}" "${opts[@]}"; then
 | 
					 | 
				
			||||||
            return 0
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
    return 1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# call kubecfg list $1,
 | 
					 | 
				
			||||||
# exclude blank lines
 | 
					 | 
				
			||||||
# skip the header stuff kubecfg prints on the first 2 lines
 | 
					 | 
				
			||||||
# append $1/ to the first column and use that in compgen
 | 
					 | 
				
			||||||
__kubecfg_parse_list()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    local kubecfg_output
 | 
					 | 
				
			||||||
    if kubecfg_output=$(kubecfg list "$1" 2>/dev/null); then
 | 
					 | 
				
			||||||
        out=($(echo "${kubecfg_output}" | awk -v prefix="$1" '/^$/ {next} NR > 2 {print prefix"/"$1}'))
 | 
					 | 
				
			||||||
        COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_kubecfg_specific_service_match()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    case "$cur" in
 | 
					 | 
				
			||||||
        pods/*)
 | 
					 | 
				
			||||||
            __kubecfg_parse_list pods
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        minions/*)
 | 
					 | 
				
			||||||
            __kubecfg_parse_list minions
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        replicationControllers/*)
 | 
					 | 
				
			||||||
            __kubecfg_parse_list replicationControllers
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        services/*)
 | 
					 | 
				
			||||||
            __kubecfg_parse_list services
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        *)
 | 
					 | 
				
			||||||
            if __has_service; then
 | 
					 | 
				
			||||||
                return 0
 | 
					 | 
				
			||||||
            fi
 | 
					 | 
				
			||||||
            compopt -o nospace
 | 
					 | 
				
			||||||
            COMPREPLY=( $( compgen -S / -W "${services[*]}" -- "$cur" ) )
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
    esac
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_kubecfg_service_match()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if __has_service; then
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    COMPREPLY=( $( compgen -W "${services[*]}" -- "$cur" ) )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_kubecfg()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    local opts=(
 | 
					 | 
				
			||||||
            -h
 | 
					 | 
				
			||||||
            -c
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    local create_services=(pods replicationControllers services)
 | 
					 | 
				
			||||||
    local update_services=(replicationControllers)
 | 
					 | 
				
			||||||
    local all_services=(pods replicationControllers services minions)
 | 
					 | 
				
			||||||
    local services=("${all_services[@]}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local json_commands=(create update)
 | 
					 | 
				
			||||||
    local all_commands=(create update get list delete stop rm rollingupdate resize)
 | 
					 | 
				
			||||||
    local commands=("${all_commands[@]}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    COMPREPLY=()
 | 
					 | 
				
			||||||
    local command
 | 
					 | 
				
			||||||
    local cur prev words cword
 | 
					 | 
				
			||||||
    _get_comp_words_by_ref -n : cur prev words cword
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if __contains_word "$prev" "${opts[@]}"; then
 | 
					 | 
				
			||||||
        case $prev in
 | 
					 | 
				
			||||||
            -c)
 | 
					 | 
				
			||||||
                _filedir '@(json|yml|yaml)'
 | 
					 | 
				
			||||||
                return 0
 | 
					 | 
				
			||||||
                ;;
 | 
					 | 
				
			||||||
            -h)
 | 
					 | 
				
			||||||
                return 0
 | 
					 | 
				
			||||||
                ;;
 | 
					 | 
				
			||||||
        esac
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if [[ "$cur" = -* ]]; then
 | 
					 | 
				
			||||||
        COMPREPLY=( $(compgen -W "${opts[*]}" -- "$cur") )
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # if you passed -c, you are limited to create or update
 | 
					 | 
				
			||||||
    if __contains_word "-c" "${words[@]}"; then
 | 
					 | 
				
			||||||
        services=("${create_services[@]}" "${update_services[@]}")
 | 
					 | 
				
			||||||
        commands=("${json_commands[@]}")
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # figure out which command they are running, remembering that arguments to
 | 
					 | 
				
			||||||
    # options don't count as the command!  So a hostname named 'create' won't
 | 
					 | 
				
			||||||
    # trip things up
 | 
					 | 
				
			||||||
    local i
 | 
					 | 
				
			||||||
    for ((i=0; i < cword; i++)); do
 | 
					 | 
				
			||||||
        if __contains_word "${words[i]}" "${commands[@]}" &&
 | 
					 | 
				
			||||||
           ! __contains_word "${words[i-1]}" "${opts[@]}"; then
 | 
					 | 
				
			||||||
            command=${words[i]}
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
    done
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # tell the list of possible commands
 | 
					 | 
				
			||||||
    if [[ -z ${command} ]]; then
 | 
					 | 
				
			||||||
        COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
 | 
					 | 
				
			||||||
        return 0
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # remove services which you can't update given your command
 | 
					 | 
				
			||||||
    if [[ ${command} == "create" ]]; then
 | 
					 | 
				
			||||||
        services=("${create_services[@]}")
 | 
					 | 
				
			||||||
    elif [[ ${command} == "update" ]]; then
 | 
					 | 
				
			||||||
        services=("${update_services[@]}")
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    case $command in
 | 
					 | 
				
			||||||
        create | list)
 | 
					 | 
				
			||||||
            _kubecfg_service_match
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        update | get | delete)
 | 
					 | 
				
			||||||
            _kubecfg_specific_service_match
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
        *)
 | 
					 | 
				
			||||||
            ;;
 | 
					 | 
				
			||||||
    esac
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
complete -F _kubecfg kubecfg
 | 
					 | 
				
			||||||
# ex: ts=4 sw=4 et filetype=sh
 | 
					 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
# Install and configure kubecfg
 | 
					# Install and configure kubectl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Download the kubecfg CLI tool
 | 
					## Download the kubectl CLI tool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Darwin
 | 
					### Darwin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -276,7 +276,7 @@ You probably have an incorrect ~/.kubernetes_vagrant_auth file for the cluster y
 | 
				
			|||||||
rm ~/.kubernetes_vagrant_auth
 | 
					rm ~/.kubernetes_vagrant_auth
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After using kubecfg.sh make sure that the correct credentials are set:
 | 
					After using kubectl.sh make sure that the correct credentials are set:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
cat ~/.kubernetes_vagrant_auth
 | 
					cat ~/.kubernetes_vagrant_auth
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@ readonly KUBE_SERVER_PLATFORMS=(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# The set of client targets that we are building for all platforms
 | 
					# The set of client targets that we are building for all platforms
 | 
				
			||||||
readonly KUBE_CLIENT_TARGETS=(
 | 
					readonly KUBE_CLIENT_TARGETS=(
 | 
				
			||||||
  cmd/kubecfg
 | 
					 | 
				
			||||||
  cmd/kubectl
 | 
					  cmd/kubectl
 | 
				
			||||||
  cmd/kubernetes
 | 
					  cmd/kubernetes
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Package kubecfg is a set of libraries that are used by the kubecfg command line tool.
 | 
					 | 
				
			||||||
// They are separated out into a library to support unit testing.  Most functionality should
 | 
					 | 
				
			||||||
// be included in this package, and the main kubecfg should really just be an entry point.
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
@@ -1,329 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
 | 
					 | 
				
			||||||
	"github.com/ghodss/yaml"
 | 
					 | 
				
			||||||
	"github.com/golang/glog"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func GetServerVersion(client *client.Client) (*version.Info, error) {
 | 
					 | 
				
			||||||
	info, err := client.ServerVersion()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return info, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func promptForString(field string, r io.Reader) string {
 | 
					 | 
				
			||||||
	fmt.Printf("Please enter %s: ", field)
 | 
					 | 
				
			||||||
	var result string
 | 
					 | 
				
			||||||
	fmt.Fscan(r, &result)
 | 
					 | 
				
			||||||
	return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type NamespaceInfo struct {
 | 
					 | 
				
			||||||
	Namespace string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LoadClientAuthInfoOrPrompt parses a clientauth.Info object from a file path. It prompts user and creates file if it doesn't exist.
 | 
					 | 
				
			||||||
// Oddly, it returns a clientauth.Info even if there is an error.
 | 
					 | 
				
			||||||
func LoadClientAuthInfoOrPrompt(path string, r io.Reader) (*clientauth.Info, error) {
 | 
					 | 
				
			||||||
	var auth clientauth.Info
 | 
					 | 
				
			||||||
	// Prompt for user/pass and write a file if none exists.
 | 
					 | 
				
			||||||
	if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
					 | 
				
			||||||
		auth.User = promptForString("Username", r)
 | 
					 | 
				
			||||||
		auth.Password = promptForString("Password", r)
 | 
					 | 
				
			||||||
		data, err := json.Marshal(auth)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return &auth, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err = ioutil.WriteFile(path, data, 0600)
 | 
					 | 
				
			||||||
		return &auth, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	authPtr, err := clientauth.LoadFromFile(path)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return authPtr, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LoadNamespaceInfo parses a NamespaceInfo object from a file path. It creates a file at the specified path if it doesn't exist with the default namespace.
 | 
					 | 
				
			||||||
func LoadNamespaceInfo(path string) (*NamespaceInfo, error) {
 | 
					 | 
				
			||||||
	var ns NamespaceInfo
 | 
					 | 
				
			||||||
	if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
					 | 
				
			||||||
		ns.Namespace = api.NamespaceDefault
 | 
					 | 
				
			||||||
		err = SaveNamespaceInfo(path, &ns)
 | 
					 | 
				
			||||||
		return &ns, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data, err := ioutil.ReadFile(path)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err = json.Unmarshal(data, &ns)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &ns, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SaveNamespaceInfo saves a NamespaceInfo object at the specified file path.
 | 
					 | 
				
			||||||
func SaveNamespaceInfo(path string, ns *NamespaceInfo) error {
 | 
					 | 
				
			||||||
	if !util.IsDNSLabel(ns.Namespace) {
 | 
					 | 
				
			||||||
		return fmt.Errorf("namespace %s is not a valid DNS Label", ns.Namespace)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data, err := json.Marshal(ns)
 | 
					 | 
				
			||||||
	err = ioutil.WriteFile(path, data, 0600)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// extracted for test speed
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	updatePollInterval = 5 * time.Second
 | 
					 | 
				
			||||||
	updatePollTimeout  = 300 * time.Second
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Update performs a rolling update of a collection of pods.
 | 
					 | 
				
			||||||
// 'name' points to a replication controller.
 | 
					 | 
				
			||||||
// 'client' is used for updating pods.
 | 
					 | 
				
			||||||
// 'updatePeriod' is the time between pod updates.
 | 
					 | 
				
			||||||
// 'imageName' is the new image to update for the template.  This will work
 | 
					 | 
				
			||||||
//     with the first container in the pod.  There is no support yet for
 | 
					 | 
				
			||||||
//     updating more complex replication controllers.  If this is blank then no
 | 
					 | 
				
			||||||
//     update of the image is performed.
 | 
					 | 
				
			||||||
func Update(ctx api.Context, name string, client client.Interface, updatePeriod time.Duration, imageName string) error {
 | 
					 | 
				
			||||||
	// TODO ctx is not needed as input to this function, should just be 'namespace'
 | 
					 | 
				
			||||||
	controller, err := client.ReplicationControllers(api.NamespaceValue(ctx)).Get(name)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(imageName) != 0 {
 | 
					 | 
				
			||||||
		controller.Spec.Template.Spec.Containers[0].Image = imageName
 | 
					 | 
				
			||||||
		controller, err = client.ReplicationControllers(controller.Namespace).Update(controller)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s := labels.Set(controller.Spec.Selector).AsSelector()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	podList, err := client.Pods(api.NamespaceValue(ctx)).List(s)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	expected := len(podList.Items)
 | 
					 | 
				
			||||||
	if expected == 0 {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, pod := range podList.Items {
 | 
					 | 
				
			||||||
		// We delete the pod here, the controller will recreate it.  This will result in pulling
 | 
					 | 
				
			||||||
		// a new Docker image.  This isn't a full "update" but it's what we support for now.
 | 
					 | 
				
			||||||
		err = client.Pods(pod.Namespace).Delete(pod.Name)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		time.Sleep(updatePeriod)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return wait.Poll(updatePollInterval, updatePollTimeout, func() (bool, error) {
 | 
					 | 
				
			||||||
		podList, err := client.Pods(api.NamespaceValue(ctx)).List(s)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return false, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return len(podList.Items) == expected, nil
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// StopController stops a controller named 'name' by setting replicas to zero.
 | 
					 | 
				
			||||||
func StopController(ctx api.Context, name string, client client.Interface) error {
 | 
					 | 
				
			||||||
	return ResizeController(ctx, name, 0, client)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ResizeController resizes a controller named 'name' by setting replicas to 'replicas'.
 | 
					 | 
				
			||||||
func ResizeController(ctx api.Context, name string, replicas int, client client.Interface) error {
 | 
					 | 
				
			||||||
	// TODO ctx is not needed, and should just be a namespace
 | 
					 | 
				
			||||||
	controller, err := client.ReplicationControllers(api.NamespaceValue(ctx)).Get(name)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller.Spec.Replicas = replicas
 | 
					 | 
				
			||||||
	controllerOut, err := client.ReplicationControllers(api.NamespaceValue(ctx)).Update(controller)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data, err := yaml.Marshal(controllerOut)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Print(string(data))
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func portsFromString(spec string) ([]api.Port, error) {
 | 
					 | 
				
			||||||
	if spec == "" {
 | 
					 | 
				
			||||||
		return []api.Port{}, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	parts := strings.Split(spec, ",")
 | 
					 | 
				
			||||||
	var result []api.Port
 | 
					 | 
				
			||||||
	for _, part := range parts {
 | 
					 | 
				
			||||||
		pieces := strings.Split(part, ":")
 | 
					 | 
				
			||||||
		if len(pieces) < 1 || len(pieces) > 2 {
 | 
					 | 
				
			||||||
			glog.Infof("Bad port spec: %s", part)
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("bad port spec: %s", part)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		host := 0
 | 
					 | 
				
			||||||
		container := 0
 | 
					 | 
				
			||||||
		var err error
 | 
					 | 
				
			||||||
		if len(pieces) == 1 {
 | 
					 | 
				
			||||||
			container, err = strconv.Atoi(pieces[0])
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Errorf("Container port is not integer: %s %v", pieces[0], err)
 | 
					 | 
				
			||||||
				return nil, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			host, err = strconv.Atoi(pieces[0])
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Errorf("Host port is not integer: %s %v", pieces[0], err)
 | 
					 | 
				
			||||||
				return nil, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			container, err = strconv.Atoi(pieces[1])
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				glog.Errorf("Container port is not integer: %s %v", pieces[1], err)
 | 
					 | 
				
			||||||
				return nil, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if container < 1 {
 | 
					 | 
				
			||||||
			glog.Errorf("Container port is not valid: %d", container)
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		result = append(result, api.Port{ContainerPort: container, HostPort: host})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RunController creates a new replication controller named 'name' which creates 'replicas' pods running 'image'.
 | 
					 | 
				
			||||||
func RunController(ctx api.Context, image, name string, replicas int, client client.Interface, portSpec string, servicePort int) error {
 | 
					 | 
				
			||||||
	// TODO replace ctx with a namespace string
 | 
					 | 
				
			||||||
	if servicePort > 0 && !util.IsDNSLabel(name) {
 | 
					 | 
				
			||||||
		return fmt.Errorf("service creation requested, but an invalid name for a service was provided (%s). Service names must be valid DNS labels.", name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ports, err := portsFromString(portSpec)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := &api.ReplicationController{
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
					 | 
				
			||||||
			Name: name,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Spec: api.ReplicationControllerSpec{
 | 
					 | 
				
			||||||
			Replicas: replicas,
 | 
					 | 
				
			||||||
			Selector: map[string]string{
 | 
					 | 
				
			||||||
				"name": name,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Template: &api.PodTemplateSpec{
 | 
					 | 
				
			||||||
				ObjectMeta: api.ObjectMeta{
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						"name": name,
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PodSpec{
 | 
					 | 
				
			||||||
					Containers: []api.Container{
 | 
					 | 
				
			||||||
						{
 | 
					 | 
				
			||||||
							Name:  strings.ToLower(name),
 | 
					 | 
				
			||||||
							Image: image,
 | 
					 | 
				
			||||||
							Ports: ports,
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	controllerOut, err := client.ReplicationControllers(api.NamespaceValue(ctx)).Create(controller)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	data, err := yaml.Marshal(controllerOut)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Print(string(data))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if servicePort > 0 {
 | 
					 | 
				
			||||||
		svc, err := createService(ctx, name, servicePort, client)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		data, err = yaml.Marshal(svc)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fmt.Printf(string(data))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func createService(ctx api.Context, name string, port int, client client.Interface) (*api.Service, error) {
 | 
					 | 
				
			||||||
	// TODO remove context in favor of just namespace string
 | 
					 | 
				
			||||||
	svc := &api.Service{
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
					 | 
				
			||||||
			Name: name,
 | 
					 | 
				
			||||||
			Labels: map[string]string{
 | 
					 | 
				
			||||||
				"name": name,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Spec: api.ServiceSpec{
 | 
					 | 
				
			||||||
			Port: port,
 | 
					 | 
				
			||||||
			Selector: map[string]string{
 | 
					 | 
				
			||||||
				"name": name,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	svc, err := client.Services(api.NamespaceValue(ctx)).Create(svc)
 | 
					 | 
				
			||||||
	return svc, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteController deletes a replication controller named 'name', requires that the controller
 | 
					 | 
				
			||||||
// already be stopped.
 | 
					 | 
				
			||||||
func DeleteController(ctx api.Context, name string, client client.Interface) error {
 | 
					 | 
				
			||||||
	// TODO remove ctx in favor of just namespace string
 | 
					 | 
				
			||||||
	controller, err := client.ReplicationControllers(api.NamespaceValue(ctx)).Get(name)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if controller.Spec.Replicas != 0 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("controller has non-zero replicas (%d), please stop it first", controller.Spec.Replicas)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return client.ReplicationControllers(api.NamespaceValue(ctx)).Delete(name)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,393 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func validateAction(expectedAction, actualAction client.FakeAction, t *testing.T) {
 | 
					 | 
				
			||||||
	if !reflect.DeepEqual(expectedAction, actualAction) {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v, expected: %#v", actualAction, expectedAction)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	updatePollInterval = 1 * time.Millisecond
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestUpdateWithPods(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{
 | 
					 | 
				
			||||||
		PodsList: api.PodList{
 | 
					 | 
				
			||||||
			Items: []api.Pod{
 | 
					 | 
				
			||||||
				{ObjectMeta: api.ObjectMeta{Name: "pod-1"}},
 | 
					 | 
				
			||||||
				{ObjectMeta: api.ObjectMeta{Name: "pod-2"}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Update(api.NewDefaultContext(), "foo", &fakeClient, 0, "")
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 5 {
 | 
					 | 
				
			||||||
		t.Fatalf("Unexpected action list %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[1], t)
 | 
					 | 
				
			||||||
	// Update deletes the pods, it relies on the replication controller to replace them.
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "delete-pod", Value: "pod-1"}, fakeClient.Actions[2], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "delete-pod", Value: "pod-2"}, fakeClient.Actions[3], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[4], t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestUpdateNoPods(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	Update(api.NewDefaultContext(), "foo", &fakeClient, 0, "")
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 2 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected action list %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[1], t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestUpdateWithNewImage(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{
 | 
					 | 
				
			||||||
		PodsList: api.PodList{
 | 
					 | 
				
			||||||
			Items: []api.Pod{
 | 
					 | 
				
			||||||
				{ObjectMeta: api.ObjectMeta{Name: "pod-1"}},
 | 
					 | 
				
			||||||
				{ObjectMeta: api.ObjectMeta{Name: "pod-2"}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Ctrl: api.ReplicationController{
 | 
					 | 
				
			||||||
			Spec: api.ReplicationControllerSpec{
 | 
					 | 
				
			||||||
				Template: &api.PodTemplateSpec{
 | 
					 | 
				
			||||||
					Spec: api.PodSpec{
 | 
					 | 
				
			||||||
						Containers: []api.Container{
 | 
					 | 
				
			||||||
							{Image: "fooImage:1"},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Update(api.NewDefaultContext(), "foo", &fakeClient, 0, "fooImage:2")
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 6 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected action list %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	newCtrl := api.Scheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController)
 | 
					 | 
				
			||||||
	newCtrl.Spec.Template.Spec.Containers[0].Image = "fooImage:2"
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[2], t)
 | 
					 | 
				
			||||||
	// Update deletes the pods, it relies on the replication controller to replace them.
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "delete-pod", Value: "pod-1"}, fakeClient.Actions[3], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "delete-pod", Value: "pod-2"}, fakeClient.Actions[4], t)
 | 
					 | 
				
			||||||
	validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[5], t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestRunController(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	image := "foo/bar"
 | 
					 | 
				
			||||||
	replicas := 3
 | 
					 | 
				
			||||||
	RunController(api.NewDefaultContext(), image, name, replicas, &fakeClient, "8080:80", -1)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 1 || fakeClient.Actions[0].Action != "create-controller" {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
 | 
					 | 
				
			||||||
	if controller.Name != name ||
 | 
					 | 
				
			||||||
		controller.Spec.Replicas != replicas ||
 | 
					 | 
				
			||||||
		controller.Spec.Template.Spec.Containers[0].Image != image {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected controller: %#v", controller)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestRunControllerWithWrongArgs(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	image := "foo/bar"
 | 
					 | 
				
			||||||
	replicas := 3
 | 
					 | 
				
			||||||
	err := RunController(api.NewDefaultContext(), image, name, replicas, &fakeClient, "8080:", -1)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected non-error: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	RunController(api.NewDefaultContext(), image, name, replicas, &fakeClient, "8080:80", -1)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 1 || fakeClient.Actions[0].Action != "create-controller" {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
 | 
					 | 
				
			||||||
	if controller.Name != name ||
 | 
					 | 
				
			||||||
		controller.Spec.Replicas != replicas ||
 | 
					 | 
				
			||||||
		controller.Spec.Template.Spec.Containers[0].Image != image {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected controller: %#v", controller)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestRunControllerWithService(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	image := "foo/bar"
 | 
					 | 
				
			||||||
	replicas := 3
 | 
					 | 
				
			||||||
	RunController(api.NewDefaultContext(), image, name, replicas, &fakeClient, "", 8000)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 2 ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[0].Action != "create-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[1].Action != "create-service" {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := fakeClient.Actions[0].Value.(*api.ReplicationController)
 | 
					 | 
				
			||||||
	if controller.Name != name ||
 | 
					 | 
				
			||||||
		controller.Spec.Replicas != replicas ||
 | 
					 | 
				
			||||||
		controller.Spec.Template.Spec.Containers[0].Image != image {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected controller: %#v", controller)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestStopController(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	StopController(api.NewDefaultContext(), name, &fakeClient)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 2 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fakeClient.Actions[0].Action != "get-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[0].Value.(string) != name {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := fakeClient.Actions[1].Value.(*api.ReplicationController)
 | 
					 | 
				
			||||||
	if fakeClient.Actions[1].Action != "update-controller" ||
 | 
					 | 
				
			||||||
		controller.Spec.Replicas != 0 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestResizeController(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	replicas := 17
 | 
					 | 
				
			||||||
	ResizeController(api.NewDefaultContext(), name, replicas, &fakeClient)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 2 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fakeClient.Actions[0].Action != "get-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[0].Value.(string) != name {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	controller := fakeClient.Actions[1].Value.(*api.ReplicationController)
 | 
					 | 
				
			||||||
	if fakeClient.Actions[1].Action != "update-controller" ||
 | 
					 | 
				
			||||||
		controller.Spec.Replicas != 17 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCloudCfgDeleteController(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	err := DeleteController(api.NewDefaultContext(), name, &fakeClient)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 2 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fakeClient.Actions[0].Action != "get-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[0].Value.(string) != name {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fakeClient.Actions[1].Action != "delete-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[1].Value.(string) != name {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCloudCfgDeleteControllerWithReplicas(t *testing.T) {
 | 
					 | 
				
			||||||
	fakeClient := client.Fake{
 | 
					 | 
				
			||||||
		Ctrl: api.ReplicationController{
 | 
					 | 
				
			||||||
			Spec: api.ReplicationControllerSpec{
 | 
					 | 
				
			||||||
				Replicas: 2,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	name := "name"
 | 
					 | 
				
			||||||
	err := DeleteController(api.NewDefaultContext(), name, &fakeClient)
 | 
					 | 
				
			||||||
	if len(fakeClient.Actions) != 1 {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected actions: %#v", fakeClient.Actions)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if fakeClient.Actions[0].Action != "get-controller" ||
 | 
					 | 
				
			||||||
		fakeClient.Actions[0].Value.(string) != name {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0])
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected non-error.")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestLoadNamespaceInfo(t *testing.T) {
 | 
					 | 
				
			||||||
	loadNamespaceInfoTests := []struct {
 | 
					 | 
				
			||||||
		nsData string
 | 
					 | 
				
			||||||
		nsInfo *NamespaceInfo
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			`{"Namespace":"test"}`,
 | 
					 | 
				
			||||||
			&NamespaceInfo{Namespace: "test"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"", nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"missing",
 | 
					 | 
				
			||||||
			&NamespaceInfo{Namespace: "default"},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, loadNamespaceInfoTest := range loadNamespaceInfoTests {
 | 
					 | 
				
			||||||
		tt := loadNamespaceInfoTest
 | 
					 | 
				
			||||||
		nsfile, err := ioutil.TempFile("", "testNamespaceInfo")
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if tt.nsData != "missing" {
 | 
					 | 
				
			||||||
			defer os.Remove(nsfile.Name())
 | 
					 | 
				
			||||||
			defer nsfile.Close()
 | 
					 | 
				
			||||||
			_, err := nsfile.WriteString(tt.nsData)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			nsfile.Close()
 | 
					 | 
				
			||||||
			os.Remove(nsfile.Name())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		nsInfo, err := LoadNamespaceInfo(nsfile.Name())
 | 
					 | 
				
			||||||
		if len(tt.nsData) == 0 && tt.nsData != "missing" {
 | 
					 | 
				
			||||||
			if err == nil {
 | 
					 | 
				
			||||||
				t.Error("LoadNamespaceInfo didn't fail on an empty file")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if tt.nsData != "missing" {
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				t.Errorf("Unexpected error: %v, %v", tt.nsData, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if !reflect.DeepEqual(nsInfo, tt.nsInfo) {
 | 
					 | 
				
			||||||
				t.Errorf("Expected %v, got %v", tt.nsInfo, nsInfo)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestLoadClientAuthInfoOrPrompt(t *testing.T) {
 | 
					 | 
				
			||||||
	loadAuthInfoTests := []struct {
 | 
					 | 
				
			||||||
		authData string
 | 
					 | 
				
			||||||
		authInfo *clientauth.Info
 | 
					 | 
				
			||||||
		r        io.Reader
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			`{"user": "user", "password": "pass"}`,
 | 
					 | 
				
			||||||
			&clientauth.Info{User: "user", Password: "pass"},
 | 
					 | 
				
			||||||
			nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"", nil, nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"missing",
 | 
					 | 
				
			||||||
			&clientauth.Info{User: "user", Password: "pass"},
 | 
					 | 
				
			||||||
			bytes.NewBufferString("user\npass"),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, loadAuthInfoTest := range loadAuthInfoTests {
 | 
					 | 
				
			||||||
		tt := loadAuthInfoTest
 | 
					 | 
				
			||||||
		aifile, err := ioutil.TempFile("", "testAuthInfo")
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if tt.authData != "missing" {
 | 
					 | 
				
			||||||
			defer os.Remove(aifile.Name())
 | 
					 | 
				
			||||||
			defer aifile.Close()
 | 
					 | 
				
			||||||
			_, err = aifile.WriteString(tt.authData)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			aifile.Close()
 | 
					 | 
				
			||||||
			os.Remove(aifile.Name())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		authInfo, err := LoadClientAuthInfoOrPrompt(aifile.Name(), tt.r)
 | 
					 | 
				
			||||||
		if len(tt.authData) == 0 && tt.authData != "missing" {
 | 
					 | 
				
			||||||
			if err == nil {
 | 
					 | 
				
			||||||
				t.Error("LoadClientAuthInfoOrPrompt didn't fail on empty file")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !reflect.DeepEqual(authInfo, tt.authInfo) {
 | 
					 | 
				
			||||||
			t.Errorf("Expected %v, got %v", tt.authInfo, authInfo)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestMakePorts(t *testing.T) {
 | 
					 | 
				
			||||||
	var successTestCases = []struct {
 | 
					 | 
				
			||||||
		spec  string
 | 
					 | 
				
			||||||
		ports []api.Port
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"8080:80,8081:8081,443:444",
 | 
					 | 
				
			||||||
			[]api.Port{
 | 
					 | 
				
			||||||
				{HostPort: 8080, ContainerPort: 80},
 | 
					 | 
				
			||||||
				{HostPort: 8081, ContainerPort: 8081},
 | 
					 | 
				
			||||||
				{HostPort: 443, ContainerPort: 444},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"",
 | 
					 | 
				
			||||||
			[]api.Port{},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, tt := range successTestCases {
 | 
					 | 
				
			||||||
		ports, err := portsFromString(tt.spec)
 | 
					 | 
				
			||||||
		if !reflect.DeepEqual(ports, tt.ports) {
 | 
					 | 
				
			||||||
			t.Errorf("Expected %#v, got %#v", tt.ports, ports)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var failTestCases = []struct {
 | 
					 | 
				
			||||||
		spec string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{"8080:"},
 | 
					 | 
				
			||||||
		{":80"},
 | 
					 | 
				
			||||||
		{":"},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, tt := range failTestCases {
 | 
					 | 
				
			||||||
		_, err := portsFromString(tt.spec)
 | 
					 | 
				
			||||||
		if err == nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected non-error")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,61 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Parser struct {
 | 
					 | 
				
			||||||
	storageToType map[string]reflect.Type
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewParser creates a new parser.
 | 
					 | 
				
			||||||
func NewParser(objectMap map[string]runtime.Object) *Parser {
 | 
					 | 
				
			||||||
	typeMap := make(map[string]reflect.Type)
 | 
					 | 
				
			||||||
	for name, obj := range objectMap {
 | 
					 | 
				
			||||||
		typeMap[name] = reflect.TypeOf(obj).Elem()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &Parser{typeMap}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ToWireFormat takes input 'data' as either json or yaml, checks that it parses as the
 | 
					 | 
				
			||||||
// appropriate object type, and returns json for sending to the API or an error.
 | 
					 | 
				
			||||||
func (p *Parser) ToWireFormat(data []byte, storage string, decode runtime.Codec, encode runtime.Codec) ([]byte, error) {
 | 
					 | 
				
			||||||
	prototypeType, found := p.storageToType[storage]
 | 
					 | 
				
			||||||
	if !found {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unknown storage type: %v", storage)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	obj := reflect.New(prototypeType).Interface().(runtime.Object)
 | 
					 | 
				
			||||||
	err := decode.DecodeInto(data, obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return encode.Encode(obj)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *Parser) SupportedWireStorage() []string {
 | 
					 | 
				
			||||||
	types := []string{}
 | 
					 | 
				
			||||||
	for k := range p.storageToType {
 | 
					 | 
				
			||||||
		types = append(types, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return types
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,157 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
					 | 
				
			||||||
	"github.com/ghodss/yaml"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseBadStorage(t *testing.T) {
 | 
					 | 
				
			||||||
	p := NewParser(map[string]runtime.Object{})
 | 
					 | 
				
			||||||
	_, err := p.ToWireFormat([]byte("{}"), "badstorage", latest.Codec, latest.Codec)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Errorf("Expected error, received none")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func DoParseTest(t *testing.T, storage string, obj runtime.Object, codec runtime.Codec, p *Parser) {
 | 
					 | 
				
			||||||
	jsonData, _ := codec.Encode(obj)
 | 
					 | 
				
			||||||
	var tmp map[string]interface{}
 | 
					 | 
				
			||||||
	json.Unmarshal(jsonData, &tmp)
 | 
					 | 
				
			||||||
	yamlData, _ := yaml.Marshal(tmp)
 | 
					 | 
				
			||||||
	t.Logf("Intermediate yaml:\n%v\n", string(yamlData))
 | 
					 | 
				
			||||||
	t.Logf("Intermediate json:\n%v\n", string(jsonData))
 | 
					 | 
				
			||||||
	jsonGot, jsonErr := p.ToWireFormat(jsonData, storage, latest.Codec, codec)
 | 
					 | 
				
			||||||
	yamlGot, yamlErr := p.ToWireFormat(yamlData, storage, latest.Codec, codec)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if jsonErr != nil {
 | 
					 | 
				
			||||||
		t.Errorf("json err: %#v", jsonErr)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if yamlErr != nil {
 | 
					 | 
				
			||||||
		t.Errorf("yaml err: %#v", yamlErr)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if string(jsonGot) != string(jsonData) {
 | 
					 | 
				
			||||||
		t.Errorf("json output didn't match:\nGot:\n%v\n\nWanted:\n%v\n",
 | 
					 | 
				
			||||||
			string(jsonGot), string(jsonData))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if string(yamlGot) != string(jsonData) {
 | 
					 | 
				
			||||||
		t.Errorf("yaml parsed output didn't match:\nGot:\n%v\n\nWanted:\n%v\n",
 | 
					 | 
				
			||||||
			string(yamlGot), string(jsonData))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var testParser = NewParser(map[string]runtime.Object{
 | 
					 | 
				
			||||||
	"pods":                   &api.Pod{},
 | 
					 | 
				
			||||||
	"services":               &api.Service{},
 | 
					 | 
				
			||||||
	"replicationControllers": &api.ReplicationController{},
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParsePod(t *testing.T) {
 | 
					 | 
				
			||||||
	DoParseTest(t, "pods", &api.Pod{
 | 
					 | 
				
			||||||
		TypeMeta:   api.TypeMeta{APIVersion: "v1beta1", Kind: "Pod"},
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "test pod"},
 | 
					 | 
				
			||||||
		Spec: api.PodSpec{
 | 
					 | 
				
			||||||
			Containers: []api.Container{
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					Name:                   "my container",
 | 
					 | 
				
			||||||
					ImagePullPolicy:        api.PullIfNotPresent,
 | 
					 | 
				
			||||||
					TerminationMessagePath: api.TerminationMessagePathDefault,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Volumes: []api.Volume{
 | 
					 | 
				
			||||||
				{Name: "volume", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
 | 
					 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}, v1beta1.Codec, testParser)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseService(t *testing.T) {
 | 
					 | 
				
			||||||
	DoParseTest(t, "services", &api.Service{
 | 
					 | 
				
			||||||
		TypeMeta: api.TypeMeta{APIVersion: "v1beta1", Kind: "Service"},
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{
 | 
					 | 
				
			||||||
			Name: "my service",
 | 
					 | 
				
			||||||
			Labels: map[string]string{
 | 
					 | 
				
			||||||
				"area": "staging",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Spec: api.ServiceSpec{
 | 
					 | 
				
			||||||
			Port: 8080,
 | 
					 | 
				
			||||||
			Selector: map[string]string{
 | 
					 | 
				
			||||||
				"area": "staging",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Protocol:        "TCP",
 | 
					 | 
				
			||||||
			SessionAffinity: "None",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}, v1beta1.Codec, testParser)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseController(t *testing.T) {
 | 
					 | 
				
			||||||
	DoParseTest(t, "replicationControllers", &api.ReplicationController{
 | 
					 | 
				
			||||||
		TypeMeta:   api.TypeMeta{APIVersion: "v1beta1", Kind: "ReplicationController"},
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "my controller"},
 | 
					 | 
				
			||||||
		Spec: api.ReplicationControllerSpec{
 | 
					 | 
				
			||||||
			Replicas: 9001,
 | 
					 | 
				
			||||||
			Template: &api.PodTemplateSpec{
 | 
					 | 
				
			||||||
				Spec: api.PodSpec{
 | 
					 | 
				
			||||||
					Containers: []api.Container{
 | 
					 | 
				
			||||||
						{
 | 
					 | 
				
			||||||
							Name:                   "my container",
 | 
					 | 
				
			||||||
							ImagePullPolicy:        api.PullIfNotPresent,
 | 
					 | 
				
			||||||
							TerminationMessagePath: api.TerminationMessagePathDefault,
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Volumes: []api.Volume{
 | 
					 | 
				
			||||||
						{Name: "volume", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
 | 
					 | 
				
			||||||
					DNSPolicy:     api.DNSClusterFirst,
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}, v1beta1.Codec, testParser)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type TestParseType struct {
 | 
					 | 
				
			||||||
	api.TypeMeta   `json:",inline"`
 | 
					 | 
				
			||||||
	api.ObjectMeta `json:"metadata"`
 | 
					 | 
				
			||||||
	Data           string `json:"data"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (*TestParseType) IsAnAPIObject() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestParseCustomType(t *testing.T) {
 | 
					 | 
				
			||||||
	api.Scheme.AddKnownTypes("", &TestParseType{})
 | 
					 | 
				
			||||||
	api.Scheme.AddKnownTypes("v1beta1", &TestParseType{})
 | 
					 | 
				
			||||||
	api.Scheme.AddKnownTypes("v1beta2", &TestParseType{})
 | 
					 | 
				
			||||||
	parser := NewParser(map[string]runtime.Object{
 | 
					 | 
				
			||||||
		"custom": &TestParseType{},
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	DoParseTest(t, "custom", &TestParseType{
 | 
					 | 
				
			||||||
		TypeMeta:   api.TypeMeta{APIVersion: "", Kind: "TestParseType"},
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "my custom object"},
 | 
					 | 
				
			||||||
		Data:       "test data",
 | 
					 | 
				
			||||||
	}, v1beta1.Codec, parser)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,81 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"net/http/httputil"
 | 
					 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server.
 | 
					 | 
				
			||||||
type ProxyServer struct {
 | 
					 | 
				
			||||||
	httputil.ReverseProxy
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewProxyServer creates and installs a new ProxyServer.
 | 
					 | 
				
			||||||
// It automatically registers the created ProxyServer to http.DefaultServeMux.
 | 
					 | 
				
			||||||
func NewProxyServer(filebase string, cfg *client.Config) (*ProxyServer, error) {
 | 
					 | 
				
			||||||
	prefix := cfg.Prefix
 | 
					 | 
				
			||||||
	if prefix == "" {
 | 
					 | 
				
			||||||
		prefix = "/api"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	target, err := url.Parse(singleJoiningSlash(cfg.Host, prefix))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	proxy := newProxyServer(target)
 | 
					 | 
				
			||||||
	if proxy.Transport, err = client.TransportFor(cfg); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	http.Handle("/api/", http.StripPrefix("/api/", proxy))
 | 
					 | 
				
			||||||
	http.Handle("/static/", newFileHandler("/static/", filebase))
 | 
					 | 
				
			||||||
	return proxy, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Serve starts the server (http.DefaultServeMux) on TCP port 8001, loops forever.
 | 
					 | 
				
			||||||
func (s *ProxyServer) Serve() error {
 | 
					 | 
				
			||||||
	return http.ListenAndServe(":8001", nil)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newProxyServer(target *url.URL) *ProxyServer {
 | 
					 | 
				
			||||||
	director := func(req *http.Request) {
 | 
					 | 
				
			||||||
		req.URL.Scheme = target.Scheme
 | 
					 | 
				
			||||||
		req.URL.Host = target.Host
 | 
					 | 
				
			||||||
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &ProxyServer{ReverseProxy: httputil.ReverseProxy{Director: director}}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newFileHandler(prefix, base string) http.Handler {
 | 
					 | 
				
			||||||
	return http.StripPrefix(prefix, http.FileServer(http.Dir(base)))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func singleJoiningSlash(a, b string) string {
 | 
					 | 
				
			||||||
	aslash := strings.HasSuffix(a, "/")
 | 
					 | 
				
			||||||
	bslash := strings.HasPrefix(b, "/")
 | 
					 | 
				
			||||||
	switch {
 | 
					 | 
				
			||||||
	case aslash && bslash:
 | 
					 | 
				
			||||||
		return a + b[1:]
 | 
					 | 
				
			||||||
	case !aslash && !bslash:
 | 
					 | 
				
			||||||
		return a + "/" + b
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return a + b
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,106 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"net/http/httptest"
 | 
					 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestFileServing(t *testing.T) {
 | 
					 | 
				
			||||||
	const (
 | 
					 | 
				
			||||||
		fname = "test.txt"
 | 
					 | 
				
			||||||
		data  = "This is test data"
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	dir, err := ioutil.TempDir("", "data")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("error creating tmp dir: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := ioutil.WriteFile(filepath.Join(dir, fname), []byte(data), 0755); err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("error writing tmp file: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const prefix = "/foo/"
 | 
					 | 
				
			||||||
	handler := newFileHandler(prefix, dir)
 | 
					 | 
				
			||||||
	server := httptest.NewServer(handler)
 | 
					 | 
				
			||||||
	defer server.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	url := server.URL + prefix + fname
 | 
					 | 
				
			||||||
	res, err := http.Get(url)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("http.Get(%q) error: %v", url, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer res.Body.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if res.StatusCode != http.StatusOK {
 | 
					 | 
				
			||||||
		t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, http.StatusOK)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	b, err := ioutil.ReadAll(res.Body)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("error reading resp body: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if string(b) != data {
 | 
					 | 
				
			||||||
		t.Errorf("have %q; want %q", string(b), data)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestAPIRequests(t *testing.T) {
 | 
					 | 
				
			||||||
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
					 | 
				
			||||||
		b, err := ioutil.ReadAll(r.Body)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "%s %s %s", r.Method, r.RequestURI, string(b))
 | 
					 | 
				
			||||||
	}))
 | 
					 | 
				
			||||||
	defer ts.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// httptest.NewServer should always generate a valid URL.
 | 
					 | 
				
			||||||
	target, _ := url.Parse(ts.URL)
 | 
					 | 
				
			||||||
	proxy := newProxyServer(target)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tests := []struct{ method, body string }{
 | 
					 | 
				
			||||||
		{"GET", ""},
 | 
					 | 
				
			||||||
		{"DELETE", ""},
 | 
					 | 
				
			||||||
		{"POST", "test payload"},
 | 
					 | 
				
			||||||
		{"PUT", "test payload"},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const path = "/api/test?fields=ID%3Dfoo&labels=key%3Dvalue"
 | 
					 | 
				
			||||||
	for i, tt := range tests {
 | 
					 | 
				
			||||||
		r, err := http.NewRequest(tt.method, path, strings.NewReader(tt.body))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("error creating request: %v", err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		w := httptest.NewRecorder()
 | 
					 | 
				
			||||||
		proxy.ServeHTTP(w, r)
 | 
					 | 
				
			||||||
		if w.Code != http.StatusOK {
 | 
					 | 
				
			||||||
			t.Errorf("%d: proxy.ServeHTTP w.Code = %d; want %d", i, w.Code, http.StatusOK)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		want := strings.Join([]string{tt.method, path, tt.body}, " ")
 | 
					 | 
				
			||||||
		if w.Body.String() != want {
 | 
					 | 
				
			||||||
			t.Errorf("%d: response body = %q; want %q", i, w.Body.String(), want)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,460 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"text/tabwriter"
 | 
					 | 
				
			||||||
	"text/template"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
 | 
					 | 
				
			||||||
	"github.com/ghodss/yaml"
 | 
					 | 
				
			||||||
	"github.com/golang/glog"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ResourcePrinter is an interface that knows how to print API resources.
 | 
					 | 
				
			||||||
type ResourcePrinter interface {
 | 
					 | 
				
			||||||
	// Print receives an arbitrary JSON body, formats it and prints it to a writer.
 | 
					 | 
				
			||||||
	Print([]byte, io.Writer) error
 | 
					 | 
				
			||||||
	PrintObj(runtime.Object, io.Writer) error
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IdentityPrinter is an implementation of ResourcePrinter which simply copies the body out to the output stream.
 | 
					 | 
				
			||||||
type IdentityPrinter struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Print is an implementation of ResourcePrinter.Print which simply writes the data to the Writer.
 | 
					 | 
				
			||||||
func (i *IdentityPrinter) Print(data []byte, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := w.Write(data)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
 | 
					 | 
				
			||||||
func (i *IdentityPrinter) PrintObj(obj runtime.Object, output io.Writer) error {
 | 
					 | 
				
			||||||
	data, err := latest.Codec.Encode(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return i.Print(data, output)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// YAMLPrinter is an implementation of ResourcePrinter which parsess JSON, and re-formats as YAML.
 | 
					 | 
				
			||||||
type YAMLPrinter struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Print parses the data as JSON, re-formats as YAML and prints the YAML.
 | 
					 | 
				
			||||||
func (y *YAMLPrinter) Print(data []byte, w io.Writer) error {
 | 
					 | 
				
			||||||
	var obj interface{}
 | 
					 | 
				
			||||||
	if err := json.Unmarshal(data, &obj); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	output, err := yaml.Marshal(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err = fmt.Fprint(w, string(output))
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PrintObj prints the data as YAML.
 | 
					 | 
				
			||||||
func (y *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
 | 
					 | 
				
			||||||
	output, err := yaml.Marshal(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err = fmt.Fprint(w, string(output))
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type handlerEntry struct {
 | 
					 | 
				
			||||||
	columns   []string
 | 
					 | 
				
			||||||
	printFunc reflect.Value
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide more elegant output.
 | 
					 | 
				
			||||||
type HumanReadablePrinter struct {
 | 
					 | 
				
			||||||
	handlerMap map[reflect.Type]*handlerEntry
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewHumanReadablePrinter creates a HumanReadablePrinter.
 | 
					 | 
				
			||||||
func NewHumanReadablePrinter() *HumanReadablePrinter {
 | 
					 | 
				
			||||||
	printer := &HumanReadablePrinter{make(map[reflect.Type]*handlerEntry)}
 | 
					 | 
				
			||||||
	printer.addDefaultHandlers()
 | 
					 | 
				
			||||||
	return printer
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance.
 | 
					 | 
				
			||||||
// printFunc is the function that will be called to print an object.
 | 
					 | 
				
			||||||
// It must be of the following type:
 | 
					 | 
				
			||||||
//  func printFunc(object ObjectType, w io.Writer) error
 | 
					 | 
				
			||||||
// where ObjectType is the type of the object that will be printed.
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) Handler(columns []string, printFunc interface{}) error {
 | 
					 | 
				
			||||||
	printFuncValue := reflect.ValueOf(printFunc)
 | 
					 | 
				
			||||||
	if err := h.validatePrintHandlerFunc(printFuncValue); err != nil {
 | 
					 | 
				
			||||||
		glog.Errorf("Unable to add print handler: %v", err)
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	objType := printFuncValue.Type().In(0)
 | 
					 | 
				
			||||||
	h.handlerMap[objType] = &handlerEntry{
 | 
					 | 
				
			||||||
		columns:   columns,
 | 
					 | 
				
			||||||
		printFunc: printFuncValue,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) validatePrintHandlerFunc(printFunc reflect.Value) error {
 | 
					 | 
				
			||||||
	if printFunc.Kind() != reflect.Func {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid print handler. %#v is not a function.", printFunc)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	funcType := printFunc.Type()
 | 
					 | 
				
			||||||
	if funcType.NumIn() != 2 || funcType.NumOut() != 1 {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid print handler." +
 | 
					 | 
				
			||||||
			"Must accept 2 parameters and return 1 value.")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() ||
 | 
					 | 
				
			||||||
		funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
 | 
					 | 
				
			||||||
		return fmt.Errorf("invalid print handler. The expected signature is: "+
 | 
					 | 
				
			||||||
			"func handler(obj %v, w io.Writer) error", funcType.In(0))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var podColumns = []string{"Name", "Image(s)", "Host", "Labels", "Status"}
 | 
					 | 
				
			||||||
var replicationControllerColumns = []string{"Name", "Image(s)", "Selector", "Replicas"}
 | 
					 | 
				
			||||||
var serviceColumns = []string{"Name", "Labels", "Selector", "IP", "Port"}
 | 
					 | 
				
			||||||
var minionColumns = []string{"Minion identifier", "Labels"}
 | 
					 | 
				
			||||||
var statusColumns = []string{"Status"}
 | 
					 | 
				
			||||||
var eventColumns = []string{"Name", "Kind", "Reason", "Message"}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// addDefaultHandlers adds print handlers for default Kubernetes types.
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) addDefaultHandlers() {
 | 
					 | 
				
			||||||
	h.Handler(podColumns, printPod)
 | 
					 | 
				
			||||||
	h.Handler(podColumns, printPodList)
 | 
					 | 
				
			||||||
	h.Handler(replicationControllerColumns, printReplicationController)
 | 
					 | 
				
			||||||
	h.Handler(replicationControllerColumns, printReplicationControllerList)
 | 
					 | 
				
			||||||
	h.Handler(serviceColumns, printService)
 | 
					 | 
				
			||||||
	h.Handler(serviceColumns, printServiceList)
 | 
					 | 
				
			||||||
	h.Handler(minionColumns, printMinion)
 | 
					 | 
				
			||||||
	h.Handler(minionColumns, printMinionList)
 | 
					 | 
				
			||||||
	h.Handler(statusColumns, printStatus)
 | 
					 | 
				
			||||||
	h.Handler(eventColumns, printEvent)
 | 
					 | 
				
			||||||
	h.Handler(eventColumns, printEventList)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "Unknown object: %s", string(data))
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error {
 | 
					 | 
				
			||||||
	if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var lines []string
 | 
					 | 
				
			||||||
	for _ = range columnNames {
 | 
					 | 
				
			||||||
		lines = append(lines, "----------")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s\n", strings.Join(lines, "\t"))
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func makeImageList(manifest api.PodSpec) string {
 | 
					 | 
				
			||||||
	var images []string
 | 
					 | 
				
			||||||
	for _, container := range manifest.Containers {
 | 
					 | 
				
			||||||
		images = append(images, container.Image)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return strings.Join(images, ",")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func makeImageListPodSpec(spec api.PodSpec) string {
 | 
					 | 
				
			||||||
	var images []string
 | 
					 | 
				
			||||||
	for _, container := range spec.Containers {
 | 
					 | 
				
			||||||
		images = append(images, container.Image)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return strings.Join(images, ",")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func podHostString(host, ip string) string {
 | 
					 | 
				
			||||||
	if host == "" && ip == "" {
 | 
					 | 
				
			||||||
		return "<unassigned>"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return host + "/" + ip
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printPod(pod *api.Pod, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
 | 
					 | 
				
			||||||
		pod.Name, makeImageList(pod.Spec),
 | 
					 | 
				
			||||||
		podHostString(pod.Status.Host, pod.Status.HostIP),
 | 
					 | 
				
			||||||
		labels.Set(pod.Labels), pod.Status.Phase)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printPodList(podList *api.PodList, w io.Writer) error {
 | 
					 | 
				
			||||||
	for _, pod := range podList.Items {
 | 
					 | 
				
			||||||
		if err := printPod(&pod, w); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printReplicationController(controller *api.ReplicationController, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
 | 
					 | 
				
			||||||
		controller.Name, makeImageListPodSpec(controller.Spec.Template.Spec),
 | 
					 | 
				
			||||||
		labels.Set(controller.Spec.Selector), controller.Spec.Replicas)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer) error {
 | 
					 | 
				
			||||||
	for _, controller := range list.Items {
 | 
					 | 
				
			||||||
		if err := printReplicationController(&controller, w); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printService(svc *api.Service, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n", svc.Name, labels.Set(svc.Labels),
 | 
					 | 
				
			||||||
		labels.Set(svc.Spec.Selector), svc.Spec.PortalIP, svc.Spec.Port)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printServiceList(list *api.ServiceList, w io.Writer) error {
 | 
					 | 
				
			||||||
	for _, svc := range list.Items {
 | 
					 | 
				
			||||||
		if err := printService(&svc, w); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printMinion(minion *api.Node, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s\t%s\n", minion.Name, labels.Set(minion.Labels))
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printMinionList(list *api.NodeList, w io.Writer) error {
 | 
					 | 
				
			||||||
	for _, minion := range list.Items {
 | 
					 | 
				
			||||||
		if err := printMinion(&minion, w); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printStatus(status *api.Status, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%v\n", status.Status)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printEvent(event *api.Event, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(
 | 
					 | 
				
			||||||
		w, "%s\t%s\t%s\t%s\n",
 | 
					 | 
				
			||||||
		event.InvolvedObject.Name,
 | 
					 | 
				
			||||||
		event.InvolvedObject.Kind,
 | 
					 | 
				
			||||||
		event.Reason,
 | 
					 | 
				
			||||||
		event.Message,
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func printEventList(list *api.EventList, w io.Writer) error {
 | 
					 | 
				
			||||||
	for i := range list.Items {
 | 
					 | 
				
			||||||
		if err := printEvent(&list.Items[i], w); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Print parses the data as JSON, then prints the parsed data in a human-friendly
 | 
					 | 
				
			||||||
// format according to the type of the data.
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error {
 | 
					 | 
				
			||||||
	var mapObj map[string]runtime.Object
 | 
					 | 
				
			||||||
	if err := json.Unmarshal([]byte(data), &mapObj); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, contains := mapObj["kind"]; !contains {
 | 
					 | 
				
			||||||
		return fmt.Errorf("unexpected object with no 'kind' field: %s", data)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	obj, err := latest.Codec.Decode(data)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return h.PrintObj(obj, output)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PrintObj prints the obj in a human-friendly format according to the type of the obj.
 | 
					 | 
				
			||||||
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
 | 
					 | 
				
			||||||
	w := tabwriter.NewWriter(output, 20, 5, 3, ' ', 0)
 | 
					 | 
				
			||||||
	defer w.Flush()
 | 
					 | 
				
			||||||
	if handler := h.handlerMap[reflect.TypeOf(obj)]; handler != nil {
 | 
					 | 
				
			||||||
		h.printHeader(handler.columns, w)
 | 
					 | 
				
			||||||
		args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w)}
 | 
					 | 
				
			||||||
		resultValue := handler.printFunc.Call(args)[0]
 | 
					 | 
				
			||||||
		if resultValue.IsNil() {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			return resultValue.Interface().(error)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return fmt.Errorf("unknown type %#v", obj)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
 | 
					 | 
				
			||||||
type TemplatePrinter struct {
 | 
					 | 
				
			||||||
	rawTemplate string
 | 
					 | 
				
			||||||
	template    *template.Template
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
 | 
					 | 
				
			||||||
	t, err := template.New("output").
 | 
					 | 
				
			||||||
		Funcs(template.FuncMap{"exists": exists}).
 | 
					 | 
				
			||||||
		Parse(string(tmpl))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &TemplatePrinter{string(tmpl), t}, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Print parses the data as JSON, and re-formats it with the Go Template.
 | 
					 | 
				
			||||||
func (t *TemplatePrinter) Print(data []byte, w io.Writer) error {
 | 
					 | 
				
			||||||
	out := map[string]interface{}{}
 | 
					 | 
				
			||||||
	err := json.Unmarshal(data, &out)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := t.safeExecute(w, out); err != nil {
 | 
					 | 
				
			||||||
		// It is way easier to debug this stuff when it shows up in
 | 
					 | 
				
			||||||
		// stdout instead of just stdin. So in addition to returning
 | 
					 | 
				
			||||||
		// a nice error, also print useful stuff with the writer.
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "Error executing template: %v\n", err)
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "template was:\n%v\n", t.rawTemplate)
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "raw data was:\n%v\n", string(data))
 | 
					 | 
				
			||||||
		fmt.Fprintf(w, "object given to template engine was:\n%+v\n", out)
 | 
					 | 
				
			||||||
		return fmt.Errorf("error executing template '%v': '%v'\n----data----\n%#v\n", t.rawTemplate, err, out)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PrintObj formats the obj with the Go Template.
 | 
					 | 
				
			||||||
func (t *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
 | 
					 | 
				
			||||||
	data, err := latest.Codec.Encode(obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return t.Print(data, w)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// safeExecute tries to execute the template, but catches panics and returns an error
 | 
					 | 
				
			||||||
// should the template engine panic.
 | 
					 | 
				
			||||||
func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
 | 
					 | 
				
			||||||
	var panicErr error
 | 
					 | 
				
			||||||
	// Sorry for the double anonymous function. There's probably a clever way
 | 
					 | 
				
			||||||
	// to do this that has the defer'd func setting the value to be returned, but
 | 
					 | 
				
			||||||
	// that would be even less obvious.
 | 
					 | 
				
			||||||
	retErr := func() error {
 | 
					 | 
				
			||||||
		defer func() {
 | 
					 | 
				
			||||||
			if x := recover(); x != nil {
 | 
					 | 
				
			||||||
				panicErr = fmt.Errorf("caught panic: %+v", x)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}()
 | 
					 | 
				
			||||||
		return p.template.Execute(w, obj)
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
	if panicErr != nil {
 | 
					 | 
				
			||||||
		return panicErr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return retErr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// exists returns true if it would be possible to call the index function
 | 
					 | 
				
			||||||
// with these arguments.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// TODO: how to document this for users?
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// index returns the result of indexing its first argument by the following
 | 
					 | 
				
			||||||
// arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
 | 
					 | 
				
			||||||
// indexed item must be a map, slice, or array.
 | 
					 | 
				
			||||||
func exists(item interface{}, indices ...interface{}) bool {
 | 
					 | 
				
			||||||
	v := reflect.ValueOf(item)
 | 
					 | 
				
			||||||
	for _, i := range indices {
 | 
					 | 
				
			||||||
		index := reflect.ValueOf(i)
 | 
					 | 
				
			||||||
		var isNil bool
 | 
					 | 
				
			||||||
		if v, isNil = indirect(v); isNil {
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		switch v.Kind() {
 | 
					 | 
				
			||||||
		case reflect.Array, reflect.Slice, reflect.String:
 | 
					 | 
				
			||||||
			var x int64
 | 
					 | 
				
			||||||
			switch index.Kind() {
 | 
					 | 
				
			||||||
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
					 | 
				
			||||||
				x = index.Int()
 | 
					 | 
				
			||||||
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | 
					 | 
				
			||||||
				x = int64(index.Uint())
 | 
					 | 
				
			||||||
			default:
 | 
					 | 
				
			||||||
				return false
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if x < 0 || x >= int64(v.Len()) {
 | 
					 | 
				
			||||||
				return false
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			v = v.Index(int(x))
 | 
					 | 
				
			||||||
		case reflect.Map:
 | 
					 | 
				
			||||||
			if !index.IsValid() {
 | 
					 | 
				
			||||||
				index = reflect.Zero(v.Type().Key())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if !index.Type().AssignableTo(v.Type().Key()) {
 | 
					 | 
				
			||||||
				return false
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if x := v.MapIndex(index); x.IsValid() {
 | 
					 | 
				
			||||||
				v = x
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				v = reflect.Zero(v.Type().Elem())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if _, isNil := indirect(v); isNil {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// stolen from text/template
 | 
					 | 
				
			||||||
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
 | 
					 | 
				
			||||||
// We indirect through pointers and empty interfaces (only) because
 | 
					 | 
				
			||||||
// non-empty interfaces have methods we might need.
 | 
					 | 
				
			||||||
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
 | 
					 | 
				
			||||||
	for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
 | 
					 | 
				
			||||||
		if v.IsNil() {
 | 
					 | 
				
			||||||
			return v, true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return v, false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,313 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/ghodss/yaml"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestYAMLPrinterPrint(t *testing.T) {
 | 
					 | 
				
			||||||
	type testStruct struct {
 | 
					 | 
				
			||||||
		Key        string         `json:"Key"`
 | 
					 | 
				
			||||||
		Map        map[string]int `json:"Map"`
 | 
					 | 
				
			||||||
		StringList []string       `json:"StringList"`
 | 
					 | 
				
			||||||
		IntList    []int          `json:"IntList"`
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	testData := testStruct{
 | 
					 | 
				
			||||||
		"testValue",
 | 
					 | 
				
			||||||
		map[string]int{"TestSubkey": 1},
 | 
					 | 
				
			||||||
		[]string{"a", "b", "c"},
 | 
					 | 
				
			||||||
		[]int{1, 2, 3},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	printer := &YAMLPrinter{}
 | 
					 | 
				
			||||||
	buf := bytes.NewBuffer([]byte{})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err := printer.Print([]byte("invalidJSON"), buf)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Error("Error: didn't fail on invalid JSON data")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	jTestData, err := json.Marshal(&testData)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatal("Unexpected error: couldn't marshal test data")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	err = printer.Print(jTestData, buf)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var poutput testStruct
 | 
					 | 
				
			||||||
	err = yaml.Unmarshal(buf.Bytes(), &poutput)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatal(err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !reflect.DeepEqual(testData, poutput) {
 | 
					 | 
				
			||||||
		t.Errorf("Test data and unmarshaled data are not equal: %#v vs %#v", poutput, testData)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	obj := &api.Pod{
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
					 | 
				
			||||||
		Spec: api.PodSpec{
 | 
					 | 
				
			||||||
			RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
 | 
					 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buf.Reset()
 | 
					 | 
				
			||||||
	printer.PrintObj(obj, buf)
 | 
					 | 
				
			||||||
	var objOut api.Pod
 | 
					 | 
				
			||||||
	err = yaml.Unmarshal([]byte(buf.String()), &objOut)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %#v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !reflect.DeepEqual(obj, &objOut) {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected inequality: %#v vs %#v", obj, &objOut)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestIdentityPrinter(t *testing.T) {
 | 
					 | 
				
			||||||
	printer := &IdentityPrinter{}
 | 
					 | 
				
			||||||
	buff := bytes.NewBuffer([]byte{})
 | 
					 | 
				
			||||||
	str := "this is a test string"
 | 
					 | 
				
			||||||
	printer.Print([]byte(str), buff)
 | 
					 | 
				
			||||||
	if buff.String() != str {
 | 
					 | 
				
			||||||
		t.Errorf("Bytes are not equal: %s vs %s", str, buff.String())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	obj := &api.Pod{
 | 
					 | 
				
			||||||
		ObjectMeta: api.ObjectMeta{Name: "foo"},
 | 
					 | 
				
			||||||
		Spec: api.PodSpec{
 | 
					 | 
				
			||||||
			RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
 | 
					 | 
				
			||||||
			DNSPolicy:     api.DNSClusterFirst,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buff.Reset()
 | 
					 | 
				
			||||||
	printer.PrintObj(obj, buff)
 | 
					 | 
				
			||||||
	objOut, err := latest.Codec.Decode([]byte(buff.String()))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %#v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !reflect.DeepEqual(obj, objOut) {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected inequality: %#v vs %#v", obj, objOut)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type TestPrintType struct {
 | 
					 | 
				
			||||||
	Data string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (*TestPrintType) IsAnAPIObject() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type TestUnknownType struct{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (*TestUnknownType) IsAnAPIObject() {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func PrintCustomType(obj *TestPrintType, w io.Writer) error {
 | 
					 | 
				
			||||||
	_, err := fmt.Fprintf(w, "%s", obj.Data)
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ErrorPrintHandler(obj *TestPrintType, w io.Writer) error {
 | 
					 | 
				
			||||||
	return fmt.Errorf("ErrorPrintHandler error")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestCustomTypePrinting(t *testing.T) {
 | 
					 | 
				
			||||||
	columns := []string{"Data"}
 | 
					 | 
				
			||||||
	printer := NewHumanReadablePrinter()
 | 
					 | 
				
			||||||
	printer.Handler(columns, PrintCustomType)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	obj := TestPrintType{"test object"}
 | 
					 | 
				
			||||||
	buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
	err := printer.PrintObj(&obj, buffer)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("An error occurred printing the custom type: %#v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	expectedOutput := "Data\n----------\ntest object"
 | 
					 | 
				
			||||||
	if buffer.String() != expectedOutput {
 | 
					 | 
				
			||||||
		t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestPrintHandlerError(t *testing.T) {
 | 
					 | 
				
			||||||
	columns := []string{"Data"}
 | 
					 | 
				
			||||||
	printer := NewHumanReadablePrinter()
 | 
					 | 
				
			||||||
	printer.Handler(columns, ErrorPrintHandler)
 | 
					 | 
				
			||||||
	obj := TestPrintType{"test object"}
 | 
					 | 
				
			||||||
	buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
	err := printer.PrintObj(&obj, buffer)
 | 
					 | 
				
			||||||
	if err == nil || err.Error() != "ErrorPrintHandler error" {
 | 
					 | 
				
			||||||
		t.Errorf("Did not get the expected error: %#v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestUnknownTypePrinting(t *testing.T) {
 | 
					 | 
				
			||||||
	printer := NewHumanReadablePrinter()
 | 
					 | 
				
			||||||
	buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
	err := printer.PrintObj(&TestUnknownType{}, buffer)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Errorf("An error was expected from printing unknown type")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestTemplateEmitsVersionedObjects(t *testing.T) {
 | 
					 | 
				
			||||||
	// kind is always blank in memory and set on the wire
 | 
					 | 
				
			||||||
	printer, err := NewTemplatePrinter([]byte(`{{.kind}}`))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("tmpl fail: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
	err = printer.PrintObj(&api.Pod{}, buffer)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("print fail: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if e, a := "Pod", string(buffer.Bytes()); e != a {
 | 
					 | 
				
			||||||
		t.Errorf("Expected %v, got %v", e, a)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestTemplatePanic(t *testing.T) {
 | 
					 | 
				
			||||||
	tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
 | 
					 | 
				
			||||||
	printer, err := NewTemplatePrinter([]byte(tmpl))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("tmpl fail: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
	err = printer.PrintObj(&api.Pod{}, buffer)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		t.Fatalf("expected that template to crash")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if buffer.String() == "" {
 | 
					 | 
				
			||||||
		t.Errorf("no debugging info was printed")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestTemplateStrings(t *testing.T) {
 | 
					 | 
				
			||||||
	// This unit tests the "exists" function as well as the template from update.sh
 | 
					 | 
				
			||||||
	table := map[string]struct {
 | 
					 | 
				
			||||||
		pod    api.Pod
 | 
					 | 
				
			||||||
		expect string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		"nilInfo":   {api.Pod{}, "false"},
 | 
					 | 
				
			||||||
		"emptyInfo": {api.Pod{Status: api.PodStatus{Info: api.PodInfo{}}}, "false"},
 | 
					 | 
				
			||||||
		"fooExists": {
 | 
					 | 
				
			||||||
			api.Pod{
 | 
					 | 
				
			||||||
				Status: api.PodStatus{
 | 
					 | 
				
			||||||
					Info: api.PodInfo{"foo": api.ContainerStatus{}},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"barExists": {
 | 
					 | 
				
			||||||
			api.Pod{
 | 
					 | 
				
			||||||
				Status: api.PodStatus{
 | 
					 | 
				
			||||||
					Info: api.PodInfo{"bar": api.ContainerStatus{}},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"bothExist": {
 | 
					 | 
				
			||||||
			api.Pod{
 | 
					 | 
				
			||||||
				Status: api.PodStatus{
 | 
					 | 
				
			||||||
					Info: api.PodInfo{
 | 
					 | 
				
			||||||
						"foo": api.ContainerStatus{},
 | 
					 | 
				
			||||||
						"bar": api.ContainerStatus{},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"oneValid": {
 | 
					 | 
				
			||||||
			api.Pod{
 | 
					 | 
				
			||||||
				Status: api.PodStatus{
 | 
					 | 
				
			||||||
					Info: api.PodInfo{
 | 
					 | 
				
			||||||
						"foo": api.ContainerStatus{},
 | 
					 | 
				
			||||||
						"bar": api.ContainerStatus{
 | 
					 | 
				
			||||||
							State: api.ContainerState{
 | 
					 | 
				
			||||||
								Running: &api.ContainerStateRunning{
 | 
					 | 
				
			||||||
									StartedAt: util.Time{},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"false",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"bothValid": {
 | 
					 | 
				
			||||||
			api.Pod{
 | 
					 | 
				
			||||||
				Status: api.PodStatus{
 | 
					 | 
				
			||||||
					Info: api.PodInfo{
 | 
					 | 
				
			||||||
						"foo": api.ContainerStatus{
 | 
					 | 
				
			||||||
							State: api.ContainerState{
 | 
					 | 
				
			||||||
								Running: &api.ContainerStateRunning{
 | 
					 | 
				
			||||||
									StartedAt: util.Time{},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
						"bar": api.ContainerStatus{
 | 
					 | 
				
			||||||
							State: api.ContainerState{
 | 
					 | 
				
			||||||
								Running: &api.ContainerStateRunning{
 | 
					 | 
				
			||||||
									StartedAt: util.Time{},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			"true",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The point of this test is to verify that the below template works. If you change this
 | 
					 | 
				
			||||||
	// template, you need to update hack/e2e-suite/update.sh.
 | 
					 | 
				
			||||||
	tmpl :=
 | 
					 | 
				
			||||||
		`{{and (exists . "currentState" "info" "foo" "state" "running") (exists . "currentState" "info" "bar" "state" "running")}}`
 | 
					 | 
				
			||||||
	useThisToDebug := `
 | 
					 | 
				
			||||||
a: {{exists . "currentState"}}
 | 
					 | 
				
			||||||
b: {{exists . "currentState" "info"}}
 | 
					 | 
				
			||||||
c: {{exists . "currentState" "info" "foo"}}
 | 
					 | 
				
			||||||
d: {{exists . "currentState" "info" "foo" "state"}}
 | 
					 | 
				
			||||||
e: {{exists . "currentState" "info" "foo" "state" "running"}}
 | 
					 | 
				
			||||||
f: {{exists . "currentState" "info" "foo" "state" "running" "startedAt"}}`
 | 
					 | 
				
			||||||
	_ = useThisToDebug // don't complain about unused var
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	printer, err := NewTemplatePrinter([]byte(tmpl))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("tmpl fail: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for name, item := range table {
 | 
					 | 
				
			||||||
		buffer := &bytes.Buffer{}
 | 
					 | 
				
			||||||
		err = printer.PrintObj(&item.pod, buffer)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("%v: unexpected err: %v", name, err)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if e, a := item.expect, buffer.String(); e != a {
 | 
					 | 
				
			||||||
			t.Errorf("%v: expected %v, got %v", name, e, a)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,51 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
limitations under the License.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package kubecfg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ValidateObject(data []byte, c *client.Client) error {
 | 
					 | 
				
			||||||
	var obj interface{}
 | 
					 | 
				
			||||||
	err := json.Unmarshal(data, &obj)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	apiVersion, found := obj.(map[string]interface{})["apiVersion"]
 | 
					 | 
				
			||||||
	if !found {
 | 
					 | 
				
			||||||
		return fmt.Errorf("couldn't find apiVersion in object")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	schemaData, err := c.RESTClient.Get().
 | 
					 | 
				
			||||||
		AbsPath("/swaggerapi/api").
 | 
					 | 
				
			||||||
		Prefix(apiVersion.(string)).
 | 
					 | 
				
			||||||
		Do().
 | 
					 | 
				
			||||||
		Raw()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return schema.ValidateBytes(data)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user