Skip to content

WIP: Generate gitlab ci yaml#8718

Closed
scottwittenburg wants to merge 98 commits intospack:developfrom
scottwittenburg:generate-gitlab-ci-yaml
Closed

WIP: Generate gitlab ci yaml#8718
scottwittenburg wants to merge 98 commits intospack:developfrom
scottwittenburg:generate-gitlab-ci-yaml

Conversation

@scottwittenburg
Copy link
Copy Markdown
Contributor

This presents another possible path to generating release binaries, similar to #8444, but with the jobs being created more statically (by a script run manually when release spec-set changes). Then each job (represented in the rebuild_package.sh shell script) can check for itself whether to build the package or whether that work is not needed.

@scottwittenburg
Copy link
Copy Markdown
Contributor Author

ping @opadron I've been running this branch against my own local gitlab/ci instance, and though it needs adjustment, it's a decent (working) start. Currently the jobs in the pipeline are discovering they need a rebuild, then failing when my test Docker image doesn't have some of the build toolchain expected.

@scottwittenburg
Copy link
Copy Markdown
Contributor Author

ping @aashish24

@aashish24
Copy link
Copy Markdown

thanks @scottwittenburg few things I have mentioned that you probably know already but I am documenting it here:

  1. get the compiler information from spec
  2. use yml config file for image/OS mapping (as you mentioned)
  3. parallel workflow (use multiple jobs per stage depending on the dependency). Although I would get the serial workflow working first.

Great start.. thanks for pinging me on this.

@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch from fc1ba49 to 9dda56c Compare July 18, 2018 23:28
Copy link
Copy Markdown
Member

@tgamblin tgamblin left a comment

Choose a reason for hiding this comment

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

@scottwittenburg @opadron: This is starting to look good to me! See my review comments for details.

FYI: I found this promising GitLab issue on allowing seed jobs to generate gitlab-ci.yml dynamically. It seems like people like it. I put in a comment about our use case and a link to this PR, but I was struck by how similar the proposal is to what we ended up doing here. I hope it gets implemented.

# full_hash doesn't match (or remote end doesn't know about
# the full_hash), then we trigger a rebuild.
remote_pkg_info = remote_pkg_index[pkg_short_hash]
if not 'full_hash' in remote_pkg_info or \
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use parens around the condition instead of \

for release_spec in release_spec_set:
pkg_name = release_spec.name
pkg_version = release_spec.version
pkg_short_hash = release_spec.dag_hash()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The name short_hash here is a little confusing, as Spack (like git) allows you to enter in uniqe prefixes of hashes and have them work. So I'd say the hash in your "short spec" is really a "short hash" 😄.

I think I see why we call it a short hash -- the full_hash is a SHA-256, while the dag_hash is a SHA-1, so it's shorter. That actually took me by surprise -- the full hash is really long and has a bunch of ==== padding at the end of it because SHA-256 length isn't evenly divisible by 5.

Can we do two things here?

  1. Since there's just going to be one hash in the end, just call this the hash
  2. change the full hash to use SHA-1/base32 like the dag_hash, so at least they're the same length. I worry about transitioning full hash to be the main hash if they're different lengths. We can talk about making the hashes longer in Spack later, but I think things will be smoother if the full hash looks like the dag hash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

#8911 represents my attempt to fulfill request (2) above.

tty.msg(' %s -> %s' % (mirror, configured_mirrors[mirror]))
mirror_url = configured_mirrors[mirror]
mirror_rebuilds = get_mirror_rebuilds(mirror, mirror_url, release_spec_set)
if len(mirror_rebuilds) > 0:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

more pythonic: if mirror_rebuilds:

outf.write(json.dumps(rebuilds))


def check_single(args):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

seems like most of this function can be replaced with a call to check_all, if check_all took a spec set and/or list of specs as a parameter instead of opening a specific file.

Right now it duplicates a fair amount of logic.

tty.msg(msg)


def check_binaries(parser, args):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this command be integrated with spack buildcache or with spack mirror? I think it makes more sense as a subcommand of one of those.

share_path = os.path.join('.', 'share', 'spack')
common_scripts_dir = os.path.join(share_path, 'docker', 'build', 'common')

os_container_mapping = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This probably belongs in a config file somewhere.


release_spec_set = None

with open(release_specs_path, 'r') as fin:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this duplicates the logic in check_all -- why not a function like CombinatorialSpecSet.from_file(path)?


with open(release_specs_path, 'r') as fin:
release_specs_contents = fin.read()
release_specs_yaml = yaml.load(release_specs_contents)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

