Record information in eclipse metadata for docker images build by IDE.
Just enough info is recored for a IDE built-image so we can avoid
parsing image metadata from the image itself (the image metadata
format is unstable and we do not have a usable api to get at it easily,
so trying to rely on it is fraught with problems).
This commit is contained in:
Kris De Volder
2022-06-24 15:31:20 -07:00
parent 078d1b1983
commit dc36d17298
6 changed files with 69 additions and 54 deletions

View File

@@ -53,6 +53,7 @@ import org.springframework.ide.eclipse.boot.dash.docker.exceptions.MissingBuildS
import org.springframework.ide.eclipse.boot.dash.docker.exceptions.MissingBuildTagException;
import org.springframework.ide.eclipse.boot.dash.docker.jmx.JmxSupport;
import org.springframework.ide.eclipse.boot.dash.docker.runtarget.BuildScriptLocator.BuildKind;
import org.springframework.ide.eclipse.boot.dash.docker.runtarget.DockerApp.BuildCommand;
import org.springframework.ide.eclipse.boot.dash.labels.BootDashLabels;
import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springframework.ide.eclipse.boot.dash.model.remote.ChildBearing;
@@ -61,6 +62,7 @@ import org.springframework.ide.eclipse.boot.dash.util.LineBasedStreamGobler;
import org.springframework.ide.eclipse.boot.launch.util.PortFinder;
import org.springframework.ide.eclipse.boot.util.JavaProjectUtil;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStoreApi;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStores;
import org.springsource.ide.eclipse.commons.frameworks.core.util.JobUtil;
import org.springsource.ide.eclipse.commons.frameworks.core.util.StringUtils;
import org.springsource.ide.eclipse.commons.livexp.core.AbstractDisposable;
@@ -87,7 +89,19 @@ import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
public class DockerApp extends AbstractDisposable implements App, ChildBearing, Deletable, ProjectRelatable, DesiredInstanceCount, SystemPropertySupport, LogSource, DevtoolsConnectable {
public class DockerApp extends AbstractDisposable implements App, ChildBearing, Deletable, ProjectRelatable, DesiredInstanceCount,
SystemPropertySupport, LogSource, DevtoolsConnectable {
public class BuildCommand {
String[] command;
boolean builtWithDevToolsArgs;
public BuildCommand(String[] command, boolean builtWithDevToolsArgs) {
super();
this.command = command;
this.builtWithDevToolsArgs = builtWithDevToolsArgs;
}
}
private static final String DOCKER_IO_LIBRARY = "docker.io/library/";
private static final String[] NO_STRINGS = new String[0];
@@ -389,14 +403,14 @@ Successfully tagged fui:latest
private String build(AppConsole console) throws Exception {
String[] imageIds = new String[BUILT_IMAGE_MESSAGE_PATS.length];
File directory = new File(project.getLocation().toString());
String[] command = getBuildCommand(directory);
BuildCommand command = getBuildCommand(directory);
ProcessBuilder builder = new ProcessBuilder(command).directory(directory);
ProcessBuilder builder = new ProcessBuilder(command.command).directory(directory);
String jhome = getJavaHome();
builder.environment().put("JAVA_HOME", jhome);
console.write("build.env.JAVA_HOME="+jhome, LogType.STDOUT);
console.write("build.directory="+directory, LogType.STDOUT);
console.logCommand(CommandUtil.escape(command));
console.logCommand(CommandUtil.escape(command.command));
Process process = builder.start();
LineBasedStreamGobler outputGobler = new LineBasedStreamGobler(process.getInputStream(), (line) -> {
System.out.println(line);
@@ -447,10 +461,10 @@ Successfully tagged fui:latest
if (images.isEmpty()) {
// maybe the 'imageTag' is not actually a tag but an id/hash.
InspectImageResponse inspect = client.inspectImageCmd(imageTag).exec();
addPersistedImage(inspect.getId());
addPersistedImage(inspect.getId(), command);
} else {
for (Image img : images) {
addPersistedImage(img.getId());
addPersistedImage(img.getId(), command);
}
}
return imageTag;
@@ -461,19 +475,22 @@ Successfully tagged fui:latest
return jvm.getInstallLocation().toString();
}
private String[] getBuildCommand(File directory) throws MissingBuildScriptException {
private BuildCommand getBuildCommand(File directory) throws MissingBuildScriptException {
BuildScriptLocator buildScriptLocator = new BuildScriptLocator(directory);
BuildKind buildKind = buildScriptLocator.getBuildKind();
if (buildKind==null) {
throw new MissingBuildScriptException(buildScriptLocator.checkedLocations);
}
boolean wantsDevtools = deployment().getSystemProperties().getOrDefault(DevtoolsUtil.REMOTE_SECRET_PROP, null)!=null;
boolean withDevtoolsArgs = false;
List<String> command = buildScriptLocator.command;
if (wantsDevtools) {
if (buildKind==BuildKind.MAVEN) {
withDevtoolsArgs = true;
command.add("-Dspring-boot.repackage.excludeDevtools=false");
} else if (buildKind==BuildKind.GRADLE) {
try {
withDevtoolsArgs = true;
command.addAll(gradle_initScript(
"allprojects {\n" +
" afterEvaluate {\n" +
@@ -488,7 +505,7 @@ Successfully tagged fui:latest
}
}
}
return command.toArray(new String[command.size()]);
return new BuildCommand(command.toArray(new String[command.size()]), withDevtoolsArgs);
}
private synchronized static List<String> gradle_initScript(String script) throws IOException {
@@ -503,7 +520,7 @@ Successfully tagged fui:latest
);
}
synchronized private void addPersistedImage(String imageId) {
synchronized private void addPersistedImage(String imageId, BuildCommand command) {
String key = imagesKey();
try {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
@@ -511,12 +528,12 @@ Successfully tagged fui:latest
builder.addAll(Arrays.asList(props.get(key, NO_STRINGS)));
builder.add(imageId);
props.put(key, builder.build().toArray(NO_STRINGS));
props.put(DockerImage.hasDevtoolsKey(imageId), context.projectHasDevtoolsDependency() && command.builtWithDevToolsArgs);
} catch (Exception e) {
Log.log(e);
}
}
private void setPersistedImages(Set<String> existingImages) {
try {
getTarget().getPersistentProperties().put(imagesKey(), existingImages.toArray(NO_STRINGS));

View File

@@ -25,7 +25,6 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -54,8 +53,6 @@ import org.springframework.ide.eclipse.boot.dash.docker.util.Ownable;
import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springframework.ide.eclipse.boot.dash.model.remote.RefreshStateTracker;
import org.springframework.ide.eclipse.boot.util.RetryUtil;
import org.springframework.ide.eclipse.editor.support.yaml.path.JavaObjectNav;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlNavigable;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlPath;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlTraversal;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
@@ -72,8 +69,6 @@ import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.StreamType;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -89,33 +84,12 @@ public class DockerContainer implements App, RunStateProvider, JmxConnectable, S
private static Map<RunState, ImageDescriptor> RUNSTATE_ICONS = null;
private DockerApp app;
private AppContext context;
public DockerContainer(DockerRunTarget target, DockerApp app, Container container) {
this.target = target;
this.app = app;
this.container = container;
this.hasDevtoolsDep = hasDevtoolsDependency(container::getLabels);
}
public static Supplier<Boolean> hasDevtoolsDependency(Supplier<Map<String,String>> labelsSupplier) {
return Suppliers.memoize(() ->{
Map<String, String> labels = labelsSupplier.get();
try {
if (labels!=null) {
String buildpackMetadata = labels.get("io.buildpacks.build.metadata");
if (buildpackMetadata!=null) {
Map<?,?> bpmd = new ObjectMapper().readValue(buildpackMetadata, Map.class);
Set<String> deps = dependencyNamePath
.traverseAmbiguously(YamlNavigable.javaObject(bpmd))
.flatMap(JavaObjectNav::asStringMaybe).collect(Collectors.toSet());
return deps.contains("spring-boot-devtools");
}
}
} catch (Exception e) {
Log.log(e);
}
return false;
});
}
@Override
@@ -285,6 +259,7 @@ public class DockerContainer implements App, RunStateProvider, JmxConnectable, S
@Override
public void setContext(AppContext context) {
this.refreshTracker.complete(context.getRefreshTracker());
this.context = context;
}
@Override
@@ -362,12 +337,16 @@ public class DockerContainer implements App, RunStateProvider, JmxConnectable, S
.thenValAt("name");
private static final boolean USE_DEDICATED_CLIENT = false;
private Supplier<Boolean> hasDevtoolsDep;
@Override
public boolean hasDevtoolsDependency() {
return this.hasDevtoolsDep.get();
if (context!=null) {
DockerImage image = context.getParent(DockerImage.class);
return image.hasDevtoolsDependency();
}
return false;
}
// for debugging... keep in comments for now

View File

@@ -38,6 +38,7 @@ import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springframework.ide.eclipse.boot.dash.model.remote.ChildBearing;
import org.springframework.ide.eclipse.boot.dash.model.remote.RefreshStateTracker;
import org.springframework.ide.eclipse.boot.util.RetryUtil;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStoreApi;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.frameworks.core.util.JobUtil;
import org.springsource.ide.eclipse.commons.livexp.ui.Stylers;
@@ -47,7 +48,6 @@ import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.Image;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
@@ -59,14 +59,12 @@ public class DockerImage implements App, ChildBearing, Styleable, ProjectRelatab
private final DockerApp app;
private final Image image;
public final CompletableFuture<RefreshStateTracker> refreshTracker = new CompletableFuture<>();
private final Supplier<Boolean> hasDevtoolsDependency;
private static Map<RunState, ImageDescriptor> RUNSTATE_ICONS = null;
public DockerImage(DockerApp app, Image image) {
this.app = app;
this.image = image;
this.hasDevtoolsDependency = DockerContainer.hasDevtoolsDependency(image::getLabels);
}
@Override
@@ -249,11 +247,16 @@ public class DockerImage implements App, ChildBearing, Styleable, ProjectRelatab
@Override
public boolean hasDevtoolsDependency() {
return this.hasDevtoolsDependency.get();
PropertyStoreApi props = getTarget().getPersistentProperties();
return props.get(hasDevtoolsKey(image.getId()), false);
}
@Override
public TemporalBoolean isDevtoolsConnectable() {
return TemporalBoolean.NEVER;
}
public static String hasDevtoolsKey(String imageId) {
return imageId +".hasDevtoolsDependency";
}
}