17813: Upload .sif file to cache project & read it from keep
authorPeter Amstutz <peter.amstutz@curii.com>
Mon, 19 Jul 2021 20:13:33 +0000 (16:13 -0400)
committerPeter Amstutz <peter.amstutz@curii.com>
Mon, 19 Jul 2021 20:13:33 +0000 (16:13 -0400)
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>

lib/crunchrun/crunchrun.go
lib/crunchrun/docker.go
lib/crunchrun/executor.go
lib/crunchrun/singularity.go

index 59ef73e65ad45eeef7d15629a4334db22f641804..1937b8815aa93ca154edbe04f5b0a05b46f670e0 100644 (file)
@@ -270,7 +270,7 @@ func (runner *ContainerRunner) LoadImage() (string, error) {
                        return "", err
                }
        } else {
-               runner.CrunchLog.Print("Docker image is available")
+               runner.CrunchLog.Print("Container image is available")
        }
        return imageID, nil
 }
@@ -1458,6 +1458,10 @@ func (runner *ContainerRunner) Run() (err error) {
                return
        }
 
+       // Communicate some additional configuration to the executor
+       runner.executor.SetArvadoClient(runner.containerClient, runner.ContainerKeepClient,
+               runner.Container, runner.ArvMountPoint)
+
        // check for and/or load image
        imageID, err := runner.LoadImage()
        if err != nil {
@@ -1559,8 +1563,6 @@ func (runner *ContainerRunner) fetchContainerRecord() error {
        }
        runner.SecretMounts = sm.SecretMounts
 
-       runner.executor.SetArvadoClient(runner.containerClient, runner.Container)
-
        return nil
 }
 
index 05e540da8083696b8f0f4c08d5778a826f733518..4d3f7caf9a75f4eb5b614f91e1fbc2e9899a7979 100644 (file)
@@ -254,5 +254,5 @@ func (e *dockerExecutor) Close() {
        e.dockerclient.ContainerRemove(context.TODO(), e.containerID, dockertypes.ContainerRemoveOptions{Force: true})
 }
 
-func (e *dockerExecutor) SetArvadoClient(containerClient *arvados.Client, container arvados.Container) {
+func (e *dockerExecutor) SetArvadoClient(containerClient *arvados.Client, keepClient IKeepClient, container arvados.Container, keepMount string) {
 }
index 09258ed9db7d161c9bf25d42ec7a127a7baa9f89..05e9adefe3adc62c0bfb25504e4f0486ba5ae5b2 100644 (file)
@@ -63,5 +63,5 @@ type containerExecutor interface {
        Close()
 
        // Give the executor access to arvados client & container info
-       SetArvadoClient(containerClient *arvados.Client, container arvados.Container)
+       SetArvadoClient(containerClient *arvados.Client, keepClient IKeepClient, container arvados.Container, keepMount string)
 }
index 45926b065fa04d743edb15f2e5ac0107f31bb0cd..cd03e32e5c0dac95060be4dcbc5ff8f89d5039d8 100644 (file)
@@ -6,6 +6,7 @@ package crunchrun
 
 import (
        "fmt"
+       "io"
        "io/ioutil"
        "os"
        "os/exec"
@@ -25,6 +26,8 @@ type singularityExecutor struct {
        imageFilename   string // "sif" image
        containerClient *arvados.Client
        container       arvados.Container
+       keepClient      IKeepClient
+       keepMount       string
 }
 
 func newSingularityExecutor(logf func(string, ...interface{})) (*singularityExecutor, error) {
@@ -47,11 +50,12 @@ func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string,
                        arvados.Filter{"owner_uuid", "=", ownerUuid},
                        arvados.Filter{"name", "=", name},
                        arvados.Filter{"group_class", "=", "project"},
-               }})
+               },
+                       Limit: 1})
        if err != nil {
                return nil, err
        }
