Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ready to Merge: Update/package singularity #11094

Merged
merged 19 commits into from May 3, 2019
Merged

Ready to Merge: Update/package singularity #11094

merged 19 commits into from May 3, 2019

Conversation

vsoch
Copy link
Member

@vsoch vsoch commented Apr 2, 2019

This pull request will close #9698, specifically we are allowing for installation of Singularity (versions 3.0 and up). Specifically:

  • Singularity version 3.0 and up is a different install flow from (now legacy) Singularity - it uses golang as opposed to Autotools. There might be a better way to do this, but since both allow for installation from branches / source .tar.gz, I thought it would be cleaner to have singularity (3.0.0 and up) and 'singularity-legacy'.

Here are some things that I learned that could help others in the future (and we can discuss).

  • Go itself is installed under gcc at opt/spack/linux-ubuntu16.04-x86_64/gcc-5.4.0/go-1.11.5-73pcics3bti7rafvlgjzmcgtaeogebxg. It has a src folder but this is not GOPATH.
  • GOPATH is not defined by the module providing go. There is no "spack location" for go modules (I was thinking that maybe there should be, but I think it's cleaner to be able to compile Golang stuffs, and then get rid of the excessive repos that are usually found on a (kept) GOPATH.

GOPATH Troubles

As I mentioned, GOPATH is not defined, but Singularity is expected to be found under $GOPATH/src/github.com/sylabs/singularity. This led to most of the issues with install - the default location that the repo is dumped into doesn't match that. This means that we are able to compile, but when the time comes to build, the entire cloned repo is expected to be found under GOPATH. If we add the building directory to GOPATH, this actually means it's expected to find <build_dir>/src/github.com/sylabs/singularity which doesn't exist. If we try to change the build to some place other than there, we then lose having vendors/ in the present working directory and it breaks.

The solution I came up with was to create a temporary GOPATH in /tmp, and in fact we move everything there. This mimics the correct path setup and allows for configure / make / make install without any issues.

Signed-off-by: Vanessa Sochat <vsochat@stanford.edu>
Signed-off-by: Vanessa Sochat <vsochat@stanford.edu>
@vsoch vsoch mentioned this pull request Apr 2, 2019
@vsoch
Copy link
Member Author

vsoch commented Apr 3, 2019

And heads up (some future release) of Singularity will use go modules, which hypothetically should make the install easier! https://github.com/sylabs/singularity/pull/3184

@vsoch
Copy link
Member Author

vsoch commented Apr 8, 2019

ping - do you need anything else from me?

Copy link
Member

@baberlevi baberlevi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, I have not tried to install it yet (will do that soon)

@vsoch
Copy link
Member Author

vsoch commented Apr 9, 2019

Awesome!

There's a new rocket emoji response! I'll save that one for after you test :)

@hartzell
Copy link
Contributor

hartzell commented Apr 9, 2019

[edit: add note about warning when setting GOPATH]
[edit^2: see updated version below]

Here's an alternate implementation of this.

It overrides the staging functionality and just moves the unpacked source tree into location that go expects to find it.

A couple of other changes which your version will also need:

  • I don't understand why you have all your dependencies constrained to the develop version, you'll need them when you build the released versions too.
  • You can't use the github "archive" URL, which points to the automagically tar'ed up file; it's missing the VERSION file and the git bits that mconfig needs to figure out the package_version. Using the "download" URL (which points to the pre-autotool'ed tarball y'all uploaded) works. You could probably also re-run autotools and make it work.

The package.py below works for me but might stand some tuning. Haven't tried @develop. Are the depends all needed all the time and are they all build,link or are some run (e.g. squashfs)? I switched to MakefilePackage then overrode most of the interesting bits, I think. Perhaps the base Package is more reasonable (@adamjstewart?)? The develop version needs git, it's used in mconfig but you might not need it in the released versions.

This warns about Suspicious requests to set or unset 'GOPATH' found, because I'm heavy-handedly overwriting the GOPATH that was set by our dependency on go. I think only having this single thing on the GOPATH is cleaner/safer/neater. Seems like there's a force option (see set_or_unset_not_first in lib/spack/spack/util/environment.py, but using it is left as an exercise for the reader (not immediately obvious to me...)). Alternatively, prepending to to GOPATH with spack_env.prepend_path('GOPATH', self.stage.path) also appears to work.

 # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
 # Spack Project Developers. See the top-level COPYRIGHT file for details.
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)

 from spack import *
 import os
 import shutil
 import tempfile

 import llnl.util.tty as tty

 class SingularityAlt(MakefilePackage):
     '''Singularity is a container technology focused on building portable
        encapsulated environments to support "Mobility of Compute" For older
        versions of Singularity (pre 3.0) you should use singularity-legacy,
        which has a different install base (Autotools).
     '''

     homepage = "https://www.sylabs.io/singularity/"
     url      = "https://github.com/sylabs/singularity/releases/download/v3.1.0/singularity-3.1.0.tar.gz"
     git      = "https://github.com/singularityware/singularity.git"

     # ##########################################################################
     # 3.1.1 Release Branch (GoLang)

     version('develop', branch='master')
     version('3.1.1', '158f58a79db5337e1d655ee0159b641e42ea7435')

     depends_on('go')
     depends_on('libuuid')
     depends_on('libgpg-error')
     depends_on('squashfs')
     depends_on('git')

     phases = ['configure', 'build', 'install']

     # Where go traditionally thinls the source should be.
     def singularity_dir(self):
         return join_path(self.stage.path, 'src/github.com/sylabs/singularity')

     # Unpack the tarball as usual, then move it to the traditional location
     def do_stage(self, mirror_only=False):
         super(SingularityAlt, self).do_stage(mirror_only)
         shutil.move(self.stage.source_path, self.singularity_dir())

     def configure(self, spec, prefix):
         with working_dir(self.singularity_dir()):
             configure = Executable('./mconfig --prefix=%s' % prefix)
             configure()

     def build(self, spec, prefix):
         with working_dir(self.singularity_dir()):
             make('-C', 'builddir', parallel=False)

     def install(self, spec, prefix):
         with working_dir(self.singularity_dir()):
             make('install', '-C', 'builddir', parallel=False)

     def setup_environment(self, spack_env, run_env):
         # Point GOPATH at the top of the staging dir
         spack_env.set('GOPATH', self.stage.path)

