From e31af6894e43beacaf2931d12ca38d3bb9f3374e Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Thu, 13 Jun 2024 18:13:01 +0800 Subject: [PATCH] feat(climc,apigateway): pod and container log (#20523) --- cmd/climc/shell/compute/containers.go | 21 +--------- cmd/climc/shell/compute/server_pod.go | 41 +++++++++++++++---- .../modules/compute/mod_containers.go | 24 +++++++++++ .../modules/webconsole/mod_webconsole.go | 34 ++++++++++++++- pkg/mcclient/options/compute/containers.go | 10 ++--- pkg/mcclient/options/compute/server_pod.go | 6 +++ pkg/util/pod/stream/stream.go | 3 -- 7 files changed, 102 insertions(+), 37 deletions(-) diff --git a/cmd/climc/shell/compute/containers.go b/cmd/climc/shell/compute/containers.go index 9775f705f3c..449ee4c76f7 100644 --- a/cmd/climc/shell/compute/containers.go +++ b/cmd/climc/shell/compute/containers.go @@ -15,9 +15,7 @@ package compute import ( - "bufio" "fmt" - "io" "io/ioutil" "os" "os/exec" @@ -125,24 +123,9 @@ func init() { if err != nil { return err } - reader, err := man.Log(s, opts.ID, input) - if err != nil { + if err := man.LogToWriter(s, opts.ID, input, os.Stdout); err != nil { return errors.Wrap(err, "get container log") } - defer reader.Close() - - r := bufio.NewReader(reader) - for { - bytes, err := r.ReadBytes('\n') - if _, err := os.Stdout.Write(bytes); err != nil { - return errors.Wrap(err, "write container log to stdout") - } - if err != nil { - if err != io.EOF { - return errors.Wrap(err, "read container log") - } - return nil - } - } + return nil }) } diff --git a/cmd/climc/shell/compute/server_pod.go b/cmd/climc/shell/compute/server_pod.go index 67cc0064b05..61d65168762 100644 --- a/cmd/climc/shell/compute/server_pod.go +++ b/cmd/climc/shell/compute/server_pod.go @@ -15,6 +15,8 @@ package compute import ( + "os" + "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/sets" @@ -44,36 +46,59 @@ func init() { return nil }) - R(&options.PodExecOptions{}, "pod-exec", "Execute a command in a container", func(s *mcclient.ClientSession, opt *options.PodExecOptions) error { + getContainerId := func(s *mcclient.ClientSession, scope string, podId string, container string) (string, error) { listOpt := map[string]string{ - "guest_id": opt.ID, + "guest_id": podId, } - if len(opt.Scope) != 0 { - listOpt["scope"] = opt.Scope + if len(scope) != 0 { + listOpt["scope"] = scope } ctrs, err := modules.Containers.List(s, jsonutils.Marshal(listOpt)) if err != nil { - return errors.Wrapf(err, "list containers by guest_id %s", opt.ID) + return "", errors.Wrapf(err, "list containers by guest_id %s", podId) } if len(ctrs.Data) == 0 { - return errors.Errorf("count of container is 0") + return "", errors.Errorf("count of container is 0") } var ctrId string - if opt.Container == "" { + if container == "" { ctrId, _ = ctrs.Data[0].GetString("id") } else { for _, ctr := range ctrs.Data { id, _ := ctr.GetString("id") name, _ := ctr.GetString("name") - if opt.Container == id || opt.Container == name { + if container == id || container == name { ctrId, _ = ctr.GetString("id") break } } } + return ctrId, nil + } + + R(&options.PodExecOptions{}, "pod-exec", "Execute a command in a container", func(s *mcclient.ClientSession, opt *options.PodExecOptions) error { + ctrId, err := getContainerId(s, opt.Scope, opt.ID, opt.Container) + if err != nil { + return err + } return modules.Containers.Exec(s, ctrId, opt.ToAPIInput()) }) + R(&options.PodLogOptions{}, "pod-log", "Get container log of a pod", func(s *mcclient.ClientSession, opt *options.PodLogOptions) error { + ctrId, err := getContainerId(s, opt.Scope, opt.ID, opt.Container) + if err != nil { + return err + } + input, err := opt.ToAPIInput() + if err != nil { + return err + } + if err := modules.Containers.LogToWriter(s, ctrId, input, os.Stdout); err != nil { + return errors.Wrap(err, "get container log") + } + return nil + }) + type MigratePortMappingsOptions struct { options.ServerIdOptions RemovePort []int `help:"remove port"` diff --git a/pkg/mcclient/modules/compute/mod_containers.go b/pkg/mcclient/modules/compute/mod_containers.go index 2b558a4d07b..c694066b3d1 100644 --- a/pkg/mcclient/modules/compute/mod_containers.go +++ b/pkg/mcclient/modules/compute/mod_containers.go @@ -15,6 +15,7 @@ package compute import ( + "bufio" "context" "fmt" "io" @@ -118,6 +119,29 @@ func (man ContainerManager) Log(s *mcclient.ClientSession, id string, opt *api.P return reader, nil } +func (man ContainerManager) LogToWriter(s *mcclient.ClientSession, id string, opt *api.PodLogOptions, out io.Writer) error { + reader, err := man.Log(s, id, opt) + if err != nil { + return errors.Wrap(err, "get container log") + } + defer reader.Close() + + r := bufio.NewReader(reader) + for { + bytes, err := r.ReadBytes('\n') + if _, err := out.Write(bytes); err != nil { + return errors.Wrap(err, "write container log to stdout") + } + if err != nil { + if err != io.EOF { + return errors.Wrap(err, "read container log") + } + return nil + } + } + return nil +} + var ( Containers ContainerManager ) diff --git a/pkg/mcclient/modules/webconsole/mod_webconsole.go b/pkg/mcclient/modules/webconsole/mod_webconsole.go index 82efeb09524..f1edb4c2bd2 100644 --- a/pkg/mcclient/modules/webconsole/mod_webconsole.go +++ b/pkg/mcclient/modules/webconsole/mod_webconsole.go @@ -29,6 +29,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient/modulebase" "yunion.io/x/onecloud/pkg/mcclient/modules/compute" "yunion.io/x/onecloud/pkg/mcclient/modules/k8s" + compute_options "yunion.io/x/onecloud/pkg/mcclient/options/compute" ) var ( @@ -76,7 +77,7 @@ func (m WebConsoleManager) DoAdbShell(s *mcclient.ClientSession, id string, para return m.DoConnect(s, "adb", id, "shell", params) } -func (m WebConsoleManager) DoContainerExec(s *mcclient.ClientSession, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { +func (m WebConsoleManager) doContainerAction(s *mcclient.ClientSession, data jsonutils.JSONObject, getArgs func(containerId string) []string) (jsonutils.JSONObject, error) { containerId, err := data.GetString("container_id") if err != nil { return nil, errors.Wrap(err, "get container_id") @@ -105,7 +106,36 @@ func (m WebConsoleManager) DoContainerExec(s *mcclient.ClientSession, data jsonu InstanceName: containerName, IPs: strings.Split(serverDetails.IPs, ","), } - return m.doCloudShell(s, info, "/opt/yunion/bin/climc", "container-exec", containerId, "sh") + args := getArgs(containerId) + return m.doCloudShell(s, info, "/opt/yunion/bin/climc", args...) +} + +func (m WebConsoleManager) DoContainerExec(s *mcclient.ClientSession, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + opt := new(compute_options.ContainerLogOptions) + data.Unmarshal(opt) + return m.doContainerAction(s, data, func(containerId string) []string { + args := []string{"container-exec"} + if opt.Tail > 0 { + args = append(args, "--tail", fmt.Sprintf("%d", opt.Tail)) + } + if opt.LimitBytes > 0 { + args = append(args, "--limit-bytes", fmt.Sprintf("%d", opt.LimitBytes)) + } + if opt.Since != "" { + args = append(args, "--since", opt.Since) + } + if opt.Follow { + args = append(args, "-f") + } + args = append(args, containerId) + return []string{"container-exec", containerId} + }) +} + +func (m WebConsoleManager) DoContainerLog(s *mcclient.ClientSession, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + return m.doContainerAction(s, data, func(containerId string) []string { + return []string{"container-log", containerId, "sh"} + }) } type CloudShellRequest struct { diff --git a/pkg/mcclient/options/compute/containers.go b/pkg/mcclient/options/compute/containers.go index b93b16882a3..2fcfff2bf1f 100644 --- a/pkg/mcclient/options/compute/containers.go +++ b/pkg/mcclient/options/compute/containers.go @@ -344,11 +344,11 @@ func (o *ContainerExecSyncOptions) Params() (jsonutils.JSONObject, error) { type ContainerLogOptions struct { ServerIdOptions - Since string `help:"Only return logs newer than a relative duration like 5s, 2m, or 3h"` - Follow bool `help:"Follow log output" short-token:"f"` - Tail int64 `help:"Lines of recent log file to display"` - Timestamps bool `help:"Show timestamps on each line in the log output"` - LimitBytes int64 `help:"Maximum amount of bytes that can be used."` + Since string `help:"Only return logs newer than a relative duration like 5s, 2m, or 3h" json:"since"` + Follow bool `help:"Follow log output" short-token:"f" json:"follow"` + Tail int64 `help:"Lines of recent log file to display" json:"tail"` + Timestamps bool `help:"Show timestamps on each line in the log output" json:"timestamps"` + LimitBytes int64 `help:"Maximum amount of bytes that can be used." json:"limitBytes"` } func (o *ContainerLogOptions) Params() (jsonutils.JSONObject, error) { diff --git a/pkg/mcclient/options/compute/server_pod.go b/pkg/mcclient/options/compute/server_pod.go index e85ed035c18..07f8544d744 100644 --- a/pkg/mcclient/options/compute/server_pod.go +++ b/pkg/mcclient/options/compute/server_pod.go @@ -254,6 +254,12 @@ type PodExecOptions struct { Container string `help:"Container name. If omitted, use the first container." short-token:"c"` } +type PodLogOptions struct { + ContainerLogOptions + Scope string `help:"Scope of containers query" choices:"system|domain|project"` + Container string `help:"Container name. If omitted, use the first container." short-token:"c"` +} + type PodSetPortMappingOptions struct { ServerIdOptions PortMapping []string `help:"Port mapping of the pod and the format is: host_port=8080,port=80,protocol=,host_port_range=-" short-token:"p"` diff --git a/pkg/util/pod/stream/stream.go b/pkg/util/pod/stream/stream.go index a9479356227..5a56d368675 100644 --- a/pkg/util/pod/stream/stream.go +++ b/pkg/util/pod/stream/stream.go @@ -11,7 +11,6 @@ import ( "golang.org/x/net/http2" "k8s.io/apimachinery/pkg/util/net" - "moul.io/http2curl/v2" "yunion.io/x/log" "yunion.io/x/pkg/util/httputils" @@ -46,8 +45,6 @@ func (r *Request) Stream(ctx context.Context, method string, url string) (io.Rea client = httputils.GetTimeoutClient(1 * time.Hour) } - curlCmd, _ := http2curl.GetCurlCommand(req) - log.Infof("curl: %s", curlCmd) resp, err := client.Do(req) if err != nil { return nil, err