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

Bug 1889223 - Submit all files to notarization before polling #957

Merged
merged 8 commits into from
Apr 26, 2024

Conversation

hneiva
Copy link
Contributor

@hneiva hneiva commented Apr 2, 2024

This is the first behavior that consumes all files at once, so it's a bit odd how it's being handled in script.py

@hneiva hneiva marked this pull request as draft April 2, 2024 20:19
@hneiva hneiva force-pushed the hneiva/async-notarize branch 3 times, most recently from d7c01e7 to b3dc1be Compare April 4, 2024 22:33
@hneiva hneiva marked this pull request as ready for review April 9, 2024 22:07
@hneiva hneiva requested a review from a team April 9, 2024 22:07
Copy link
Contributor

@bhearsum bhearsum left a comment

Choose a reason for hiding this comment

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

This overall logic here is fine, but I'd really prefer to see some cleanup happen that avoids the need for having completely separate paths in script.py for the stacked notarization and other formats. Doing that would make some of the other comments irrelevant.

I'd also like to see if we can upload a single zip instead of a bunch of smaller requests (see comment further down).

filelist_dict = build_filelist_dict(context)
for path, path_dict in filelist_dict.items():
if path_dict["formats"] == ["apple_notarization_stacked"]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this actually function correctly if some files are not apple_notarization_stacked? Eg: will we sign (or notarize) them correctly, and then move on and do the stacked ones correctly? If not, we should disallow apple_notarization_stacked for only some files, and bail at a higher level here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I'll disallow mixed formats when stacked is present

Copy link
Contributor

Choose a reason for hiding this comment

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

Did disallowing mixed formats get implemented?

@@ -54,6 +57,15 @@ async def async_main(context):
copy_to_dir(os.path.join(work_dir, source), context.config["artifact_dir"], target=source)
if "gpg" in path_dict["formats"] or "autograph_gpg" in path_dict["formats"]:
copy_to_dir(context.config["gpg_pubkey"], context.config["artifact_dir"], target="public/build/KEY")