@hartzell
Copy link
Contributor

hartzell commented Apr 9, 2019

What are you thinking about doing about the setuid bits?

@hartzell
Copy link
Contributor

hartzell commented Apr 9, 2019

pps. I'm pretty sure that in my alt implementation above there's a way to tell the package, just once, that configure and build and etc... should all be with working_dir(self.singuarity_dir()):, but I haven't dug it up yet.

@hartzell
Copy link
Contributor

I played golf with this a bit, took advantage of MakefilePackage's use of build_directory and generally cleaned/tightened it up. Both 3.1.1 and @develop build for me on CentOS 7. Haven't tried running them [yet].

 # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
 # Spack Project Developers. See the top-level COPYRIGHT file for details.
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)

 from spack import *
 import shutil


 class SingularityAlt(MakefilePackage):
     '''Singularity is a container technology focused on building portable
        encapsulated environments to support "Mobility of Compute" For older
        versions of Singularity (pre 3.0) you should use singularity-legacy,
        which has a different install base (Autotools).
     '''

     homepage = "https://www.sylabs.io/singularity/"
     url      = "https://github.com/sylabs/singularity/releases/download/v3.1.1/singularity-3.1.1.tar.gz"
     git      = "https://github.com/singularityware/singularity.git"

     version('develop', branch='master')
     version('3.1.1', '158f58a79db5337e1d655ee0159b641e42ea7435')

     depends_on('go')
     depends_on('libuuid')
     depends_on('libgpg-error')
     depends_on('squashfs', type='run')
     depends_on('git', when='@develop')  # mconfig uses it for version info

     # Where go traditionally thinks the source should be.
     # MakefilePackage uses this as working_dir in build/install steps
     @property
     def build_directory(self):
         return join_path(self.stage.path, 'src/github.com/sylabs/singularity')

     # Unpack the tarball as usual, then move it to the traditional location.
     def do_stage(self, mirror_only=False):
         super(SingularityAlt, self).do_stage(mirror_only)
         shutil.move(self.stage.source_path, self.build_directory)

     # Hijack the edit stage to run mconfig.
     def edit(self, spec, prefix):
         with working_dir(self.build_directory):
             configure = Executable('./mconfig --prefix=%s' % prefix)
             configure()

     # Set these for use by MakefilePackage's default build/install methods.
     build_targets = ['-C', 'builddir', 'parallel=False']
     install_targets = ['install', '-C', 'builddir', 'parallel=False']

     def setup_environment(self, spack_env, run_env):
         # Point GOPATH at the top of the staging dir.
         spack_env.prepend_path('GOPATH', self.stage.path)

@hartzell
Copy link
Contributor

hartzell commented Apr 10, 2019

[edit: added a note about moving the run_before after the edit def'n]

Thinking it might appear less magical, I tried replacing the do_stage method with something uses the @run_before('edit'), but it never gets called. I'm not sure why... Leaves me with two questions: do others think it's less magical and why doesn't it work?

E.g., commented out the do_stage and added this:

     @run_before('edit')
     def move_src_tree(self):
         shutil.move(self.stage.source_path, self.build_directory)

I also tried moving this @run_before bit after I define the edit method, and it still does not seem to be called. 😕

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

@hartzell thanks for looking at the PR! Is there a compelling reason to not use what I developed, with small tweaks to ensure dependencies are installed for tags other than develop? I tested (both older and new Singularity) and it works.

I didn't look at setuid because (I don't recall) it was supported for the (now deprecated) previous version of Singularity. As a general rule, I don't add features until users ask for them.