-       if len(gp.Items) > 0 {
+       if len(gp.Items) == 1 {
                return &gp.Items[0], nil
        }
        if !create {
@@ -74,16 +78,56 @@ func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string,
        return &rgroup, nil
 }
 
-func (e *singularityExecutor) ImageLoaded(string) bool {
+func (e *singularityExecutor) ImageLoaded(imageId string) bool {
        // Check if docker image is cached in keep & if so set imageFilename
 
-       return false
+       // Cache the image to keep
+       cacheGroup, err := e.getOrCreateProject(e.container.RuntimeUserUUID, ".cache", false)
+       if err != nil {
+               e.logf("error getting '.cache' project: %v", err)
+               return false
+       }
+       imageGroup, err := e.getOrCreateProject(cacheGroup.UUID, "auto-generated singularity images", false)
+       if err != nil {
+               e.logf("error getting 'auto-generated singularity images' project: %s", err)
+               return false
+       }
+
+       collectionName := fmt.Sprintf("singularity image for %v", imageId)
+       var cl arvados.CollectionList
+       err = e.containerClient.RequestAndDecode(&cl,
+               arvados.EndpointCollectionList.Method,
+               arvados.EndpointCollectionList.Path,
+               nil, arvados.ListOptions{Filters: []arvados.Filter{
+                       arvados.Filter{"owner_uuid", "=", imageGroup.UUID},
+                       arvados.Filter{"name", "=", collectionName},
+               },
+                       Limit: 1})
+       if err != nil {
+               e.logf("error getting collection '%v' project: %v", err)
+               return false
+       }
+       if len(cl.Items) == 0 {
+               e.logf("no cached image '%v' found", collectionName)
+               return false
+       }
+
+       path := fmt.Sprintf("%s/by_id/%s/image.sif", e.keepMount, cl.Items[0].PortableDataHash)
+       e.logf("Looking for %v", path)
+       if _, err = os.Stat(path); os.IsNotExist(err) {
+               return false
+       }
+       e.imageFilename = path
+
+       return true
 }
 
 // LoadImage will satisfy ContainerExecuter interface transforming
 // containerImage into a sif file for later use.
 func (e *singularityExecutor) LoadImage(imageTarballPath string) error {
        if e.imageFilename != "" {
+               e.logf("using singularity image %v", e.imageFilename)
+
                // was set by ImageLoaded
                return nil
        }
@@ -118,30 +162,62 @@ func (e *singularityExecutor) LoadImage(imageTarballPath string) error {
        // Cache the image to keep
        cacheGroup, err := e.getOrCreateProject(e.container.RuntimeUserUUID, ".cache", true)
        if err != nil {
-               e.logf("error getting '.cache' project: %s", err)
+               e.logf("error getting '.cache' project: %v", err)
                return nil
        }
        imageGroup, err := e.getOrCreateProject(cacheGroup.UUID, "auto-generated singularity images", true)
        if err != nil {
-               e.logf("error getting 'auto-generated singularity images' project: %s", err)
+               e.logf("error getting 'auto-generated singularity images' project: %v", err)
                return nil
        }
 
        parts := strings.Split(imageTarballPath, "/")
        imageId := parts[len(parts)-1]
+       if strings.HasSuffix(imageId, ".tar") {
+               imageId = imageId[0 : len(imageId)-4]
+       }
+
+       fs, err := (&arvados.Collection{ManifestText: ""}).FileSystem(e.containerClient, e.keepClient)
+       if err != nil {
+               e.logf("error creating FileSystem: %s", err)
+       }
+
+       dst, err := fs.OpenFile("image.sif", os.O_CREATE|os.O_WRONLY, 0666)
+       if err != nil {
+               e.logf("error creating opening collection file for writing: %s", err)
+       }
+
+       src, err := os.Open(e.imageFilename)
+       if err != nil {
+               dst.Close()
+               return nil
+       }
+       defer src.Close()
+       _, err = io.Copy(dst, src)
+       if err != nil {
+               dst.Close()
+               return nil
+       }
+
+       manifestText, err := fs.MarshalManifest(".")
+       if err != nil {
+               e.logf("error creating manifest text: %s", err)
+       }
 
        var imageCollection arvados.Collection
+       collectionName := fmt.Sprintf("singularity image for %s", imageId)
        err = e.containerClient.RequestAndDecode(&imageCollection,
                arvados.EndpointCollectionCreate.Method,
                arvados.EndpointCollectionCreate.Path,
                nil, map[string]interface{}{
                        "collection": map[string]string{
-                               "owner_uuid": imageGroup.UUID,
-                               "name": fmt.Sprintf("singularity image for %s", imageId),
-                       }
+                               "owner_uuid":    imageGroup.UUID,
+                               "name":          collectionName,
+                               "manifest_text": manifestText,
+                       },
                })
        if err != nil {
-               e.logf("error creating 'auto-generated singularity images' collection: %s", err)
+               e.logf("error creating '%v' collection: %s", collectionName, err)
        }
 
        return nil
@@ -232,7 +308,9 @@ func (e *singularityExecutor) Close() {
        }
 }
 
-func (e *singularityExecutor) SetArvadoClient(containerClient *arvados.Client, container arvados.Container) {
+func (e *singularityExecutor) SetArvadoClient(containerClient *arvados.Client, keepClient IKeepClient, container arvados.Container, keepMount string) {
        e.containerClient = containerClient
        e.container = container
+       e.keepClient = keepClient
+       e.keepMount = keepMount
 }