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

Split image name into snapshot and release registries, with a separate path parameter #645

Merged
merged 5 commits into from
Feb 10, 2020

Conversation

mccheah
Copy link

@mccheah mccheah commented Feb 6, 2020

In the docker image generator, we were originally publishing all built images to the same registry. It is however useful to publish snapshot versions of one's Spark images to one registry, and release versions to another registry. As such, the schema for the configuration is now as follows:

sparkDocker {
  baseImage <full path to base image, including the registry and tag>
  imagePath <The path component only of the image name>
  snapshotRegistry <The registry to use when pushing snapshot versions>
  releaseRegistry <The registry to use when pushing release versions>
  tags <set of tags to push>
}

We follow the SLS versioning specification to determine whether or not an image is being built on a release version or a snapshot version.

@@ -28,7 +28,7 @@

private File dockerFile;
private File dockerBuildDirectory;
private Property<String> imageName;
private Provider<String> imageName;

Choose a reason for hiding this comment

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

re-assigning a provider like this will give you quite weird behaviour if anyone uses the getImageName accessor and tries to .map it or access it later:

1. someone sets up a .map
2. someone else calls setImageName(..) which _replaces_ the internal provider field
3. the new value never makes it to person (1)

I'd recommend creating a private final Property here instead.

Copy link
Author

Choose a reason for hiding this comment

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

The problem is that the imageName is derived from the other fields in the extension (both registries + image path), and thus is not a property that can be set directly in the extension. We can set the three properties in each place we need the image name (this task, the tag-all task, and push task). But that would require deriving the image name from those fields each time. And Property#flatMap returns a Provider, not a Property. Thoughts?

Copy link

@iamdanfox iamdanfox Feb 6, 2020

Choose a reason for hiding this comment

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

Property has a setter which takes a provider, so it should be fine to set up all the derivation once (which should produce a provider) and then set this task's property to be hooked up to that imageName provider.

Copy link
Author

Choose a reason for hiding this comment

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

Ah, missed that - makes sense that these can remain as Property fields backed by the deriving Provider then. (Seems weird to keep having to go back and forth between Property and Provider and back to Property again.)

@@ -53,7 +53,7 @@ public final void setDockerBuildDirectory(File dockerBuildDirectory) {
this.dockerBuildDirectory = dockerBuildDirectory;
}

public final void setImageName(Property<String> imageName) {
public final void setImageName(Provider<String> imageName) {
this.imageName = imageName;

Choose a reason for hiding this comment

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

when the imageName is a Property, you can use .set instead of assignment. This allows anyone who is observing the original value to notice when a new value has been 'set'.

Copy link
Author

Choose a reason for hiding this comment

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

But ImageName will not be initialized to anything (null) on the no-arg constructor of this task. I think it's fine to use setter for now - again looking to minimize changes from the original.

@@ -19,28 +19,28 @@
import java.util.List;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.TaskAction;

public class LazyExecTask extends DefaultTask {

Choose a reason for hiding this comment

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

I think bog-standard exec tasks have a CommandLineArgumentProvider abstraction which might obviate this class?

Copy link
Author

Choose a reason for hiding this comment

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

I'm not too keen on trying to use this PR to do refactors and cleanups as well - only the minimal necessary to complete the task at hand.

Copy link
Author

Choose a reason for hiding this comment

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

We can file this as a TODO

project,
imagePath,
snapshotRegistry,
releaseRegistry))));

Choose a reason for hiding this comment

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

I think it might be useful to put this logic as a read-only provider on the extension, because won't people need to grab the derived value you create and template it into their configuration.yml?

Copy link
Author

Choose a reason for hiding this comment

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

That could make sense, although we're having concerns about using a Provider in these fields at all, right? See #645 (comment). But this is what I meant about needing a Provider and not a Property because of the derivation matter.

@mccheah
Copy link
Author

mccheah commented Feb 6, 2020

@iamdanfox I switched to using a Property instead of a Provider, that's automatically computed read-only by the extension. I didn't address some of the other things you have mentioned, since I think they belong in separate refactor PRs rather than in a PR that is introducing new functionality.

project,
resolvedImagePath,
resolvedSnapshotRegistry,
resolvedReleaseRegistry)))));

Choose a reason for hiding this comment

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

I think all the flatmaps and casting is a little more complicated than necessary. Why not just construct a fresh provider which just happens to call .get() on all the others?

project.provider(() -> {
  return ImageResolver.resolveImageName(p, imagePath.get(), snapshotRegistry.get() ... );
}

}
imageNameBuilder.append(imagePath);
return imageNameBuilder.toString();
}

Choose a reason for hiding this comment

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

Do you think it's worth doing some regex validation on the imagePath, just so that people don't leave values like docker.palantir/foo/bar in there and get some bizarre concatenation of everything?

Copy link
Author

Choose a reason for hiding this comment

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

I'm ok with not having validation here, again, keeping parity to how things were before - we weren't validating the scheme of the URI for the full image field.

@mccheah
Copy link
Author

mccheah commented Feb 10, 2020

@iamdanfox any further comments here?

Copy link

@iamdanfox iamdanfox left a comment

Choose a reason for hiding this comment

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

Looks great, thanks @mccheah!

@mccheah mccheah merged commit b5c101e into master Feb 10, 2020
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.

2 participants