I saw in an email (but can't find the comment here) a question about why I didn't have the dependencies used for (non develop) install. The only reason is that I'm completely new at Spack, and didn't know to do this.

Anyway, if you get something fully working I can offer to test. Otherwise, I think it would be logical to move forward with the code I wrote.

@hartzell
Copy link
Contributor

I started playing with it because it felt icky to have to build outside of the normal place that Spack builds things (it goes to lengths to manage its directories, having a special case seemed like a problem waiting to happen). This, I think, is a genuine shortcoming of your approach.

Perhaps someone (@tgamblin, @adamjstewart, @alalazo, @davydden ???) could chime in about whether doing the build in /tmp (actually tempfile.gettempdir()) is ok.

I think that my version is more idiomatic (though I'm likely not unbiased), leveraging the built-ins from MakefilePackage means that there's less code to write and if/when something global needs to be changed, SinguarityPackage will get it for free. On the other hand, it's a bit less explicit (more magical) if one doesn't know MakefilePackage and Spack well. On the gripping hand, I'm fudging a bit with my [ab]use of the edit phase, so....

You should try your builds in a minimal environment. There's no way to build [new versions of] singularity without go, so the only way your 3.3.x version could have built is if you were picking up go from elsewhere. Ditto the other dependencies. Here are the dependency docs. I suspect that you need all the things for all versions (unless e.g. develop has added new features that require new dependencies). I think that most of them are used to build singularity (type=('build','link')) but that squashfs is used at runtime when manipulating images (type='run'). libgpg-error doesn't seem to be needed at build-time, not sure what's up with it. You should double check my reasoning.

Your version didn't work for me, out of the box. I don't have GOPATH defined in my environment.

==> Installing singularity-van
==> Searching for binary cache of singularity-van
==> Finding buildcaches in /bifx/apps/spack/mirror/build_cache
==> No binary for singularity-van found: installing from source
==> Using cached archive: /home/ghartzell/spack/var/spack/cache/singularity-van/singularity-van-3.1.0.tar.gz
==> Staging archive: /home/ghartzell/spack/var/spack/stage/singularity-van-3.1.0-64wpjn4hnmhjb5euepoqi2ofgcbz5sdi/v3.1.0.tar.gz
==> Created stage in /home/ghartzell/spack/var/spack/stage/singularity-van-3.1.0-64wpjn4hnmhjb5euepoqi2ofgcbz5sdi
==> No patches needed for singularity-van
==> Building singularity-van [Package]
==> Executing phase: 'configure'
==> Error: KeyError: 'GOPATH'

/home/ghartzell/spack/var/spack/repos/builtin/packages/singularity-van/package.py:39, in configure:
         36    def configure(self, spec, prefix):
         37
         38        # $GOPATH/src/github.com/sylabs/singularity
  >>     39        tmpgo = prepare_gopath()
         40
         41        # Remove old install
         42        if os.path.exists(tmpgo):

See build log for details:
  /home/ghartzell/spack/var/spack/stage/singularity-van-3.1.0-64wpjn4hnmhjb5euepoqi2ofgcbz5sdi/spack-build.out

That said, environment changes should be made in a setup_environment method (docs here). Using its prepend_path (or set) avoids you having to check whether it's already set.

Patching over that, building fails because mconfig needs git to determine the version info.

Adding that dependency still fails for 3.1.0 because you're using the archive tarball (just a tar of the repo at that commit) and it doesn't include the VERSION file or the .git subdir. That can be fixed by switching to the download URL that points to the tar.gz file y'all uploaded.

Here's a diff that gets your approach to build both versions on CentOS 7 (ignore the bit where I rename the class so that it can reside alongside mine and the original in my screw-around-tree).

[ghartzell@bifx1n03 spack]$ diff -u var/spack/repos/builtin/packages/singularity-van/package.py var/spack/repos/builtin/packages/singularity-van/package.py.orig
--- var/spack/repos/builtin/packages/singularity-van/package.py	2019-04-10 09:04:52.466773041 -0700
+++ var/spack/repos/builtin/packages/singularity-van/package.py.orig	2019-04-10 09:07:20.444913558 -0700
@@ -9,7 +9,7 @@
 import tempfile


-class SingularityVan(Package):
+class Singularity(Package):
     '''Singularity is a container technology focused on building portable
        encapsulated environments to support "Mobility of Compute" For older
        versions of Singularity (pre 3.0) you should use singularity-legacy,
@@ -17,20 +17,19 @@
     '''

     homepage = "https://www.sylabs.io/singularity/"
-    url      = "https://github.com/sylabs/singularity/releases/download/v3.1.0/singularity-3.1.0.tar.gz"
+    url      = "https://github.com/sylabs/singularity/archive/v3.1.0.tar.gz"
     git      = "https://github.com/singularityware/singularity.git"

     # ##########################################################################
     # 3.1.0 Release Branch (GoLang)

     version('develop', branch='master')
-    version('3.1.0', 'd3a963ae85c527521434723b1cf8dda9594bf6c6')
+    version('3.1.0', 'f3f1388f7e904dbcc1f7934740b847a771965019')

-    depends_on('go')
-    depends_on('libuuid')
-    # depends_on('libgpg-error', when='@develop')
-    depends_on('squashfs', type='run')
-    depends_on('git')
+    depends_on('go', when='@develop')
+    depends_on('libuuid', when='@develop')
+    depends_on('libgpg-error', when='@develop')
+    depends_on('squashfs', when='@develop')

     phases = ['configure', 'build', 'install']

@@ -75,7 +74,7 @@
     '''
     gopath = os.path.join(tempfile.gettempdir(), 'go')
     tmpgo = os.path.join(gopath, 'src', 'github.com', 'sylabs', 'singularity')
-    os.environ['GOPATH'] = gopath
+    os.environ['GOPATH'] = gopath + ':' + os.environ['GOPATH']

     # Create gopath
     if not os.path.exists(gopath):

You could also replace the calls to os.path.join with path_join, which is always available.

@hartzell
Copy link
Contributor

Anyway, if you get something fully working I can offer to test. Otherwise, I think it would be logical to move forward with the code I wrote.

FWIW, the version in my initial comment is fully working, as far as i know. Its use of do_stage has established precedent (tho' I'm the establisher, in the bcl2fastq package) so it should be acceptable; I was just playing with @run_before to see if there was a prettier alternative.

Signed-off-by: Vanessa Sochat <vsochat@stanford.edu>
@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

Haha, you called it "singularity-van..."

Here comes the Singularity Van! All aboard!

It looks like the formatting of your changes is doing the reverse - your changes are in red (and my original green). I just want to clarify / state that in case anyone else gets confused (I did).

I just made some tweaks to address the GOPATH, .tar.gz referenced at url, and the dependencies. I tested for @develop and without, and didn't get any issues.

I think there are two choices here. We can either have a "spack maintained" GOPATH that keeps the enormous numbers of dependencies for compile (this will get big very fast, and also be confusing about what was found in the user GOPATH vs. spack), and given cloning from master branches it will be hard to manage when several builds need the same repo) or just create this temporary install location, and then build from there, and cleanly remove. I chose the latter because I thought managing an internal GOPATH would be messy and annoying. Definitely would be okay to do otherwise, but be prepared that soon there will be go modules that don't need this.

@hartzell
Copy link
Contributor

hartzell commented Apr 10, 2019

[edit: add question mark to first sentence :)]

What are you worried about being on GOPATH? It appears that everything that singularity needs to build is within it's vendor directory, so all you need to do is point GOPATH at a directory (either Spack's stage dir or your tempdir) that contains a path named src/github.com/sylabs/singularity that is a directory that contains your unpacked tarball. There's nothing else needed, I think/assume.

Am I incorrect about what's in its vendor dir?

If you were using go get to pull down Go dependencies, then you'd be being non-Spacky and should declare resources for them (not sure how that's being handled at the moment for Go dependencies). But I think that you've vendored everything that you need.

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

"vendor" is expected to be in the present working directory of the build, and Singularity now (current version) has quite a bit of content in there. With modules this will change (and further complicate things) but for now it must be the case that the cloned repo is in GOPATH, and the PWD of the build has vendor in it to be found.

You can try it other ways if you want, I spent quite a bit of time and this was what I got working.

@hartzell
Copy link
Contributor

Do the current 3.x.y versions (ignoring future use of Go modules, for now) need anything that's not included in the vendor dir?

If so, then there's still work to be done because Spack frowns on downloading things during the build/install step; it's a control-freak that likes to do it itself during its stage step, with digest values for repeatability and etc....

If not, then we're nearly in violent agreement, the major difference is this bit:

  • you've relocated the unpacked tarball into a subdir of tempfile.gettempdir() and pointed GOPATH at it; and
  • I've relocated the unpacked tarball into a subdir of Spack's managed staging dir and pointed GOPATH at it.

The big difference here is whether the build should happen within Spack's managed spaces (like all of its other builds do) or elsewhere.

The other bits (inheriting from MakefilePackage, using setup_environment, etc...) are just idiomatic Spack suggestions.

Go modules will eventually introduce another layer of complication. I, personally, hope that applications like Singularity will continue to vendor all of their dependencies (which modules will support) rather than downloading them on the fly. But....

You can try it other ways if you want, I spent quite a bit of time and this was what I got working.

I appreciate the amount of time it takes to get going with Spack. That's why I thought it was worth sharing a working idiomatic alternative rather than just commenting line by line.

But, the most important question is, does the Singularity-Van play tunes like the Ice Cream truck?

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

Singularity-Van serves ice cream and avocados, primarily :)

@hartzell
Copy link
Contributor

FWIW,

  1. I just used the --keep-stage argument to spack install (leveraging the existing mechanisms) so that it doesn't remove the stage/build directory used by my alternative implementation above.

    It does not appear that make/Go is dragging in any unforseen dependencies. In other words, it seems like Singularity has everything it needs stashed within its vendor dir.

    Side effect of the above is that I'm convinced that relocating the src dir using do_stage is a better approach than trying to use @run_before('edit'), because after spack stage ... runs it's in location that a Go user would expect it to be.

  2. I used ldd to look at the resulting binaries and they don't seem to depend on any unexpected shared libraries; I don't seem to be linking to anything from the system.

    But, the dependency list for 3.1 has a couple of things that make me nervous:

    • wget; which is presumably used to fetch images. If so, it should have a depends_on of type run.
    • openssl; I'm not sure how this gets used. My build doesn't seem to link to anything from it, so....
  3. W.R.T. setuid capabilities, I can't find any mention in the current 3.1 docs about anything being installed setuid. There is a discussion of it here in the deprecated docs, but the components it mentions are no longer installed.

    There is a libexec/singularity/bin/starter-suid installed; though it's not actually setuid. This bit of the generated builddir/Makefile:

    # starter suid
    starter_suid_INSTALL := $(DESTDIR)$(LIBEXECDIR)/singularity/bin/starter-suid
    $(starter_suid_INSTALL): $(starter)
        @echo " INSTALL SUID" $@
        $(V)install -d $(@D)
        $(V)install -m 4755 $(starter) $(starter_suid_INSTALL)

    looks like it would be setting that bit if it could.

    I don't think that many people run their Spack builds as root (I know I don't), so I don't think that there's anything the build can do to set that bit.

    It might be helpful by printing out a message as part of the build, suggesting that the user might want to sudo chmod 4755 $(spack location -i singularity)/libexec/singularity/bin/starter-suid (but build up the string w/in the package.py using prefix and etc...). That is, of course, assuming that that is the/a file that should be setuid.

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

Wait, there is no install with root? Singularity is extremely limited without that. That's not super logical to me - is there an active user of the (current) Singularity via Spack that can comment on this?

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

Let's step back a second. The installation procedure is driven by the use cases of the users installing it, and this is different than I anticipated (I expected an install with root, Singularity doesn't function the same without it). Do you know of a set of users that can comment on how they have used the Spack installation of Singularity (with or without root?) We need to take these into consideration.

@scheibelp
Copy link
Member

Wait, there is no install with root?

I'm not familiar with singularity so apologies if this is a silly question, but are we talking about root permissions, or the root package in Spack?

If we're talking about root permissions, then no, Spack currently doesn't support installing as root for any package, although I have an in-progress PR to support that at #9521

@vsoch
Copy link
Member Author

vsoch commented Apr 10, 2019

strange, let me swing back to the (one) issue that I know of where a user asked about Singularity - we should clarify this before moving forward. I'll reference the PR here.

@hartzell
Copy link
Contributor

@scheibelp -- Singularity runs containers, like (this is going to give a bunch of people a big headache) Docker but in a way that's much more sensible in an HPC environment. More info here. I'm a big fan!

In order to get its full benefits, there's a bit that needs to be setuid. Admins can decide whether to install it that way (which enables full functionality) or without the making that helper prog setuid (which limits the functionality). There's a good discussion here at the old site.

If you install it by hand using their instructions, your last step would be something like sudo make install and there's a bit that does the requisite chmod.

It looks like #9521 would indeed fix things, at least for interactive builds (I wonder what happens with my Jenkins job?).

@vsoch -- I've used the old package in the past, and my solution was to just

sudo chmod 4755 $(spack location -i singularity)/path/to/files/that/needed/to/be/setuid

I think that having the package in Spack is quite useful, even if people have to do this bit by hand.

The alternatives seem to be to build it "by hand" or install it from RPMS that I dig up from somewhere. I try really hard to limit the set of repo's from which I pull RPMS and building it by hand requires either having the prereqs in the base system (I try to run a minimal base system) or link into some Spack tree (at which point...).

@sknigh
Copy link
Contributor

sknigh commented Apr 11, 2019

@vsoch In my use case, I install in two passes, the first I install singularity's dependencies / everything else as a normally privileged user (which some packages like tar seem to require), then I install singularity as root. It's a pain, but I would consider it a shortcoming in spack's workflow, not the singularity package.

@hartzell
Copy link
Contributor

You are missing the detail that the directory was changed to src not because it exists, but because we call a variable (the property decorator) that changes the value at the same time as the function call because we need the directory there to copy things to.

Given this call:

shutil.move(self.stage.source_path,
            self.singularity_gopath_dir)

I'm nearly certain that the call to self.stage.source_path must be evaluated before control passes in to shutil.move because it's one of the arguments to the function call. At that point, I don't see how it can return /.../src (but I'm just doing more of the restating thing, see below).


I’m not going to argue anymore for my approach, this is clearly some kind of race condition and regardless of what should or shouldn’t happening this is what is happening and it’s not a good use of time to consistently tell me it shouldn’t be happening.

I'm not trying to argue with you, I'm trying to wrestle with the problem. But outside my own head that distinction's probably hard to see and less fun to be part of. Apologies if I'm sounds argumentative. Software that works mysteriously and patches that paper over race conditions (or whatever's going on here) drives me bonkers. That's my weakness (or, sometimes, strength).

I can't recreate the problem (heck, your docker image Works For Me(tm)) so the only way I've been able to try to see what's going on is getting you to try things on your system for me. You've been great to team up with on this.


That all said, I think we can ask for this to be merged (or perhaps close this and make a shorter, neater PR that refers back to this one).

I'd suggest these two changes, use the local source_path var in the tty.debug() call in do_path and remove the two tty.info() calls in edit():

diff --git a/var/spack/repos/builtin/packages/singularity/package.py b/var/spack/repos/builtin/packages/singularity/package.py
index 6ad694521..4287773a7 100644
--- a/var/spack/repos/builtin/packages/singularity/package.py
+++ b/var/spack/repos/builtin/packages/singularity/package.py
@@ -55,7 +55,7 @@ class Singularity(MakefilePackage):
         source_path = self.stage.source_path
         if not os.path.exists(self.singularity_gopath_dir):
             tty.debug("Moving {0} to {1}".format(
-                self.stage.source_path, self.singularity_gopath_dir))
+                source_path, self.singularity_gopath_dir))
             mkdirp(self.sylabs_gopath_dir)
             shutil.move(source_path,
                         self.singularity_gopath_dir)
@@ -67,9 +67,7 @@ class Singularity(MakefilePackage):

     # Hijack the edit stage to run mconfig.
     def edit(self, spec, prefix):
-        tty.info(os.getcwd())
         with working_dir(self.build_directory):
-            tty.info(os.getcwd())
             configure = Executable('./mconfig --prefix=%s' % prefix)
             configure()

@vsoch
Copy link
Member Author

vsoch commented Apr 24, 2019

Those changes would be okay with me!

I'm afk but feel free to push and then we can call it done, and pick this up in another issue. Thanks for you collaborative debugging on this, glad we found something that works for both of us.

- Use the local `source_path` variable consistently instead of
  `self.stage.source_path` in the debug output, since that's what
  we're actually using in the code

- Drop the info statements from the edit function, they're left over
  from debugging.
@hartzell
Copy link
Contributor

hartzell commented Apr 24, 2019

[edit, added OS info]

Pushed.

I tested by building both @3.1.1 and @develop on CentOS 7. In each case I ran the spack_perms_fix.sh then ran

[ghartzell@ft spack]$ singularity cache clean -a
[ghartzell@ft spack]$ singularity run  docker://ubuntu:latest cat /etc/debian_version
INFO:    Converting OCI blobs to SIF format
INFO:    Starting build...
Getting image source signatures
Copying blob sha256:898c46f3b1a1f39827ed135f020c32e2038c87ae0690a8fe73d94e5df9e6a2d6
 30.96 MiB / 30.96 MiB [====================================================] 0s
Copying blob sha256:63366dfa0a5076458e37ebae948bc7823bab256ca27e09ab94d298e37df4c2a3
 851 B / 851 B [============================================================] 0s
Copying blob sha256:041d4cd74a929bc4b66ee955ab5b229de098fa389d1a1fb9565e536d8878e15f
 545 B / 545 B [============================================================] 0s
Copying blob sha256:6e1bee0f8701f0ae53a5129dc82115967ae36faa30d7701b195dfc6ec317a51d
 162 B / 162 B [============================================================] 0s
Copying config sha256:885f8bbcf9de83145135878cfe491a84c36cce0522b777a1e4b4a776c220bb0d
 2.42 KiB / 2.42 KiB [======================================================] 0s
Writing manifest to image destination
Storing signatures
INFO:    Creating SIF file...
INFO:    Build complete: /home/ghartzell/.singularity/cache/oci-tmp/017eef0b616011647b269b5c65826e2e2ebddbe5d1f8c1e56b3599fb14fabec8/ubuntu_latest.sif
INFO:    Image cached as SIF at /home/ghartzell/.singularity/cache/oci-tmp/017eef0b616011647b269b5c65826e2e2ebddbe5d1f8c1e56b3599fb14fabec8/ubuntu_latest.sif
buster/sid
[ghartzell@ft spack]$

I'll leave it to you to think about opening up another clean PR using this branch or sticking with this. If you stick with this one, you might edit the title, adding something like "Ready to merge", to help catch the attention of the committers.

Yay/Phew.

@vsoch
Copy link
Member Author

vsoch commented Apr 24, 2019

Woot! I’m still out for my run - I’ll do-all-the-things mentioned above so we can update Singularity and still study this behavior (in another thread). Most likely tomorrow again, I’m pooped.

@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

@hartzell one quick question before closing this up - with an install I don't see any message to alert me to use the script as a final step. It's there:

$ ls /tmp/spack/opt/spack/linux-ubuntu16.04-x86_64/gcc-5.4.0/singularity-3.1.1-3n7skikliv4pd6olbouxjtjsdl2j4vla/bin
run-singularity  singularity  spack_perms_fix.sh

but if I didn't know to look for it, I wouldn't. Did you have in mind to tell the user about this? Are we relying on online documentation?

@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

Hey @hartzell - there isn't an issue with singularity / src if we remove the command to create the directory (it shouldn't be there). Did you have a reason for including it? I think it would fix finding two folders in there.

@hartzell
Copy link
Contributor

Did you have in mind to tell the user about this? Are we relying on online documentation?

Output during the build phase currently ends up in the build file. Known issue, mentioned above.

@adamjstewart has a fix in this PR, awaiting feedback from @tgamblin. Once this is in place, output will be sent to the terminal, in a standout color.

I also included a small blurb in the package info string (spack info singularity). Not sure what else to do, but open to suggestions.

@hartzell
Copy link
Contributor

Re: the mkdir()

Did you have a reason for including it?

shutil.move docs are silent about whether it will create the parent directories. I swear on a stack of Lions' Commentary that I tried it somewhere and it failed if the dest parent path didn't exist, but now that I'm trying to recreate it to show here, it Just Works(tm). Sigh

I'm fine w/ removing it, if someone, somewhere runs into a problem it won't be subtle....

@hartzell
Copy link
Contributor

Re^2: the mkdir().

If you only see the error involving /<stage_dir>/src when the function includes the mkdir(), then you've solved the mystery that was driving me bonkers. I thought you'd commented it out and were still seeing the error....

💥

@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

I just ran through it again with the mkdir commented out, and I made you some create comforts asciinema recording evidence so you believe what happened! https://asciinema.org/a/ZNT6epLmEZ47dLgDKgqLS6wsb?speed=2

And it worked at the end! :D

The last bit then, I think would be to remove this extra line, and then give a message to the user to execute that script?

And yes, sometimes the error is right in front of our noses. The two directories were there at the same time leading to the wrong property for the self.stage.source_path because... we put it there.

@hartzell
Copy link
Contributor

Gotta love asciinema and evidence. Data driven!

I can't figure out any way to message the user (as mentioned a bit earlier).

At this point I'd say LGTM, based on "Don't let perfect be the enemy of the good." or some similar aphorism.

By way of solutions that I've explored....

I just had the cute idea that I could add "caveats" phase to the list of phases, to run after "install" and hook that to send the warning. Sadly, it seems to run in the subprocess also and the warnings still end up in the build.out file.

--- a/var/spack/repos/builtin/packages/singularity/package.py
+++ b/var/spack/repos/builtin/packages/singularity/package.py
@@ -40,6 +40,8 @@ class Singularity(MakefilePackage):
     def gopath(self):
         return join_path(self.stage.path)

+    phases = ['edit', 'build', 'install', 'caveats']
+
     @property
     def sylabs_gopath_dir(self):
         return join_path(self.gopath, 'src/github.com/sylabs/')
@@ -126,10 +128,13 @@ class Singularity(MakefilePackage):
         chmod = which('chmod')
         chmod('555', script)

+    def caveats(self, spec, prefix):
+        pass
+
     # Until tty output works better from build steps, this ends up in
     # the build log.  See https://github.com/spack/spack/pull/10412.
-    @run_after('install')
-    def caveats(self):
+    @run_after('caveats')
+    def perm_script_warnings(self):
         tty.warn("""
         For full functionality, you'll need to chown and chmod some files
         after installing the package.  This has security implications.

Re-reading the relevant bit of code agrees with the empirical evidence.

I also explored adding a new hook and using it, discussion in #11244.

@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

Ah, yes I remember those discussions from before! I'm in complete agreement - let's get this PR finished up, and then update with a message to the user when the protocol is implemented.

@vsoch vsoch changed the title Update/package singularity Ready to Merge: Update/package singularity Apr 25, 2019
@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

@baberlevi this PR is ready to merge. We've addressed issues with installing Singularity 3.0, and remaining to do is to alert the user (with a message) about running a final script to change permissions. When a method is finished to allow for this, we'll open another PR to add the message.

@hartzell
Copy link
Contributor

When a method is finished to allow for this, we'll open another PR to add the message.

The message is already there. If/when #10412 is merged, the message will be sent to the console instead of (or in addition to, depending on #10412) ending up in the build.log (as it does now).

We won't need to do anything. If #10412 doesn't merge, then we'll need to figure out what to do next.

Here's an example of how it currently appears:

[ghartzell@ft spack]$ cat /home/ghartzell/spack/opt/spack/linux-centos7-x86_64/gcc-8.2.0/singularity-3.1.1-kuf7bim7pigb6flgtwcbsimkxietlwpr/.spack/build.out
[...]
==> [2019-04-25-14:12:18.394128] Warning:
        For full functionality, you'll need to chown and chmod some files
        after installing the package.  This has security implications.
        See: https://singularity.lbl.gov/docs-security for details.

        We've installed a script that will make the necessary changes;
        read through it and then execute it as root (e.g. via sudo).

        The script is named:

        /home/ghartzell/spack/opt/spack/linux-centos7-x86_64/gcc-8.2.0/singularity-3.1.1-kuf7bim7pigb6flgtwcbsimkxietlwpr/bin/spack_perms_fix.sh

@vsoch
Copy link
Member Author

vsoch commented Apr 25, 2019

Even better :)

Copy link
Contributor

@hartzell hartzell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Details in the conversation.

@hartzell
Copy link
Contributor

Any committers want to take a swing at this?

@vsoch
Copy link
Member Author

vsoch commented Apr 30, 2019

Yes please ! ⚾

@hartzell
Copy link
Contributor

hartzell commented May 2, 2019

I mentioned this PR in https://groups.google.com/forum/#!topic/spack/Ps_pPbHbrvU

@scheibelp scheibelp self-assigned this May 2, 2019
@scheibelp scheibelp merged commit 4aeb0d1 into spack:develop May 3, 2019
@scheibelp
Copy link
Member

I squashed this and hopefully captured the important points in the commit message.

The logic added for the go build in Singularity likely ought to be generalized into a new build system. You mention

Go has novel ideas about how projects should be organized

what is the general expected structure?

Also, this creates a chmod script for the new Singularity, but I presume that singularity-legacy would also benefit from this script. Likewise does the makesquashfs config not apply to singularity-legacy? Not that this needs to be handled by you, but a TODO would be great if either of these changes needs to be made for singularity-legacy.

@hartzell
Copy link
Contributor

hartzell commented May 3, 2019

Thanks!

ought to be generalized into a new build system.

I think that, with N =~ 3 (or so), we're getting close to having enough Go based projects in the tree to think about generalizing.

The sticky bit, however, is that "it's complicated" and I'm (personally) inclined to wait and see what new packages need before we start pouring concrete. YMMV, however....

There are probably two major cases:

  • old skool GOPATH based builds, which expect to have the GOPATH environment variable pointing at a directory that contains bin, pkg, and src. src has a structure that mirrors package (I think that's the right term), e.g. all of the Singularity stuff is under src/github.com/sylabs/singularity E.g.

    (alice)[20:05:18]~>>tree -L 3 go
    go
    ├── bin
    │   ├── aws-okta
    │   ├── dep
    │   ├── dlv
    │   ├── ft
    │   ├── godef
    │   ├── goimports
    │   ├── guru
    │   ├── markdownfmt
    │   ├── markdownfmt.standard
    │   └── terraform-inventory
    ├── pkg
    │   ├── darwin_amd64
    │   │   ├── github.com
    │   │   ├── golang.org
    │   │   └── gopkg.in
    │   ├── dep
    │   │   └── sources
    │   └── mod
    │       ├── cache
    │       └── github.com
    └── src
        ├── github.com
        │   ├── adammck
        │   ├── biogo
        │   ├── blang
        │   ├── client9
        │   ├── davecgh
        │   ├── derekparker
        │   ├── gocarina
        │   ├── golang
        │   ├── hartzell
        │   ├── mattevans
        │   ├── mattn
        │   ├── microcosm-cc
        │   ├── rogpeppe
        │   ├── russross
        │   ├── segmentio
        │   ├── sergi
        │   ├── shurcooL
        │   ├── smartystreets
        │   └── sourcegraph
        ├── golang.org
        │   └── x
        └── gopkg.in
            └── russross
    
    36 directories, 10 files
    

    In theory, all of one's go based projects reside under the common root. That didn't work out as well as they thought.

    This is basically what we set up for the singularity package.

  • new-style "modules" style builds, which are more "project" centric and have a go in the root directory that describes their dependencies.

Given a particular structure, some projects are simple enough that they can just use a series of calls to the go tool, perhaps as simple as go build. Others are more complicated and come with Makefiles and/or autoconf setups, that do various bits and then call the go tool.

Independent of this, some projects "vendor" all of their dependencies, using a variety of different tools, though the build steps are probably similar. Vendoring and modules was not well defined in the early modules days and I'm not entire sure how it settled out. The singularity source tree includes all of it's prereqs, which makes it pretty simple. Left to their own devices, packages that do NOT vendor their prereqs will download them at build time, and we all know how we feel about that around here. Package authors will need to set up resources for them and figure out how their package is building and short-circuit it.

The modules stuff is only just reaching maturity. The GOPATH stuff will go the way of the dodo, eventually, but it's unclear how long it'll hang around.

@hartzell
Copy link
Contributor

hartzell commented May 3, 2019

[...] presume that singularity-legacy would also benefit [from a chmod script and mksquashfs magic]

Well, the legacy stuff is legacy, I'm not sure how much energy to put into it. Perhaps one of the current users could comment.

The particular set of files to chown and chmod are different, but the concept is straight forward.

I believe that it uses a different mechanism to hunt down the mksquashfs executable to the particular hack for v3 is not applicable, but there's probably an equivalent. I also believe that the old version didn't depend_on the package but relied silently on the system version.

@current-singularity-users?

I'd be inclined to let it be, but if there's demand.

@vsoch
Copy link
Member Author

vsoch commented May 3, 2019

For Singularity Legacy, I agree with @hartzell - let's not solve problems that aren't being reported by users. If nobody is using / asking for the equivalent script, it's not optimizing use of our time.

I squashed this and hopefully captured the important points in the commit message.

Looks good to me :)

The logic added for the go build in Singularity likely ought to be generalized into a new build system. You mention

So - I want to suggest that we wait on this. Modules are new, yes, but they will still be ready soon enough to warrant not investing a huge amount of time into something that is guaranteed to be deprecated. For Singularity alone modules will be used in the next official release, and we can update to use them. I would even go as far to say that if someone wants to install something with GoLang that doesn't have a module, it would be better use of time to (develop for the future) and do PR to their repos to add modules. And hey, just a few days ago they announced the mirrors are up!. So -1 to add this weirdo GOPATH install business as something proper, +1 to testing Singularity with modules after next release, and then having "that thing we figure out" be the protocol.

@vsoch vsoch deleted the update/package-singularity branch May 3, 2019 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Installation issue: singularity@develop
5 participants