# notarization_stacked is a special format that takes in all files at once instead of sequentially like other formats
notarization_dict = {path: path_dict for path, path_dict in filelist_dict.items() if "apple_notarization_stacked" in path_dict["formats"]}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is using a new format for this just to make it safer to roll out? If so, what are the plans for apple_notarization and apple_notarization_geckodriver after we switch to stacked? (Ideally, it seems like we ought to move everything to stacked.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I initially replaced apple_notarization but decided against it to allow for the feature to ride the train.
I'll add deprecation notices on the old functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave geckodriver alone for now, and refactor that as we move things around later.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's get an issue on file to remove this later, too.

if extension == ".pkg":
path = os.path.join(notarization_workdir, relpath)
utils.copy_to_dir(path_dict["full_path"], notarization_workdir, target=relpath)
submissions_map[path] = await retry_async(
Copy link
Contributor

Choose a reason for hiding this comment

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

How many files will we be notarization in parallel? We may want to limit how many we do at once. Especially spread over all of the iscript machines, this could end up doing a lot at once, and potentially hitting a rate limit or other problems.

(Note: if we end up making a single request with a big zip file, this is not an issue.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's why I called it stacked and not parallel.
It's still submitting each file sequentially, but it doesn't start polling for results until all files have been submitted.
That way the max parallel submissions are controlled by our chunking still.
Hopefully this won't hit a limit on apple's end.

Copy link
Contributor

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but we're still going to have more files in the process of being notarized at the same time, no?

Copy link
Contributor

Choose a reason for hiding this comment

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

Just want to highlight this one last time. We're submitted the files synchronously, but we're not waiting on one to finish notarizating before we submit the next one - which means more will be in Apple's system at any given time than before. I have no issue with this, but please keep an eye on notarization tasks when this is activated to see if we start to hit more timeouts or intermittents. If we do, we should add some limits.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ultimately I think this gets us closer to the previous behaviour where one task was submitting files to apple, and then a separate task was polling the service to get the notarized artifact back and staple it?

relpath_index_map = {}
task_index = 0
# Submit to notarization one by one
for relpath, path_dict in filelist_dict.items():
Copy link
Contributor

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but I seem to recall that we can upload a zipfile with multiple files we want notarized inside. Assuming that's true, I suggest we construct a new zipfile and make a single request rather than making a bunch of smaller ones. (This would avoid potential issues with rate limiting or similar problems.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For what I've gathered it's supported - but on the apple side it will check the apps sequentially, making things slower.
With the current setup we're making the exact same amount of parallel requests, so not much of a risk to be rate-limited - unless they could by requests per ~minute(?), which would be weird.



@time_async_function
async def apple_notarize_stacked(context, filelist_dict):
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a lot of overlap between this function and _notarize_all. If we're planning to keep the latter, some refactoring to avoid all of the duplication would be nice.

workdir_files = os.listdir(notarization_workdir)
supported_files = [filename for filename in workdir_files if _can_notarize(filename, (".app", ".pkg"))]
if not supported_files:
raise SigningScriptError("No supported files found")
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be preferable to do this sort of sanity checking prior to submitting anything - otherwise we're making notarization requests that will never be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're mostly handling multiple l10n versions of the same app, this should only only happen if something is broken upstream, and likely would be missing for all packages. It's a rare enough case that there's no point in optimizing for.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is about optimization, it's about being defensive, not trusting your inputs, and failing early.

signingscript/src/signingscript/sign.py Outdated Show resolved Hide resolved
signingscript/src/signingscript/task.py Outdated Show resolved Hide resolved
@@ -1633,8 +1633,7 @@ async def apple_notarize(context, path, *args, **kwargs):
"""
# Setup workdir
notarization_workdir = os.path.join(context.config["work_dir"], "apple_notarize")
shutil.rmtree(notarization_workdir, ignore_errors=True)
utils.mkdir(notarization_workdir)
utils.mkdir(notarization_workdir, delete_before_create=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is delete_before_create necessary now? Especially for the existing notarization functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's more of a safeguard. I don't see why we wouldn't want that..?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess what I'm wondering is: why is it getting added? Did you run into something? (I don't really have a problem with it...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be fair, this is more relevant for local testing to ensure we don't have anything in the workdir. AIUI In CI scriptworker takes care of deleting everything after each task is complete.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think something called mkdir should be doing rmtree. Since this seems to be a no-op (we were calling rmtree + mkdir, now we're calling mkdir which itself calls rmtree before re-creating the dir), I'd rather this was reverted.

@hneiva hneiva force-pushed the hneiva/async-notarize branch from 025be75 to d9a2d13 Compare April 11, 2024 19:27
@hneiva hneiva requested review from bhearsum and jcristau April 22, 2024 21:15
Copy link
Contributor

@bhearsum bhearsum left a comment

Choose a reason for hiding this comment

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

Leaving unapproved for the moment for the question about disallowing mixed formats - I'm OK with what's here though.

if not context.config.get("apple_notarization_configs", False):
raise Exception("Apple notarization is enabled but apple_notarization_configs is not defined")
setup_apple_notarization_credentials(context)

context.session = session
context.autograph_configs = load_autograph_configs(context.config["autograph_configs"])

work_dir = context.config["work_dir"]
# TODO: Make task.sign take in the whole filelist_dict and return a dict of output files.
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's get an issue on file for this, please.

filelist_dict = build_filelist_dict(context)
for path, path_dict in filelist_dict.items():
if path_dict["formats"] == ["apple_notarization_stacked"]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Did disallowing mixed formats get implemented?

@@ -54,6 +57,15 @@ async def async_main(context):
copy_to_dir(os.path.join(work_dir, source), context.config["artifact_dir"], target=source)
if "gpg" in path_dict["formats"] or "autograph_gpg" in path_dict["formats"]:
copy_to_dir(context.config["gpg_pubkey"], context.config["artifact_dir"], target="public/build/KEY")

# notarization_stacked is a special format that takes in all files at once instead of sequentially like other formats
notarization_dict = {path: path_dict for path, path_dict in filelist_dict.items() if "apple_notarization_stacked" in path_dict["formats"]}
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's get an issue on file to remove this later, too.

if extension == ".pkg":
path = os.path.join(notarization_workdir, relpath)
utils.copy_to_dir(path_dict["full_path"], notarization_workdir, target=relpath)
submissions_map[path] = await retry_async(
Copy link
Contributor

Choose a reason for hiding this comment

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

Just want to highlight this one last time. We're submitted the files synchronously, but we're not waiting on one to finish notarizating before we submit the next one - which means more will be in Apple's system at any given time than before. I have no issue with this, but please keep an eye on notarization tasks when this is activated to see if we start to hit more timeouts or intermittents. If we do, we should add some limits.

@@ -56,6 +58,8 @@
"autograph_rsa": sign_file_detached,
"apple_notarization": apple_notarize,
"apple_notarization_geckodriver": apple_notarize_geckodriver,
# This format is handled in script.py
# "apple_notarization_stacked": apple_notarize_stacked,
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be able to map this format to a function once the first todo above (about sign) is addressed. When you file that issue, please reference it here as well.

Copy link
Contributor

@bhearsum bhearsum left a comment

Choose a reason for hiding this comment

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

Thanks for filing #980!

Please add a comment near the relevant code before merging this.

Copy link
Contributor

@jcristau jcristau left a comment

Choose a reason for hiding this comment

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

Requesting changes for the mkdir bits, I'm fine with the rest of this, thanks.

@@ -1633,8 +1633,7 @@ async def apple_notarize(context, path, *args, **kwargs):
"""
# Setup workdir
notarization_workdir = os.path.join(context.config["work_dir"], "apple_notarize")
shutil.rmtree(notarization_workdir, ignore_errors=True)
utils.mkdir(notarization_workdir)
utils.mkdir(notarization_workdir, delete_before_create=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think something called mkdir should be doing rmtree. Since this seems to be a no-op (we were calling rmtree + mkdir, now we're calling mkdir which itself calls rmtree before re-creating the dir), I'd rather this was reverted.

if extension == ".pkg":
path = os.path.join(notarization_workdir, relpath)
utils.copy_to_dir(path_dict["full_path"], notarization_workdir, target=relpath)
submissions_map[path] = await retry_async(
Copy link
Contributor

Choose a reason for hiding this comment

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

Ultimately I think this gets us closer to the previous behaviour where one task was submitting files to apple, and then a separate task was polling the service to get the notarized artifact back and staple it?

workdir_files = os.listdir(notarization_workdir)
supported_files = [filename for filename in workdir_files if _can_notarize(filename, (".app", ".pkg"))]
if not supported_files:
raise SigningScriptError("No supported files found")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is about optimization, it's about being defensive, not trusting your inputs, and failing early.

@hneiva hneiva force-pushed the hneiva/async-notarize branch from 3babb54 to 7925b96 Compare April 25, 2024 17:53
@hneiva
Copy link
Contributor Author

hneiva commented Apr 25, 2024

Removed delete_before_create

Ultimately I think this gets us closer to the previous behaviour where one task was submitting files to apple, and then a separate task was polling the service to get the notarized artifact back and staple it?

At a high level, yes!

I don't think this is about optimization, it's about being defensive, not trusting your inputs, and failing early.

This feels like we're over-engineering for a red herring case, but I've updated the code to check everything before submitting.

@hneiva hneiva enabled auto-merge (squash) April 26, 2024 17:19
@hneiva hneiva merged commit 06e2958 into master Apr 26, 2024
9 checks passed
@hneiva hneiva deleted the hneiva/async-notarize branch April 26, 2024 17:24
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.

3 participants