Add support for pausing/unpausing specific namespaces

pull/5962/head
Thomas Stromberg 2020-01-21 22:06:05 -08:00
parent 0f40eef9a7
commit 09b2780fb2
6 changed files with 139 additions and 63 deletions

View File

@ -1,5 +1,5 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Copyright 2020 The Kubernetes Authors 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.
@ -18,6 +18,7 @@ package cmd
import (
"os"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
@ -29,52 +30,77 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/pause"
)
var (
namespaces []string
allNamespaces bool
)
// pauseCmd represents the docker-pause command
var pauseCmd = &cobra.Command{
Use: "pause",
Short: "pause containers",
Run: func(cmd *cobra.Command, args []string) {
cname := viper.GetString(config.MachineProfile)
api, err := machine.NewAPIClient()
if err != nil {
exit.WithError("Error getting client", err)
}
defer api.Close()
cc, err := config.Load(cname)
if err != nil && !os.IsNotExist(err) {
exit.WithError("Error loading profile config", err)
}
if err != nil {
out.ErrT(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": cname})
os.Exit(1)
}
glog.Infof("config: %+v", cc)
host, err := cluster.CheckIfHostExistsAndLoad(api, cname)
if err != nil {
exit.WithError("Error getting host", err)
}
r, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Failed to get command runner", err)
}
config := cruntime.Config{Type: cc.ContainerRuntime, Runner: r}
cr, err := cruntime.New(config)
if err != nil {
exit.WithError("Failed runtime", err)
}
err = pause.Pause(cr, r)
if err != nil {
exit.WithError("Pause", err)
}
out.T(out.Pause, "The '{{.name}}' cluster is now paused", out.V{"name": cc.Name})
},
Run: runPause,
}
func runPause(cmd *cobra.Command, args []string) {
cname := viper.GetString(config.MachineProfile)
api, err := machine.NewAPIClient()
if err != nil {
exit.WithError("Error getting client", err)
}
defer api.Close()
cc, err := config.Load(cname)
if err != nil && !os.IsNotExist(err) {
exit.WithError("Error loading profile config", err)
}
if err != nil {
out.ErrT(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": cname})
os.Exit(1)
}
glog.Infof("config: %+v", cc)
host, err := cluster.CheckIfHostExistsAndLoad(api, cname)
if err != nil {
exit.WithError("Error getting host", err)
}
r, err := machine.CommandRunner(host)
if err != nil {
exit.WithError("Failed to get command runner", err)
}
config := cruntime.Config{Type: cc.ContainerRuntime, Runner: r}
cr, err := cruntime.New(config)
if err != nil {
exit.WithError("Failed runtime", err)
}
glog.Infof("namespaces: %v keys: %v", namespaces, viper.AllSettings())
if allNamespaces {
namespaces = nil //all
} else {
if len(namespaces) == 0 {
exit.WithCodeT(exit.BadUsage, "Use -A to specify all namespaces")
}
}
err = cluster.Pause(cr, r, namespaces)
if err != nil {
exit.WithError("Pause", err)
}
if namespaces == nil {
out.T(out.Pause, "Paused all namespaces in the '{{.name}}' cluster", out.V{"name": cc.Name})
} else {
out.T(out.Pause, "Paused the following namespaces in '{{.name}}': {{.namespaces}}", out.V{"name": cc.Name, "namespaces": strings.Join(namespaces, ", ")})
}
}
func init() {
pauseCmd.Flags().StringSliceVarP(&namespaces, "--namespaces", "n", cluster.DefaultNamespaces, "namespaces to pause")
pauseCmd.Flags().BoolVarP(&allNamespaces, "all-namespaces", "A", false, "If set, pause all namespaces")
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Copyright 2020 The Kubernetes Authors 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.
@ -29,7 +29,6 @@ import (
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/pause"
)
// unpauseCmd represents the docker-pause command
@ -69,10 +68,26 @@ var unpauseCmd = &cobra.Command{
if err != nil {
exit.WithError("Failed runtime", err)
}
err = pause.Unpause(cr, r)
glog.Infof("namespaces: %v keys: %v", namespaces, viper.AllSettings())
if allNamespaces {
namespaces = nil //all
} else {
if len(namespaces) == 0 {
exit.WithCodeT(exit.BadUsage, "Use -A to specify all namespaces")
}
}
err = cluster.Unpause(cr, r, namespaces)
if err != nil {
exit.WithError("Pause", err)
}
out.T(out.Unpause, "The '{{.name}}' cluster is now unpaused", out.V{"name": cc.Name})
},
}
func init() {
unpauseCmd.Flags().StringSliceVarP(&namespaces, "--namespaces", "n", cluster.DefaultNamespaces, "namespaces to unpause")
unpauseCmd.Flags().BoolVarP(&allNamespaces, "all-namespaces", "A", false, "If set, unpause all namespaces")
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package pause
package cluster
import (
"github.com/golang/glog"
@ -24,15 +24,22 @@ import (
"k8s.io/minikube/pkg/minikube/kubelet"
)
// DefaultNamespaces are namespaces used by minikube, including addons
var DefaultNamespaces = []string{
"kube-system",
"kubernetes-dashboard",
"storage-gluster",
}
// Pause pauses a Kubernetes cluster
func Pause(cr cruntime.Manager, r command.Runner) error {
func Pause(cr cruntime.Manager, r command.Runner, namespaces []string) error {
if err := kubelet.Disable(r); err != nil {
return errors.Wrap(err, "kubelet disable")
}
if err := kubelet.Stop(r); err != nil {
return errors.Wrap(err, "kubelet stop")
}
ids, err := cr.ListContainers(cruntime.ListOptions{State: cruntime.Running})
ids, err := cr.ListContainers(cruntime.ListOptions{State: cruntime.Running, Namespaces: namespaces})
if err != nil {
return errors.Wrap(err, "list running")
}
@ -45,8 +52,8 @@ func Pause(cr cruntime.Manager, r command.Runner) error {
}
// Unpause unpauses a Kubernetes cluster
func Unpause(cr cruntime.Manager, r command.Runner) error {
ids, err := cr.ListContainers(cruntime.ListOptions{State: cruntime.Paused})
func Unpause(cr cruntime.Manager, r command.Runner, namespaces []string) error {
ids, err := cr.ListContainers(cruntime.ListOptions{State: cruntime.Paused, Namespaces: namespaces})
if err != nil {
return errors.Wrap(err, "list paused")
}

View File

@ -27,6 +27,7 @@ import (
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/command"
)
// container maps to 'runc list -f json'
@ -35,19 +36,37 @@ type container struct {
Status string
}
// crictlList returns the output of 'crictl ps' in an efficient manner
func crictlList(cr CommandRunner, root string, o ListOptions) (*command.RunResult, error) {
glog.Infof("listing CRI containers in root %s: %+v", root, o)
// Use -a because otherwise paused containers are missed
baseCmd := []string{"crictl", "ps", "-a", "--quiet"}
if o.Name != "" {
baseCmd = append(baseCmd, fmt.Sprintf("--name=%s", o.Name))
}
// shortcut for all namespaces
if len(o.Namespaces) == 0 {
return cr.RunCmd(exec.Command("sudo", baseCmd...))
}
// Gather containers for all namespaces without causing extraneous shells to be launched
cmds := []string{}
for _, ns := range o.Namespaces {
cmd := fmt.Sprintf("%s --label io.kubernetes.pod.namespace=%s", strings.Join(baseCmd, " "), ns)
cmds = append(cmds, cmd)
}
return cr.RunCmd(exec.Command("sudo", "-s", "eval", strings.Join(cmds, "; ")))
}
// listCRIContainers returns a list of containers
func listCRIContainers(cr CommandRunner, root string, o ListOptions) ([]string, error) {
// First use crictl, because it reliably matches names
args := []string{"crictl", "ps", "--quiet"}
if o.State == All {
args = append(args, "-a")
}
if o.Name != "" {
args = append(args, fmt.Sprintf("--name=%s", o.Name))
}
rr, err := cr.RunCmd(exec.Command("sudo", args...))
rr, err := crictlList(cr, root, o)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "crictl list")
}
// Avoid an id named ""
@ -70,7 +89,7 @@ func listCRIContainers(cr CommandRunner, root string, o ListOptions) ([]string,
// crictl does not understand paused pods
cs := []container{}
args = []string{"runc"}
args := []string{"runc"}
if root != "" {
args = append(args, "--root", root)
}

View File

@ -111,6 +111,8 @@ type ListOptions struct {
State ContainerState
// Name is a name filter
Name string
// Namespaces is the namespaces to look into
Namespaces []string
}
// New returns an appropriately configured runtime

View File

@ -154,7 +154,14 @@ func (r *Docker) ListContainers(o ListOptions) ([]string, error) {
case Paused:
args = append(args, "--filter", "status=paused")
}
args = append(args, fmt.Sprintf("--filter=name=%s", KubernetesContainerPrefix+o.Name), "--format={{.ID}}")
nameFilter := KubernetesContainerPrefix + o.Name
if len(o.Namespaces) > 0 {
// Example result: k8s.*(kube-system|kubernetes-dashboard)
nameFilter = fmt.Sprintf("%s.*_(%s)_", nameFilter, strings.Join(o.Namespaces, "|"))
}
args = append(args, fmt.Sprintf("--filter=name=%s", nameFilter), "--format={{.ID}}")
rr, err := r.Runner.RunCmd(exec.Command("docker", args...))
if err != nil {
return nil, errors.Wrapf(err, "docker")