diff --git a/pkg/mcclient/options/webconsole.go b/pkg/mcclient/options/webconsole.go index 038f4eb342..49a0200102 100644 --- a/pkg/mcclient/options/webconsole.go +++ b/pkg/mcclient/options/webconsole.go @@ -1,6 +1,8 @@ package options import ( + "time" + "yunion.io/x/jsonutils" ) @@ -26,6 +28,22 @@ type PodShellOptions struct { type PodLogOptoins struct { PodBaseOptions + Since string `help:"Only return logs newer than a relative duration like 5s, 2m or 3h"` +} + +func (opt *PodLogOptoins) Params() (*jsonutils.JSONDict, error) { + params, err := opt.PodBaseOptions.Params() + if err != nil { + return nil, err + } + if opt.Since != "" { + _, err = time.ParseDuration(opt.Since) + if err != nil { + return nil, err + } + params.Add(jsonutils.NewString(opt.Since), "since") + } + return params, nil } type WebConsoleBaremetalOptions struct { diff --git a/pkg/webconsole/command/kube_command.go b/pkg/webconsole/command/kube_command.go index c90b9bae8e..eea412fef9 100644 --- a/pkg/webconsole/command/kube_command.go +++ b/pkg/webconsole/command/kube_command.go @@ -4,12 +4,23 @@ import ( "fmt" "os" "os/exec" + "time" + "yunion.io/x/jsonutils" "yunion.io/x/log" o "yunion.io/x/onecloud/pkg/webconsole/options" ) +type K8sEnv struct { + Cluster string + Namespace string + Pod string + Container string + Kubeconfig string + Data jsonutils.JSONObject +} + type Kubectl struct { *BaseCommand kubeconfig string @@ -88,12 +99,12 @@ func (c *KubectlExec) Command(cmd string, args ...string) *KubectlExec { return c } -func NewPodBashCommand(kubeconfig, namespace, pod, container string) ICommand { - return NewKubectlCommand(kubeconfig, namespace).Exec(). +func NewPodBashCommand(env *K8sEnv) ICommand { + return NewKubectlCommand(env.Kubeconfig, env.Namespace).Exec(). Stdin(). TTY(). - Pod(pod). - Container(container). + Pod(env.Pod). + Container(env.Container). Command("sh") } @@ -131,9 +142,24 @@ func (c *KubectlLog) Container(name string) *KubectlLog { return c } -func NewPodLogCommand(kubeconfig, namespace, pod, container string) ICommand { - return NewKubectlCommand(kubeconfig, namespace).Logs(). +func (c *KubectlLog) Since(data jsonutils.JSONObject) *KubectlLog { + durationStr, _ := data.GetString("since") + if durationStr == "" { + return c + } + // --since: Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used + if _, err := time.ParseDuration(durationStr); err != nil { + log.Errorf("Failed to parse log since opt: %v", err) + return c + } + c.AppendArgs("--since", durationStr) + return c +} + +func NewPodLogCommand(env *K8sEnv) ICommand { + return NewKubectlCommand(env.Kubeconfig, env.Namespace).Logs(). Follow(). - Pod(pod). - Container(container) + Pod(env.Pod). + Since(env.Data). + Container(env.Container) } diff --git a/pkg/webconsole/handlers.go b/pkg/webconsole/handlers.go index 9a23f652d5..cc0ab79aff 100644 --- a/pkg/webconsole/handlers.go +++ b/pkg/webconsole/handlers.go @@ -7,6 +7,7 @@ import ( "net/http" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/cloudcommon/policy" @@ -34,15 +35,7 @@ func InitHandlers(app *appsrv.Application) { app.AddHandler("POST", ApiPathPrefix+"server/", auth.Authenticate(handleServerRemoteConsole)) } -type K8sEnv struct { - Cluster string - Namespace string - Pod string - Container string - KubeConfig string -} - -func fetchK8sEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (*K8sEnv, error) { +func fetchK8sEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (*command.K8sEnv, error) { params, _, body := appsrv.FetchEnv(ctx, w, r) cluster, _ := body.GetString("cluster") if cluster == "" { @@ -84,12 +77,13 @@ func fetchK8sEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (* f.WriteString(conf) defer f.Close() - return &K8sEnv{ + return &command.K8sEnv{ Cluster: cluster, Namespace: namespace, Pod: podName, Container: container, - KubeConfig: f.Name(), + Kubeconfig: f.Name(), + Data: body, }, nil } @@ -121,7 +115,7 @@ func handleK8sCommand( ctx context.Context, w http.ResponseWriter, r *http.Request, - cmdFactory func(kubeconfig, namespace, pod, container string) command.ICommand, + cmdFactory func(*command.K8sEnv) command.ICommand, ) { env, err := fetchK8sEnv(ctx, w, r) if err != nil { @@ -129,7 +123,7 @@ func handleK8sCommand( return } - cmd := cmdFactory(env.KubeConfig, env.Namespace, env.Pod, env.Container) + cmd := cmdFactory(env) handleCommandSession(cmd, w) }