Add support for pausing/unpausing specific namespaces
parent
0f40eef9a7
commit
09b2780fb2
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue