-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Deprecate ambiguous constructors #2839
Conversation
*/ | ||
public class FixedHostPortGenericContainer<SELF extends FixedHostPortGenericContainer<SELF>> extends GenericContainer<SELF> { | ||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*/ | |
public class FixedHostPortGenericContainer<SELF extends FixedHostPortGenericContainer<SELF>> extends GenericContainer<SELF> { | |
@Deprecated | |
*/ | |
@Deprecated | |
public class FixedHostPortGenericContainer<SELF extends FixedHostPortGenericContainer<SELF>> extends GenericContainer<SELF> { |
😂 😂 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deprecated without replacement - because it's evil.
this.image = new RemoteDockerImage(dockerImageName); | ||
} | ||
|
||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about GenericContainer(Future)
? Since it can be replaced with new GenericContainer(new RemoteDockerImage(future))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh, interesting suggestion - I'll explore that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not done this yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've had a quick try, but I'm not sure about this.
This example from existing code:
new GenericContainer(new ImageFromDockerfile() ...
would become
new GenericContainer(new RemoteDockerImage(new ImageFromDockerfile() ...
which I think is quite ugly - it's really not a 'remote docker image', so naming-wise it's a bit of a stretch.
I wonder if, really, the way to make this work would be to rename RemoteDockerImage
to FutureDockerImageName
, which more accurately reflects what it's used for. So the example would become:
new GenericContainer(new FutureDockerImageName(new ImageFromDockerfile() ...
We'd of course keep RemoteDockerImage
around, deprecated, as no more than an alias to FutureDockerImageName
.
Two problems I see with this that I don't think we can magic away. I think I could live with these:
- the three levels of constructors involved are a bit clunky, but it's not a big deal
- it's such as shame that type erasure prevents us from overloading the
GenericContainer(Future<String>)
constructor, because otherwiseFutureDockerImageName
could just beFuture<DockerImageName>
😂 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree that "Remote" is misleading here.
But the example you provided made me think whether we should even wrap ImageFromDockerfile
with RemoteDockerImage
, since it is definitely local, does not need an auth resolve, etc etc...
what if we extract a common interface from RemoteDockerImage
and make ImageFromDockerfile
implement it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the interface being DockerImage
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on Slack, we'll defer this to a separate PR.
It feels like more extensive and complicated changes are needed to refactor the Remote/Future images code, and I feel it would be sensible to not add that complexity on top of this, already massive, PR.
b60d19c
to
68ee3bc
Compare
…-constructors # Conflicts: # core/src/test/java/org/testcontainers/images/RemoteDockerImageTest.java # modules/kafka/src/main/java/org/testcontainers/containers/KafkaContainer.java # modules/kafka/src/test/java/org/testcontainers/containers/KafkaContainerTest.java # modules/pulsar/src/main/java/org/testcontainers/containers/PulsarContainer.java # modules/selenium/src/test/java/org/testcontainers/junit/ChromeRecordingWebDriverContainerTest.java
/** | ||
* @deprecated use {@link SocatContainer(DockerImageName)} instead | ||
*/ | ||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking... what if we keep the default constructors in "utility" classes like this one, where the users do not / should not care about the version used? it is a bit contradictory to the idea of this whole change, but I think a few exceptions can be made
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was thinking the same. If these had not been essentially in our public API it would have been a no-brainer.
But I'm fine with doing it. I'll move to deprecate these classes away from view (e.g. if we can eventually get them to be package-private then this becomes moot).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, I would not hide/deprecate them, because they are helpful for building complex 3rd party containers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted
this.imageNameFuture = CompletableFuture.completedFuture(dockerImageName); | ||
} | ||
|
||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the contract was always that RemoteDockerImage
accepts a full image name, perhaps we can keep this and (String,String)
constructors?
Otherwise, it becomes a bit too long:
new GenericContainer(new RemoveDockerImage(new DockerImageName("missed-me:1"))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will defer as per #2839 (comment)
@@ -124,8 +144,13 @@ public String getRegistry() { | |||
return registry; | |||
} | |||
|
|||
public DockerImageName withTag(final String newTag) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it!
Made me thinking whether we should even have a fully featured builder as well:
DockerImageName.builder()
.registry("repo.example.com")
.image("services/foo")
.tag("123")
.build();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the halfway house of Dockerfilename.of("repo.example.com/services/foo").withTag("123")
is probably enough - I'd like to try that first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR the builder idea is just an idea, don't need to be in this PR and can easily be a follow up :)
@@ -61,8 +67,8 @@ | |||
@Parameterized.Parameters(name = "{0}") | |||
public static Object[][] data() { | |||
return new Object[][] { | |||
{ "generic", new GenericContainer(IMAGE_FUTURE), true }, | |||
{ "anonymous generic", new GenericContainer(IMAGE_FUTURE) {}, true }, | |||
{ "generic", new GenericContainer<>(new RemoteDockerImage(IMAGE_FUTURE)), true }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR if I remember correctly, I had to use IMAGE_FUTURE
here to avoid the side effect we had in the constructor, but now it can be as simple as new GenericContainer<>(TINY_IMAGE)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
public class ImagePullPolicyTest { | ||
|
||
@ClassRule | ||
public static GenericContainer<?> registry = new GenericContainer<>("registry:2") | ||
public static GenericContainer<?> registry = new GenericContainer<>(new DockerImageName("registry:2.7.0")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I liked the idea of TestImages
, btw. Perhaps we can/should move such references to that class as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I missed this one!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
@@ -74,7 +74,7 @@ public void setUp() { | |||
@Test | |||
public void pullsByDefault() { | |||
try ( | |||
GenericContainer<?> container = new GenericContainer<>(imageName) | |||
GenericContainer<?> container = new GenericContainer(new DockerImageName(imageName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: imageName
field can be changed to DockerImageName
, to make tests' bodies a little bit smaller
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
*/ | ||
@Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bit unrelated, but... don't we have a mongodb module now? :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we should discuss nuking this example...
/** | ||
* Taken from <a href="https://docs.mongodb.com/manual/core/transactions/">https://docs.mongodb.com</a> | ||
*/ | ||
@Test | ||
public void shouldExecuteTransactions() { | ||
try ( | ||
// creatingMongoDBContainer { | ||
final MongoDBContainer mongoDBContainer = new MongoDBContainer() | ||
final MongoDBContainer mongoDBContainer = new MongoDBContainer(MONGO_IMAGE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps we should inline the image here, as it will be in the docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java
Show resolved
Hide resolved
core/src/test/java/org/testcontainers/images/ImagePullPolicyTest.java
Outdated
Show resolved
Hide resolved
…st.java Co-authored-by: Sergei Egorov <bsideup@gmail.com>
@bsideup just saw your tweet for testcontainers 1.15-rc1 and I wanted to -1 the deprecation of An eventual removal of public class MyContainer extends GenericContainer<MyContainer> {
public MyContainer() {
super("mycontainer:1.0");
}
// ...
} Deprecating this in favor of: Instead of deprecating the constructor, can we simply change it to this? public GenericContainer(@NonNull final String dockerImageName) {
this(new DockerImageName(dockerImageName));
} |
@aguibert it's my PR so I'll respond first ;) One of the problems we're trying to undo is the inconsistent meaning of stringly-typed parameters; in some container classes it's a full image name, and in other places it's a tag name (meant to be concatenated against a provided image name).
We're just as keen to get rid of these constructors as we are to get rid of the default no-args constructors, though:
For these cases we can't just flip the meaning of a single string argument to be a full docker image name - I wish we could, but it would be an immediate breaking change for everyone who has used So, we don't think it is over-typing, as one of the problems we're trying to tackle is the tech debt of having an ambiguous type 😀 As for breakage, we're only deprecating the constructors, not removing them. We intend to give a significant period of time for people to migrate. You can see from our previous deprecation-removal cycles that we're pretty glacial about this, so I wouldn't expect an actual breaking change here in 2020. |
@rnorth btw, I was thinking about this one:
since we have the parsing logic, theoretically, we could detect tag vs image and handle both :) |
Yeah, I thought about it but it feels like inviting spectacular unforeseen consequences into our lives 😆 |
:D true. Although I think in this case the rules are "strict enough":
sounds like we can simply check for |
We’ve come up with what we think is a better compromise:
Thanks for raising your concerns @aguibert - it’s made us rethink this and find a (hopefully) better solution. |
Our constructors for
GenericContainer
and all the classes that inherit from it tend to be a bit inconsistent. In some places, a no-args constructor is favoured, in other places a String parameter is favoured - which can be either a full image name or a tag.This raises confusion, and in the case of an image tag being the parameter, it restricts which images people can easily use.
Another problem this raises is when the container class specifies a default tag version: this frequently falls out of date. We're stuck for options: some users (and image maintainers) would like to see newer image versions being used, but this could be a breaking change for existing users. This is especially risky when a major version bump is proposed.
For example, the following open PRs suffer varying degrees of this risk: #2825, #2773, #2320, #2436.
This PR aims to:
DockerImageName
object. This class is preferred to a String because its meaning is unambiguous.DockerImageName
with a static factory method and a conveniencewithTag
method so that users can easily create and derive new instances.This PR will not introduce any breaking changes, but is likely to be an 'annoying' change. I'm considering whether a script could be written to quickly migrate code, especially for people with large codebases.
Note that I'm not touching the
Future<String>
constructor. I would have liked to, but type erasure makes adding an adjacentFuture<DockerImageName>
constructor impossible. As such, I don't think we can do anything about this particular parameter without a breaking change. I'd be open to suggestions, though!