##// END OF EJS Templates
Fixed docker build output handling. Handled with 'info' log level.
cin -
r21:452b9915903c v1.3.1 default
parent child
Show More
@@ -1,2 +1,2
1 group=org.implab.gradle
1 group=org.implab.gradle
2 version=1.3.0 No newline at end of file
2 version=1.3.1 No newline at end of file
@@ -1,106 +1,116
1 package org.implab.gradle.containers.cli;
1 package org.implab.gradle.containers.cli;
2
2
3 import java.io.File;
3 import java.io.File;
4 import java.io.IOException;
4 import java.io.IOException;
5 import java.lang.ProcessBuilder.Redirect;
5 import java.util.ArrayList;
6 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collection;
7 import java.util.List;
8 import java.util.List;
8 import java.util.concurrent.CompletableFuture;
9 import java.util.concurrent.CompletableFuture;
9 import java.util.concurrent.ExecutionException;
10 import java.util.concurrent.ExecutionException;
10
11
11 public class ProcessSpec {
12 public class ProcessSpec {
12 private final ProcessBuilder builder;
13 private final ProcessBuilder builder;
13
14
14 private RedirectFrom inputRedirect;
15 private RedirectFrom inputRedirect;
15
16
16 private RedirectTo outputRedirect;
17 private RedirectTo outputRedirect;
17
18
18 private RedirectTo errorRedirect;
19 private RedirectTo errorRedirect;
19
20
20 private List<String> command = new ArrayList<>();
21 private List<String> command = new ArrayList<>();
21
22
22 private File directory;
23 private File directory;
23
24
24 public ProcessSpec() {
25 public ProcessSpec() {
25 builder = new ProcessBuilder();
26 builder = new ProcessBuilder();
26 }
27 }
27
28
28 public CompletableFuture<Integer> start() throws IOException {
29 public CompletableFuture<Integer> start() throws IOException {
29 var tasks = new ArrayList<CompletableFuture<?>>();
30 var tasks = new ArrayList<CompletableFuture<?>>();
30
31
31 builder.command(this.command);
32 builder.command(this.command);
32 builder.directory(directory);
33 builder.directory(directory);
33
34
35 // discard stdout if not redirected
36 if (outputRedirect == null)
37 builder.redirectOutput(Redirect.DISCARD);
38
39 // discard stderr if not redirected
40 if (errorRedirect == null)
41 builder.redirectError(Redirect.DISCARD);
42
43 // run process
34 var proc = builder.start();
44 var proc = builder.start();
35
45
36 tasks.add(proc.onExit());
46 tasks.add(proc.onExit());
37
47
38 if (inputRedirect != null)
48 if (inputRedirect != null)
39 tasks.add(inputRedirect.redirect(proc.getOutputStream()));
49 tasks.add(inputRedirect.redirect(proc.getOutputStream()));
40
50
41 if (outputRedirect != null)
51 if (outputRedirect != null)
42 tasks.add(outputRedirect.redirect(proc.getInputStream()));
52 tasks.add(outputRedirect.redirect(proc.getInputStream()));
43
53
44 if (errorRedirect != null)
54 if (errorRedirect != null)
45 tasks.add(errorRedirect.redirect(proc.getErrorStream()));
55 tasks.add(errorRedirect.redirect(proc.getErrorStream()));
46
56
47 return CompletableFuture
57 return CompletableFuture
48 .allOf(tasks.toArray(new CompletableFuture<?>[0]))
58 .allOf(tasks.toArray(new CompletableFuture<?>[0]))
49 .thenApply(t -> proc.exitValue());
59 .thenApply(t -> proc.exitValue());
50 }
60 }
51
61
52 public void exec() throws InterruptedException, ExecutionException, IOException {
62 public void exec() throws InterruptedException, ExecutionException, IOException {
53 var code = start().get();
63 var code = start().get();
54 if (code != 0)
64 if (code != 0)
55 throw new IOException("The process exited with error code " + code);
65 throw new IOException("The process exited with error code " + code);
56 }
66 }
57
67
58 public List<String> command() {
68 public List<String> command() {
59 return this.command;
69 return this.command;
60 }
70 }
61
71
62 public ProcessSpec command(String... args) {
72 public ProcessSpec command(String... args) {
63 this.command = new ArrayList<>();
73 this.command = new ArrayList<>();
64 args(args);
74 args(args);
65 return this;
75 return this;
66 }
76 }
67
77
68 public ProcessSpec directory(File workingDir) {
78 public ProcessSpec directory(File workingDir) {
69 this.directory = workingDir;
79 this.directory = workingDir;
70 return this;
80 return this;
71 }
81 }
72
82
73 public ProcessSpec command(Collection<String> args) {
83 public ProcessSpec command(Collection<String> args) {
74 this.command = new ArrayList<>();
84 this.command = new ArrayList<>();
75 args(args);
85 args(args);
76 return this;
86 return this;
77 }
87 }
78
88
79 public ProcessSpec args(String... args) {
89 public ProcessSpec args(String... args) {
80 for (String arg : args)
90 for (String arg : args)
81 this.command.add(arg);
91 this.command.add(arg);
82
92
83 return this;
93 return this;
84 }
94 }
85
95
86 public ProcessSpec args(Collection<String> args) {
96 public ProcessSpec args(Collection<String> args) {
87 this.command.addAll(args);
97 this.command.addAll(args);
88
98
89 return this;
99 return this;
90 }
100 }
91
101
92 public ProcessSpec redirectStderr(RedirectTo to) {
102 public ProcessSpec redirectStderr(RedirectTo to) {
93 this.errorRedirect = to;
103 this.errorRedirect = to;
94 return this;
104 return this;
95 }
105 }
96
106
97 public ProcessSpec redirectStdin(RedirectFrom from) {
107 public ProcessSpec redirectStdin(RedirectFrom from) {
98 this.inputRedirect = from;
108 this.inputRedirect = from;
99 return this;
109 return this;
100 }
110 }
101
111
102 public ProcessSpec redirectStdout(RedirectTo to) {
112 public ProcessSpec redirectStdout(RedirectTo to) {
103 this.outputRedirect = to;
113 this.outputRedirect = to;
104 return this;
114 return this;
105 }
115 }
106 }
116 }
@@ -1,170 +1,169
1 package org.implab.gradle.containers.cli;
1 package org.implab.gradle.containers.cli;
2
2
3 import java.io.ByteArrayOutputStream;
3 import java.io.ByteArrayOutputStream;
4 import java.io.Closeable;
4 import java.io.Closeable;
5 import java.io.File;
5 import java.io.File;
6 import java.io.FileNotFoundException;
6 import java.io.FileNotFoundException;
7 import java.io.FileOutputStream;
7 import java.io.FileOutputStream;
8 import java.io.FileReader;
8 import java.io.FileReader;
9 import java.io.IOException;
9 import java.io.IOException;
10 import java.io.InputStream;
10 import java.io.InputStream;
11 import java.io.OutputStream;
11 import java.io.OutputStream;
12 import java.io.Reader;
12 import java.io.Reader;
13 import java.util.Scanner;
13 import java.util.Scanner;
14 import java.util.Set;
14 import java.util.Set;
15 import java.util.concurrent.CompletableFuture;
15 import java.util.concurrent.CompletableFuture;
16 import java.util.stream.Collectors;
16 import java.util.stream.Collectors;
17 import java.util.stream.StreamSupport;
17 import java.util.stream.StreamSupport;
18 import java.nio.file.Files;
18 import java.nio.file.Files;
19 import java.util.List;
19 import java.util.List;
20 import org.gradle.api.Action;
20 import org.gradle.api.Action;
21 import org.gradle.api.GradleException;
21 import org.gradle.api.GradleException;
22 import org.gradle.api.file.Directory;
23 import org.gradle.api.file.FileSystemLocation;
22 import org.gradle.api.file.FileSystemLocation;
24 import org.gradle.internal.impldep.org.bouncycastle.util.Iterable;
23 import org.gradle.internal.impldep.org.bouncycastle.util.Iterable;
25
24
26 import com.fasterxml.jackson.core.exc.StreamWriteException;
25 import com.fasterxml.jackson.core.exc.StreamWriteException;
27 import com.fasterxml.jackson.databind.DatabindException;
26 import com.fasterxml.jackson.databind.DatabindException;
28 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.fasterxml.jackson.databind.ObjectMapper;
29 import com.fasterxml.jackson.databind.SerializationFeature;
28 import com.fasterxml.jackson.databind.SerializationFeature;
30 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
29 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
31
30
32 import groovy.json.JsonGenerator;
31 import groovy.json.JsonGenerator;
33 import groovy.json.JsonOutput;
32 import groovy.json.JsonOutput;
34 import groovy.json.JsonGenerator.Converter;
33 import groovy.json.JsonGenerator.Converter;
35 import groovy.lang.Closure;
34 import groovy.lang.Closure;
36
35
37 public final class Utils {
36 public final class Utils {
38 public static CompletableFuture<Void> redirectIO(final InputStream src, final Action<String> consumer) {
37 public static CompletableFuture<Void> redirectIO(final InputStream src, final Action<String> consumer) {
39 return CompletableFuture.runAsync(() -> {
38 return CompletableFuture.runAsync(() -> {
40 try (Scanner sc = new Scanner(src)) {
39 try (Scanner sc = new Scanner(src)) {
41 while (sc.hasNextLine()) {
40 while (sc.hasNextLine()) {
42 consumer.execute(sc.nextLine());
41 consumer.execute(sc.nextLine());
43 }
42 }
44 }
43 }
45 });
44 });
46 }
45 }
47
46
48 public static CompletableFuture<Void> redirectIO(final InputStream src, final File file) {
47 public static CompletableFuture<Void> redirectIO(final InputStream src, final File file) {
49 return CompletableFuture.runAsync(() -> {
48 return CompletableFuture.runAsync(() -> {
50 try (OutputStream out = new FileOutputStream(file)) {
49 try (OutputStream out = new FileOutputStream(file)) {
51 src.transferTo(out);
50 src.transferTo(out);
52 } catch (Exception e) {
51 } catch (Exception e) {
53 // silence!
52 // silence!
54 }
53 }
55 });
54 });
56 }
55 }
57
56
58 public static CompletableFuture<Void> redirectIO(final InputStream src, final OutputStream dst) {
57 public static CompletableFuture<Void> redirectIO(final InputStream src, final OutputStream dst) {
59 return CompletableFuture.runAsync(() -> {
58 return CompletableFuture.runAsync(() -> {
60 try (dst) {
59 try (dst) {
61 src.transferTo(dst);
60 src.transferTo(dst);
62 } catch (Exception e) {
61 } catch (Exception e) {
63 // silence!
62 // silence!
64 }
63 }
65 });
64 });
66 }
65 }
67
66
68 public static void closeSilent(Closeable handle) {
67 public static void closeSilent(Closeable handle) {
69 try {
68 try {
70 handle.close();
69 handle.close();
71 } catch (Exception e) {
70 } catch (Exception e) {
72 // silence!
71 // silence!
73 }
72 }
74 }
73 }
75
74
76 public static String readAll(final InputStream src) throws IOException {
75 public static String readAll(final InputStream src) throws IOException {
77 ByteArrayOutputStream out = new ByteArrayOutputStream();
76 ByteArrayOutputStream out = new ByteArrayOutputStream();
78 src.transferTo(out);
77 src.transferTo(out);
79 return out.toString();
78 return out.toString();
80 }
79 }
81
80
82 public static <T> T readJson(final Reader reader, Class<T> type) throws IOException {
81 public static <T> T readJson(final Reader reader, Class<T> type) throws IOException {
83 ObjectMapper objectMapper = new ObjectMapper()
82 ObjectMapper objectMapper = new ObjectMapper()
84 .registerModule(new Jdk8Module());
83 .registerModule(new Jdk8Module());
85 return objectMapper.readValue(reader, type);
84 return objectMapper.readValue(reader, type);
86 }
85 }
87
86
88 public static void writeJson(final File file, Object value)
87 public static void writeJson(final File file, Object value)
89 throws StreamWriteException, DatabindException, IOException {
88 throws StreamWriteException, DatabindException, IOException {
90 ObjectMapper objectMapper = new ObjectMapper()
89 ObjectMapper objectMapper = new ObjectMapper()
91 .enable(SerializationFeature.INDENT_OUTPUT)
90 .enable(SerializationFeature.INDENT_OUTPUT)
92 .registerModule(new Jdk8Module());
91 .registerModule(new Jdk8Module());
93 objectMapper.writeValue(file, value);
92 objectMapper.writeValue(file, value);
94 }
93 }
95
94
96 public static ImageRef readImageRef(final File file) throws FileNotFoundException, IOException {
95 public static ImageRef readImageRef(final File file) throws FileNotFoundException, IOException {
97 try (var reader = new FileReader(file)) {
96 try (var reader = new FileReader(file)) {
98 return readJson(reader, ImageRef.class);
97 return readJson(reader, ImageRef.class);
99 }
98 }
100 }
99 }
101
100
102 public static String readAll(final File src) {
101 public static String readAll(final File src) {
103 try {
102 try {
104 return src.isFile() ? Files.readString(src.toPath()) : null;
103 return src.isFile() ? Files.readString(src.toPath()) : null;
105 } catch (IOException e) {
104 } catch (IOException e) {
106 throw new GradleException("Failed to read file " + src.toString(), e);
105 throw new GradleException("Failed to read file " + src.toString(), e);
107 }
106 }
108 }
107 }
109
108
110 public static List<String> readAll(final Iterable<? extends File> files) {
109 public static List<String> readAll(final Iterable<? extends File> files) {
111 return StreamSupport.stream(files.spliterator(), false)
110 return StreamSupport.stream(files.spliterator(), false)
112 .map(Utils::readAll)
111 .map(Utils::readAll)
113 .toList();
112 .toList();
114 }
113 }
115
114
116 public static String readAll(final InputStream src, String charset) throws IOException {
115 public static String readAll(final InputStream src, String charset) throws IOException {
117 ByteArrayOutputStream out = new ByteArrayOutputStream();
116 ByteArrayOutputStream out = new ByteArrayOutputStream();
118 src.transferTo(out);
117 src.transferTo(out);
119 return out.toString(charset);
118 return out.toString(charset);
120 }
119 }
121
120
122 public static JsonGenerator createDefaultJsonGenerator() {
121 public static JsonGenerator createDefaultJsonGenerator() {
123 return new JsonGenerator.Options()
122 return new JsonGenerator.Options()
124 .excludeNulls()
123 .excludeNulls()
125 .addConverter(new Converter() {
124 .addConverter(new Converter() {
126 public boolean handles(Class<?> type) {
125 public boolean handles(Class<?> type) {
127 return (File.class == type);
126 return (File.class == type);
128 }
127 }
129
128
130 public Object convert(Object value, String key) {
129 public Object convert(Object value, String key) {
131 return ((File) value).getPath();
130 return ((File) value).getPath();
132 }
131 }
133 })
132 })
134 .build();
133 .build();
135 }
134 }
136
135
137 public static String toJsonPretty(Object value) {
136 public static String toJsonPretty(Object value) {
138 return JsonOutput.prettyPrint(createDefaultJsonGenerator().toJson(value));
137 return JsonOutput.prettyPrint(createDefaultJsonGenerator().toJson(value));
139 }
138 }
140
139
141 public static boolean isNullOrEmptyString(String value) {
140 public static boolean isNullOrEmptyString(String value) {
142 return (value == null || value.length() == 0);
141 return (value == null || value.length() == 0);
143 }
142 }
144
143
145 public static <T> Action<T> wrapClosure(Closure<?> closure) {
144 public static <T> Action<T> wrapClosure(Closure<?> closure) {
146 return x -> {
145 return x -> {
147 closure.setDelegate(x);
146 closure.setDelegate(x);
148 closure.setResolveStrategy(Closure.OWNER_FIRST);
147 closure.setResolveStrategy(Closure.OWNER_FIRST);
149 closure.call(x);
148 closure.call(x);
150 };
149 };
151 }
150 }
152
151
153 public static Set<String> mapToString(Set<Object> set) {
152 public static Set<String> mapToString(Set<Object> set) {
154 return set.stream().map(Object::toString).collect(Collectors.toSet());
153 return set.stream().map(Object::toString).collect(Collectors.toSet());
155 }
154 }
156
155
157 public static File normalizeFile(Object file) {
156 public static File normalizeFile(Object file) {
158 if (file == null)
157 if (file == null)
159 throw new IllegalArgumentException("file");
158 throw new IllegalArgumentException("file");
160
159
161 if (file instanceof String)
160 if (file instanceof String)
162 return new File((String)file);
161 return new File((String)file);
163 if (file instanceof FileSystemLocation)
162 if (file instanceof FileSystemLocation)
164 return ((FileSystemLocation)file).getAsFile();
163 return ((FileSystemLocation)file).getAsFile();
165 else if (file instanceof File)
164 else if (file instanceof File)
166 return (File)file;
165 return (File)file;
167 throw new ClassCastException();
166 throw new ClassCastException();
168 }
167 }
169
168
170 } No newline at end of file
169 }
@@ -1,132 +1,139
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.util.ArrayList;
3 import java.util.ArrayList;
4 import java.util.HashMap;
4 import java.util.HashMap;
5 import java.util.List;
5 import java.util.List;
6 import java.util.Map;
6 import java.util.Map;
7
7
8 import org.gradle.api.Action;
8 import org.gradle.api.Action;
9 import org.gradle.api.file.Directory;
9 import org.gradle.api.file.Directory;
10 import org.gradle.api.file.DirectoryProperty;
10 import org.gradle.api.file.DirectoryProperty;
11 import org.gradle.api.file.RegularFile;
11 import org.gradle.api.file.RegularFile;
12 import org.gradle.api.file.RegularFileProperty;
12 import org.gradle.api.file.RegularFileProperty;
13 import org.gradle.api.provider.MapProperty;
13 import org.gradle.api.provider.MapProperty;
14 import org.gradle.api.provider.Property;
14 import org.gradle.api.provider.Property;
15 import org.gradle.api.provider.Provider;
15 import org.gradle.api.provider.Provider;
16 import org.gradle.api.tasks.Input;
16 import org.gradle.api.tasks.Input;
17 import org.gradle.api.tasks.InputDirectory;
17 import org.gradle.api.tasks.InputDirectory;
18 import org.gradle.api.tasks.Internal;
18 import org.gradle.api.tasks.Internal;
19 import org.gradle.api.tasks.Optional;
19 import org.gradle.api.tasks.Optional;
20 import org.gradle.api.tasks.OutputFile;
20 import org.gradle.api.tasks.OutputFile;
21 import org.gradle.api.tasks.SkipWhenEmpty;
21 import org.gradle.api.tasks.SkipWhenEmpty;
22 import org.gradle.api.tasks.TaskAction;
22 import org.gradle.api.tasks.TaskAction;
23 import java.io.File;
23 import java.io.File;
24 import java.io.IOException;
24 import java.io.IOException;
25
25
26 import org.implab.gradle.containers.cli.ImageRef;
26 import org.implab.gradle.containers.cli.ImageRef;
27 import org.implab.gradle.containers.cli.RedirectTo;
27 import org.implab.gradle.containers.cli.Utils;
28 import org.implab.gradle.containers.cli.Utils;
28 import org.implab.gradle.containers.dsl.MapPropertyEntry;
29 import org.implab.gradle.containers.dsl.MapPropertyEntry;
29 import org.implab.gradle.containers.dsl.OptionsMixin;
30 import org.implab.gradle.containers.dsl.OptionsMixin;
30
31
31 import groovy.lang.Closure;
32 import groovy.lang.Closure;
32
33
33 public abstract class BuildImage extends DockerCliTask implements OptionsMixin {
34 public abstract class BuildImage extends DockerCliTask implements OptionsMixin {
34
35
35 @InputDirectory
36 @InputDirectory
36 @SkipWhenEmpty
37 @SkipWhenEmpty
37 public abstract DirectoryProperty getContextDirectory();
38 public abstract DirectoryProperty getContextDirectory();
38
39
39 @Input
40 @Input
40 public abstract MapProperty<String, String> getBuildArgs();
41 public abstract MapProperty<String, String> getBuildArgs();
41
42
42 @Input
43 @Input
43 @Optional
44 @Optional
44 public abstract Property<String> getBuildTarget();
45 public abstract Property<String> getBuildTarget();
45
46
46 @Input
47 @Input
47 public abstract Property<Object> getImageName();
48 public abstract Property<Object> getImageName();
48
49
49 @Internal
50 @Internal
50 public abstract RegularFileProperty getImageIdFile();
51 public abstract RegularFileProperty getImageIdFile();
51
52
52 @OutputFile
53 @OutputFile
53 public Provider<File> getImageIdFileOutput() {
54 public Provider<File> getImageIdFileOutput() {
54 return getImageIdFile().map(RegularFile::getAsFile);
55 return getImageIdFile().map(RegularFile::getAsFile);
55 }
56 }
56
57
57 public BuildImage() {
58 public BuildImage() {
58 getOutputs().upToDateWhen(task -> getImageIdFile()
59 getOutputs().upToDateWhen(task -> getImageIdFile()
59 .map(this::imageExists)
60 .map(this::imageExists)
60 .getOrElse(false));
61 .getOrElse(false));
61 }
62 }
62
63
63 public void buildArgs(Action<Map<String, String>> spec) {
64 public void buildArgs(Action<Map<String, String>> spec) {
64 getBuildArgs().putAll(provider(() -> {
65 getBuildArgs().putAll(provider(() -> {
65 Map<String, String> args = new HashMap<>();
66 Map<String, String> args = new HashMap<>();
66 spec.execute(args);
67 spec.execute(args);
67 getLogger().info("add buildArgs {}", args);
68 getLogger().info("add buildArgs {}", args);
68 return args;
69 return args;
69 }));
70 }));
70 }
71 }
71
72
72 public void buildArgs(Closure<Map<String, String>> spec) {
73 public void buildArgs(Closure<Map<String, String>> spec) {
73 buildArgs(Utils.wrapClosure(spec));
74 buildArgs(Utils.wrapClosure(spec));
74 }
75 }
75
76
76 public MapPropertyEntry<String, String> buildArg(String key) {
77 public MapPropertyEntry<String, String> buildArg(String key) {
77 return new MapPropertyEntry<String, String>(getBuildArgs(), key, getProviders());
78 return new MapPropertyEntry<String, String>(getBuildArgs(), key, getProviders());
78 }
79 }
79
80
80 private boolean imageExists(RegularFile file) {
81 private boolean imageExists(RegularFile file) {
81 return readRefId(file.getAsFile()).map(this::imageExists).orElse(false);
82 return readRefId(file.getAsFile()).map(this::imageExists).orElse(false);
82 }
83 }
83
84
84 private java.util.Optional<String> readRefId(File idFile) {
85 private java.util.Optional<String> readRefId(File idFile) {
85 try {
86 try {
86 return idFile.exists() ? java.util.Optional.of(Utils.readImageRef(idFile).getId())
87 return idFile.exists() ? java.util.Optional.of(Utils.readImageRef(idFile).getId())
87 : java.util.Optional.empty();
88 : java.util.Optional.empty();
88 } catch (IOException e) {
89 } catch (IOException e) {
89 getLogger().error("Failed to read imageId {}: {}", idFile, e);
90 getLogger().error("Failed to read imageId {}: {}", idFile, e);
90 return java.util.Optional.empty();
91 return java.util.Optional.empty();
91 }
92 }
92 }
93 }
93
94
95 @Override
96 protected java.util.Optional<RedirectTo> loggerErrorRedirect() {
97 // https://github.com/moby/buildkit/issues/1186
98 return loggerInfoRedirect();
99 }
100
94 @TaskAction
101 @TaskAction
95 public void run() throws Exception {
102 public void run() throws Exception {
96 List<String> args = new ArrayList<>();
103 List<String> args = new ArrayList<>();
97
104
98 // create a temp file to store image id
105 // create a temp file to store image id
99 var iidFile = new File(this.getTemporaryDir(), "imageid");
106 var iidFile = new File(this.getTemporaryDir(), "imageid");
100
107
101 // specify where to write image id
108 // specify where to write image id
102 args.addAll(List.of("--iidfile", iidFile.getAbsolutePath()));
109 args.addAll(List.of("--iidfile", iidFile.getAbsolutePath()));
103
110
104 getBuildArgs().get().forEach((k, v) -> {
111 getBuildArgs().get().forEach((k, v) -> {
105 args.add("--build-arg");
112 args.add("--build-arg");
106 args.add(String.format("%s=%s", k, v));
113 args.add(String.format("%s=%s", k, v));
107 });
114 });
108
115
109 // add --target if specified for multi-stage build
116 // add --target if specified for multi-stage build
110 if (getBuildTarget().isPresent()) {
117 if (getBuildTarget().isPresent()) {
111 args.add("--target");
118 args.add("--target");
112 args.add(getBuildTarget().get());
119 args.add(getBuildTarget().get());
113 }
120 }
114
121
115 // add extra parameters
122 // add extra parameters
116 getOptions().get().forEach(args::add);
123 getOptions().get().forEach(args::add);
117
124
118 var imageTag = getImageName().map(Object::toString).get();
125 var imageTag = getImageName().map(Object::toString).get();
119
126
120 // build image
127 // build image
121 var spec = docker().imageBuild(
128 var spec = docker().imageBuild(
122 imageTag,
129 imageTag,
123 getContextDirectory().map(Directory::getAsFile).get(),
130 getContextDirectory().map(Directory::getAsFile).get(),
124 args);
131 args);
125
132
126 exec(spec);
133 exec(spec);
127
134
128 // read the built image id and store image ref metadata
135 // read the built image id and store image ref metadata
129 var imageRef = new ImageRef(imageTag, Utils.readAll(iidFile));
136 var imageRef = new ImageRef(imageTag, Utils.readAll(iidFile));
130 Utils.writeJson(getImageIdFile().map(RegularFile::getAsFile).get(), imageRef);
137 Utils.writeJson(getImageIdFile().map(RegularFile::getAsFile).get(), imageRef);
131 }
138 }
132 }
139 }
@@ -1,150 +1,153
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.io.File;
3 import java.io.File;
4 import java.io.IOException;
4 import java.io.IOException;
5 import java.util.Optional;
5 import java.util.Optional;
6 import java.util.concurrent.ExecutionException;
6 import java.util.concurrent.ExecutionException;
7
7
8 import org.gradle.api.DefaultTask;
8 import org.gradle.api.DefaultTask;
9 import org.gradle.api.GradleException;
9 import org.gradle.api.GradleException;
10 import org.gradle.api.file.RegularFile;
10 import org.gradle.api.file.RegularFile;
11 import org.gradle.api.provider.Property;
11 import org.gradle.api.provider.Property;
12 import org.gradle.api.tasks.Input;
12 import org.gradle.api.tasks.Input;
13 import org.gradle.api.tasks.Internal;
13 import org.gradle.api.tasks.Internal;
14 import org.implab.gradle.containers.ContainerExtension;
14 import org.implab.gradle.containers.ContainerExtension;
15 import org.implab.gradle.containers.PropertiesMixin;
15 import org.implab.gradle.containers.PropertiesMixin;
16 import org.implab.gradle.containers.cli.ComposeTraits;
16 import org.implab.gradle.containers.cli.ComposeTraits;
17 import org.implab.gradle.containers.cli.DockerTraits;
17 import org.implab.gradle.containers.cli.DockerTraits;
18 import org.implab.gradle.containers.cli.ProcessSpec;
18 import org.implab.gradle.containers.cli.ProcessSpec;
19 import org.implab.gradle.containers.cli.RedirectFrom;
19 import org.implab.gradle.containers.cli.RedirectFrom;
20 import org.implab.gradle.containers.cli.RedirectTo;
20 import org.implab.gradle.containers.cli.RedirectTo;
21 import org.implab.gradle.containers.cli.Utils;
21 import org.implab.gradle.containers.cli.Utils;
22
22
23 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin {
23 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin {
24
24
25 @Input
25 @Input
26 public abstract Property<String> getCliCmd();
26 public abstract Property<String> getCliCmd();
27
27
28 /**
28 /**
29 * Returns working directory for docker commands
29 * Returns working directory for docker commands
30 */
30 */
31 @Input
31 @Input
32 @org.gradle.api.tasks.Optional
32 @org.gradle.api.tasks.Optional
33 public abstract Property<File> getWorkingDirectory();
33 public abstract Property<File> getWorkingDirectory();
34
34
35 @Internal
35 @Internal
36 protected ContainerExtension getContainerExtension() {
36 protected ContainerExtension getContainerExtension() {
37 return getProject().getExtensions().getByType(ContainerExtension.class);
37 return getProject().getExtensions().getByType(ContainerExtension.class);
38 }
38 }
39
39
40 public DockerCliTask() {
40 public DockerCliTask() {
41 getCliCmd().convention(getContainerExtension().getCliCmd());
41 getCliCmd().convention(getContainerExtension().getCliCmd());
42
42
43 }
43 }
44
44
45 protected Optional<RedirectTo> loggerInfoRedirect() {
45 protected Optional<RedirectTo> loggerInfoRedirect() {
46 return getLogger().isInfoEnabled() ? Optional.of(RedirectTo.consumer(getLogger()::info)) : Optional.empty();
46 return getLogger().isInfoEnabled() ?
47 Optional.of(RedirectTo.consumer(getLogger()::info)) :
48 Optional.empty();
47 }
49 }
48
50
49 protected Optional<RedirectTo> loggerErrorRedirect() {
51 protected Optional<RedirectTo> loggerErrorRedirect() {
50 return getLogger().isErrorEnabled() ? Optional.of(RedirectTo.consumer(getLogger()::error)) : Optional.empty();
52 return getLogger().isErrorEnabled() ?
53 Optional.of(RedirectTo.consumer(getLogger()::error)) :
54 Optional.empty();
51 }
55 }
52
56
53 protected Optional<RedirectTo> stdoutRedirection() {
57 protected Optional<RedirectTo> stdoutRedirection() {
54 return loggerInfoRedirect();
58 return loggerInfoRedirect();
55 }
59 }
56
60
57 protected Optional<RedirectTo> stderrRedirection() {
61 protected Optional<RedirectTo> stderrRedirection() {
58 return loggerErrorRedirect();
62 return loggerErrorRedirect();
59 }
63 }
60
64
61 protected Optional<RedirectFrom> stdinRedirection() {
65 protected Optional<RedirectFrom> stdinRedirection() {
62 return Optional.empty();
66 return Optional.empty();
63 }
67 }
64
68
65 protected TaskDockerTraits docker() {
69 protected TaskDockerTraits docker() {
66 return new TaskDockerTraits();
70 return new TaskDockerTraits();
67 }
71 }
68
72
69 protected boolean imageExists(String imageId) {
73 protected boolean imageExists(String imageId) {
70 try {
74 try {
71 return checkRetCode(docker().imageExists(imageId), 0);
75 return checkRetCode(docker().imageExists(imageId), 0);
72 } catch (InterruptedException | ExecutionException | IOException e) {
76 } catch (InterruptedException | ExecutionException | IOException e) {
73 // wrap to unchecked exception
77 // wrap to unchecked exception
74 throw new GradleException("Failed to execute imageExists", e);
78 throw new GradleException("Failed to execute imageExists", e);
75 }
79 }
76 }
80 }
77
81
78 protected boolean containerExists(String containerId) {
82 protected boolean containerExists(String containerId) {
79 try {
83 try {
80 return checkRetCode(docker().containerExists(containerId), 0);
84 return checkRetCode(docker().containerExists(containerId), 0);
81 } catch (InterruptedException | ExecutionException | IOException e) {
85 } catch (InterruptedException | ExecutionException | IOException e) {
82 // wrap to unchecked exception
86 // wrap to unchecked exception
83 throw new GradleException("Failed to execute imageExists", e);
87 throw new GradleException("Failed to execute imageExists", e);
84 }
88 }
85 }
89 }
86
90
87 protected void exec(ProcessSpec spec) throws InterruptedException, ExecutionException, IOException {
91 protected void exec(ProcessSpec spec) throws InterruptedException, ExecutionException, IOException {
88
89 stdoutRedirection().ifPresent(spec::redirectStdout);
92 stdoutRedirection().ifPresent(spec::redirectStdout);
90 stderrRedirection().ifPresent(spec::redirectStderr);
93 stderrRedirection().ifPresent(spec::redirectStderr);
91 stdinRedirection().ifPresent(spec::redirectStdin);
94 stdinRedirection().ifPresent(spec::redirectStdin);
92
95
93 getLogger().info("Staring: {}", spec.command());
96 getLogger().info("Staring: {}", spec.command());
94
97
95 // runs the command and checks the error code
98 // runs the command and checks the error code
96 spec.exec();
99 spec.exec();
97 }
100 }
98
101
99 protected boolean checkRetCode(ProcessSpec proc, int code)
102 protected boolean checkRetCode(ProcessSpec proc, int code)
100 throws InterruptedException, ExecutionException, IOException {
103 throws InterruptedException, ExecutionException, IOException {
101 if (getLogger().isInfoEnabled()) {
104 if (getLogger().isInfoEnabled()) {
102 proc.redirectStdout(RedirectTo.consumer(getLogger()::info))
105 proc.redirectStdout(RedirectTo.consumer(getLogger()::info))
103 .redirectStderr(RedirectTo.consumer(getLogger()::info));
106 .redirectStderr(RedirectTo.consumer(getLogger()::info));
104 }
107 }
105
108
106 getLogger().info("Starting: {}", proc.command());
109 getLogger().info("Starting: {}", proc.command());
107
110
108 return proc.start().get() == code;
111 return proc.start().get() == code;
109 }
112 }
110
113
111 /**
114 /**
112 * Helper function to read
115 * Helper function to read
113 * @param idFile
116 * @param idFile
114 * @return
117 * @return
115 */
118 */
116 protected String readId(File idFile) {
119 protected String readId(File idFile) {
117 if (idFile.exists()) {
120 if (idFile.exists()) {
118 try {
121 try {
119 return Utils.readImageRef(idFile).getId();
122 return Utils.readImageRef(idFile).getId();
120 } catch (IOException e) {
123 } catch (IOException e) {
121 getLogger().error("Failed to read imageId {}: {}", idFile, e);
124 getLogger().error("Failed to read imageId {}: {}", idFile, e);
122 return null;
125 return null;
123 }
126 }
124 } else {
127 } else {
125 return null;
128 return null;
126 }
129 }
127 }
130 }
128
131
129 protected ProcessSpec commandBuilder(String... command) {
132 protected ProcessSpec commandBuilder(String... command) {
130 var spec = new ProcessSpec().args(getCliCmd().get()).args(command);
133 var spec = new ProcessSpec().args(getCliCmd().get()).args(command);
131
134
132 if (getWorkingDirectory().isPresent())
135 if (getWorkingDirectory().isPresent())
133 spec.directory(getWorkingDirectory().get());
136 spec.directory(getWorkingDirectory().get());
134
137
135 return spec;
138 return spec;
136 }
139 }
137
140
138 class TaskDockerTraits extends DockerTraits {
141 class TaskDockerTraits extends DockerTraits {
139
142
140 @Override
143 @Override
141 protected ProcessSpec builder(String... command) {
144 protected ProcessSpec builder(String... command) {
142 return commandBuilder(command);
145 return commandBuilder(command);
143 }
146 }
144
147
145 public ComposeTraits compose(ComposeTaskMixin props) {
148 public ComposeTraits compose(ComposeTaskMixin props) {
146 return compose(props.getComposeFile().map(RegularFile::getAsFile).get(), props.getProfiles().get());
149 return compose(props.getComposeFile().map(RegularFile::getAsFile).get(), props.getProfiles().get());
147 }
150 }
148
151
149 }
152 }
150 }
153 }
General Comments 0
You need to be logged in to leave comments. Login now