-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Deprecate ambiguous constructors #2839
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
Changes from all commits
68ee3bc
46c7e1b
054b637
196f1a9
f3fe807
c523b38
296f9f7
aa94289
77aa418
592f08a
1edc307
113f3b3
fc7f625
cd7e13b
a35b3c4
868f962
faf1217
ee45003
8960ceb
f4e314d
cc4190d
0eee5d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,12 +38,18 @@ public class RemoteDockerImage extends LazyFuture<String> { | |
@ToString.Exclude | ||
private DockerClient dockerClient = DockerClientFactory.lazyClient(); | ||
|
||
public RemoteDockerImage(DockerImageName dockerImageName) { | ||
this.imageNameFuture = CompletableFuture.completedFuture(dockerImageName); | ||
} | ||
|
||
@Deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the contract was always that 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 commentThe reason will be displayed to describe this comment to others. Learn more. Will defer as per #2839 (comment) |
||
public RemoteDockerImage(String dockerImageName) { | ||
this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(dockerImageName)); | ||
this.imageNameFuture = CompletableFuture.completedFuture(DockerImageName.parse(dockerImageName)); | ||
} | ||
|
||
@Deprecated | ||
public RemoteDockerImage(@NonNull String repository, @NonNull String tag) { | ||
this.imageNameFuture = CompletableFuture.completedFuture(new DockerImageName(repository, tag)); | ||
this.imageNameFuture = CompletableFuture.completedFuture(DockerImageName.parse(repository).withTag(tag)); | ||
} | ||
|
||
public RemoteDockerImage(@NonNull Future<String> imageFuture) { | ||
|
@@ -57,7 +63,7 @@ protected final String resolve() { | |
Logger logger = DockerLoggerFactory.getLogger(imageName.toString()); | ||
try { | ||
if (!imagePullPolicy.shouldPull(imageName)) { | ||
return imageName.toString(); | ||
return imageName.asCanonicalNameString(); | ||
} | ||
|
||
// The image is not available locally - pull it | ||
|
@@ -76,7 +82,7 @@ protected final String resolve() { | |
|
||
LocalImagesCache.INSTANCE.refreshCache(imageName); | ||
|
||
return imageName.toString(); | ||
return imageName.asCanonicalNameString(); | ||
} catch (InterruptedException | InternalServerErrorException e) { | ||
// these classes of exception often relate to timeout/connection errors so should be retried | ||
lastFailure = e; | ||
|
@@ -104,7 +110,7 @@ private String imageNameToString() { | |
} | ||
|
||
try { | ||
return getImageName().toString(); | ||
return getImageName().asCanonicalNameString(); | ||
} catch (InterruptedException | ExecutionException e) { | ||
return e.getMessage(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,16 @@ | |
|
||
|
||
import com.google.common.net.HostAndPort; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import lombok.EqualsAndHashCode; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
@EqualsAndHashCode(exclude = "rawName") | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
public final class DockerImageName { | ||
|
||
/* Regex patterns used for validation */ | ||
|
@@ -19,22 +23,42 @@ public final class DockerImageName { | |
private final String rawName; | ||
private final String registry; | ||
private final String repo; | ||
private final Versioning versioning; | ||
@NotNull private final Versioning versioning; | ||
|
||
public DockerImageName(String name) { | ||
this.rawName = name; | ||
final int slashIndex = name.indexOf('/'); | ||
/** | ||
* Parses a docker image name from a provided string. | ||
* | ||
* @param fullImageName in standard Docker format, e.g. <code>name:tag</code>, | ||
* <code>some.registry/path/name:tag</code>, | ||
* <code>some.registry/path/name@sha256:abcdef...</code>, etc. | ||
*/ | ||
public static DockerImageName parse(String fullImageName) { | ||
return new DockerImageName(fullImageName); | ||
} | ||
|
||
/** | ||
* Parses a docker image name from a provided string. | ||
* | ||
* @param fullImageName in standard Docker format, e.g. <code>name:tag</code>, | ||
* <code>some.registry/path/name:tag</code>, | ||
* <code>some.registry/path/name@sha256:abcdef...</code>, etc. | ||
* @deprecated use {@link DockerImageName#parse(String)} instead | ||
*/ | ||
@Deprecated | ||
public DockerImageName(String fullImageName) { | ||
this.rawName = fullImageName; | ||
final int slashIndex = fullImageName.indexOf('/'); | ||
|
||
String remoteName; | ||
if (slashIndex == -1 || | ||
(!name.substring(0, slashIndex).contains(".") && | ||
!name.substring(0, slashIndex).contains(":") && | ||
!name.substring(0, slashIndex).equals("localhost"))) { | ||
(!fullImageName.substring(0, slashIndex).contains(".") && | ||
!fullImageName.substring(0, slashIndex).contains(":") && | ||
!fullImageName.substring(0, slashIndex).equals("localhost"))) { | ||
registry = ""; | ||
remoteName = name; | ||
remoteName = fullImageName; | ||
} else { | ||
registry = name.substring(0, slashIndex); | ||
remoteName = name.substring(slashIndex + 1); | ||
registry = fullImageName.substring(0, slashIndex); | ||
remoteName = fullImageName.substring(slashIndex + 1); | ||
} | ||
|
||
if (remoteName.contains("@sha256:")) { | ||
|
@@ -49,28 +73,40 @@ public DockerImageName(String name) { | |
} | ||
} | ||
|
||
public DockerImageName(String name, String tag) { | ||
this.rawName = name; | ||
final int slashIndex = name.indexOf('/'); | ||
/** | ||
* Parses a docker image name from a provided string, and uses a separate provided version. | ||
* | ||
* @param nameWithoutTag in standard Docker format, e.g. <code>name</code>, | ||
* <code>some.registry/path/name</code>, | ||
* <code>some.registry/path/name</code>, etc. | ||
* @param version a docker image version identifier, either as a tag or sha256 checksum, e.g. | ||
* <code>tag</code>, | ||
* <code>sha256:abcdef...</code>. | ||
* @deprecated use {@link DockerImageName#parse(String)}.{@link DockerImageName#withTag(String)} instead | ||
*/ | ||
@Deprecated | ||
public DockerImageName(String nameWithoutTag, @NotNull String version) { | ||
this.rawName = nameWithoutTag; | ||
final int slashIndex = nameWithoutTag.indexOf('/'); | ||
|
||
String remoteName; | ||
if (slashIndex == -1 || | ||
(!name.substring(0, slashIndex).contains(".") && | ||
!name.substring(0, slashIndex).contains(":") && | ||
!name.substring(0, slashIndex).equals("localhost"))) { | ||
(!nameWithoutTag.substring(0, slashIndex).contains(".") && | ||
!nameWithoutTag.substring(0, slashIndex).contains(":") && | ||
!nameWithoutTag.substring(0, slashIndex).equals("localhost"))) { | ||
registry = ""; | ||
remoteName = name; | ||
remoteName = nameWithoutTag; | ||
} else { | ||
registry = name.substring(0, slashIndex); | ||
remoteName = name.substring(slashIndex + 1); | ||
registry = nameWithoutTag.substring(0, slashIndex); | ||
remoteName = nameWithoutTag.substring(slashIndex + 1); | ||
} | ||
|
||
if (tag.startsWith("sha256:")) { | ||
if (version.startsWith("sha256:")) { | ||
repo = remoteName; | ||
versioning = new Sha256Versioning(tag.replace("sha256:", "")); | ||
versioning = new Sha256Versioning(version.replace("sha256:", "")); | ||
} else { | ||
repo = remoteName; | ||
versioning = new TagVersioning(tag); | ||
versioning = new TagVersioning(version); | ||
} | ||
} | ||
|
||
|
@@ -92,13 +128,16 @@ public String getVersionPart() { | |
return versioning.toString(); | ||
} | ||
|
||
/** | ||
* @return canonical name for the image | ||
*/ | ||
public String asCanonicalNameString() { | ||
return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
if (versioning == null) { | ||
return getUnversionedPart(); | ||
} else { | ||
return getUnversionedPart() + versioning.getSeparator() + versioning.toString(); | ||
} | ||
return asCanonicalNameString(); | ||
} | ||
|
||
/** | ||
|
@@ -111,10 +150,6 @@ public void assertValid() { | |
if (!REPO_NAME.matcher(repo).matches()) { | ||
throw new IllegalArgumentException(repo + " is not a valid Docker image name (in " + rawName + ")"); | ||
} | ||
if (versioning == null) { | ||
throw new IllegalArgumentException("No image tag was specified in docker image name " + | ||
"(" + rawName + "). Please provide a tag; this may be 'latest' or a specific version"); | ||
} | ||
if (!versioning.isValid()) { | ||
throw new IllegalArgumentException(versioning + " is not a valid image versioning identifier (in " + rawName + ")"); | ||
} | ||
|
@@ -124,8 +159,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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. I feel like the halfway house of There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 :) |
||
return new DockerImageName(rawName, registry, repo, new TagVersioning(newTag)); | ||
} | ||
|
||
private interface Versioning { | ||
boolean isValid(); | ||
|
||
String getSeparator(); | ||
} | ||
|
||
|
@@ -155,7 +195,7 @@ public String toString() { | |
} | ||
|
||
@Data | ||
private class Sha256Versioning implements Versioning { | ||
private static class Sha256Versioning implements Versioning { | ||
public static final String HASH_REGEX = "[0-9a-fA-F]{32,}"; | ||
private final String hash; | ||
|
||
|
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 withnew 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:
would become
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
toFutureDockerImageName
, which more accurately reflects what it's used for. So the example would become:We'd of course keep
RemoteDockerImage
around, deprecated, as no more than an alias toFutureDockerImageName
.Two problems I see with this that I don't think we can magic away. I think I could live with these:
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
withRemoteDockerImage
, since it is definitely local, does not need an auth resolve, etc etc...what if we extract a common interface from
RemoteDockerImage
and makeImageFromDockerfile
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.