Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: cri manager could get the log of container #928

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions daemon/containerio/cri_log_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package containerio

import (
"bufio"
"bytes"
"io"
"os"
"time"

"github.com/sirupsen/logrus"
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)

const (
// delimiter used in cri logging format.
delimiter = ' '
// eof is end-of-line.
eol = '\n'
// timestampFormat is the timestamp format used in cri logging format.
timestampFormat = time.RFC3339Nano
// pipeBufSize is the system PIPE_BUF size, on linux it is 4096 bytes.
pipeBufSize = 4096
// bufSize is the size of the read buffer.
bufSize = pipeBufSize - len(timestampFormat) - len(Stdout) - 2 /*2 delimiter*/ - 1 /*eol*/
)

// StreamType is the type of the stream.
type StreamType string

const (
// Stdin stream type.
Stdin StreamType = "stdin"
// Stdout stream type.
Stdout StreamType = "stdout"
// Stderr stream type.
Stderr StreamType = "stderr"
)

func init() {
Register(func() Backend {
return &criLogFile{}
})
}

type criLogFile struct {
file *os.File
pipeWriter *io.PipeWriter
pipeReader *io.PipeReader
closed bool
}

func (c *criLogFile) Name() string {
return "cri-log-file"
}

func (c *criLogFile) Init(opt *Option) error {
c.file = opt.criLogFile
c.pipeReader, c.pipeWriter = io.Pipe()
// TODO: redirect stderr.
go redirectLogs(c.file, c.pipeReader, Stdout)
return nil
}

func redirectLogs(w io.WriteCloser, r io.ReadCloser, stream StreamType) {
defer r.Close()
defer w.Close()
streamBytes := []byte(stream)
delimiterBytes := []byte{delimiter}
partialBytes := []byte(runtime.LogTagPartial)
fullBytes := []byte(runtime.LogTagFull)
br := bufio.NewReaderSize(r, bufSize)
for {
lineBytes, isPrefix, err := br.ReadLine()
if err != nil {
if err == io.EOF {
logrus.Infof("finish redirecting log file")
} else {
logrus.Errorf("failed to redirect log file: %v", err)
}
return
}
tagBytes := fullBytes
if isPrefix {
tagBytes = partialBytes
}
timestampBytes := time.Now().AppendFormat(nil, time.RFC3339Nano)
data := bytes.Join([][]byte{timestampBytes, streamBytes, tagBytes, lineBytes}, delimiterBytes)
data = append(data, eol)
_, err = w.Write(data)
if err != nil {
logrus.Errorf("failed to write %q log to log file: %v", stream, err)
}
}
}

func (c *criLogFile) Out() io.Writer {
return c.pipeWriter
}

func (c *criLogFile) In() io.Reader {
// Log doesn't need stdin.
return nil
}

func (c *criLogFile) Close() error {
if c.closed {
return nil
}
c.closed = true
return c.pipeWriter.Close()
}
13 changes: 13 additions & 0 deletions daemon/containerio/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package containerio
import (
"bytes"
"net/http"
"os"

"github.com/alibaba/pouch/cri/stream/remotecommand"
)
Expand All @@ -17,6 +18,7 @@ type Option struct {
stdinBackend string
memBuffer *bytes.Buffer
streams *remotecommand.Streams
criLogFile *os.File
}

// NewOption creates the Option instance.
Expand Down Expand Up @@ -111,3 +113,14 @@ func WithStdinStream() func(*Option) {
opt.stdinBackend = "streams"
}
}

// WithCriLogFile specified the cri log file backend.
func WithCriLogFile(criLogFile *os.File) func(*Option) {
return func(opt *Option) {
if opt.backends == nil {
opt.backends = make(map[string]struct{})
}
opt.backends["cri-log-file"] = struct{}{}
opt.criLogFile = criLogFile
}
}
8 changes: 8 additions & 0 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,10 @@ func (mgr *ContainerManager) openAttachIO(id string, attach *AttachConfig) (*con
options = append(options, containerio.WithStdinStream())
}
}

if attach.CriLogFile != nil {
options = append(options, containerio.WithCriLogFile(attach.CriLogFile))
}
} else {
options = append(options, containerio.WithDiscard())
}
Expand Down Expand Up @@ -932,6 +936,10 @@ func (mgr *ContainerManager) openIO(id string, attach *AttachConfig, exec bool)
options = append(options, containerio.WithStdinStream())
}
}

if attach.CriLogFile != nil {
options = append(options, containerio.WithCriLogFile(attach.CriLogFile))
}
} else if !exec {
options = append(options, containerio.WithRawFile())

Expand Down
4 changes: 4 additions & 0 deletions daemon/mgr/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mgr
import (
"bytes"
"net/http"
"os"
"sync"
"time"

Expand Down Expand Up @@ -55,6 +56,9 @@ type AttachConfig struct {

// Attach using streams.
Streams *remotecommand.Streams

// Attach to the container to get its log.
CriLogFile *os.File
}

// ContainerRemoveOption wraps the container remove interface params.
Expand Down
24 changes: 23 additions & 1 deletion daemon/mgr/cri.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"time"

apitypes "github.com/alibaba/pouch/apis/types"
Expand Down Expand Up @@ -421,7 +422,28 @@ func (c *CriManager) CreateContainer(ctx context.Context, r *runtime.CreateConta
return nil, fmt.Errorf("failed to create container for sandbox %q: %v", podSandboxID, err)
}

return &runtime.CreateContainerResponse{ContainerId: createResp.ID}, nil
containerID := createResp.ID

// Get container log.
if config.GetLogPath() != "" {
logPath := filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath())
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640)
if err != nil {
return nil, fmt.Errorf("failed to create container for opening log file failed: %v", err)
}
// Attach to the container to get log.
attachConfig := &AttachConfig{
Stdout: true,
Stderr: true,
CriLogFile: f,
}
err = c.ContainerMgr.Attach(context.Background(), containerID, attachConfig)
if err != nil {
return nil, fmt.Errorf("failed to attach to container %q to get its log: %v", containerID, err)
}
}

return &runtime.CreateContainerResponse{ContainerId: containerID}, nil
}

// StartContainer starts the container.
Expand Down
2 changes: 1 addition & 1 deletion hack/cri-test/test-cri.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ POUCH_SOCK="/var/run/pouchcri.sock"

# CRI_FOCUS focuses the test to run.
# With the CRI manager completes its function, we may need to expand this field.
CRI_FOCUS=${CRI_FOCUS:-"PodSandbox|AppArmor|Privileged is true|basic operations on container|Runtime info|mount propagation|volume and device|RunAsUser|Networking|Streaming|NamespaceOption|SupplementalGroups"}
CRI_FOCUS=${CRI_FOCUS:-"PodSandbox|AppArmor|Privileged is true|Runtime info|Container|RunAsUser|Networking|Streaming|NamespaceOption|SupplementalGroups"}

# CRI_SKIP skips the test to skip.
CRI_SKIP=${CRI_SKIP:-"RunAsUserName|HostNetwork"}
Expand Down