Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: Conformance test failed: xref What you expected to happen: I expected this conformance test to pass. How to reproduce it (as minimally and precisely as possible): We have configured docker's logging driver to use journald, and then ran sonobouy tests against a k8s 1.16.3 cluster. Anything else we need to know?: Environment: Kubernetes version (use ):Cloud provider or hardware configuration: OS (e.g: ): Red Hat Enterprise Linux Server release 7.7 (Maipo) Kernel (e.g. ): 5.3.11- SMP Tue Nov 12 14:45:59 EST 2019 x8664 x8664 x8664 GNU/Linux Install tools: Network plugin and version (if this is a network-related bug): Others:
There are no sig labels on this issue. Please add a sig label by either: a sig: e.g., to notify the contributor experience sig, OR the label manually: e.g., to apply the label Note: Method 1 will trigger an email to the group. See the . The in method 1 has to be replaced with one of these: bugs, feature-requests, pr-reviews, test-failures, proposals. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/assign
/sig cli
/assign
I have a working patch that fixes this, will push a PR shortly.

Discussion:
import ( "context" <ins> "errors" </ins> "fmt" "io" "strconv"

"k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubetypes "k8s.io/apimachinery/pkg/types" <ins> "k8s.io/klog" </ins> kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/kuberuntime"

opts.Tail = strconv.FormatInt(*logOptions.TailLines, 10) } <ins> if logOptions.LimitBytes != nil { // stdout and stderr share the total write limit max := *logOptions.LimitBytes stderr = sharedLimitWriter(stderr, &max) stdout = sharedLimitWriter(stdout, &max) } </ins> sopts := libdocker.StreamOptions{ OutputStream: stdout, ErrorStream:  stderr, RawTerminal:  container.Config.Tty, } <del> return d.client.Logs(containerID.ID, opts, sopts) </del> <ins> err = d.client.Logs(containerID.ID, opts, sopts) if errors.Is(err, errMaximumWrite) { klog.V(2).Infof("finished logs, hit byte limit %d", *logOptions.LimitBytes) err = nil } return err </ins> } // GetContainerLogTail attempts to read up to MaxContainerTerminationMessageLogLength

package dockershim import ( <ins> "errors" </ins> "fmt" <ins> "io" </ins> "regexp" "strconv" "strings" <ins> "sync/atomic" </ins> dockertypes "github.com/docker/docker/api/types" dockercontainer "github.com/docker/docker/api/types/container"

func (d dockerOpt) GetKV() (string, string) { return d.key, d.value } <ins> // sharedWriteLimiter limits the total output written across one or more streams. type sharedWriteLimiter struct { delegate io.Writer limit    *int64 } func (w sharedWriteLimiter) Write(p []byte) (int, error) { if len(p) == 0 { return 0, nil } limit := atomic.LoadInt64(w.limit) if limit <= 0 { return 0, errMaximumWrite } var truncated bool if limit < int64(len(p)) { p = p[0:limit] truncated = true } n, err := w.delegate.Write(p) if n > 0 { atomic.AddInt64(w.limit, -1*int64(n)) } if err == nil && truncated { err = errMaximumWrite } return n, err } func sharedLimitWriter(w io.Writer, limit *int64) io.Writer { if w == nil { return nil } return &sharedWriteLimiter{ delegate: w, limit:    limit, } } var errMaximumWrite = errors.New("maximum write") </ins>

package dockershim import ( <ins> "bytes" "errors" </ins> "fmt" <ins> "sync" </ins> "testing" dockertypes "github.com/docker/docker/api/types"

assert.Equal(t, expectedResult, result) } <ins> func TestLimitedWriter(t *testing.T) { max := func(x, y int64) int64 { if x > y { return x } return y } for name, tc := range map[string]struct { w        bytes.Buffer toWrite  string limit    int64 wants    string wantsErr error }{ "nil": {}, "neg": { toWrite:  "a", wantsErr: errMaximumWrite, limit:    -1, }, "1byte-over": { toWrite:  "a", wantsErr: errMaximumWrite, }, "1byte-maxed": { toWrite: "a", wants:   "a", limit:   1, }, "1byte-under": { toWrite: "a", wants:   "a", limit:   2, }, "6byte-over": { toWrite:  "foobar", wants:    "foo", limit:    3, wantsErr: errMaximumWrite, }, "6byte-maxed": { toWrite: "foobar", wants:   "foobar", limit:   6, }, "6byte-under": { toWrite: "foobar", wants:   "foobar", limit:   20, }, } { t.Run(name, func(t *testing.T) { limit := tc.limit w := sharedLimitWriter(&tc.w, &limit) n, err := w.Write([]byte(tc.toWrite)) if int64(n) > max(0, tc.limit) { t.Fatalf("bytes written (%d) exceeds limit (%d)", n, tc.limit) } if (err != nil) != (tc.wantsErr != nil) { if err != nil { t.Fatal("unexpected error:", err) } t.Fatal("expected error:", err) } if err != nil { if !errors.Is(err, tc.wantsErr) { t.Fatal("expected error: ", tc.wantsErr, " instead of: ", err) } if !errors.Is(err, errMaximumWrite) { return } // check contents for errMaximumWrite } if s := tc.w.String(); s != tc.wants { t.Fatalf("expected %q instead of %q", tc.wants, s) } }) } // test concurrency. run this test a bunch of times to attempt to flush // out any data races or concurrency issues. for i := 0; i < 1000; i++ { var ( b1, b2 bytes.Buffer limit  = int64(10) w1     = sharedLimitWriter(&b1, &limit) w2     = sharedLimitWriter(&b2, &limit) ch     = make(chan struct{}) wg     sync.WaitGroup ) wg.Add(2) go func() { defer wg.Done(); <-ch; w1.Write([]byte("hello")) }() go func() { defer wg.Done(); <-ch; w2.Write([]byte("world")) }() close(ch) wg.Wait() if limit != 0 { t.Fatalf("expected max limit to be reached, instead of %d", limit) } } } </ins>