Skip to content

Commit

Permalink
Merge pull request #886 from HusterWan/zr/add-logs-interface
Browse files Browse the repository at this point in the history
feature: add pouch logs cli command
  • Loading branch information
HusterWan authored Mar 15, 2018
2 parents a6e3da4 + 4bead34 commit 9943ac1
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 0 deletions.
8 changes: 8 additions & 0 deletions apis/server/container_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,11 @@ func (s *Server) upgradeContainer(ctx context.Context, rw http.ResponseWriter, r
func (s *Server) topContainer(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
return nil
}

func (s *Server) logsContainer(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
//opts := &types.ContainerLogsOptions{}

// TODO
return nil

}
1 change: 1 addition & 0 deletions apis/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func initRoute(s *Server) http.Handler {
r.Path(versionMatcher + "/containers/{name:.*}/update").Methods(http.MethodPost).Handler(s.filter(s.updateContainer))
r.Path(versionMatcher + "/containers/{name:.*}/upgrade").Methods(http.MethodPost).Handler(s.filter(s.upgradeContainer))
r.Path(versionMatcher + "/containers/{name:.*}/top").Methods(http.MethodGet).Handler(s.filter(s.topContainer))
r.Path(versionMatcher + "/containers/{name:.*}/logs").Methods(http.MethodGet).Handler(s.filter(s.logsContainer))

// image
r.Path(versionMatcher + "/images/create").Methods(http.MethodPost).Handler(s.filter(s.pullImage))
Expand Down
98 changes: 98 additions & 0 deletions apis/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,72 @@ paths:
required: true
tags: ["Exec"]

/containers/{id}/logs:
get:
summary: "Get container logs"
description: |
Get `stdout` and `stderr` logs from a container.
Note: This endpoint works only for containers with the `json-file` or `journald` logging driver.
operationId: "ContainerLogs"
responses:
101:
description: "logs returned as a stream"
schema:
type: "string"
format: "binary"
200:
description: "logs returned as a string in response body"
schema:
type: "string"
404:
$ref: "#/responses/404ErrorResponse"
500:
$ref: "#/responses/500ErrorResponse"
parameters:
- name: "id"
in: "path"
required: true
description: "ID or name of the container"
type: "string"
- name: "follow"
in: "query"
description: |
Return the logs as a stream.
type: "boolean"
default: false
- name: "stdout"
in: "query"
description: "Return logs from `stdout`"
type: "boolean"
default: false
- name: "stderr"
in: "query"
description: "Return logs from `stderr`"
type: "boolean"
default: false
- name: "since"
in: "query"
description: "Only return logs since this time, as a UNIX timestamp"
type: "integer"
default: 0
- name: "until"
in: "query"
description: "Only return logs before this time, as a UNIX timestamp"
type: "integer"
default: 0
- name: "timestamps"
in: "query"
description: "Add timestamps to every log line"
type: "boolean"
default: false
- name: "tail"
in: "query"
description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines."
type: "string"
default: "all"
tags: ["Container"]

/exec/{id}/start:
post:
summary: "Start an exec instance"
Expand Down Expand Up @@ -2606,6 +2672,38 @@ definitions:
description: "The time when this container last exited."
type: "string"

ContainerLogsOptions:
type: "object"
properties:
ShowStdout:
description: "Return logs from `stdout`"
type: "boolean"
ShowStderr:
description: "Return logs from `stderr`"
type: "boolean"
Since:
description: "Only return logs after this time, as a UNIX timestamp"
type: "string"
Until:
description: "Only reture logs before this time, as a UNIX timestamp"
type: "string"
Timestamps:
description: "Add timestamps to every log line"
type: "boolean"
Follow:
description: "Return logs as a stream"
type: "boolean"
Tail:
description: "Only reture this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines."
type: "string"
Details:
description: "Show extra details provided to logs"
type: "boolean"


description: The parameters to filter the log.


Status:
description: The status of the container. For example, "running" or "exited".
type: "string"
Expand Down
87 changes: 87 additions & 0 deletions apis/types/container_logs_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 103 additions & 0 deletions cli/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"bytes"
"context"
"fmt"

"github.com/alibaba/pouch/apis/types"

"github.com/spf13/cobra"
)

var validDrivers = map[string]bool{
"json-file": true,
"journald": true,
}

// logsDescription is used to describe logs command in detail and auto generate command doc.
var logsDescription = ""

// LogsCommand use to implement 'logs' command, it is used to print a container's logs
type LogsCommand struct {
baseCommand
details bool
follow bool
since string
tail string
timestamps bool
}

// Init initialize logs command.
func (lc *LogsCommand) Init(c *Cli) {
lc.cli = c
lc.cmd = &cobra.Command{
Use: "logs [OPTIONS] CONTAINER",
Short: "Print a container's logs",
Long: logsDescription,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return lc.runLogs(args)
},
Example: logsExample(),
}
lc.addFlags()
}

