github.com/containerd/nerdctl@v1.7.7/pkg/testutil/testutil_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package testutil
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  func mirrorOf(s string) string {
    28  	// plain mirror, NOT stargz-converted images
    29  	return fmt.Sprintf("ghcr.io/stargz-containers/%s-org", s)
    30  }
    31  
    32  var (
    33  	BusyboxImage                = "ghcr.io/containerd/busybox:1.28"
    34  	AlpineImage                 = mirrorOf("alpine:3.13")
    35  	NginxAlpineImage            = mirrorOf("nginx:1.19-alpine")
    36  	NginxAlpineIndexHTMLSnippet = "<title>Welcome to nginx!</title>"
    37  	RegistryImage               = mirrorOf("registry:2")
    38  	WordpressImage              = mirrorOf("wordpress:5.7")
    39  	WordpressIndexHTMLSnippet   = "<title>WordPress &rsaquo; Installation</title>"
    40  	MariaDBImage                = mirrorOf("mariadb:10.5")
    41  	DockerAuthImage             = mirrorOf("cesanta/docker_auth:1.7")
    42  	FluentdImage                = mirrorOf("fluent/fluentd:v1.14-1")
    43  	KuboImage                   = mirrorOf("ipfs/kubo:v0.16.0")
    44  
    45  	// Source: https://gist.github.com/cpuguy83/fcf3041e5d8fb1bb5c340915aabeebe0
    46  	NonDistBlobImage = "ghcr.io/cpuguy83/non-dist-blob:latest"
    47  	// Foreign layer digest
    48  	NonDistBlobDigest = "sha256:be691b1535726014cdf3b715ff39361b19e121ca34498a9ceea61ad776b9c215"
    49  
    50  	CommonImage = AlpineImage
    51  
    52  	// This error string is expected when attempting to connect to a TCP socket
    53  	// for a service which actively refuses the connection.
    54  	// (e.g. attempting to connect using http to an https endpoint).
    55  	// It should be "connection refused" as per the TCP RFC.
    56  	// https://www.rfc-editor.org/rfc/rfc793
    57  	ExpectedConnectionRefusedError = "connection refused"
    58  
    59  	SigProxyTrueOut    = "received SIGINT"
    60  	SigProxyTimeoutMsg = "Timed Out; No signal received"
    61  	SigProxyTestScript = `#!/bin/sh
    62  	set -eu
    63  
    64  	sig_msg () {
    65  		printf "` + SigProxyTrueOut + `"
    66  		end
    67  	}
    68  
    69  	trap sig_msg INT
    70  	timeout=0
    71  	while [ $timeout -ne 10 ]; do
    72  		timeout=$((timeout+1))
    73  		sleep 1
    74  	done
    75  	printf "` + SigProxyTimeoutMsg + `"
    76  	end`
    77  )
    78  
    79  const (
    80  	FedoraESGZImage = "ghcr.io/stargz-containers/fedora:30-esgz"            // eStargz
    81  	FfmpegSociImage = "public.ecr.aws/soci-workshop-examples/ffmpeg:latest" // SOCI
    82  	UbuntuImage     = "public.ecr.aws/docker/library/ubuntu:23.10"          // Large enough for testing soci index creation
    83  )
    84  
    85  type delayOnceReader struct {
    86  	once    sync.Once
    87  	wrapped io.Reader
    88  }
    89  
    90  // NewDelayOnceReader returns a wrapper around io.Reader that delays the first Read() by one second.
    91  // It is used to test detaching from a container, and the reason why we need this is described below:
    92  //
    93  // Since detachableStdin.closer cancels the corresponding container's IO,
    94  // it has to be invoked after the corresponding task is started,
    95  // or the container could be resulted in an invalid state.
    96  //
    97  // However, in taskutil.go, the goroutines that copy the container's IO start
    98  // right after container.NewTask(ctx, ioCreator) is invoked and before the function returns,
    99  // which means that detachableStdin.closer could be invoked before the task is started,
   100  // and that's indeed the case for e2e test as the detach keys are "entered immediately".
   101  //
   102  // Since detaching from a container is only applicable when there is a TTY,
   103  // which usually means that there's a human in front of the computer waiting for a prompt to start typing,
   104  // it's reasonable to assume that the user will not type the detach keys before the task is started.
   105  //
   106  // Besides delaying the first Read() by one second,
   107  // the returned reader also sleeps for one second if EOF is reached for the wrapped reader.
   108  // The reason follows:
   109  //
   110  // NewDelayOnceReader is usually used with `unbuffer -p`, which has a caveat:
   111  // "unbuffer simply exits when it encounters an EOF from either its input or process2." [1]
   112  // The implication is if we use `unbuffer -p` to feed a command to container shell,
   113  // `unbuffer -p` will exit right after it finishes reading the command (i.e., encounter an EOF from its input),
   114  // and by that time, the container may have not executed the command and printed the wanted results to stdout,
   115  // which would fail the test if it asserts stdout to contain certain strings.
   116  //
   117  // As a result, to avoid flaky tests,
   118  // we give the container shell one second to process the command before `unbuffer -p` exits.
   119  //
   120  // [1] https://linux.die.net/man/1/unbuffer
   121  func NewDelayOnceReader(wrapped io.Reader) io.Reader {
   122  	return &delayOnceReader{wrapped: wrapped}
   123  }
   124  
   125  func (r *delayOnceReader) Read(p []byte) (int, error) {
   126  	r.once.Do(func() { time.Sleep(time.Second) })
   127  	n, err := r.wrapped.Read(p)
   128  	if errors.Is(err, io.EOF) {
   129  		time.Sleep(time.Second)
   130  	}
   131  	return n, err
   132  }