minor point: consider using spack_yaml instead of yaml. spack_yaml preserves file/line info from the file as attributes on the data structure it reads in (try typing spack config blame config if you are interested).

@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 2 times, most recently from 4a8dff1 to 3d130c7 Compare August 14, 2018 22:40
@scottwittenburg
Copy link
Copy Markdown
Contributor Author

I think this thing is ready for another review pass @tgamblin, at your convenience. Pinging @scheibelp for visibility as well.

The jobs specified in the auto-generated .gitlab-ci.yaml are passing on my personal gitlab instance (i.e. sometimes detecting that a package exists on the mirror and the full_hash matches, sometimes detecting a need to rebuild and doing so). What they're currently missing is pushing the built packages back to the binary mirror. Once we have #8664 from @bryonbean working, we should be able to add that.

Before I try pushing this branch to the gitlab instance set up by @opadron, I will need to regenerate the .gitlab-ci.yaml using the appropriate mirror url referring to our S3 account. Do you know who I need to ask about that?

One thing I don't think I have seen yet is how dependencies will get pulled as binaries from the mirror if they exist. Is that something that should already exist if we just have a mirror configured? Or should I be thinking about how that will work?

ping @aashish24

@opadron
Copy link
Copy Markdown
Member

opadron commented Aug 17, 2018

@scottwittenburg just as an FYI: the kubernetes cluster should be easier than ever to use! (#9018)
Just keep in mind that we're still running on community edition, and soon (like Tuesday) we should start talking about how to move forward on getting Ultimate Edition up and running.

@scottwittenburg
Copy link
Copy Markdown
Contributor Author

fyi @zackgalbreath

@tgamblin
Copy link
Copy Markdown
Member

@scottwittenburg @opadron and @bryonbean: the S3 mirror should be set up in Kitware's AWS, maybe with Cloudflare but we can see if we really need that once the traffic starts coming in.

There is some info here on hosting static stuff like this via S3. It would be nice if the bucket had a spack-ish domain name... maybe spack-mirror.s3.amazonaws.com or something like that.

It is worth considering that as the bucket gets more distributed -- i.e. if we enable CDN and start having files propagated out all over -- I think the likelihood that binaries will appear there quickly goes down. So passing artifacts another way may be the better way to go.

Copy link
Copy Markdown
Member

@tgamblin tgamblin left a comment

Choose a reason for hiding this comment

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

@scottwittenburg: Thanks! This mostly looks good -- just a few comment inline. See above on the S3 mirror.

either of these options to ``spack install``:

* ``--log-format=cdash-simple``
* ``--log-format=cdash-complete``
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's actually just --log-format=cdash (and --log-format=junit) now. Sorry this is dated. Do you want me to fix it or maybe do you or @zackgalbreath want to take a stab?

``report.configure.xml``, and ``report.test.xml``, for the build,
configure, and tests steps, respectively.

If you want to upload these fils to a CDash instance, you can use ``curl``:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should probably document @zackgalbreath's --cdash-upload-url here, before the curl stuff. Also @zackgalbreath, does that support authenticated submissions? Should that be documented?

.. _cmd-spack-test-suite:

---------------------
CDash test suites
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This whole section can be ripped out and replaced with dos on @scottwittenburg's new infrastructure when it's ready.

'Use -t to install all downloaded keys')


def read_from_url(file_uri):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This probably belongs in spack.util.web, and at least the urlopen part should be replaced with spack.util.web._urlopen for backward-compatibility with Python 2.6. I think you can factor out the logic in spack.util.web._spider() (the parallel web spider used by spack checksum) and use it here -- that stuff already handles things like disabling SSL verification for spack -k/--insecure, and this should respect -k.

Possibly useful (but maybe annoying) side note: Spack is a bit confused about how to fetch from URLs at the moment. fetch_strategy.URLFetchStrategy is probably the main tool for downloading, and it uses curl. It seems like system curl is the most likely tool on HPC systems to have SSL set up properly. Many HPC Pythons don't have SSL cert settings configured right, so using urlopen in Python can fail. curl can be set up wrong too, though, so that might fail as well. At the moment, we don't really have a good fallback system, or a way to try to automatically find certs in the places you might expect them to be.

Probably best not to worry about this too much here and just use _urlopen and the stuff already in spack.util.web, but in case it helps, now you have some more details.

