123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- package environment // import "github.com/docker/docker/testutil/environment"
- import (
- "context"
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "testing"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/filters"
- "github.com/docker/docker/api/types/system"
- "github.com/docker/docker/client"
- "github.com/docker/docker/testutil/fixtures/load"
- "github.com/pkg/errors"
- "gotest.tools/v3/assert"
- )
- // Execution contains information about the current test execution and daemon
- // under test
- type Execution struct {
- client client.APIClient
- DaemonInfo system.Info
- DaemonVersion types.Version
- PlatformDefaults PlatformDefaults
- protectedElements protectedElements
- }
- // PlatformDefaults are defaults values for the platform of the daemon under test
- type PlatformDefaults struct {
- BaseImage string
- VolumesConfigPath string
- ContainerStoragePath string
- }
- // New creates a new Execution struct
- // This is configured using the env client (see client.FromEnv)
- func New(ctx context.Context) (*Execution, error) {
- c, err := client.NewClientWithOpts(client.FromEnv)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to create client")
- }
- return FromClient(ctx, c)
- }
- // FromClient creates a new Execution environment from the passed in client
- func FromClient(ctx context.Context, c *client.Client) (*Execution, error) {
- info, err := c.Info(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get info from daemon")
- }
- v, err := c.ServerVersion(context.Background())
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get version info from daemon")
- }
- return &Execution{
- client: c,
- DaemonInfo: info,
- DaemonVersion: v,
- PlatformDefaults: getPlatformDefaults(info),
- protectedElements: newProtectedElements(),
- }, nil
- }
- func getPlatformDefaults(info system.Info) PlatformDefaults {
- volumesPath := filepath.Join(info.DockerRootDir, "volumes")
- containersPath := filepath.Join(info.DockerRootDir, "containers")
- switch info.OSType {
- case "linux":
- return PlatformDefaults{
- BaseImage: "scratch",
- VolumesConfigPath: toSlash(volumesPath),
- ContainerStoragePath: toSlash(containersPath),
- }
- case "windows":
- baseImage := "mcr.microsoft.com/windows/servercore:ltsc2022"
- if overrideBaseImage := os.Getenv("WINDOWS_BASE_IMAGE"); overrideBaseImage != "" {
- baseImage = overrideBaseImage
- if overrideBaseImageTag := os.Getenv("WINDOWS_BASE_IMAGE_TAG"); overrideBaseImageTag != "" {
- baseImage = baseImage + ":" + overrideBaseImageTag
- }
- }
- fmt.Println("INFO: Windows Base image is ", baseImage)
- return PlatformDefaults{
- BaseImage: baseImage,
- VolumesConfigPath: filepath.FromSlash(volumesPath),
- ContainerStoragePath: filepath.FromSlash(containersPath),
- }
- default:
- panic(fmt.Sprintf("unknown OSType for daemon: %s", info.OSType))
- }
- }
- // Make sure in context of daemon, not the local platform. Note we can't
- // use filepath.ToSlash here as that is a no-op on Unix.
- func toSlash(path string) string {
- return strings.ReplaceAll(path, `\`, `/`)
- }
- // IsLocalDaemon is true if the daemon under test is on the same
- // host as the test process.
- //
- // Deterministically working out the environment in which CI is running
- // to evaluate whether the daemon is local or remote is not possible through
- // a build tag.
- //
- // For example Windows to Linux CI under Jenkins tests the 64-bit
- // Windows binary build with the daemon build tag, but calls a remote
- // Linux daemon.
- //
- // We can't just say if Windows then assume the daemon is local as at
- // some point, we will be testing the Windows CLI against a Windows daemon.
- //
- // Similarly, it will be perfectly valid to also run CLI tests from
- // a Linux CLI (built with the daemon tag) against a Windows daemon.
- func (e *Execution) IsLocalDaemon() bool {
- return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
- }
- // IsRemoteDaemon is true if the daemon under test is on different host
- // as the test process.
- func (e *Execution) IsRemoteDaemon() bool {
- return !e.IsLocalDaemon()
- }
- // DaemonAPIVersion returns the negotiated daemon api version
- func (e *Execution) DaemonAPIVersion() string {
- version, err := e.APIClient().ServerVersion(context.TODO())
- if err != nil {
- return ""
- }
- return version.APIVersion
- }
- // Print the execution details to stdout
- // TODO: print everything
- func (e *Execution) Print() {
- if e.IsLocalDaemon() {
- fmt.Println("INFO: Testing against a local daemon")
- } else {
- fmt.Println("INFO: Testing against a remote daemon")
- }
- }
- // APIClient returns an APIClient connected to the daemon under test
- func (e *Execution) APIClient() client.APIClient {
- return e.client
- }
- // IsUserNamespace returns whether the user namespace remapping is enabled
- func (e *Execution) IsUserNamespace() bool {
- root := os.Getenv("DOCKER_REMAP_ROOT")
- return root != ""
- }
- // RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows
- func (e *Execution) RuntimeIsWindowsContainerd() bool {
- return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
- }
- // IsRootless returns whether the rootless mode is enabled
- func (e *Execution) IsRootless() bool {
- return os.Getenv("DOCKER_ROOTLESS") != ""
- }
- // IsUserNamespaceInKernel returns whether the kernel supports user namespaces
- func (e *Execution) IsUserNamespaceInKernel() bool {
- if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
- /*
- * This kernel-provided file only exists if user namespaces are
- * supported
- */
- return false
- }
- // We need extra check on redhat based distributions
- if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil {
- defer f.Close()
- b := make([]byte, 1)
- _, _ = f.Read(b)
- return string(b) != "N"
- }
- return true
- }
- // UsingSnapshotter returns whether containerd snapshotters are used for the
- // tests by checking if the "TEST_INTEGRATION_USE_SNAPSHOTTER" is set to a
- // non-empty value.
- func (e *Execution) UsingSnapshotter() bool {
- return os.Getenv("TEST_INTEGRATION_USE_SNAPSHOTTER") != ""
- }
- // HasExistingImage checks whether there is an image with the given reference.
- // Note that this is done by filtering and then checking whether there were any
- // results -- so ambiguous references might result in false-positives.
- func (e *Execution) HasExistingImage(t testing.TB, reference string) bool {
- imageList, err := e.APIClient().ImageList(context.Background(), types.ImageListOptions{
- All: true,
- Filters: filters.NewArgs(
- filters.Arg("dangling", "false"),
- filters.Arg("reference", reference),
- ),
- })
- assert.NilError(t, err, "failed to list images")
- return len(imageList) > 0
- }
- // EnsureFrozenImagesLinux loads frozen test images into the daemon
- // if they aren't already loaded
- func EnsureFrozenImagesLinux(ctx context.Context, testEnv *Execution) error {
- if testEnv.DaemonInfo.OSType == "linux" {
- err := load.FrozenImagesLinux(ctx, testEnv.APIClient(), frozenImages...)
- if err != nil {
- return errors.Wrap(err, "error loading frozen images")
- }
- }
- return nil
- }
- // GitHubActions is true if test is executed on a GitHub Runner.
- func (e *Execution) GitHubActions() bool {
- return os.Getenv("GITHUB_ACTIONS") != ""
- }
- // NotAmd64 returns true if the daemon's architecture is not amd64
- func (e *Execution) NotAmd64() bool {
- return e.DaemonVersion.Arch != "amd64"
- }
|