Merge pull request #2258 from fabianofranz/expose_cobra_helpers

Extract several helpers from pkg/kubectl/cmd/cmd.go
pull/6/head
bgrant0607 2014-11-10 10:05:21 -08:00
commit 91b83f54db
11 changed files with 170 additions and 144 deletions

View File

@ -19,11 +19,7 @@ package cmd
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
@ -128,66 +124,13 @@ func runHelp(cmd *cobra.Command, args []string) {
cmd.Help()
}
func getFlagString(cmd *cobra.Command, flag string) string {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
return f.Value.String()
}
func getFlagBool(cmd *cobra.Command, flag string) bool {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
// Caseless compare.
if strings.ToLower(f.Value.String()) == "true" {
return true
}
return false
}
// Returns nil if the flag wasn't set.
func getFlagBoolPtr(cmd *cobra.Command, flag string) *bool {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
// Check if flag was not set at all.
if !f.Changed && f.DefValue == f.Value.String() {
return nil
}
var ret bool
// Caseless compare.
if strings.ToLower(f.Value.String()) == "true" {
ret = true
} else {
ret = false
}
return &ret
}
// Assumes the flag has a default value.
func getFlagInt(cmd *cobra.Command, flag string) int {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
v, err := strconv.Atoi(f.Value.String())
// This is likely not a sufficiently friendly error message, but cobra
// should prevent non-integer values from reaching here.
checkErr(err)
return v
}
func getKubeNamespace(cmd *cobra.Command) string {
result := api.NamespaceDefault
if ns := getFlagString(cmd, "namespace"); len(ns) > 0 {
if ns := GetFlagString(cmd, "namespace"); len(ns) > 0 {
result = ns
glog.V(2).Infof("Using namespace from -ns flag")
} else {
nsPath := getFlagString(cmd, "ns-path")
nsPath := GetFlagString(cmd, "ns-path")
nsInfo, err := kubectl.LoadNamespaceInfo(nsPath)
if err != nil {
glog.Fatalf("Error loading current namespace: %v", err)
@ -202,7 +145,7 @@ func getKubeNamespace(cmd *cobra.Command) string {
// user explicitly provided on the command line, or false if no
// such namespace was specified.
func getExplicitKubeNamespace(cmd *cobra.Command) (string, bool) {
if ns := getFlagString(cmd, "namespace"); len(ns) > 0 {
if ns := GetFlagString(cmd, "namespace"); len(ns) > 0 {
return ns, true
}
// TODO: determine when --ns-path is set but equal to the default
@ -214,7 +157,7 @@ func getKubeConfig(cmd *cobra.Command) *client.Config {
config := &client.Config{}
var host string
if hostFlag := getFlagString(cmd, "server"); len(hostFlag) > 0 {
if hostFlag := GetFlagString(cmd, "server"); len(hostFlag) > 0 {
host = hostFlag
glog.V(2).Infof("Using server from -s flag: %s", host)
} else if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
@ -231,7 +174,7 @@ func getKubeConfig(cmd *cobra.Command) *client.Config {
// Get the values from the file on disk (or from the user at the
// command line). Override them with the command line parameters, if
// provided.
authPath := getFlagString(cmd, "auth-path")
authPath := GetFlagString(cmd, "auth-path")
authInfo, err := kubectl.LoadAuthInfo(authPath, os.Stdin)
if err != nil {
glog.Fatalf("Error loading auth: %v", err)
@ -240,13 +183,13 @@ func getKubeConfig(cmd *cobra.Command) *client.Config {
config.Username = authInfo.User
config.Password = authInfo.Password
// First priority is flag, then file.
config.CAFile = firstNonEmptyString(getFlagString(cmd, "certificate-authority"), authInfo.CAFile)
config.CertFile = firstNonEmptyString(getFlagString(cmd, "client-certificate"), authInfo.CertFile)
config.KeyFile = firstNonEmptyString(getFlagString(cmd, "client-key"), authInfo.KeyFile)
config.CAFile = FirstNonEmptyString(GetFlagString(cmd, "certificate-authority"), authInfo.CAFile)
config.CertFile = FirstNonEmptyString(GetFlagString(cmd, "client-certificate"), authInfo.CertFile)
config.KeyFile = FirstNonEmptyString(GetFlagString(cmd, "client-key"), authInfo.KeyFile)
config.BearerToken = authInfo.BearerToken
// For config.Insecure, the command line ALWAYS overrides the authInfo
// file, regardless of its setting.
if insecureFlag := getFlagBoolPtr(cmd, "insecure-skip-tls-verify"); insecureFlag != nil {
if insecureFlag := GetFlagBoolPtr(cmd, "insecure-skip-tls-verify"); insecureFlag != nil {
config.Insecure = *insecureFlag
} else if authInfo.Insecure != nil {
config.Insecure = *authInfo.Insecure
@ -254,7 +197,7 @@ func getKubeConfig(cmd *cobra.Command) *client.Config {
}
// The API version (e.g. v1beta1), not the binary version.
config.Version = getFlagString(cmd, "api-version")
config.Version = GetFlagString(cmd, "api-version")
return config
}
@ -263,7 +206,7 @@ func getKubeClient(cmd *cobra.Command) *client.Client {
config := getKubeConfig(cmd)
// The binary version.
matchVersion := getFlagBool(cmd, "match-server-version")
matchVersion := GetFlagBool(cmd, "match-server-version")
c, err := kubectl.GetKubeClient(config, matchVersion)
if err != nil {
@ -271,64 +214,3 @@ func getKubeClient(cmd *cobra.Command) *client.Client {
}
return c
}
// Returns the first non-empty string out of the ones provided. If all
// strings are empty, returns an empty string.
func firstNonEmptyString(args ...string) string {
for _, s := range args {
if len(s) > 0 {
return s
}
}
return ""
}
// readConfigData reads the bytes from the specified filesytem or network
// location or from stdin if location == "-".
func readConfigData(location string) ([]byte, error) {
if len(location) == 0 {
return nil, fmt.Errorf("Location given but empty")
}
if location == "-" {
// Read from stdin.
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, fmt.Errorf(`Read from stdin specified ("-") but no data found`)
}
return data, nil
}
// Use the location as a file path or URL.
return readConfigDataFromLocation(location)
}
func readConfigDataFromLocation(location string) ([]byte, error) {
// we look for http:// or https:// to determine if valid URL, otherwise do normal file IO
if strings.Index(location, "http://") == 0 || strings.Index(location, "https://") == 0 {
resp, err := http.Get(location)
if err != nil {
return nil, fmt.Errorf("Unable to access URL %s: %v\n", location, err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Unable to read URL, server reported %d %s", resp.StatusCode, resp.Status)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Unable to read URL %s: %v\n", location, err)
}
return data, nil
} else {
data, err := ioutil.ReadFile(location)
if err != nil {
return nil, fmt.Errorf("Unable to read %s: %v\n", location, err)
}
return data, nil
}
}

View File

@ -39,7 +39,7 @@ Examples:
$ cat pod.json | kubectl create -f -
<create a pod based on the json passed into stdin>`,
Run: func(cmd *cobra.Command, args []string) {
filename := getFlagString(cmd, "filename")
filename := GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must specify filename to create")
}

View File

@ -93,12 +93,12 @@ Examples:
return getKubeClient(cmd).RESTClient, nil
}
filename := getFlagString(cmd, "filename")
filename := GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must pass a filename to update")
}
data, err := readConfigData(filename)
data, err := ReadConfigData(filename)
checkErr(err)
items, errs := DataToObjects(mapper, typer, data)

View File

@ -49,7 +49,7 @@ Examples:
$ kubectl delete pod 1234-56-7890-234234-456456
<delete a pod with ID 1234-56-7890-234234-456456>`,
Run: func(cmd *cobra.Command, args []string) {
filename := getFlagString(cmd, "filename")
filename := GetFlagString(cmd, "filename")
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, f.Typer, f.Mapper)
client, err := f.Client(cmd, mapping)
checkErr(err)

View File

@ -49,16 +49,16 @@ Examples:
Run: func(cmd *cobra.Command, args []string) {
mapping, namespace, name := ResourceOrTypeFromArgs(cmd, args, f.Mapper)
selector := getFlagString(cmd, "selector")
selector := GetFlagString(cmd, "selector")
labels, err := labels.ParseSelector(selector)
checkErr(err)
client, err := f.Client(cmd, mapping)
checkErr(err)
outputFormat := getFlagString(cmd, "output")
templateFile := getFlagString(cmd, "template")
defaultPrinter, err := f.Printer(cmd, mapping, getFlagBool(cmd, "no-headers"))
outputFormat := GetFlagString(cmd, "output")
templateFile := GetFlagString(cmd, "template")
defaultPrinter, err := f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
checkErr(err)
printer, versioned, err := kubectl.GetPrinter(outputFormat, templateFile, defaultPrinter)
@ -68,7 +68,7 @@ Examples:
checkErr(err)
if versioned {
outputVersion := getFlagString(cmd, "output-version")
outputVersion := GetFlagString(cmd, "output-version")
if len(outputVersion) == 0 {
outputVersion = mapping.APIVersion
}

143
pkg/kubectl/cmd/helpers.go Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
func GetFlagString(cmd *cobra.Command, flag string) string {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
return f.Value.String()
}
func GetFlagBool(cmd *cobra.Command, flag string) bool {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
// Caseless compare.
if strings.ToLower(f.Value.String()) == "true" {
return true
}
return false
}
// Returns nil if the flag wasn't set.
func GetFlagBoolPtr(cmd *cobra.Command, flag string) *bool {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
// Check if flag was not set at all.
if !f.Changed && f.DefValue == f.Value.String() {
return nil
}
var ret bool
// Caseless compare.
if strings.ToLower(f.Value.String()) == "true" {
ret = true
} else {
ret = false
}
return &ret
}
// Assumes the flag has a default value.
func GetFlagInt(cmd *cobra.Command, flag string) int {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
v, err := strconv.Atoi(f.Value.String())
// This is likely not a sufficiently friendly error message, but cobra
// should prevent non-integer values from reaching here.
checkErr(err)
return v
}
// Returns the first non-empty string out of the ones provided. If all
// strings are empty, returns an empty string.
func FirstNonEmptyString(args ...string) string {
for _, s := range args {
if len(s) > 0 {
return s
}
}
return ""
}
// ReadConfigData reads the bytes from the specified filesytem or network
// location or from stdin if location == "-".
func ReadConfigData(location string) ([]byte, error) {
if len(location) == 0 {
return nil, fmt.Errorf("Location given but empty")
}
if location == "-" {
// Read from stdin.
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, fmt.Errorf(`Read from stdin specified ("-") but no data found`)
}
return data, nil
}
// Use the location as a file path or URL.
return ReadConfigDataFromLocation(location)
}
func ReadConfigDataFromLocation(location string) ([]byte, error) {
// we look for http:// or https:// to determine if valid URL, otherwise do normal file IO
if strings.Index(location, "http://") == 0 || strings.Index(location, "https://") == 0 {
resp, err := http.Get(location)
if err != nil {
return nil, fmt.Errorf("Unable to access URL %s: %v\n", location, err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Unable to read URL, server reported %d %s", resp.StatusCode, resp.Status)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Unable to read URL %s: %v\n", location, err)
}
return data, nil
} else {
data, err := ioutil.ReadFile(location)
if err != nil {
return nil, fmt.Errorf("Unable to read %s: %v\n", location, err)
}
return data, nil
}
}

View File

@ -20,6 +20,7 @@ import (
"io"
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/spf13/cobra"
)
@ -39,7 +40,7 @@ Examples:
$ kubectl namespace other
Set current namespace to other`,
Run: func(cmd *cobra.Command, args []string) {
nsPath := getFlagString(cmd, "ns-path")
nsPath := GetFlagString(cmd, "ns-path")
var err error
var ns *kubectl.NamespaceInfo
switch len(args) {

View File

@ -30,9 +30,9 @@ func NewCmdProxy(out io.Writer) *cobra.Command {
Short: "Run a proxy to the Kubernetes API server",
Long: `Run a proxy to the Kubernetes API server.`,
Run: func(cmd *cobra.Command, args []string) {
port := getFlagInt(cmd, "port")
port := GetFlagInt(cmd, "port")
glog.Infof("Starting to serve on localhost:%d", port)
server, err := kubectl.NewProxyServer(getFlagString(cmd, "www"), getKubeConfig(cmd), port)
server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), getKubeConfig(cmd), port)
checkErr(err)
glog.Fatal(server.Serve())
},

View File

@ -120,7 +120,7 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
// resolve to a known type an error is returned. The returned mapping can be used to determine
// the correct REST endpoint to modify this resource with.
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
configData, err := readConfigData(filename)
configData, err := ReadConfigData(filename)
checkErr(err)
data = configData

View File

@ -39,7 +39,7 @@ Examples:
$ cat pod.json | kubectl update -f -
<update a pod based on the json passed into stdin>`,
Run: func(cmd *cobra.Command, args []string) {
filename := getFlagString(cmd, "filename")
filename := GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must specify filename to update")
}

View File

@ -28,7 +28,7 @@ func NewCmdVersion(out io.Writer) *cobra.Command {
Use: "version",
Short: "Print version of client and server",
Run: func(cmd *cobra.Command, args []string) {
if getFlagBool(cmd, "client") {
if GetFlagBool(cmd, "client") {
kubectl.GetClientVersion(out)
} else {
kubectl.GetVersion(out, getKubeClient(cmd))