[ARVADOS] updated: 2.1.0-1081-g5ef89a24d
Git user
git at public.arvados.org
Thu Jul 22 20:23:12 UTC 2021
Summary of changes:
lib/crunchrun/crunchrun.go | 23 +++--
lib/crunchrun/docker.go | 15 ++--
lib/crunchrun/executor.go | 10 +--
lib/crunchrun/singularity.go | 210 +++++++++++++++++--------------------------
4 files changed, 102 insertions(+), 156 deletions(-)
via 5ef89a24d5fa8bc6926a433e22360a09fdb3154d (commit)
from cc476f769c06ac6150a4f6d406eb2656f41e1dc3 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 5ef89a24d5fa8bc6926a433e22360a09fdb3154d
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Thu Jul 22 16:22:39 2021 -0400
17813: Refactor singularity image loading / caching / conversion
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/lib/crunchrun/crunchrun.go b/lib/crunchrun/crunchrun.go
index 1937b8815..d4a865072 100644
--- a/lib/crunchrun/crunchrun.go
+++ b/lib/crunchrun/crunchrun.go
@@ -260,18 +260,15 @@ func (runner *ContainerRunner) LoadImage() (string, error) {
return "", fmt.Errorf("cannot choose from multiple tar files in image collection: %v", tarfiles)
}
imageID := tarfiles[0][:len(tarfiles[0])-4]
- imageFile := runner.ArvMountPoint + "/by_id/" + runner.Container.ContainerImage + "/" + tarfiles[0]
runner.CrunchLog.Printf("Using Docker image id %q", imageID)
- if !runner.executor.ImageLoaded(imageID) {
- runner.CrunchLog.Print("Loading Docker image from keep")
- err = runner.executor.LoadImage(imageFile)
- if err != nil {
- return "", err
- }
- } else {
- runner.CrunchLog.Print("Container image is available")
+ runner.CrunchLog.Print("Loading Docker image from keep")
+ err = runner.executor.LoadImage(imageID, runner.Container, runner.ArvMountPoint,
+ runner.containerClient, runner.ContainerKeepClient)
+ if err != nil {
+ return "", err
}
+
return imageID, nil
}
@@ -599,6 +596,7 @@ func (runner *ContainerRunner) SetupMounts() (map[string]bindmount, error) {
} else {
arvMountCmd = append(arvMountCmd, "--mount-by-id", "by_id")
}
+ arvMountCmd = append(arvMountCmd, "--mount-by-id", "by_uuid")
arvMountCmd = append(arvMountCmd, runner.ArvMountPoint)
runner.ArvMount, err = runner.RunArvMount(arvMountCmd, token)
@@ -1201,12 +1199,14 @@ func (runner *ContainerRunner) CleanupDirs() {
}
}
}
+ runner.ArvMount = nil
}
if runner.ArvMountPoint != "" {
if rmerr := os.Remove(runner.ArvMountPoint); rmerr != nil {
runner.CrunchLog.Printf("While cleaning up arv-mount directory %s: %v", runner.ArvMountPoint, rmerr)
}
+ runner.ArvMountPoint = ""
}
if rmerr := os.RemoveAll(runner.parentTemp); rmerr != nil {
@@ -1441,6 +1441,7 @@ func (runner *ContainerRunner) Run() (err error) {
}
checkErr("stopHoststat", runner.stopHoststat())
checkErr("CommitLogs", runner.CommitLogs())
+ runner.CleanupDirs()
checkErr("UpdateContainerFinal", runner.UpdateContainerFinal())
}()
@@ -1458,10 +1459,6 @@ 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 {
diff --git a/lib/crunchrun/docker.go b/lib/crunchrun/docker.go
index 4d3f7caf9..10a2dd38e 100644
--- a/lib/crunchrun/docker.go
+++ b/lib/crunchrun/docker.go
@@ -46,12 +46,16 @@ func newDockerExecutor(containerUUID string, logf func(string, ...interface{}),
}, err
}
-func (e *dockerExecutor) ImageLoaded(imageID string) bool {
+func (e *dockerExecutor) LoadImage(imageID string, container arvados.Container, arvMountPoint string,
+ containerClient *arvados.Client, keepClient IKeepClient) error {
_, _, err := e.dockerclient.ImageInspectWithRaw(context.TODO(), imageID)
- return err == nil
-}
+ if err == nil {
+ // already loaded
+ return nil
+ }
+
+ filename := arvMountPoint + "/by_id/" + container.ContainerImage + "/" + imageID + ".tar"
-func (e *dockerExecutor) LoadImage(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
@@ -253,6 +257,3 @@ func (e *dockerExecutor) handleStdoutStderr(stdout, stderr io.Writer, reader io.
func (e *dockerExecutor) Close() {
e.dockerclient.ContainerRemove(context.TODO(), e.containerID, dockertypes.ContainerRemoveOptions{Force: true})
}
-
-func (e *dockerExecutor) SetArvadoClient(containerClient *arvados.Client, keepClient IKeepClient, container arvados.Container, keepMount string) {
-}
diff --git a/lib/crunchrun/executor.go b/lib/crunchrun/executor.go
index 05e9adefe..787edda01 100644
--- a/lib/crunchrun/executor.go
+++ b/lib/crunchrun/executor.go
@@ -34,13 +34,10 @@ type containerSpec struct {
// containerExecutor is an interface to a container runtime
// (docker/singularity).
type containerExecutor interface {
- // ImageLoaded determines whether the given image is already
- // available to use without calling ImageLoad.
- ImageLoaded(imageID string) bool
-
// ImageLoad loads the image from the given tarball such that
// it can be used to create/start a container.
- LoadImage(filename string) error
+ LoadImage(imageID string, container arvados.Container, keepMount string,
+ containerClient *arvados.Client, keepClient IKeepClient) error
// Wait for the container process to finish, and return its
// exit code. If applicable, also remove the stopped container
@@ -61,7 +58,4 @@ type containerExecutor interface {
// Release resources (temp dirs, stopped containers)
Close()
-
- // Give the executor access to arvados client & container info
- SetArvadoClient(containerClient *arvados.Client, keepClient IKeepClient, container arvados.Container, keepMount string)
}
diff --git a/lib/crunchrun/singularity.go b/lib/crunchrun/singularity.go
index 1a1567451..5d930626a 100644
--- a/lib/crunchrun/singularity.go
+++ b/lib/crunchrun/singularity.go
@@ -6,28 +6,23 @@ package crunchrun
import (
"fmt"
- "io"
"io/ioutil"
"os"
"os/exec"
"sort"
- "strings"
"syscall"
+ "time"
"git.arvados.org/arvados.git/sdk/go/arvados"
"golang.org/x/net/context"
)
type singularityExecutor struct {
- logf func(string, ...interface{})
- spec containerSpec
- tmpdir string
- child *exec.Cmd
- imageFilename string // "sif" image
- containerClient *arvados.Client
- container arvados.Container
- keepClient IKeepClient
- keepMount string
+ logf func(string, ...interface{})
+ spec containerSpec
+ tmpdir string
+ child *exec.Cmd
+ imageFilename string // "sif" image
}
func newSingularityExecutor(logf func(string, ...interface{})) (*singularityExecutor, error) {
@@ -41,9 +36,9 @@ func newSingularityExecutor(logf func(string, ...interface{})) (*singularityExec
}, nil
}
-func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string, create bool) (*arvados.Group, error) {
+func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string, containerClient *arvados.Client) (*arvados.Group, error) {
var gp arvados.GroupList
- err := e.containerClient.RequestAndDecode(&gp,
+ err := containerClient.RequestAndDecode(&gp,
arvados.EndpointGroupList.Method,
arvados.EndpointGroupList.Path,
nil, arvados.ListOptions{Filters: []arvados.Filter{
@@ -58,11 +53,9 @@ func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string,
if len(gp.Items) == 1 {
return &gp.Items[0], nil
}
- if !create {
- return nil, nil
- }
+
var rgroup arvados.Group
- err = e.containerClient.RequestAndDecode(&rgroup,
+ err = containerClient.RequestAndDecode(&rgroup,
arvados.EndpointGroupCreate.Method,
arvados.EndpointGroupCreate.Path,
nil, map[string]interface{}{
@@ -78,28 +71,22 @@ func (e *singularityExecutor) getOrCreateProject(ownerUuid string, name string,
return &rgroup, nil
}
-func (e *singularityExecutor) ImageLoaded(imageId string) bool {
- if e.containerClient == nil {
- return false
- }
-
- // Check if docker image is cached in keep & if so set imageFilename
+func (e *singularityExecutor) checkImageCache(dockerImageID string, container arvados.Container, arvMountPoint string,
+ containerClient *arvados.Client, keepClient IKeepClient) (collectionUuid string, err error) {
// Cache the image to keep
- cacheGroup, err := e.getOrCreateProject(e.container.RuntimeUserUUID, ".cache", false)
+ cacheGroup, err := e.getOrCreateProject(container.RuntimeUserUUID, ".cache", containerClient)
if err != nil {
- e.logf("error getting '.cache' project: %v", err)
- return false
+ return "", fmt.Errorf("error getting '.cache' project: %v", err)
}
- imageGroup, err := e.getOrCreateProject(cacheGroup.UUID, "auto-generated singularity images", false)
+ imageGroup, err := e.getOrCreateProject(cacheGroup.UUID, "auto-generated singularity images", containerClient)
if err != nil {
- e.logf("error getting 'auto-generated singularity images' project: %s", err)
- return false
+ return "", fmt.Errorf("error getting 'auto-generated singularity images' project: %s", err)
}
- collectionName := fmt.Sprintf("singularity image for %v", imageId)
+ collectionName := fmt.Sprintf("singularity image for %v", dockerImageID)
var cl arvados.CollectionList
- err = e.containerClient.RequestAndDecode(&cl,
+ err = containerClient.RequestAndDecode(&cl,
arvados.EndpointCollectionList.Method,
arvados.EndpointCollectionList.Path,
nil, arvados.ListOptions{Filters: []arvados.Filter{
@@ -108,125 +95,99 @@ func (e *singularityExecutor) ImageLoaded(imageId string) bool {
},
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
+ return "", fmt.Errorf("error querying for collection '%v': %v", err)
}
+ var imageCollection arvados.Collection
+ if len(cl.Items) == 1 {
+ imageCollection = cl.Items[0]
+ } else {
+ collectionName := collectionName + " " + time.Now().UTC().Format(time.RFC3339)
+
+ err = containerClient.RequestAndDecode(&imageCollection,
+ arvados.EndpointCollectionCreate.Method,
+ arvados.EndpointCollectionCreate.Path,
+ nil, map[string]interface{}{
+ "collection": map[string]string{
+ "owner_uuid": imageGroup.UUID,
+ "name": collectionName,
+ },
+ })
+ if err != nil {
+ e.logf("error creating '%v' collection: %s", collectionName, err)
+ }
- 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
+ return imageCollection.UUID, nil
}
// 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
- }
+func (e *singularityExecutor) LoadImage(dockerImageID string, container arvados.Container, arvMountPoint string,
+ containerClient *arvados.Client, keepClient IKeepClient) error {
- e.logf("building singularity image")
- // "singularity build" does not accept a
- // docker-archive://... filename containing a ":" character,
- // as in "/path/to/sha256:abcd...1234.tar". Workaround: make a
- // symlink that doesn't have ":" chars.
- err := os.Symlink(imageTarballPath, e.tmpdir+"/image.tar")
- if err != nil {
- return err
- }
- e.imageFilename = e.tmpdir + "/image.sif"
- build := exec.Command("singularity", "build", e.imageFilename, "docker-archive://"+e.tmpdir+"/image.tar")
- e.logf("%v", build.Args)
- out, err := build.CombinedOutput()
- // INFO: Starting build...
- // Getting image source signatures
- // Copying blob ab15617702de done
- // Copying config 651e02b8a2 done
- // Writing manifest to image destination
- // Storing signatures
- // 2021/04/22 14:42:14 info unpack layer: sha256:21cbfd3a344c52b197b9fa36091e66d9cbe52232703ff78d44734f85abb7ccd3
- // INFO: Creating SIF file...
- // INFO: Build complete: arvados-jobs.latest.sif
- e.logf("%s", out)
+ sifCollectionUUID, err := e.checkImageCache(dockerImageID, container, arvMountPoint, containerClient, keepClient)
if err != nil {
return err
}
- if e.containerClient == nil {
- return nil
- }
+ imageTarballPath := arvMountPoint + "/by_id/" + container.ContainerImage + "/" + dockerImageID + ".tar"
- // Cache the image to keep
- cacheGroup, err := e.getOrCreateProject(e.container.RuntimeUserUUID, ".cache", true)
- if err != nil {
- 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: %v", err)
- return nil
- }
+ imageFilename := fmt.Sprintf("%s/by_uuid/%s/image.sif", arvMountPoint, sifCollectionUUID)
- parts := strings.Split(imageTarballPath, "/")
- imageId := parts[len(parts)-1]
- if strings.HasSuffix(imageId, ".tar") {
- imageId = imageId[0 : len(imageId)-4]
- }
+ if _, err = os.Stat(imageFilename); os.IsNotExist(err) {
+ exec.Command("find", arvMountPoint+"/by_id/").Run()
- fs, err := (&arvados.Collection{ManifestText: ""}).FileSystem(e.containerClient, e.keepClient)
- if err != nil {
- e.logf("error creating FileSystem: %s", err)
- }
+ e.logf("building singularity image")
+ // "singularity build" does not accept a
+ // docker-archive://... filename containing a ":" character,
+ // as in "/path/to/sha256:abcd...1234.tar". Workaround: make a
+ // symlink that doesn't have ":" chars.
+ err := os.Symlink(imageTarballPath, e.tmpdir+"/image.tar")
+ if err != nil {
+ return 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)
+ build := exec.Command("singularity", "build", imageFilename, "docker-archive://"+e.tmpdir+"/image.tar")
+ e.logf("%v", build.Args)
+ out, err := build.CombinedOutput()
+ // INFO: Starting build...
+ // Getting image source signatures
+ // Copying blob ab15617702de done
+ // Copying config 651e02b8a2 done
+ // Writing manifest to image destination
+ // Storing signatures
+ // 2021/04/22 14:42:14 info unpack layer: sha256:21cbfd3a344c52b197b9fa36091e66d9cbe52232703ff78d44734f85abb7ccd3
+ // INFO: Creating SIF file...
+ // INFO: Build complete: arvados-jobs.latest.sif
+ e.logf("%s", out)
+ if err != nil {
+ return 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
- }
+ // update TTL to now + two weeks
+ exp := time.Now().Add(24 * 7 * 2 * time.Hour)
- manifestText, err := fs.MarshalManifest(".")
+ uuidPath, err := containerClient.PathForUUID("update", sifCollectionUUID)
if err != nil {
- e.logf("error creating manifest text: %s", err)
+ e.logf("error PathForUUID: %v", err)
+ return nil
}
-
var imageCollection arvados.Collection
- collectionName := fmt.Sprintf("singularity image for %s", imageId)
- err = e.containerClient.RequestAndDecode(&imageCollection,
- arvados.EndpointCollectionCreate.Method,
- arvados.EndpointCollectionCreate.Path,
+ err = containerClient.RequestAndDecode(&imageCollection,
+ arvados.EndpointCollectionUpdate.Method,
+ uuidPath,
nil, map[string]interface{}{
"collection": map[string]string{
- "owner_uuid": imageGroup.UUID,
- "name": collectionName,
- "manifest_text": manifestText,
+ "name": fmt.Sprintf("singularity image for %v", dockerImageID),
+ "trash_at": exp.UTC().Format(time.RFC3339),
},
})
if err != nil {
- e.logf("error creating '%v' collection: %s", collectionName, err)
+ e.logf("error updating collection trash_at: %v", err)
}
+ e.imageFilename = fmt.Sprintf("%s/by_id/%s/image.sif", arvMountPoint, imageCollection.PortableDataHash)
return nil
}
@@ -321,10 +282,3 @@ func (e *singularityExecutor) Close() {
e.logf("error removing temp dir: %s", err)
}
}
-
-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
-}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list