mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #99004 from smarterclayton/simplify_debug
kubectl: exec and attach break scripting and should honor `--quiet`
This commit is contained in:
commit
9cc3665bd3
@ -23,7 +23,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -33,6 +32,7 @@ import (
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubectl/pkg/cmd/exec"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
@ -68,9 +68,7 @@ type AttachOptions struct {
|
||||
// whether to disable use of standard error when streaming output from tty
|
||||
DisableStderr bool
|
||||
|
||||
CommandName string
|
||||
ParentCommandName string
|
||||
EnableSuggestedCmdUsage bool
|
||||
CommandName string
|
||||
|
||||
Pod *corev1.Pod
|
||||
|
||||
@ -115,6 +113,7 @@ func NewCmdAttach(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra
|
||||
cmd.Flags().StringVarP(&o.ContainerName, "container", "c", o.ContainerName, "Container name. If omitted, the first container in the pod will be chosen")
|
||||
cmd.Flags().BoolVarP(&o.Stdin, "stdin", "i", o.Stdin, "Pass stdin to the container")
|
||||
cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, "Stdin is a TTY")
|
||||
cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "Only print output from the remote session")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -184,14 +183,6 @@ func (o *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []s
|
||||
o.Resources = args
|
||||
o.restClientGetter = f
|
||||
|
||||
cmdParent := cmd.Parent()
|
||||
if cmdParent != nil {
|
||||
o.ParentCommandName = cmdParent.CommandPath()
|
||||
}
|
||||
if len(o.ParentCommandName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
|
||||
o.EnableSuggestedCmdUsage = true
|
||||
}
|
||||
|
||||
config, err := f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -257,8 +248,8 @@ func (o *AttachOptions) Run() error {
|
||||
}
|
||||
if o.TTY && !containerToAttach.TTY {
|
||||
o.TTY = false
|
||||
if o.ErrOut != nil {
|
||||
fmt.Fprintf(o.ErrOut, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name)
|
||||
if !o.Quiet && o.ErrOut != nil {
|
||||
fmt.Fprintf(o.ErrOut, "error: Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name)
|
||||
}
|
||||
} else if !o.TTY && containerToAttach.TTY {
|
||||
// the container was launched with a TTY, so we have to force a TTY here, otherwise you'll get
|
||||
@ -292,7 +283,7 @@ func (o *AttachOptions) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.Stdin && t.Raw && o.Pod.Spec.RestartPolicy == corev1.RestartPolicyAlways {
|
||||
if !o.Quiet && o.Stdin && t.Raw && o.Pod.Spec.RestartPolicy == corev1.RestartPolicyAlways {
|
||||
fmt.Fprintf(o.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", o.CommandName, o.Pod.Name, containerToAttach.Name)
|
||||
}
|
||||
return nil
|
||||
@ -311,32 +302,7 @@ func (o *AttachOptions) findAttachablePod(obj runtime.Object) (*corev1.Pod, erro
|
||||
// containerToAttach returns a reference to the container to attach to, given
|
||||
// by name or the first container if name is empty.
|
||||
func (o *AttachOptions) containerToAttachTo(pod *corev1.Pod) (*corev1.Container, error) {
|
||||
if len(o.ContainerName) > 0 {
|
||||
for i := range pod.Spec.Containers {
|
||||
if pod.Spec.Containers[i].Name == o.ContainerName {
|
||||
return &pod.Spec.Containers[i], nil
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.InitContainers {
|
||||
if pod.Spec.InitContainers[i].Name == o.ContainerName {
|
||||
return &pod.Spec.InitContainers[i], nil
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
if pod.Spec.EphemeralContainers[i].Name == o.ContainerName {
|
||||
return (*corev1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon), nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("container not found (%s)", o.ContainerName)
|
||||
}
|
||||
|
||||
if o.EnableSuggestedCmdUsage {
|
||||
fmt.Fprintf(o.ErrOut, "Defaulting container name to %s.\n", pod.Spec.Containers[0].Name)
|
||||
fmt.Fprintf(o.ErrOut, "Use '%s describe pod/%s -n %s' to see all of the containers in this pod.\n", o.ParentCommandName, o.PodName, o.Namespace)
|
||||
}
|
||||
|
||||
klog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name)
|
||||
return &pod.Spec.Containers[0], nil
|
||||
return podcmd.FindOrDefaultContainerByName(pod, o.ContainerName, o.Quiet, o.ErrOut)
|
||||
}
|
||||
|
||||
// GetContainerName returns the name of the container to attach to, with a fallback.
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package attach
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -35,6 +36,7 @@ import (
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"k8s.io/kubectl/pkg/cmd/exec"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
)
|
||||
@ -65,6 +67,7 @@ func TestPodAndContainerAttach(t *testing.T) {
|
||||
expectError string
|
||||
expectedPodName string
|
||||
expectedContainerName string
|
||||
expectOut string
|
||||
obj *corev1.Pod
|
||||
}{
|
||||
{
|
||||
@ -85,6 +88,25 @@ func TestPodAndContainerAttach(t *testing.T) {
|
||||
expectedPodName: "foo",
|
||||
expectedContainerName: "bar",
|
||||
obj: attachPod(),
|
||||
expectOut: `Defaulted container "bar" out of: bar, debugger (ephem), initfoo (init)`,
|
||||
},
|
||||
{
|
||||
name: "no container, no flags, sets default expected container as annotation",
|
||||
options: &AttachOptions{GetPodTimeout: defaultPodLogsTimeout},
|
||||
args: []string{"foo"},
|
||||
expectedPodName: "foo",
|
||||
expectedContainerName: "bar",
|
||||
obj: setDefaultContainer(attachPod(), "initfoo"),
|
||||
expectOut: ``,
|
||||
},
|
||||
{
|
||||
name: "no container, no flags, sets default missing container as annotation",
|
||||
options: &AttachOptions{GetPodTimeout: defaultPodLogsTimeout},
|
||||
args: []string{"foo"},
|
||||
expectedPodName: "foo",
|
||||
expectedContainerName: "bar",
|
||||
obj: setDefaultContainer(attachPod(), "does-not-exist"),
|
||||
expectOut: `Defaulted container "bar" out of: bar, debugger (ephem), initfoo (init)`,
|
||||
},
|
||||
{
|
||||
name: "container in flag",
|
||||
@ -115,7 +137,7 @@ func TestPodAndContainerAttach(t *testing.T) {
|
||||
options: &AttachOptions{StreamOptions: exec.StreamOptions{ContainerName: "wrong"}, GetPodTimeout: 10},
|
||||
args: []string{"foo"},
|
||||
expectedPodName: "foo",
|
||||
expectError: "container not found",
|
||||
expectError: "container wrong not found in pod foo",
|
||||
obj: attachPod(),
|
||||
},
|
||||
{
|
||||
@ -153,11 +175,23 @@ func TestPodAndContainerAttach(t *testing.T) {
|
||||
pod, err := test.options.findAttachablePod(&corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "test"},
|
||||
Spec: corev1.PodSpec{
|
||||
InitContainers: []corev1.Container{
|
||||
{
|
||||
Name: "initfoo",
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "foobar",
|
||||
},
|
||||
},
|
||||
EphemeralContainers: []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Name: "ephemfoo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@ -171,10 +205,17 @@ func TestPodAndContainerAttach(t *testing.T) {
|
||||
t.Errorf("unexpected pod name: expected %q, got %q", test.expectedContainerName, pod.Name)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
test.options.ErrOut = &buf
|
||||
container, err := test.options.containerToAttachTo(attachPod())
|
||||
|
||||
if len(test.expectOut) > 0 && !strings.Contains(buf.String(), test.expectOut) {
|
||||
t.Errorf("unexpected output: output did not contain %q\n---\n%s", test.expectOut, buf.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if test.expectError == "" || !strings.Contains(err.Error(), test.expectError) {
|
||||
t.Errorf("unexpected error: expected %q, got %q", err, test.expectError)
|
||||
t.Errorf("unexpected error: expected %q, got %q", test.expectError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -230,7 +271,7 @@ func TestAttach(t *testing.T) {
|
||||
attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach",
|
||||
pod: attachPod(),
|
||||
container: "foo",
|
||||
expectedErr: "cannot attach to the container: container not found (foo)",
|
||||
expectedErr: "cannot attach to the container: container foo not found in pod foo",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@ -435,3 +476,11 @@ func attachPod() *corev1.Pod {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setDefaultContainer(pod *corev1.Pod, name string) *corev1.Pod {
|
||||
if pod.Annotations == nil {
|
||||
pod.Annotations = make(map[string]string)
|
||||
}
|
||||
pod.Annotations[podcmd.DefaultContainerAnnotationName] = name
|
||||
return pod
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
@ -100,6 +101,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C
|
||||
cmd.Flags().StringVarP(&options.ContainerName, "container", "c", options.ContainerName, "Container name. If omitted, the first container in the pod will be chosen")
|
||||
cmd.Flags().BoolVarP(&options.Stdin, "stdin", "i", options.Stdin, "Pass stdin to the container")
|
||||
cmd.Flags().BoolVarP(&options.TTY, "tty", "t", options.TTY, "Stdin is a TTY")
|
||||
cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", options.Quiet, "Only print output from the remote session")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -152,9 +154,6 @@ type ExecOptions struct {
|
||||
Command []string
|
||||
EnforceNamespace bool
|
||||
|
||||
ParentCommandName string
|
||||
EnableSuggestedCmdUsage bool
|
||||
|
||||
Builder func() *resource.Builder
|
||||
ExecutablePodFn polymorphichelpers.AttachablePodForObjectFunc
|
||||
restClientGetter genericclioptions.RESTClientGetter
|
||||
@ -174,10 +173,14 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
|
||||
if argsLenAtDash > -1 {
|
||||
p.Command = argsIn[argsLenAtDash:]
|
||||
} else if len(argsIn) > 1 {
|
||||
fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.\n")
|
||||
if !p.Quiet {
|
||||
fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.\n")
|
||||
}
|
||||
p.Command = argsIn[1:]
|
||||
} else if len(argsIn) > 0 && len(p.FilenameOptions.Filenames) != 0 {
|
||||
fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.\n")
|
||||
if !p.Quiet {
|
||||
fmt.Fprint(p.ErrOut, "kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.\n")
|
||||
}
|
||||
p.Command = argsIn[0:]
|
||||
p.ResourceName = ""
|
||||
}
|
||||
@ -198,14 +201,6 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
|
||||
p.Builder = f.NewBuilder
|
||||
p.restClientGetter = f
|
||||
|
||||
cmdParent := cmd.Parent()
|
||||
if cmdParent != nil {
|
||||
p.ParentCommandName = cmdParent.CommandPath()
|
||||
}
|
||||
if len(p.ParentCommandName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
|
||||
p.EnableSuggestedCmdUsage = true
|
||||
}
|
||||
|
||||
p.Config, err = f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -260,7 +255,7 @@ func (o *StreamOptions) SetupTTY() term.TTY {
|
||||
if !o.isTerminalIn(t) {
|
||||
o.TTY = false
|
||||
|
||||
if o.ErrOut != nil {
|
||||
if !o.Quiet && o.ErrOut != nil {
|
||||
fmt.Fprintln(o.ErrOut, "Unable to use a TTY - input is not a terminal or the right kind of file")
|
||||
}
|
||||
|
||||
@ -323,7 +318,14 @@ func (p *ExecOptions) Run() error {
|
||||
return fmt.Errorf("cannot exec into a container in a completed pod; current phase is %s", pod.Status.Phase)
|
||||
}
|
||||
|
||||
containerName := getDefaultContainerName(p, pod)
|
||||
containerName := p.ContainerName
|
||||
if len(containerName) == 0 {
|
||||
container, err := podcmd.FindOrDefaultContainerByName(pod, containerName, p.Quiet, p.ErrOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerName = container.Name
|
||||
}
|
||||
|
||||
// ensure we can recover the terminal while attached
|
||||
t := p.SetupTTY()
|
||||
@ -368,12 +370,3 @@ func (p *ExecOptions) Run() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDefaultContainerName(p *ExecOptions, pod *corev1.Pod) string {
|
||||
containerName := p.ContainerName
|
||||
if len(containerName) != 0 {
|
||||
return containerName
|
||||
}
|
||||
|
||||
return cmdutil.GetDefaultContainerName(pod, p.EnableSuggestedCmdUsage, p.ErrOut)
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import (
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -45,7 +44,6 @@ import (
|
||||
"k8s.io/client-go/scale"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kubectl/pkg/util/podutils"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
@ -746,28 +744,3 @@ func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
|
||||
oldGeneratorName,
|
||||
)
|
||||
}
|
||||
|
||||
// GetDefaultContainerName returns the default container name for a pod.
|
||||
// it checks the annotation kubectl.kubernetes.io/default-container at first and then the first container name.
|
||||
func GetDefaultContainerName(pod *corev1.Pod, enableSuggestedCmdUsage bool, w io.Writer) string {
|
||||
if len(pod.Spec.Containers) > 1 {
|
||||
// in case the "kubectl.kubernetes.io/default-container" annotation is present, we preset the opts.Containers to default to selected
|
||||
// container. This gives users ability to preselect the most interesting container in pod.
|
||||
if annotations := pod.Annotations; annotations != nil && len(annotations[podutils.DefaultContainerAnnotationName]) > 0 {
|
||||
containerName := annotations[podutils.DefaultContainerAnnotationName]
|
||||
if exists, _ := podutils.FindContainerByName(pod, containerName); exists != nil {
|
||||
fmt.Fprintf(w, "Defaulting container name to container %s.\n", containerName)
|
||||
return containerName
|
||||
} else {
|
||||
fmt.Fprintf(w, "Default container name %q in annotation not found in a pod\n", containerName)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "Defaulting container name to first container %s.\n", pod.Spec.Containers[0].Name)
|
||||
|
||||
if enableSuggestedCmdUsage {
|
||||
fmt.Fprintf(w, "Use 'kubectl describe pod/%s -n %s' to see all of the containers in this pod.\n", pod.Name, pod.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return pod.Spec.Containers[0].Name
|
||||
}
|
||||
|
104
staging/src/k8s.io/kubectl/pkg/cmd/util/podcmd/podcmd.go
Normal file
104
staging/src/k8s.io/kubectl/pkg/cmd/util/podcmd/podcmd.go
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
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 podcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// DefaultContainerAnnotationName is an annotation name that can be used to preselect the interesting container
|
||||
// from a pod when running kubectl.
|
||||
const DefaultContainerAnnotationName = "kubectl.kubernetes.io/default-container"
|
||||
|
||||
// FindContainerByName selects the named container from the spec of
|
||||
// the provided pod or return nil if no such container exists.
|
||||
func FindContainerByName(pod *v1.Pod, name string) (*v1.Container, string) {
|
||||
for i := range pod.Spec.Containers {
|
||||
if pod.Spec.Containers[i].Name == name {
|
||||
return &pod.Spec.Containers[i], fmt.Sprintf("spec.containers{%s}", name)
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.InitContainers {
|
||||
if pod.Spec.InitContainers[i].Name == name {
|
||||
return &pod.Spec.InitContainers[i], fmt.Sprintf("spec.initContainers{%s}", name)
|
||||
}
|
||||
}
|
||||
for i := range pod.Spec.EphemeralContainers {
|
||||
if pod.Spec.EphemeralContainers[i].Name == name {
|
||||
return (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon), fmt.Sprintf("spec.ephemeralContainers{%s}", name)
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// FindOrDefaultContainerByName defaults a container for a pod to the first container if any
|
||||
// exists, or returns an error. It will print a message to the user indicating a default was
|
||||
// selected if there was more than one container.
|
||||
func FindOrDefaultContainerByName(pod *v1.Pod, name string, quiet bool, warn io.Writer) (*v1.Container, error) {
|
||||
var container *v1.Container
|
||||
|
||||
if len(name) > 0 {
|
||||
container, _ = FindContainerByName(pod, name)
|
||||
if container == nil {
|
||||
return nil, fmt.Errorf("container %s not found in pod %s", name, pod.Name)
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
// this should never happen, but just in case
|
||||
if len(pod.Spec.Containers) == 0 {
|
||||
return nil, fmt.Errorf("pod %s/%s does not have any containers", pod.Namespace, pod.Name)
|
||||
}
|
||||
|
||||
// read the default container the annotation as per
|
||||
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/2227-kubectl-default-container
|
||||
if name := pod.Annotations[DefaultContainerAnnotationName]; len(name) > 0 {
|
||||
if container, _ = FindContainerByName(pod, name); container != nil {
|
||||
klog.V(4).Infof("Defaulting container name from annotation %s", container.Name)
|
||||
return container, nil
|
||||
}
|
||||
klog.V(4).Infof("Default container name from annotation %s was not found in the pod", name)
|
||||
}
|
||||
|
||||
// pick the first container as per existing behavior
|
||||
container = &pod.Spec.Containers[0]
|
||||
if !quiet && (len(pod.Spec.Containers) > 1 || len(pod.Spec.InitContainers) > 0 || len(pod.Spec.EphemeralContainers) > 0) {
|
||||
fmt.Fprintf(warn, "Defaulted container %q out of: %s\n", container.Name, allContainerNames(pod))
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Defaulting container name to %s", container.Name)
|
||||
return &pod.Spec.Containers[0], nil
|
||||
}
|
||||
|
||||
func allContainerNames(pod *v1.Pod) string {
|
||||
var containers []string
|
||||
for _, container := range pod.Spec.Containers {
|
||||
containers = append(containers, container.Name)
|
||||
}
|
||||
for _, container := range pod.Spec.EphemeralContainers {
|
||||
containers = append(containers, fmt.Sprintf("%s (ephem)", container.Name))
|
||||
}
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
containers = append(containers, fmt.Sprintf("%s (init)", container.Name))
|
||||
}
|
||||
return strings.Join(containers, ", ")
|
||||
}
|
@ -30,6 +30,7 @@ import (
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/reference"
|
||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/podutils"
|
||||
)
|
||||
@ -79,8 +80,8 @@ func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, opt
|
||||
// container. This gives users ability to preselect the most interesting container in pod.
|
||||
if annotations := t.GetAnnotations(); annotations != nil && len(opts.Container) == 0 {
|
||||
var containerName string
|
||||
if len(annotations[podutils.DefaultContainerAnnotationName]) > 0 {
|
||||
containerName = annotations[podutils.DefaultContainerAnnotationName]
|
||||
if len(annotations[podcmd.DefaultContainerAnnotationName]) > 0 {
|
||||
containerName = annotations[podcmd.DefaultContainerAnnotationName]
|
||||
} else if len(annotations[defaultLogsContainerAnnotationName]) > 0 {
|
||||
// Only log deprecation if we have only the old annotation. This allows users to
|
||||
// set both to support multiple versions of kubectl; if they are setting both
|
||||
@ -90,7 +91,7 @@ func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, opt
|
||||
fmt.Fprintf(os.Stderr, "Using deprecated annotation `kubectl.kubernetes.io/default-logs-container` in pod/%v. Please use `kubectl.kubernetes.io/default-container` instead\n", t.Name)
|
||||
}
|
||||
if len(containerName) > 0 {
|
||||
if exists, _ := podutils.FindContainerByName(t, containerName); exists != nil {
|
||||
if exists, _ := podcmd.FindContainerByName(t, containerName); exists != nil {
|
||||
opts.Container = containerName
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Default container name %q not found in a pod\n", containerName)
|
||||
@ -121,7 +122,7 @@ func logsForObjectWithClient(clientset corev1client.CoreV1Interface, object, opt
|
||||
containerName = opts.Container
|
||||
}
|
||||
|
||||
container, fieldPath := podutils.FindContainerByName(t, containerName)
|
||||
container, fieldPath := podcmd.FindContainerByName(t, containerName)
|
||||
if container == nil {
|
||||
return nil, fmt.Errorf("container %s is not valid for pod %s", opts.Container, t.Name)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
fakeexternal "k8s.io/client-go/kubernetes/fake"
|
||||
testclient "k8s.io/client-go/testing"
|
||||
"k8s.io/kubectl/pkg/util/podutils"
|
||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -430,7 +430,7 @@ func TestLogsForObjectWithClient(t *testing.T) {
|
||||
name: "two container pod with default container selected",
|
||||
podFn: func() *corev1.Pod {
|
||||
pod := testPodWithTwoContainers()
|
||||
pod.Annotations = map[string]string{podutils.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
return pod
|
||||
},
|
||||
podLogOptions: &corev1.PodLogOptions{},
|
||||
@ -440,7 +440,7 @@ func TestLogsForObjectWithClient(t *testing.T) {
|
||||
name: "two container pod with default container selected but also container set explicitly",
|
||||
podFn: func() *corev1.Pod {
|
||||
pod := testPodWithTwoContainers()
|
||||
pod.Annotations = map[string]string{podutils.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
return pod
|
||||
},
|
||||
podLogOptions: &corev1.PodLogOptions{
|
||||
@ -452,7 +452,7 @@ func TestLogsForObjectWithClient(t *testing.T) {
|
||||
name: "two container pod with non-existing default container selected",
|
||||
podFn: func() *corev1.Pod {
|
||||
pod := testPodWithTwoContainers()
|
||||
pod.Annotations = map[string]string{podutils.DefaultContainerAnnotationName: "non-existing"}
|
||||
pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "non-existing"}
|
||||
return pod
|
||||
},
|
||||
podLogOptions: &corev1.PodLogOptions{},
|
||||
@ -462,7 +462,7 @@ func TestLogsForObjectWithClient(t *testing.T) {
|
||||
name: "two container pod with default container set, but allContainers also set",
|
||||
podFn: func() *corev1.Pod {
|
||||
pod := testPodWithTwoContainers()
|
||||
pod.Annotations = map[string]string{podutils.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
pod.Annotations = map[string]string{podcmd.DefaultContainerAnnotationName: "foo-2-c1"}
|
||||
return pod
|
||||
},
|
||||
allContainers: true,
|
||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||
package podutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -25,10 +24,6 @@ import (
|
||||
"k8s.io/utils/integer"
|
||||
)
|
||||
|
||||
// DefaultContainerAnnotationName is an annotation name that can be used to preselect the interesting container
|
||||
// from a pod when running kubectl.
|
||||
const DefaultContainerAnnotationName = "kubectl.kubernetes.io/default-container"
|
||||
|
||||
// IsPodAvailable returns true if a pod is available; false otherwise.
|
||||
// Precondition for an available pod is that it must be ready. On top
|
||||
// of that, there are two cases when a pod can be considered available:
|
||||
@ -191,25 +186,3 @@ func maxContainerRestarts(pod *corev1.Pod) int {
|
||||
}
|
||||
return maxRestarts
|
||||
}
|
||||
|
||||
// FindContainerByName searches for a container by name amongst all containers in a pod.
|
||||
// Returns a pointer to a container and a field path.
|
||||
func FindContainerByName(pod *corev1.Pod, name string) (container *corev1.Container, fieldPath string) {
|
||||
for _, c := range pod.Spec.InitContainers {
|
||||
if c.Name == name {
|
||||
return &c, fmt.Sprintf("spec.initContainers{%s}", c.Name)
|
||||
}
|
||||
}
|
||||
for _, c := range pod.Spec.Containers {
|
||||
if c.Name == name {
|
||||
return &c, fmt.Sprintf("spec.containers{%s}", c.Name)
|
||||
}
|
||||
}
|
||||
for _, c := range pod.Spec.EphemeralContainers {
|
||||
if c.Name == name {
|
||||
containerCommon := corev1.Container(c.EphemeralContainerCommon)
|
||||
return &containerCommon, fmt.Sprintf("spec.ephemeralContainers{%s}", containerCommon.Name)
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -2407,6 +2407,7 @@ k8s.io/kubectl/pkg/cmd/top
|
||||
k8s.io/kubectl/pkg/cmd/util
|
||||
k8s.io/kubectl/pkg/cmd/util/editor
|
||||
k8s.io/kubectl/pkg/cmd/util/editor/crlf
|
||||
k8s.io/kubectl/pkg/cmd/util/podcmd
|
||||
k8s.io/kubectl/pkg/cmd/util/sanity
|
||||
k8s.io/kubectl/pkg/cmd/version
|
||||
k8s.io/kubectl/pkg/cmd/wait
|
||||
|
Loading…
Reference in New Issue
Block a user