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 }