shim: support shim v2 logging plugin

Now kata shim only supports stdout/stderr of fifo from
containerd/CRI-O, but shim v2 supports logging plugins,
and nerdctl default will use the binary schema for logs.

This commit will add the others type of log plugins:

- file
- binary

In case of binary, kata shim will receive a stdout/stderr like:

binary:///nerdctl?_NERDCTL_INTERNAL_LOGGING=/var/lib/nerdctl/1935db59

That means the nerdctl process will handle the logs(stdout/stderr)

Fixes: #4420

Signed-off-by: Bin Liu <bin@hyper.sh>
This commit is contained in:
Bin Liu
2022-06-08 20:58:19 +08:00
committed by liubin
parent a238d8c6bd
commit 4e30e11b31
11 changed files with 752 additions and 63 deletions

View File

@@ -0,0 +1,57 @@
/*
Copyright The containerd Authors.
Licensed 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 ioutil
import "io"
// writeCloseInformer wraps a reader with a close function.
type wrapReadCloser struct {
reader *io.PipeReader
writer *io.PipeWriter
}
// NewWrapReadCloser creates a wrapReadCloser from a reader.
// NOTE(random-liu): To avoid goroutine leakage, the reader passed in
// must be eventually closed by the caller.
func NewWrapReadCloser(r io.Reader) io.ReadCloser {
pr, pw := io.Pipe()
go func() {
_, _ = io.Copy(pw, r)
pr.Close()
pw.Close()
}()
return &wrapReadCloser{
reader: pr,
writer: pw,
}
}
// Read reads up to len(p) bytes into p.
func (w *wrapReadCloser) Read(p []byte) (int, error) {
n, err := w.reader.Read(p)
if err == io.ErrClosedPipe {
return n, io.EOF
}
return n, err
}
// Close closes read closer.
func (w *wrapReadCloser) Close() error {
w.reader.Close()
w.writer.Close()
return nil
}

View File

@@ -0,0 +1,102 @@
/*
Copyright The containerd Authors.
Licensed 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 ioutil
import (
"io"
"sync"
)
// writeCloseInformer wraps passed in write closer with a close channel.
// Caller could wait on the close channel for the write closer to be
// closed.
type writeCloseInformer struct {
close chan struct{}
wc io.WriteCloser
}
// NewWriteCloseInformer creates the writeCloseInformer from a write closer.
func NewWriteCloseInformer(wc io.WriteCloser) (io.WriteCloser, <-chan struct{}) {
close := make(chan struct{})
return &writeCloseInformer{
close: close,
wc: wc,
}, close
}
// Write passes through the data into the internal write closer.
func (w *writeCloseInformer) Write(p []byte) (int, error) {
return w.wc.Write(p)
}
// Close closes the internal write closer and inform the close channel.
func (w *writeCloseInformer) Close() error {
err := w.wc.Close()
close(w.close)
return err
}
// nopWriteCloser wraps passed in writer with a nop close function.
type nopWriteCloser struct {
w io.Writer
}
// NewNopWriteCloser creates the nopWriteCloser from a writer.
func NewNopWriteCloser(w io.Writer) io.WriteCloser {
return &nopWriteCloser{w: w}
}
// Write passes through the data into the internal writer.
func (n *nopWriteCloser) Write(p []byte) (int, error) {
return n.w.Write(p)
}
// Close is a nop close function.
func (n *nopWriteCloser) Close() error {
return nil
}
// serialWriteCloser wraps a write closer and makes sure all writes
// are done in serial.
// Parallel write won't intersect with each other. Use case:
// 1) Pipe: Write content longer than PIPE_BUF.
// See http://man7.org/linux/man-pages/man7/pipe.7.html
// 2) <3.14 Linux Kernel: write is not atomic
// See http://man7.org/linux/man-pages/man2/write.2.html
type serialWriteCloser struct {
mu sync.Mutex
wc io.WriteCloser
}
// NewSerialWriteCloser creates a SerialWriteCloser from a write closer.
func NewSerialWriteCloser(wc io.WriteCloser) io.WriteCloser {
return &serialWriteCloser{wc: wc}
}
// Write writes a group of byte arrays in order atomically.
func (s *serialWriteCloser) Write(data []byte) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.wc.Write(data)
}
// Close closes the write closer.
func (s *serialWriteCloser) Close() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.wc.Close()
}

View File

@@ -0,0 +1,105 @@
/*
Copyright The containerd Authors.
Licensed 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 ioutil
import (
"errors"
"io"
"sync"
)
// WriterGroup is a group of writers. Writer could be dynamically
// added and removed.
type WriterGroup struct {
mu sync.Mutex
writers map[string]io.WriteCloser
closed bool
}
var _ io.Writer = &WriterGroup{}
// NewWriterGroup creates an empty writer group.
func NewWriterGroup() *WriterGroup {
return &WriterGroup{
writers: make(map[string]io.WriteCloser),
}
}
// Add adds a writer into the group. The writer will be closed
// if the writer group is closed.
func (g *WriterGroup) Add(key string, w io.WriteCloser) {
g.mu.Lock()
defer g.mu.Unlock()
if g.closed {
w.Close()
return
}
g.writers[key] = w
}
// Get gets a writer from the group, returns nil if the writer
// doesn't exist.
func (g *WriterGroup) Get(key string) io.WriteCloser {
g.mu.Lock()
defer g.mu.Unlock()
return g.writers[key]
}
// Remove removes a writer from the group.
func (g *WriterGroup) Remove(key string) {
g.mu.Lock()
defer g.mu.Unlock()
w, ok := g.writers[key]
if !ok {
return
}
w.Close()
delete(g.writers, key)
}
// Write writes data into each writer. If a writer returns error,
// it will be closed and removed from the writer group. It returns
// error if writer group is empty.
func (g *WriterGroup) Write(p []byte) (int, error) {
g.mu.Lock()
defer g.mu.Unlock()
for k, w := range g.writers {
n, err := w.Write(p)
if err == nil && len(p) == n {
continue
}
// The writer is closed or in bad state, remove it.
w.Close()
delete(g.writers, k)
}
if len(g.writers) == 0 {
return 0, errors.New("writer group is empty")
}
return len(p), nil
}
// Close closes the writer group. Write will return error after
// closed.
func (g *WriterGroup) Close() {
g.mu.Lock()
defer g.mu.Unlock()
for _, w := range g.writers {
w.Close()
}
g.writers = nil
g.closed = true
}