Mimimal support for building image with Dockerfile
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.boot.dash.docker.exceptions;
|
||||
|
||||
import org.springframework.ide.eclipse.boot.dash.api.AppConsole;
|
||||
|
||||
public class DockerBuildException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DockerBuildException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
public void writeDetailedExplanation(AppConsole console) throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.boot.dash.docker.exceptions;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.eclipse.boot.dash.api.AppConsole;
|
||||
import org.springframework.ide.eclipse.boot.dash.console.LogType;
|
||||
|
||||
/**
|
||||
* Exception raised when BootDash wants to build a docker image but can not
|
||||
* find / determine how to build it. I.e. we are looking for a number of
|
||||
* different build scripts in project-relative locations. If none of
|
||||
* them are found then this exception is raised.
|
||||
*/
|
||||
public class MissingBuildScriptException extends DockerBuildException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final List<File> locationsChecked;
|
||||
|
||||
public MissingBuildScriptException(List<File> locationsChecked) {
|
||||
super("Neither maven wrapper, gradle wrapper or custom build script found.");
|
||||
this.locationsChecked = locationsChecked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDetailedExplanation(AppConsole console) throws Exception {
|
||||
console.write("Places we looked for build script: ", LogType.STDERROR);
|
||||
for (File loc : this.locationsChecked) {
|
||||
console.write(" - "+loc, LogType.STDERROR);
|
||||
}
|
||||
showBuildScriptHelp(console);
|
||||
}
|
||||
|
||||
private void showBuildScriptHelp(AppConsole console) {
|
||||
String[] help = {
|
||||
"To build a docker image, Boot Dash needs to run a build script from your project.",
|
||||
"Three different types are supported and checked for in this order:",
|
||||
"",
|
||||
"1. "+this.locationsChecked.get(0).getAbsoluteFile().getName(),
|
||||
" A custom script placed by you at the project root.",
|
||||
" Typically this runs a custom maven or gradle command on your project.",
|
||||
"",
|
||||
"2. maven",
|
||||
" If your project has a mvnw, we will use that to execute the `spring-boot:build-image` task",
|
||||
"",
|
||||
"3. gradle",
|
||||
" If your project has a gradlew, we will use that to execute the `bootBuildImage` task",
|
||||
};
|
||||
try {
|
||||
for (String line : help) {
|
||||
console.write(line, LogType.STDERROR);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.boot.dash.docker.exceptions;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.ide.eclipse.boot.dash.api.AppConsole;
|
||||
import org.springframework.ide.eclipse.boot.dash.console.LogType;
|
||||
|
||||
public class MissingBuildTagException extends DockerBuildException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final Pattern[] whatWeLookedFor;
|
||||
|
||||
public MissingBuildTagException(Pattern... whatWeLookedFor) {
|
||||
super("Couldn't detect the image id or tag");
|
||||
this.whatWeLookedFor = whatWeLookedFor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDetailedExplanation(AppConsole console) throws Exception {
|
||||
console.write("We detect the image tag or hash by matching specific regexp patterns", LogType.STDERROR);
|
||||
console.write("in the build output. But none of the patterns we look for where found.", LogType.STDERROR);
|
||||
console.write("", LogType.STDERROR);
|
||||
console.write("These are the patterns that we looked for:", LogType.STDERROR);
|
||||
console.write("", LogType.STDERROR);
|
||||
for (Pattern pattern : whatWeLookedFor) {
|
||||
console.write(" regexp: /"+pattern+"/", LogType.STDERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -49,6 +48,9 @@ import org.springframework.ide.eclipse.boot.dash.api.SystemPropertySupport;
|
||||
import org.springframework.ide.eclipse.boot.dash.api.TemporalBoolean;
|
||||
import org.springframework.ide.eclipse.boot.dash.console.LogType;
|
||||
import org.springframework.ide.eclipse.boot.dash.devtools.DevtoolsUtil;
|
||||
import org.springframework.ide.eclipse.boot.dash.docker.exceptions.DockerBuildException;
|
||||
import org.springframework.ide.eclipse.boot.dash.docker.exceptions.MissingBuildScriptException;
|
||||
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.labels.BootDashLabels;
|
||||
@@ -70,6 +72,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateContainerCmd;
|
||||
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||
import com.github.dockerjava.api.command.InspectImageResponse;
|
||||
import com.github.dockerjava.api.exception.NotModifiedException;
|
||||
import com.github.dockerjava.api.model.Container;
|
||||
import com.github.dockerjava.api.model.ExposedPort;
|
||||
@@ -246,46 +249,17 @@ public class DockerApp extends AbstractDisposable implements App, ChildBearing,
|
||||
return build(console);
|
||||
} catch (Exception e) {
|
||||
console.write(ExceptionUtil.getMessage(e), LogType.STDERROR);
|
||||
if (e instanceof MissingBuildScriptException) {
|
||||
console.write("Places we looked for build script: ", LogType.STDERROR);
|
||||
for (File loc : ((MissingBuildScriptException) e).locationsChecked) {
|
||||
console.write(" - "+loc, LogType.STDERROR);
|
||||
}
|
||||
showBuildScriptHelp(console, (MissingBuildScriptException) e);
|
||||
if (e instanceof DockerBuildException) {
|
||||
((DockerBuildException) e).writeDetailedExplanation(console);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
refreshTracker.run("Starting container '" + image + "'" + + BootDashLabels.ELLIPSIS, () -> {
|
||||
run(console, image, deployment);
|
||||
});
|
||||
}
|
||||
|
||||
private void showBuildScriptHelp(AppConsole console, MissingBuildScriptException e) {
|
||||
String[] help = {
|
||||
"To build a docker image, Boot Dash needs to run a build script from your project.",
|
||||
"Three different types are supported and checked for in this order:",
|
||||
"",
|
||||
"1. "+e.locationsChecked.get(0).getAbsoluteFile().getName(),
|
||||
" A custom script placed by you at the project root.",
|
||||
" Typically this runs a custom maven or gradle command on your project.",
|
||||
"",
|
||||
"2. maven",
|
||||
" If your project has a mvnw, we will use that to execute the `spring-boot:build-image` task",
|
||||
"",
|
||||
"3. gradle",
|
||||
" If your project has a gradlew, we will use that to execute the `bootBuildImage` task",
|
||||
};
|
||||
try {
|
||||
for (String line : help) {
|
||||
console.write(line, LogType.STDERROR);
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void run(AppConsole console, String image, DockerDeployment deployment) throws Exception {
|
||||
if (client==null) {
|
||||
console.write("Cannot start container... Docker client is disconnected!", LogType.STDERROR);
|
||||
@@ -395,11 +369,24 @@ public class DockerApp extends AbstractDisposable implements App, ChildBearing,
|
||||
containerLogConnection.setValue(DockerContainer.connectLog(target, c.getId(), console, true));
|
||||
}
|
||||
}
|
||||
|
||||
/* Sample output from `docker build -t fui .`
|
||||
Successfully built f3157a980fd2
|
||||
Successfully tagged fui:latest
|
||||
*/
|
||||
|
||||
private static final Pattern BUILT_IMAGE_MESSAGE = Pattern.compile("Successfully built image.*\\'(.*)\\'");
|
||||
/**
|
||||
* List of patterns that we look for in order of priority. If more than one pattern matches, the first pattern
|
||||
* takes in this list takes priority over the next.
|
||||
*/
|
||||
private static final Pattern[] BUILT_IMAGE_MESSAGE_PATS = {
|
||||
Pattern.compile("Successfully built image.*\\'(.*)\\'"), //from mvn spring-boot:build-image
|
||||
Pattern.compile("Successfully tagged ([^\\s]+)"), //from `docker build -t <name> .`
|
||||
Pattern.compile("Successfully built ([a-f0-9]+)"), //from `docker build .`
|
||||
};
|
||||
|
||||
private String build(AppConsole console) throws Exception {
|
||||
AtomicReference<String> image = new AtomicReference<>();
|
||||
String[] imageIds = new String[BUILT_IMAGE_MESSAGE_PATS.length];
|
||||
File directory = new File(project.getLocation().toString());
|
||||
String[] command = getBuildCommand(directory);
|
||||
|
||||
@@ -412,9 +399,11 @@ public class DockerApp extends AbstractDisposable implements App, ChildBearing,
|
||||
Process process = builder.start();
|
||||
LineBasedStreamGobler outputGobler = new LineBasedStreamGobler(process.getInputStream(), (line) -> {
|
||||
System.out.println(line);
|
||||
Matcher matcher = BUILT_IMAGE_MESSAGE.matcher(line);
|
||||
if (matcher.find()) {
|
||||
image.set(matcher.group(1));
|
||||
for (int i = 0; i < BUILT_IMAGE_MESSAGE_PATS.length; i++) {
|
||||
Matcher matcher = BUILT_IMAGE_MESSAGE_PATS[i].matcher(line);
|
||||
if (matcher.find()) {
|
||||
imageIds[i] = matcher.group(1);
|
||||
}
|
||||
}
|
||||
try {
|
||||
console.write(line, LogType.APP_OUT);
|
||||
@@ -435,16 +424,29 @@ public class DockerApp extends AbstractDisposable implements App, ChildBearing,
|
||||
}
|
||||
outputGobler.join();
|
||||
|
||||
String imageTag = image.get();
|
||||
String imageTag = null;
|
||||
for (String found : imageIds) {
|
||||
if (found!=null) {
|
||||
imageTag = found;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (imageTag==null) {
|
||||
throw new MissingBuildTagException(BUILT_IMAGE_MESSAGE_PATS);
|
||||
}
|
||||
if (imageTag.startsWith(DOCKER_IO_LIBRARY)) {
|
||||
imageTag = imageTag.substring(DOCKER_IO_LIBRARY.length());
|
||||
}
|
||||
List<Image> images = client.listImagesCmd().withImageNameFilter(imageTag).exec();
|
||||
|
||||
for (Image img : images) {
|
||||
addPersistedImage(img.getId());
|
||||
if (images.isEmpty()) {
|
||||
// maybe the 'imageTag' is not actually a tag but an id/hash.
|
||||
InspectImageResponse inspect = client.inspectImageCmd(imageTag).exec();
|
||||
addPersistedImage(inspect.getId());
|
||||
} else {
|
||||
for (Image img : images) {
|
||||
addPersistedImage(img.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return imageTag;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,11 +100,16 @@ public class DockerContainer implements App, RunStateProvider, JmxConnectable, S
|
||||
return Suppliers.memoize(() ->{
|
||||
Map<String, String> labels = labelsSupplier.get();
|
||||
try {
|
||||
Map<?,?> bpmd = new ObjectMapper().readValue(labels.get("io.buildpacks.build.metadata"), Map.class);
|
||||
Set<String> deps = dependencyNamePath
|
||||
.traverseAmbiguously(YamlNavigable.javaObject(bpmd))
|
||||
.flatMap(JavaObjectNav::asStringMaybe).collect(Collectors.toSet());
|
||||
return deps.contains("spring-boot-devtools");
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ public class FakeDockerRunCommand {
|
||||
bindTo.getHostIp()+":"+bindTo.getHostPortSpec()+":"+exposed.getPort()+"/"+exposed.getProtocol()
|
||||
);
|
||||
}
|
||||
System.out.println(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.eclipse.boot.dash.docker.runtarget;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exception raised when BootDash wants to build a docker image but can not
|
||||
* find / determine how to build it. I.e. we are looking for a number of
|
||||
* different build scripts in project-relative locations. If none of
|
||||
* them are found then this exception is raised.
|
||||
*/
|
||||
public class MissingBuildScriptException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final List<File> locationsChecked;
|
||||
|
||||
public MissingBuildScriptException(List<File> locationsChecked) {
|
||||
super("Niether maven wraper, gradle wrapper or custom build script found.");
|
||||
this.locationsChecked = locationsChecked;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user