mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #1941 from derekwaynecarr/kubectl_ns
Kubectl namespace support
This commit is contained in:
commit
1007743e93
@ -25,6 +25,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||||
@ -55,6 +56,8 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
|||||||
cmds.PersistentFlags().String("client-certificate", "", "Path to a client certificate for TLS.")
|
cmds.PersistentFlags().String("client-certificate", "", "Path to a client certificate for TLS.")
|
||||||
cmds.PersistentFlags().String("client-key", "", "Path to a client key file for TLS.")
|
cmds.PersistentFlags().String("client-key", "", "Path to a client key file for TLS.")
|
||||||
cmds.PersistentFlags().Bool("insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
|
cmds.PersistentFlags().Bool("insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
|
||||||
|
cmds.PersistentFlags().String("ns-path", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
|
||||||
|
cmds.PersistentFlags().StringP("namespace", "n", "", "If present, the namespace scope for this CLI request.")
|
||||||
|
|
||||||
cmds.AddCommand(NewCmdVersion(out))
|
cmds.AddCommand(NewCmdVersion(out))
|
||||||
cmds.AddCommand(NewCmdProxy(out))
|
cmds.AddCommand(NewCmdProxy(out))
|
||||||
@ -63,6 +66,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
|||||||
cmds.AddCommand(NewCmdCreate(out))
|
cmds.AddCommand(NewCmdCreate(out))
|
||||||
cmds.AddCommand(NewCmdUpdate(out))
|
cmds.AddCommand(NewCmdUpdate(out))
|
||||||
cmds.AddCommand(NewCmdDelete(out))
|
cmds.AddCommand(NewCmdDelete(out))
|
||||||
|
cmds.AddCommand(NewCmdNamespace(out))
|
||||||
|
|
||||||
if err := cmds.Execute(); err != nil {
|
if err := cmds.Execute(); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -138,6 +142,23 @@ func getFlagInt(cmd *cobra.Command, flag string) int {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKubeNamespace(cmd *cobra.Command) string {
|
||||||
|
result := api.NamespaceDefault
|
||||||
|
if ns := getFlagString(cmd, "namespace"); len(ns) > 0 {
|
||||||
|
result = ns
|
||||||
|
glog.V(2).Infof("Using namespace from -ns flag")
|
||||||
|
} else {
|
||||||
|
nsPath := getFlagString(cmd, "ns-path")
|
||||||
|
nsInfo, err := kubectl.LoadNamespaceInfo(nsPath)
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("Error loading current namespace: %v", err)
|
||||||
|
}
|
||||||
|
result = nsInfo.Namespace
|
||||||
|
}
|
||||||
|
glog.V(2).Infof("Using namespace %s", result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func getKubeConfig(cmd *cobra.Command) *client.Config {
|
func getKubeConfig(cmd *cobra.Command) *client.Config {
|
||||||
config := &client.Config{}
|
config := &client.Config{}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ Examples:
|
|||||||
data, err := readConfigData(filename)
|
data, err := readConfigData(filename)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, kubectl.ModifyCreate, data)
|
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), kubectl.ModifyCreate, data)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ Examples:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO Add ability to require a resource-version check for delete.
|
// TODO Add ability to require a resource-version check for delete.
|
||||||
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, kubectl.ModifyDelete, data)
|
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), kubectl.ModifyDelete, data)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ Examples:
|
|||||||
outputFormat := getFlagString(cmd, "output")
|
outputFormat := getFlagString(cmd, "output")
|
||||||
templateFile := getFlagString(cmd, "template")
|
templateFile := getFlagString(cmd, "template")
|
||||||
selector := getFlagString(cmd, "selector")
|
selector := getFlagString(cmd, "selector")
|
||||||
err := kubectl.Get(out, getKubeClient(cmd).RESTClient, resource, id, selector, outputFormat, getFlagBool(cmd, "no-headers"), templateFile)
|
err := kubectl.Get(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), resource, id, selector, outputFormat, getFlagBool(cmd, "no-headers"), templateFile)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
60
pkg/kubectl/cmd/namespace.go
Normal file
60
pkg/kubectl/cmd/namespace.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCmdNamespace(out io.Writer) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "namespace [<namespace>]",
|
||||||
|
Short: "Set and view the current Kubernetes namespace",
|
||||||
|
Long: `Set and view the current Kubernetes namespace scope for command line requests.
|
||||||
|
|
||||||
|
A Kubernetes namespace subdivides the cluster into groups of logically related pods, services, and replication controllers.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$ kubectl namespace
|
||||||
|
Using namespace default
|
||||||
|
|
||||||
|
$ kubectl namespace other
|
||||||
|
Set current namespace to other`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
nsPath := getFlagString(cmd, "ns-path")
|
||||||
|
var err error
|
||||||
|
var ns *kubectl.NamespaceInfo
|
||||||
|
switch len(args) {
|
||||||
|
case 0:
|
||||||
|
ns, err = kubectl.LoadNamespaceInfo(nsPath)
|
||||||
|
fmt.Printf("Using namespace %s\n", ns.Namespace)
|
||||||
|
case 1:
|
||||||
|
ns = &kubectl.NamespaceInfo{Namespace: args[0]}
|
||||||
|
err = kubectl.SaveNamespaceInfo(nsPath, ns)
|
||||||
|
fmt.Printf("Set current namespace to %s\n", ns.Namespace)
|
||||||
|
default:
|
||||||
|
usageError(cmd, "kubectl namespace [<namespace>]")
|
||||||
|
}
|
||||||
|
checkErr(err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
@ -46,7 +46,7 @@ Examples:
|
|||||||
data, err := readConfigData(filename)
|
data, err := readConfigData(filename)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, kubectl.ModifyUpdate, data)
|
err = kubectl.Modify(out, getKubeClient(cmd).RESTClient, getKubeNamespace(cmd), kubectl.ModifyUpdate, data)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,13 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Get(w io.Writer, c *client.RESTClient, resource string, id string, selector string, format string, noHeaders bool, templateFile string) error {
|
func Get(w io.Writer, c *client.RESTClient, namespace string, resource string, id string, selector string, format string, noHeaders bool, templateFile string) error {
|
||||||
path, err := resolveResource(resolveToPath, resource)
|
path, err := resolveResource(resolveToPath, resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := c.Verb("GET").Path(path)
|
r := c.Verb("GET").Namespace(namespace).Path(path)
|
||||||
if len(id) > 0 {
|
if len(id) > 0 {
|
||||||
r.Path(id)
|
r.Path(id)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
|
||||||
"gopkg.in/v1/yaml"
|
"gopkg.in/v1/yaml"
|
||||||
)
|
)
|
||||||
@ -68,6 +69,39 @@ type AuthInfo struct {
|
|||||||
Insecure *bool
|
Insecure *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NamespaceInfo struct {
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// LoadAuthInfo parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
|
// 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) {
|
func LoadAuthInfo(path string, r io.Reader) (*AuthInfo, error) {
|
||||||
var auth AuthInfo
|
var auth AuthInfo
|
||||||
|
@ -33,6 +33,58 @@ func validateAction(expectedAction, actualAction client.FakeAction, t *testing.T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 TestLoadAuthInfo(t *testing.T) {
|
func TestLoadAuthInfo(t *testing.T) {
|
||||||
loadAuthInfoTests := []struct {
|
loadAuthInfoTests := []struct {
|
||||||
authData string
|
authData string
|
||||||
|
@ -33,7 +33,7 @@ const (
|
|||||||
ModifyDelete = ModifyAction("delete")
|
ModifyDelete = ModifyAction("delete")
|
||||||
)
|
)
|
||||||
|
|
||||||
func Modify(w io.Writer, c *client.RESTClient, action ModifyAction, data []byte) error {
|
func Modify(w io.Writer, c *client.RESTClient, namespace string, action ModifyAction, data []byte) error {
|
||||||
if action != ModifyCreate && action != ModifyUpdate && action != ModifyDelete {
|
if action != ModifyCreate && action != ModifyUpdate && action != ModifyDelete {
|
||||||
return fmt.Errorf("Action not recognized")
|
return fmt.Errorf("Action not recognized")
|
||||||
}
|
}
|
||||||
@ -64,11 +64,11 @@ func Modify(w io.Writer, c *client.RESTClient, action ModifyAction, data []byte)
|
|||||||
var id string
|
var id string
|
||||||
switch action {
|
switch action {
|
||||||
case "create":
|
case "create":
|
||||||
id, err = doCreate(c, resource, data)
|
id, err = doCreate(c, namespace, resource, data)
|
||||||
case "update":
|
case "update":
|
||||||
id, err = doUpdate(c, resource, obj)
|
id, err = doUpdate(c, namespace, resource, obj)
|
||||||
case "delete":
|
case "delete":
|
||||||
id, err = doDelete(c, resource, obj)
|
id, err = doDelete(c, namespace, resource, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -80,8 +80,8 @@ func Modify(w io.Writer, c *client.RESTClient, action ModifyAction, data []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates the object then returns the ID of the newly created object.
|
// Creates the object then returns the ID of the newly created object.
|
||||||
func doCreate(c *client.RESTClient, resource string, data []byte) (string, error) {
|
func doCreate(c *client.RESTClient, namespace string, resource string, data []byte) (string, error) {
|
||||||
obj, err := c.Post().Path(resource).Body(data).Do().Get()
|
obj, err := c.Post().Namespace(namespace).Path(resource).Body(data).Do().Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ func doCreate(c *client.RESTClient, resource string, data []byte) (string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates the object then returns the ID of the newly created object.
|
// Creates the object then returns the ID of the newly created object.
|
||||||
func doUpdate(c *client.RESTClient, resource string, obj runtime.Object) (string, error) {
|
func doUpdate(c *client.RESTClient, namespace string, resource string, obj runtime.Object) (string, error) {
|
||||||
// Figure out the ID of the object to update by introspecting into the
|
// Figure out the ID of the object to update by introspecting into the
|
||||||
// object.
|
// object.
|
||||||
id, err := getIDFromObj(obj)
|
id, err := getIDFromObj(obj)
|
||||||
@ -99,7 +99,7 @@ func doUpdate(c *client.RESTClient, resource string, obj runtime.Object) (string
|
|||||||
|
|
||||||
// Get the object from the server to find out its current resource
|
// Get the object from the server to find out its current resource
|
||||||
// version to prevent race conditions in updating the object.
|
// version to prevent race conditions in updating the object.
|
||||||
serverObj, err := c.Get().Path(resource).Path(id).Do().Get()
|
serverObj, err := c.Get().Namespace(namespace).Path(resource).Path(id).Do().Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Item Name %s does not exist for update: %v", id, err)
|
return "", fmt.Errorf("Item Name %s does not exist for update: %v", id, err)
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func doUpdate(c *client.RESTClient, resource string, obj runtime.Object) (string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do the update.
|
// Do the update.
|
||||||
err = c.Put().Path(resource).Path(id).Body(data).Do().Error()
|
err = c.Put().Namespace(namespace).Path(resource).Path(id).Body(data).Do().Error()
|
||||||
fmt.Printf("r: %q, i: %q, d: %s", resource, id, data)
|
fmt.Printf("r: %q, i: %q, d: %s", resource, id, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -132,7 +132,7 @@ func doUpdate(c *client.RESTClient, resource string, obj runtime.Object) (string
|
|||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func doDelete(c *client.RESTClient, resource string, obj runtime.Object) (string, error) {
|
func doDelete(c *client.RESTClient, namespace string, resource string, obj runtime.Object) (string, error) {
|
||||||
id, err := getIDFromObj(obj)
|
id, err := getIDFromObj(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Name not retrievable from object for delete: %v", err)
|
return "", fmt.Errorf("Name not retrievable from object for delete: %v", err)
|
||||||
@ -141,7 +141,7 @@ func doDelete(c *client.RESTClient, resource string, obj runtime.Object) (string
|
|||||||
return "", fmt.Errorf("The supplied resource has no Name and cannot be deleted")
|
return "", fmt.Errorf("The supplied resource has no Name and cannot be deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Delete().Path(resource).Path(id).Do().Error()
|
err = c.Delete().Namespace(namespace).Path(resource).Path(id).Do().Error()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user