# full_hash doesn't match (or remote end doesn't know about
# the full_hash), then we trigger a rebuild.
remote_pkg_info = buildcache_index[pkg_hash]
if ('full_hash' not in remote_pkg_info or
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this logic LGTM, but can you add a note that eventually it'll be less complicated (when the dag_hash is the full_hash)?

except URLError as url_err:
msg = 'Unable to open url {0} due to {1}'.format(
file_uri, url_err.message)
raise spack.error.SpackError(msg)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

probably ok not to wrap this here, as the function's only used by routines in this file -- the SpackError is immediately caught by needs_rebuild, anyway -- you could just let it catch the URLError.

with open(output_file, 'w') as outf:
outf.write(json.dumps(rebuilds))

return 1 if rebuilds else 0
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure if it's clearer or not but int(bool(rebuilds)) would work too :).

return json.loads(index_contents)


def check(mirrors, specs, no_index=False, output_file=None):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

probably needs a more descriptive name -- I had to think about this a bit.

remote_pkg_index = get_remote_index(mirror_url)

for spec in specs:
rebuild_spec = needs_rebuild(spec, mirror_url, remote_pkg_index)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if this could be made faster with logic like spack.util.web._spider or just its own multithreading.Pool -- right now it does a bunch of potentially slow web requests sequentially.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My thought is that there are two potential use cases for this, 1) parallel jobs will run this with the --no-index flag, which means one job will fetch one remote .spec.yaml, or 2) if a single process runs this, it will not use --no-index, which means that it will fetch the remote package index from the mirror and then use that to look up the full hashes of all the specs it needs to check.

So given that we don't expect a single process to sequentially make a bunch of slow web requests (though it is possible through misuse/misunderstanding), do we still need to worry about making this faster?

specs = [Spec(args.spec)]
else:
release_specs_path = \
os.path.join(etc_path, 'spack', 'defaults', 'release.yaml')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just break at the first ( instead of \

@opadron
Copy link
Copy Markdown
Member

opadron commented Aug 22, 2018

@tgamblin would another spack subdomain that redirects to the actual S3 bucket be desirable?
Something like s3.spack.io redirecting to whatever the S3 bucket actually ends up being?

@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 3 times, most recently from d02fb63 to 5bcdde7 Compare August 29, 2018 16:42
@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 2 times, most recently from cc65500 to 44ef17c Compare September 4, 2018 23:31
@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 3 times, most recently from 003a079 to 02b0403 Compare September 11, 2018 23:34
@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 6 times, most recently from 938da3f to e5a37c5 Compare September 24, 2018 23:17
@scottwittenburg scottwittenburg changed the base branch from develop to PR/pymock November 27, 2018 22:18
@scottwittenburg scottwittenburg changed the base branch from PR/pymock to develop November 27, 2018 22:19
@scottwittenburg
Copy link
Copy Markdown
Contributor Author

Heads up to reviewers (currently just @tgamblin or @opadron): The diff shown here on github does not match the diff I see locally between develop and generate-gitlab-ci-yaml branches. This may be because I picked commits from one or more PRs that have since been merged, or because the branch has lived and grown for too long, or maybe just due to bugs in github? I'm not sure.

At any rate, as an example the diff shown here for lib/spack/spack/spec.py is much longer than what I see if I run the diff on my machine (first updating both branches). Here's what I see locally:

$ git diff develop generate-gitlab-ci-yaml lib/spack/spack/spec.py
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index f899141..4f84104 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1503,9 +1503,9 @@ class Spec(object):
 
         return syaml_dict([('spec', node_list)])
 
-    def to_yaml(self, stream=None):
+    def to_yaml(self, stream=None, all_deps=False):
         return syaml.dump(
-            self.to_dict(), stream=stream, default_flow_style=False)
+            self.to_dict(all_deps), stream=stream, default_flow_style=False)
 
     def to_json(self, stream=None):
         return sjson.dump(self.to_dict(), stream)

This may indicate we should close this PR and start over. At which point it may make sense to re-factor into commits which more accurately reflect the logical changes. Let me know what you think.

@scottwittenburg scottwittenburg force-pushed the generate-gitlab-ci-yaml branch 2 times, most recently from fa9d462 to bb7cdfd Compare November 28, 2018 06:15
scottwittenburg and others added 7 commits November 27, 2018 23:28
Also use presence of cdash build id in install output as condition
for whether install succeeded or failed, as it seems a configure
error may not result in a non-zero exit code from install command.
@opadron
Copy link
Copy Markdown
Member

opadron commented Jan 3, 2019

@scottwittenburg scottwittenburg mentioned this pull request Jan 7, 2019
3 tasks
@scottwittenburg
Copy link
Copy Markdown
Contributor Author

Closing this in favor of #10274, which is mostly the same but cleaned up and rebased on the latest develop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants