github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/testutil/environment/environment.go (about)

     1  package environment // import "github.com/Prakhar-Agarwal-byte/moby/testutil/environment"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
    12  	"github.com/Prakhar-Agarwal-byte/moby/api/types/filters"
    13  	"github.com/Prakhar-Agarwal-byte/moby/api/types/system"
    14  	"github.com/Prakhar-Agarwal-byte/moby/client"
    15  	"github.com/Prakhar-Agarwal-byte/moby/testutil/fixtures/load"
    16  	"github.com/pkg/errors"
    17  	"gotest.tools/v3/assert"
    18  )
    19  
    20  // Execution contains information about the current test execution and daemon
    21  // under test
    22  type Execution struct {
    23  	client            client.APIClient
    24  	DaemonInfo        system.Info
    25  	DaemonVersion     types.Version
    26  	PlatformDefaults  PlatformDefaults
    27  	protectedElements protectedElements
    28  }
    29  
    30  // PlatformDefaults are defaults values for the platform of the daemon under test
    31  type PlatformDefaults struct {
    32  	BaseImage            string
    33  	VolumesConfigPath    string
    34  	ContainerStoragePath string
    35  }
    36  
    37  // New creates a new Execution struct
    38  // This is configured using the env client (see client.FromEnv)
    39  func New(ctx context.Context) (*Execution, error) {
    40  	c, err := client.NewClientWithOpts(client.FromEnv)
    41  	if err != nil {
    42  		return nil, errors.Wrapf(err, "failed to create client")
    43  	}
    44  	return FromClient(ctx, c)
    45  }
    46  
    47  // FromClient creates a new Execution environment from the passed in client
    48  func FromClient(ctx context.Context, c *client.Client) (*Execution, error) {
    49  	info, err := c.Info(ctx)
    50  	if err != nil {
    51  		return nil, errors.Wrapf(err, "failed to get info from daemon")
    52  	}
    53  	v, err := c.ServerVersion(context.Background())
    54  	if err != nil {
    55  		return nil, errors.Wrapf(err, "failed to get version info from daemon")
    56  	}
    57  
    58  	return &Execution{
    59  		client:            c,
    60  		DaemonInfo:        info,
    61  		DaemonVersion:     v,
    62  		PlatformDefaults:  getPlatformDefaults(info),
    63  		protectedElements: newProtectedElements(),
    64  	}, nil
    65  }
    66  
    67  func getPlatformDefaults(info system.Info) PlatformDefaults {
    68  	volumesPath := filepath.Join(info.DockerRootDir, "volumes")
    69  	containersPath := filepath.Join(info.DockerRootDir, "containers")
    70  
    71  	switch info.OSType {
    72  	case "linux":
    73  		return PlatformDefaults{
    74  			BaseImage:            "scratch",
    75  			VolumesConfigPath:    toSlash(volumesPath),
    76  			ContainerStoragePath: toSlash(containersPath),
    77  		}
    78  	case "windows":
    79  		baseImage := "mcr.microsoft.com/windows/servercore:ltsc2022"
    80  		if overrideBaseImage := os.Getenv("WINDOWS_BASE_IMAGE"); overrideBaseImage != "" {
    81  			baseImage = overrideBaseImage
    82  			if overrideBaseImageTag := os.Getenv("WINDOWS_BASE_IMAGE_TAG"); overrideBaseImageTag != "" {
    83  				baseImage = baseImage + ":" + overrideBaseImageTag
    84  			}
    85  		}
    86  		fmt.Println("INFO: Windows Base image is ", baseImage)
    87  		return PlatformDefaults{
    88  			BaseImage:            baseImage,
    89  			VolumesConfigPath:    filepath.FromSlash(volumesPath),
    90  			ContainerStoragePath: filepath.FromSlash(containersPath),
    91  		}
    92  	default:
    93  		panic(fmt.Sprintf("unknown OSType for daemon: %s", info.OSType))
    94  	}
    95  }
    96  
    97  // Make sure in context of daemon, not the local platform. Note we can't
    98  // use filepath.ToSlash here as that is a no-op on Unix.
    99  func toSlash(path string) string {
   100  	return strings.ReplaceAll(path, `\`, `/`)
   101  }
   102  
   103  // IsLocalDaemon is true if the daemon under test is on the same
   104  // host as the test process.
   105  //
   106  // Deterministically working out the environment in which CI is running
   107  // to evaluate whether the daemon is local or remote is not possible through
   108  // a build tag.
   109  //
   110  // For example Windows to Linux CI under Jenkins tests the 64-bit
   111  // Windows binary build with the daemon build tag, but calls a remote
   112  // Linux daemon.
   113  //
   114  // We can't just say if Windows then assume the daemon is local as at
   115  // some point, we will be testing the Windows CLI against a Windows daemon.
   116  //
   117  // Similarly, it will be perfectly valid to also run CLI tests from
   118  // a Linux CLI (built with the daemon tag) against a Windows daemon.
   119  func (e *Execution) IsLocalDaemon() bool {
   120  	return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
   121  }
   122  
   123  // IsRemoteDaemon is true if the daemon under test is on different host
   124  // as the test process.
   125  func (e *Execution) IsRemoteDaemon() bool {
   126  	return !e.IsLocalDaemon()
   127  }
   128  
   129  // DaemonAPIVersion returns the negotiated daemon api version
   130  func (e *Execution) DaemonAPIVersion() string {
   131  	version, err := e.APIClient().ServerVersion(context.TODO())
   132  	if err != nil {
   133  		return ""
   134  	}
   135  	return version.APIVersion
   136  }
   137  
   138  // Print the execution details to stdout
   139  // TODO: print everything
   140  func (e *Execution) Print() {
   141  	if e.IsLocalDaemon() {
   142  		fmt.Println("INFO: Testing against a local daemon")
   143  	} else {
   144  		fmt.Println("INFO: Testing against a remote daemon")
   145  	}
   146  }
   147  
   148  // APIClient returns an APIClient connected to the daemon under test
   149  func (e *Execution) APIClient() client.APIClient {
   150  	return e.client
   151  }
   152  
   153  // IsUserNamespace returns whether the user namespace remapping is enabled
   154  func (e *Execution) IsUserNamespace() bool {
   155  	root := os.Getenv("DOCKER_REMAP_ROOT")
   156  	return root != ""
   157  }
   158  
   159  // RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows
   160  func (e *Execution) RuntimeIsWindowsContainerd() bool {
   161  	return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
   162  }
   163  
   164  // IsRootless returns whether the rootless mode is enabled
   165  func (e *Execution) IsRootless() bool {
   166  	return os.Getenv("DOCKER_ROOTLESS") != ""
   167  }
   168  
   169  // IsUserNamespaceInKernel returns whether the kernel supports user namespaces
   170  func (e *Execution) IsUserNamespaceInKernel() bool {
   171  	if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
   172  		/*
   173  		 * This kernel-provided file only exists if user namespaces are
   174  		 * supported
   175  		 */
   176  		return false
   177  	}
   178  
   179  	// We need extra check on redhat based distributions
   180  	if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil {
   181  		defer f.Close()
   182  		b := make([]byte, 1)
   183  		_, _ = f.Read(b)
   184  		return string(b) != "N"
   185  	}
   186  
   187  	return true
   188  }
   189  
   190  // UsingSnapshotter returns whether containerd snapshotters are used for the
   191  // tests by checking if the "TEST_INTEGRATION_USE_SNAPSHOTTER" is set to a
   192  // non-empty value.
   193  func (e *Execution) UsingSnapshotter() bool {
   194  	return os.Getenv("TEST_INTEGRATION_USE_SNAPSHOTTER") != ""
   195  }
   196  
   197  // HasExistingImage checks whether there is an image with the given reference.
   198  // Note that this is done by filtering and then checking whether there were any
   199  // results -- so ambiguous references might result in false-positives.
   200  func (e *Execution) HasExistingImage(t testing.TB, reference string) bool {
   201  	imageList, err := e.APIClient().ImageList(context.Background(), types.ImageListOptions{
   202  		All: true,
   203  		Filters: filters.NewArgs(
   204  			filters.Arg("dangling", "false"),
   205  			filters.Arg("reference", reference),
   206  		),
   207  	})
   208  	assert.NilError(t, err, "failed to list images")
   209  
   210  	return len(imageList) > 0
   211  }
   212  
   213  // EnsureFrozenImagesLinux loads frozen test images into the daemon
   214  // if they aren't already loaded
   215  func EnsureFrozenImagesLinux(ctx context.Context, testEnv *Execution) error {
   216  	if testEnv.DaemonInfo.OSType == "linux" {
   217  		err := load.FrozenImagesLinux(ctx, testEnv.APIClient(), frozenImages...)
   218  		if err != nil {
   219  			return errors.Wrap(err, "error loading frozen images")
   220  		}
   221  	}
   222  	return nil
   223  }
   224  
   225  // GitHubActions is true if test is executed on a GitHub Runner.
   226  func (e *Execution) GitHubActions() bool {
   227  	return os.Getenv("GITHUB_ACTIONS") != ""
   228  }
   229  
   230  // NotAmd64 returns true if the daemon's architecture is not amd64
   231  func (e *Execution) NotAmd64() bool {
   232  	return e.DaemonVersion.Arch != "amd64"
   233  }