blob: 63d9d88b7b16103235783e5bb29250d0b616a2cd [file]
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 (
"context"
"fmt"
"io"
"sort"
"github.com/apache/dubbo-kubernetes/cli/pkg/cli"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
klabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
)
type adminLogArgs struct {
namespace string
deployment string
container string
tail int64
}
func AdminCmd(ctx cli.Context) *cobra.Command {
command := &cobra.Command{
Use: "admin",
Short: "Access dubbod admin information",
}
command.AddCommand(adminLogCmd(ctx))
return command
}
func adminLogCmd(ctx cli.Context) *cobra.Command {
args := &adminLogArgs{
namespace: ctx.Namespace(),
deployment: "dubbod",
container: "execute",
tail: 200,
}
command := &cobra.Command{
Use: "log",
Short: "Print dubbod logs",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
client, err := ctx.CLIClient()
if err != nil {
return err
}
return printDeploymentLogs(cmd.Context(), client.Kube(), *args, cmd.OutOrStdout())
},
}
flags := command.Flags()
flags.StringVarP(&args.namespace, "namespace", "n", args.namespace, "Namespace of the dubbod deployment")
flags.StringVar(&args.deployment, "deployment", args.deployment, "Dubbod deployment name")
flags.StringVar(&args.container, "container", args.container, "Preferred container name")
flags.Int64Var(&args.tail, "tail", args.tail, "Number of recent log lines to print")
return command
}
func printDeploymentLogs(ctx context.Context, client kubernetes.Interface, args adminLogArgs, writer io.Writer) error {
pods, err := deploymentPods(ctx, client, args.namespace, args.deployment)
if err != nil {
return err
}
if len(pods) == 0 {
return fmt.Errorf("no pods found for deployment %s/%s", args.namespace, args.deployment)
}
for _, pod := range pods {
for _, container := range logContainers(pod, args.container) {
if _, err := fmt.Fprintf(writer, "==> %s/%s:%s <==\n", pod.Namespace, pod.Name, container); err != nil {
return err
}
raw, err := client.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &corev1.PodLogOptions{
Container: container,
TailLines: &args.tail,
Timestamps: true,
}).DoRaw(ctx)
if err != nil {
return fmt.Errorf("get logs %s/%s container %s: %w", pod.Namespace, pod.Name, container, err)
}
if _, err := writer.Write(raw); err != nil {
return err
}
if len(raw) == 0 || raw[len(raw)-1] != '\n' {
if _, err := fmt.Fprintln(writer); err != nil {
return err
}
}
}
}
return nil
}
func deploymentPods(ctx context.Context, client kubernetes.Interface, namespace, name string) ([]corev1.Pod, error) {
deployment, err := client.AppsV1().Deployments(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("get deployment %s/%s: %w", namespace, name, err)
}
if deployment.Spec.Selector == nil {
return nil, fmt.Errorf("deployment %s/%s has no selector", namespace, name)
}
selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
if err != nil {
return nil, fmt.Errorf("build pod selector for deployment %s/%s: %w", namespace, name, err)
}
if selector.Empty() {
selector = klabels.SelectorFromSet(deployment.Spec.Template.Labels)
}
pods, err := client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
LabelSelector: selector.String(),
})
if err != nil {
return nil, fmt.Errorf("list pods for deployment %s/%s: %w", namespace, name, err)
}
sort.SliceStable(pods.Items, func(i, j int) bool {
return pods.Items[i].Name < pods.Items[j].Name
})
return pods.Items, nil
}
func logContainers(pod corev1.Pod, preferred string) []string {
if preferred != "" {
for _, container := range pod.Spec.Containers {
if container.Name == preferred {
return []string{preferred}
}
}
}
out := make([]string, 0, len(pod.Spec.Containers))
for _, container := range pod.Spec.Containers {
out = append(out, container.Name)
}
return out
}