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