mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-08 20:50:24 +00:00
kubectl: kubecfg rewrite for better modularity and improved UX
This commit is contained in:
228
pkg/kubectl/kubectl.go
Normal file
228
pkg/kubectl/kubectl.go
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
|
||||
package kubectl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||
"gopkg.in/v1/yaml"
|
||||
)
|
||||
|
||||
var apiVersionToUse = "v1beta1"
|
||||
|
||||
func GetKubeClient(config *client.Config, matchVersion bool) (*client.Client, error) {
|
||||
// TODO: get the namespace context when kubectl ns is completed
|
||||
c, err := client.New(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if matchVersion {
|
||||
clientVersion := version.Get()
|
||||
serverVersion, err := c.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't read version from server: %v\n", err)
|
||||
}
|
||||
if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
|
||||
return nil, fmt.Errorf("Server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type AuthInfo struct {
|
||||
User string
|
||||
Password string
|
||||
CAFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
Insecure *bool
|
||||
}
|
||||
|
||||
// LoadAuthInfo parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
|
||||
func LoadAuthInfo(path string, r io.Reader) (*AuthInfo, error) {
|
||||
var auth AuthInfo
|
||||
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
|
||||
}
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &auth, err
|
||||
}
|
||||
|
||||
func promptForString(field string, r io.Reader) string {
|
||||
fmt.Printf("Please enter %s: ", field)
|
||||
var result string
|
||||
fmt.Fscan(r, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
func CreateResource(resource, id string) ([]byte, error) {
|
||||
kind, err := resolveResource(resolveToKind, resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := fmt.Sprintf(`{"kind": "%s", "apiVersion": "%s", "id": "%s"}`, kind, apiVersionToUse, id)
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
// TODO Move to labels package.
|
||||
func formatLabels(labelMap map[string]string) string {
|
||||
l := labels.Set(labelMap).String()
|
||||
if l == "" {
|
||||
l = "<none>"
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func makeImageList(manifest api.ContainerManifest) string {
|
||||
var images []string
|
||||
for _, container := range manifest.Containers {
|
||||
images = append(images, container.Image)
|
||||
}
|
||||
return strings.Join(images, ",")
|
||||
}
|
||||
|
||||
// Takes input 'data' as either json or yaml and attemps to decode it into the
|
||||
// supplied object.
|
||||
func dataToObject(data []byte) (runtime.Object, error) {
|
||||
// This seems hacky but we can't get the codec from kubeClient.
|
||||
versionInterfaces, err := latest.InterfacesFor(apiVersionToUse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, err := versionInterfaces.Codec.Decode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
const (
|
||||
resolveToPath = "path"
|
||||
resolveToKind = "kind"
|
||||
)
|
||||
|
||||
// Takes a human-friendly reference to a resource and converts it to either a
|
||||
// resource path for an API call or to a Kind to construct a JSON definition.
|
||||
// See usages of the function for more context.
|
||||
//
|
||||
// target is one of the above constants ("path" or "kind") to determine what to
|
||||
// resolve the resource to.
|
||||
//
|
||||
// resource is the human-friendly reference to the resource you want to
|
||||
// convert.
|
||||
func resolveResource(target, resource string) (string, error) {
|
||||
if target != resolveToPath && target != resolveToKind {
|
||||
return "", fmt.Errorf("Unrecognized target to convert to: %s", target)
|
||||
}
|
||||
|
||||
var resolved string
|
||||
var err error
|
||||
// Caseless comparison.
|
||||
resource = strings.ToLower(resource)
|
||||
switch resource {
|
||||
case "pods", "pod", "po":
|
||||
if target == resolveToPath {
|
||||
resolved = "pods"
|
||||
} else {
|
||||
resolved = "Pod"
|
||||
}
|
||||
case "replicationcontrollers", "replicationcontroller", "rc":
|
||||
if target == resolveToPath {
|
||||
resolved = "replicationControllers"
|
||||
} else {
|
||||
resolved = "ReplicationController"
|
||||
}
|
||||
case "services", "service", "se":
|
||||
if target == resolveToPath {
|
||||
resolved = "services"
|
||||
} else {
|
||||
resolved = "Service"
|
||||
}
|
||||
case "minions", "minion", "mi":
|
||||
if target == resolveToPath {
|
||||
resolved = "minions"
|
||||
} else {
|
||||
resolved = "Minion"
|
||||
}
|
||||
default:
|
||||
// It might be a GUID, but we don't know how to handle those for now.
|
||||
err = fmt.Errorf("Resource %s not recognized; need pods, replicationContollers, services or minions.", resource)
|
||||
}
|
||||
return resolved, err
|
||||
}
|
||||
|
||||
func resolveKindToResource(kind string) (resource string, err error) {
|
||||
// Determine the REST resource according to the type in data.
|
||||
switch kind {
|
||||
case "Pod":
|
||||
resource = "pods"
|
||||
case "ReplicationController":
|
||||
resource = "replicationControllers"
|
||||
case "Service":
|
||||
resource = "services"
|
||||
default:
|
||||
err = fmt.Errorf("Object %s not recognized", kind)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// versionAndKind will return the APIVersion and Kind of the given wire-format
|
||||
// enconding of an APIObject, or an error. This is hacked in until the
|
||||
// migration to v1beta3.
|
||||
func versionAndKind(data []byte) (version, kind string, err error) {
|
||||
findKind := struct {
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
|
||||
}{}
|
||||
// yaml is a superset of json, so we use it to decode here. That way,
|
||||
// we understand both.
|
||||
err = yaml.Unmarshal(data, &findKind)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("couldn't get version/kind: %v", err)
|
||||
}
|
||||
return findKind.APIVersion, findKind.Kind, nil
|
||||
}
|
Reference in New Issue
Block a user