// Copyright (c) 2019, Sylabs Inc. All rights reserved. // This software is licensed under a 3-clause BSD license. Please consult the // LICENSE.md file distributed with the sources of this project regarding your // rights to use or distribute this software. package cmdenvvars import ( "io/ioutil" "os" "path/filepath" "testing" "github.com/sylabs/scs-library-client/client" "github.com/sylabs/singularity/e2e/internal/e2e" ) type ctx struct { env e2e.TestEnv } func setupTempDirs(t *testing.T, readOnly bool) (string, string, func(t *testing.T)) { cacheDir, err := ioutil.TempDir("", "e2e-imgcache-") if err != nil { t.Fatalf("failed to create temporary directory: %s", err) } testDir, err := ioutil.TempDir("", "") if err != nil { os.RemoveAll(cacheDir) // Something went wrong before we can setup a cleanup function so we do our best to manually cleanup t.Fatalf("failed to create temporary directory: %s", err) } return testDir, cacheDir, func(t *testing.T) { err := os.RemoveAll(cacheDir) if err != nil { t.Fatalf("failed to delete temporary directory %s: %s", cacheDir, err) } err = os.RemoveAll(testDir) if err != nil { t.Fatalf("failed to delete temporary directory %s: %s", testDir, err) } } } func (c *ctx) testSingularityImgCache(t *testing.T, disableCache bool) string { if disableCache { c.env.DisableCache = true } imgName := "testImg.sif" imgPath := filepath.Join(c.env.TestDir, imgName) cmdArgs := []string{imgPath, "library://alpine:latest"} // Build the image. We make sure to use RunSingularity since the goal here is to check // whether it does the correct thing or not. c.env.RunSingularity( t, e2e.WithPrivileges(false), e2e.WithCommand("pull"), e2e.WithArgs(cmdArgs...), e2e.ExpectExit(0), ) return imgPath } // cacheExists checks that the image cache that is associated to the test exists // and is valid (i.e., include the correct entry) func (c *ctx) cacheIsNotExist(t *testing.T, imgPath string) { cacheRoot := filepath.Join(c.env.ImgCacheDir, "cache") if _, err := os.Stat(cacheRoot); !os.IsNotExist(err) { // The root of the cache does exists t.Fatalf("cache has been incorrectly created (cache root: %s)", cacheRoot) } } func (c *ctx) testSingularityCacheDir(t *testing.T) { // The intent of the test is simple: // - create 2 temporary directories, one where the image will be pulled and one where the // image cache should be created, // - pull an image, // - check whether we have the correct entry in the cache, within the directory we created. // If the file is in our cache, it means the e2e framework correctly set the SINGULARITY_CACHE_DIR // while executing the pull command. testDir, cacheDir, cleanup := setupTempDirs(t, false) c.env.TestDir = testDir defer cleanup(t) c.env.ImgCacheDir = cacheDir imgPath := c.testSingularityImgCache(t, false) // The cache should exist and have the correct entry shasum, err := client.ImageHash(imgPath) if err != nil { t.Fatalf("Cannot get the shasum for image %s: %s", imgPath, err) } cacheEntryPath := filepath.Join(c.env.ImgCacheDir, "cache", "library", shasum, "alpine_latest.sif") if _, err := os.Stat(cacheEntryPath); os.IsNotExist(err) { t.Fatalf("Cache entry %s does not exists: %s", cacheEntryPath, err) } } func (c *ctx) testSingularityDisableCache(t *testing.T) { testDir, cacheDir, cleanup := setupTempDirs(t, false) c.env.TestDir = testDir defer cleanup(t) c.env.ImgCacheDir = cacheDir imgPath := c.testSingularityImgCache(t, true) // the cache should not exist c.cacheIsNotExist(t, imgPath) } // This test checks if the cache is correctly and implicitly disabled // when its target location is read-only. // // This use case is common in the context of Grid computing where the // usage of sandboxes shared between users is a common practice. In that // context, the home directory ends up being read-only and no caching // is required. func (c *ctx) testSingularityReadOnlyCacheDir(t *testing.T) { testDir, cacheDir, cleanup := setupTempDirs(t, true) c.env.TestDir = testDir defer cleanup(t) // Change the mode of the image cache to read-only err := os.Chmod(cacheDir, 0444) if err != nil { t.Fatalf("failed to change the access mode to read-only: %s", err) } c.env.ImgCacheDir = cacheDir imgPath := c.testSingularityImgCache(t, false) // Change the mode of the image cache back so we can actually check everything err = os.Chmod(cacheDir, 0755) if err != nil { t.Fatalf("failed to change the access mode to read-only: %s", err) } // the cache should not exist c.cacheIsNotExist(t, imgPath) } func (c *ctx) testSingularitySypgpDir(t *testing.T) { // Create a temporary directory to be used for a keyring keyringDir, err := ioutil.TempDir("", "e2e-sypgp-env-") if err != nil { t.Fatalf("failed to create temporary directory: %s", err) } defer func() { err := os.RemoveAll(keyringDir) if err != nil { t.Fatalf("failed to delete temporary directory %s: %s", keyringDir, err) } }() // Run 'key list' to initialize the keyring. cmdArgs := []string{"list"} c.env.KeyringDir = keyringDir c.env.RunSingularity( t, e2e.WithPrivileges(false), e2e.WithCommand("key"), e2e.WithArgs(cmdArgs...), e2e.ExpectExit(0), ) pubKeyringPath := filepath.Join(keyringDir, "pgp-public") if _, err := os.Stat(pubKeyringPath); os.IsNotExist(err) { t.Fatalf("failed to find keyring (expected: %s)", pubKeyringPath) } privKeyringPath := filepath.Join(keyringDir, "pgp-secret") if _, err := os.Stat(privKeyringPath); os.IsNotExist(err) { t.Fatalf("failed to find keyring (expected: %s)", privKeyringPath) } } // RunE2ETests is the bootstrap to run all instance tests. func RunE2ETests(env e2e.TestEnv) func(*testing.T) { c := &ctx{ env: env, } return func(t *testing.T) { t.Run("testSingularityCacheDir", c.testSingularityCacheDir) t.Run("testSingularityDisableDir", c.testSingularityDisableCache) t.Run("testSingularitySypgpDir", c.testSingularitySypgpDir) t.Run("testReadOnlyCacheDir", c.testSingularityReadOnlyCacheDir) } }