mirror of
https://github.com/kubernetes/kubectl.git
synced 2026-04-15 22:00:02 -04:00
* cmd/kubectl: make 'kubectl logs' default to the first container. While running 'kubectl logs <pod>', If '-c' is omited and the pod has more than one container, and no default container can be determined from annotations, this command shows an error message and exits. With this fix, it defaults to the first container in such scenarios and show its logs. This aligns behavior with what 'kubectl exec' does currently, and is more in line with KEP SIG-CLI 2227 design. * fix unit test(forgotten) * fix spelling typo Kubernetes-commit: 0977a5d7cda59d5bd324bf2730846905e072fbbf
104 lines
3.8 KiB
Go
104 lines
3.8 KiB
Go
/*
|
|
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, ", ")
|
|
}
|