// addFlags adds flags for specific command.
func (lc *LogsCommand) addFlags() {
flagSet := lc.cmd.Flags()
flagSet.BoolVarP(&lc.details, "details", "", false, "Show extra provided to logs")
flagSet.BoolVarP(&lc.follow, "follow", "f", false, "Follow log output")
flagSet.StringVarP(&lc.since, "since", "", "", "Show logs since timestamp")
flagSet.StringVarP(&lc.tail, "tail", "", "all", "Number of lines to show from the end of the logs default \"all\"")
flagSet.BoolVarP(&lc.timestamps, "timestamps", "t", false, "Show timestamps")
}

// runLogs is the entry of LogsCommand command.
func (lc *LogsCommand) runLogs(args []string) error {
// TODO

containerName := args[0]

ctx := context.Background()
apiClient := lc.cli.Client()

c, err := apiClient.ContainerGet(ctx, containerName)
if err != nil {
return err
}

if !validDrivers[c.HostConfig.LogConfig.Type] {
return fmt.Errorf("\"logs\" command is supported only for \"json-file\" and \"journald\" logging drivers (got; %s)", c.HostConfig.LogConfig.Type)
}

opts := types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Since: lc.since,
Timestamps: lc.timestamps,
Follow: lc.follow,
Tail: lc.tail,
Details: lc.details,
}

resp, err := apiClient.ContainerLogs(ctx, containerName, opts)
if err != nil {
return err
}

defer resp.Close()

buf := new(bytes.Buffer)
buf.ReadFrom(resp)

fmt.Printf(buf.String())

return nil
}

// logsExample shows examples in logs command, and is used in auto-generated cli docs.
func logsExample() string {
return ``
}
2 changes: 2 additions & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func main() {
cli.AddCommand(base, &UpdateCommand{})
cli.AddCommand(base, &LogoutCommand{})
cli.AddCommand(base, &UpgradeCommand{})
cli.AddCommand(base, &TopCommand{})
cli.AddCommand(base, &LogsCommand{})

// add generate doc command
cli.AddCommand(base, &GenDocCommand{})
Expand Down
41 changes: 41 additions & 0 deletions client/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"bufio"
"context"
"io"
"net"
"net/url"
"strings"
Expand Down Expand Up @@ -205,3 +206,43 @@ func (client *APIClient) ContainerTop(ctx context.Context, name string, argument
ensureCloseReader(resp)
return response, err
}

// ContainerLogs return the logs generated by a container in an io.ReadCloser.
func (client *APIClient) ContainerLogs(ctx context.Context, name string, options types.ContainerLogsOptions) (io.ReadCloser, error) {
query := url.Values{}
if options.ShowStdout {
query.Set("stdout", "1")
}

if options.ShowStderr {
query.Set("stderr", "1")
}

if options.Since != "" {
// TODO
}

if options.Until != "" {
// TODO
}

if options.Timestamps {
query.Set("timestamps", "1")
}

if options.Details {
query.Set("details", "1")
}

if options.Follow {
query.Set("follow", "1")
}
query.Set("tail", options.Tail)

resp, err := client.get(ctx, "/containers/"+name+"/logs", query, nil)
if err != nil {
return nil, err
}
ensureCloseReader(resp)
return resp.Body, nil
}
1 change: 1 addition & 0 deletions client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type ContainerAPIClient interface {
ContainerUpdate(ctx context.Context, name string, config *types.UpdateConfig) error
ContainerUpgrade(ctx context.Context, name string, config types.ContainerConfig, hostConfig *types.HostConfig) error
ContainerTop(ctx context.Context, name string, arguments []string) (types.ContainerProcessList, error)
ContainerLogs(ctx context.Context, name string, options types.ContainerLogsOptions) (io.ReadCloser, error)
}

// ImageAPIClient defines methods of Image client.
Expand Down
Loading

0 comments on commit 9943ac1

Please sign in to comment.