##// END OF EJS Templates
moving docker commands to DockerTraits, refacotring
cin -
r9:04caf9830434 default
parent child
Show More
@@ -0,0 +1,67
1 package org.implab.gradle.containers.cli;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.List;
9 import java.util.Optional;
10 import org.gradle.api.logging.Logger;
11
12 public abstract class DockerTraits {
13
14 public abstract Logger getLogger();
15
16 public abstract Optional<File> getWorkingDir();
17
18 public abstract String getCliCmd();
19
20 boolean execute(ProcessBuilder builder, int code) throws IOException, InterruptedException {
21 var proc = builder.start();
22 return proc.waitFor() == code;
23 }
24
25 protected ProcessBuilder builder(String... args) {
26 var command = new ArrayList<String>(args.length + 1);
27 command.add(getCliCmd());
28 Arrays.stream(args).forEach(command::add);
29
30 var builder = new ProcessBuilder(command);
31
32 getWorkingDir().ifPresent(builder::directory);
33 return builder();
34 }
35
36 protected ProcessBuilder builder(List<String> command) {
37 var builder = new ProcessBuilder(command);
38
39 getWorkingDir().ifPresent(builder::directory);
40 return builder();
41 }
42
43 public void build(List<String> args) {
44
45 }
46
47 public boolean imageExists(String imageId) throws InterruptedException, IOException {
48 getLogger().info("Check image {} exists", imageId);
49
50 var builder = builder("image", "inspect", "--format", "image-exists", imageId);
51
52 return execute(builder, 0);
53 }
54
55 public boolean imageExists(File imageIdFile) {
56 if (imageIdFile.exists()) {
57 try {
58 var imageId = Files.readString(imageIdFile.toPath());
59 return imageExists(imageId);
60 } catch (IOException | InterruptedException e) {
61 getLogger().error("Failed to read imageId {}: {}", imageIdFile, e);
62 return false;
63 }
64 }
65 return false;
66 }
67 }
@@ -0,0 +1,93
1 package org.implab.gradle.containers.cli;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.Closeable;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.OutputStream;
11 import java.nio.file.Files;
12 import java.util.Scanner;
13
14 import org.gradle.api.Action;
15
16 import groovy.json.JsonGenerator;
17 import groovy.json.JsonOutput;
18 import groovy.json.JsonGenerator.Converter;
19 import groovy.lang.Closure;
20
21 public final class Utils {
22 public static void redirectIO(final InputStream src, final Action<String> consumer) {
23 new Thread(() -> {
24 try (Scanner sc = new Scanner(src)) {
25 while (sc.hasNextLine()) {
26 consumer.execute(sc.nextLine());
27 }
28 }
29 }).start();
30 }
31
32 public static void redirectIO(final InputStream src, final File file) {
33 new Thread(() -> {
34 try (OutputStream out = new FileOutputStream(file)) {
35 src.transferTo(out);
36 } catch (Exception e) {
37 // silence!
38 }
39 }).start();
40 }
41
42 public static void closeSilent(Closeable handle) {
43 try {
44 handle.close();
45 } catch (Exception e) {
46 // silence!
47 }
48 }
49
50 public static String readAll(final InputStream src) throws IOException {
51 ByteArrayOutputStream out = new ByteArrayOutputStream();
52 src.transferTo(out);
53 return out.toString();
54 }
55
56 public static String readAll(final InputStream src, String charset) throws IOException {
57 ByteArrayOutputStream out = new ByteArrayOutputStream();
58 src.transferTo(out);
59 return out.toString(charset);
60 }
61
62 public static JsonGenerator createDefaultJsonGenerator() {
63 return new JsonGenerator.Options()
64 .excludeNulls()
65 .addConverter(new Converter() {
66 public boolean handles(Class<?> type) {
67 return (File.class == type);
68 }
69
70 public Object convert(Object value, String key) {
71 return ((File) value).getPath();
72 }
73 })
74 .build();
75 }
76
77 public static String toJsonPretty(Object value) {
78 return JsonOutput.prettyPrint(createDefaultJsonGenerator().toJson(value));
79 }
80
81 public static boolean isNullOrEmptyString(String value) {
82 return (value == null || value.length() == 0);
83 }
84
85 public static <T> Action<T> wrapClosure(Closure<?> closure) {
86 return x -> {
87 closure.setDelegate(x);
88 closure.setResolveStrategy(Closure.DELEGATE_FIRST);
89 closure.call(x);
90 };
91 }
92
93 } No newline at end of file
@@ -0,0 +1,35
1 package org.implab.gradle.containers.dsl;
2
3 import java.util.concurrent.Callable;
4 import org.gradle.api.provider.MapProperty;
5 import org.gradle.api.provider.Provider;
6 import org.gradle.api.provider.ProviderFactory;
7
8 public class MapPropertyEntry<K,V> {
9
10 private final MapProperty<K,V> map;
11
12 private final K key;
13
14 private final ProviderFactory providerFactory;
15
16 public MapPropertyEntry(MapProperty<K,V> map, K key, ProviderFactory providerFactory) {
17 this.map = map;
18 this.key = key;
19 this.providerFactory = providerFactory;
20 }
21
22 void put(Callable<V> value) {
23 map.put(key, providerFactory.provider(value));
24 }
25
26 void put(V value) {
27 map.put(key, value);
28 }
29
30 void put(Provider<V> value) {
31 map.put(key, value);
32 }
33
34
35 }
@@ -1,57 +1,66
1 package org.implab.gradle.containers;
1 package org.implab.gradle.containers;
2
2
3 import java.io.File;
3 import java.io.File;
4 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.List;
6 import java.util.Optional;
7 import java.util.Optional;
7
8
8 import org.gradle.api.logging.Logger;
9 import org.gradle.api.logging.Logger;
10 import org.gradle.api.tasks.Internal;
11 import org.implab.gradle.containers.cli.Utils;
9
12
10 public interface ExecuteMixin {
13 public interface ExecuteMixin {
11
14
15 @Internal
12 Logger getLogger();
16 Logger getLogger();
13
17
18 @Internal
14 Optional<File> getWorkingDir();
19 Optional<File> getWorkingDir();
15
20
16 default void Execute() throws Exception {
21 default void Execute() throws IOException, InterruptedException {
17 final Logger log = getLogger();
22 final Logger log = getLogger();
18
23
19 List<String> command = new ArrayList<>();
24 List<String> command = new ArrayList<>();
20
25
21 preparingCommand(command);
26 preparingCommand(command);
22
27
23 ProcessBuilder builder = new ProcessBuilder(command);
28 ProcessBuilder builder = new ProcessBuilder(command);
24 getWorkingDir().ifPresent(workingDir -> builder.directory(workingDir));
29 getWorkingDir().ifPresent(workingDir -> builder.directory(workingDir));
25
30
26 log.info("Starting: {}", builder.command());
31 log.info("Starting: {}", builder.command());
27
32
28 Process p = builder.start();
33 Process p = builder.start();
29
34
30 processStarted(p);
35 processStarted(p);
31
36
32 if (log.isErrorEnabled()) {
37 if (log.isErrorEnabled()) {
33 Utils.redirectIO(p.getErrorStream(), log::error);
38 Utils.redirectIO(p.getErrorStream(), log::error);
34 }
39 }
35
40
36 int code = p.waitFor();
41 int code = p.waitFor();
37 if (code != 0)
42 if (code != 0) {
38 throw new Exception("Process exited with the error: " + code);
43 log.error("Process: {}", builder.command());
44 log.error("Process exited with code {}", code);
45
46 throw new IOException("Process exited with code: " + code);
47 }
39 }
48 }
40
49
41 void preparingCommand(List<String> command);
50 void preparingCommand(List<String> command);
42
51
43
52
44 /**
53 /**
45 * Invoked after the process is started, can be used to handle the process
54 * Invoked after the process is started, can be used to handle the process
46 * output.
55 * output.
47 *
56 *
48 * Default implementation redirects output to the logger as INFO messages.
57 * Default implementation redirects output to the logger as INFO messages.
49 *
58 *
50 * @param p The current process.
59 * @param p The current process.
51 */
60 */
52
61
53 default void processStarted(Process p) {
62 default void processStarted(Process p) {
54 final Logger log = getLogger();
63 final Logger log = getLogger();
55 Utils.redirectIO(p.getInputStream(), log::info);
64 Utils.redirectIO(p.getInputStream(), log::info);
56 }
65 }
57 }
66 }
@@ -1,96 +1,142
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.io.FileInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.nio.file.Files;
3 import java.util.ArrayList;
7 import java.util.ArrayList;
4 import java.util.Collection;
8 import java.util.Collection;
5 import java.util.Collections;
9 import java.util.Collections;
6 import java.util.HashMap;
10 import java.util.HashMap;
7 import java.util.List;
11 import java.util.List;
8 import java.util.Map;
12 import java.util.Map;
9 import java.util.Optional;
13 import java.util.Optional;
10
14
15 import org.apache.tools.ant.taskdefs.UpToDate;
11 import org.gradle.api.Action;
16 import org.gradle.api.Action;
12 import org.gradle.api.file.DirectoryProperty;
17 import org.gradle.api.file.DirectoryProperty;
18 import org.gradle.api.file.RegularFile;
13 import org.gradle.api.file.RegularFileProperty;
19 import org.gradle.api.file.RegularFileProperty;
14 import org.gradle.api.provider.ListProperty;
20 import org.gradle.api.provider.ListProperty;
15 import org.gradle.api.provider.MapProperty;
21 import org.gradle.api.provider.MapProperty;
16 import org.gradle.api.provider.Property;
22 import org.gradle.api.provider.Property;
17 import org.gradle.api.provider.Provider;
18 import org.gradle.api.tasks.Input;
23 import org.gradle.api.tasks.Input;
19 import org.gradle.api.tasks.InputDirectory;
24 import org.gradle.api.tasks.InputDirectory;
20 import org.gradle.api.tasks.OutputFile;
25 import org.gradle.api.tasks.OutputFile;
21 import org.gradle.api.tasks.SkipWhenEmpty;
26 import org.gradle.api.tasks.SkipWhenEmpty;
27 import org.gradle.api.tasks.TaskAction;
22 import org.implab.gradle.containers.ImageName;
28 import org.implab.gradle.containers.ImageName;
23 import org.implab.gradle.containers.Utils;
29 import org.implab.gradle.containers.cli.Utils;
30 import org.implab.gradle.containers.dsl.MapPropertyEntry;
24
31
25 import groovy.lang.Closure;
32 import groovy.lang.Closure;
26
33
27 public abstract class BuildImage extends DockerCliTask {
34 public abstract class BuildImage extends DockerCliTask {
28
35
29 public final String BUILD_COMMAND = "build";
36 public final String BUILD_COMMAND = "build";
30
37
31 @InputDirectory
38 @InputDirectory
32 @SkipWhenEmpty
39 @SkipWhenEmpty
33 public abstract DirectoryProperty getContextDirectory();
40 public abstract DirectoryProperty getContextDirectory();
34
41
35 @Input
42 @Input
36 public abstract MapProperty<String, String> getBuildArgs();
43 public abstract MapProperty<String, String> getBuildArgs();
37
44
38 @Input
45 @Input
39 public abstract ListProperty<String> getExtraCommandArgs();
46 public abstract ListProperty<String> getExtraCommandArgs();
40
47
41 @Input
48 @Input
49 @org.gradle.api.tasks.Optional
42 public abstract Property<String> getBuildTarget();
50 public abstract Property<String> getBuildTarget();
43
51
44 @Input
52 @Input
45 public abstract Property<ImageName> getImageName();
53 public abstract Property<ImageName> getImageName();
46
54
47 @OutputFile
55 @OutputFile
48 public abstract RegularFileProperty getImageIdFile();
56 public abstract RegularFileProperty getImageIdFile();
49
57
58 protected BuildImage() {
59 getOutputs().upToDateWhen(task -> getImageIdFile()
60 .map(RegularFile::getAsFile)
61 .map(docker()::imageExists)
62 .getOrElse(false));
63 }
64
50 public void buildArgs(Action<Map<String, String>> spec) {
65 public void buildArgs(Action<Map<String, String>> spec) {
51 getBuildArgs().putAll(provider(() -> {
66 getBuildArgs().putAll(provider(() -> {
52 Map<String, String> args = new HashMap<>();
67 Map<String, String> args = new HashMap<>();
53 spec.execute(args);
68 spec.execute(args);
54 return args;
69 return args;
55 }));
70 }));
56 }
71 }
57
72
58 public void buildArgs(Closure<Map<String, String>> spec) {
73 public void buildArgs(Closure<Map<String, String>> spec) {
59 buildArgs(Utils.wrapClosure(spec));
74 buildArgs(Utils.wrapClosure(spec));
60 }
75 }
61
76
77 public MapPropertyEntry<String, String> buildArg(String key) {
78 return new MapPropertyEntry<String, String>(getBuildArgs(), key, getProviders());
79 }
80
81 @TaskAction
82 public void Run() throws Exception {
83 List<String> args = new ArrayList<>();
84
85 args.addAll(List.of(
86 "-t", getImageName().get().toString(),
87 "--iidfile", getImageIdFile().getAsFile().get().toString()));
88
89 getBuildArgs().get().forEach((k, v) -> {
90 args.add("--build-arg");
91 args.add(String.format("%s=%s", k, v));
92 });
93
94 // add --target if specified for multi-stage build
95 if (getBuildTarget().isPresent()) {
96 args.add("--target");
97 args.add(getBuildTarget().get());
98 }
99
100 // add extra parameters
101 getExtraCommandArgs().getOrElse(Collections.emptyList())
102 .forEach(args::add);
103
104 args.add(getContextDirectory().getAsFile().get().toString());
105
106 docker().build(args);
107 }
108
109
62 @Override
110 @Override
63 protected Optional<String> getSubCommand() {
111 protected Optional<String> getSubCommand() {
64 return Optional.of(BUILD_COMMAND);
112 return Optional.of(BUILD_COMMAND);
65 }
113 }
66
114
67 @Override
115 @Override
68 protected Collection<String> getSubCommandArguments() {
116 protected Collection<String> getSubCommandArguments() {
69 List<String> args = new ArrayList<>();
117 List<String> args = new ArrayList<>();
70
118
71 args.addAll(List.of(
119 args.addAll(List.of(
72 "-t", getImageName().get().toString(),
120 "-t", getImageName().get().toString(),
73 "--iidfile", getImageIdFile().getAsFile().get().toString()));
121 "--iidfile", getImageIdFile().getAsFile().get().toString()));
74
122
75 if (imageBuildArgs.isPresent()) {
123 getBuildArgs().get().forEach((k, v) -> {
76 imageBuildArgs.get().forEach((k, v) -> {
77 args.add("--build-arg");
124 args.add("--build-arg");
78 args.add(String.format("%s=%s", k, v));
125 args.add(String.format("%s=%s", k, v));
79 });
126 });
80 }
81
127
82 // add --target if specified for multi-stage build
128 // add --target if specified for multi-stage build
83 if (getBuildTarget().isPresent()) {
129 if (getBuildTarget().isPresent()) {
84 args.add("--target");
130 args.add("--target");
85 args.add(getBuildTarget().get());
131 args.add(getBuildTarget().get());
86 }
132 }
87
133
88 // add extra parameters
134 // add extra parameters
89 getExtraCommandArgs().getOrElse(Collections.emptyList())
135 getExtraCommandArgs().getOrElse(Collections.emptyList())
90 .forEach(args::add);
136 .forEach(args::add);
91
137
92 args.add(getContextDirectory().getAsFile().get().toString());
138 args.add(getContextDirectory().getAsFile().get().toString());
93
139
94 return args;
140 return args;
95 }
141 }
96 }
142 }
@@ -1,81 +1,110
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.util.Collection;
4 import java.util.Collection;
5 import java.util.Collections;
5 import java.util.Collections;
6 import java.util.List;
6 import java.util.List;
7 import java.util.Optional;
7 import java.util.Optional;
8
8
9 import org.gradle.api.DefaultTask;
9 import org.gradle.api.DefaultTask;
10 import org.gradle.api.file.Directory;
10 import org.gradle.api.file.DirectoryProperty;
11 import org.gradle.api.file.DirectoryProperty;
12 import org.gradle.api.logging.Logger;
11 import org.gradle.api.provider.Property;
13 import org.gradle.api.provider.Property;
12 import org.gradle.api.provider.Provider;
14 import org.gradle.api.provider.Provider;
13 import org.gradle.api.tasks.Input;
15 import org.gradle.api.tasks.Input;
14 import org.gradle.api.tasks.Internal;
16 import org.gradle.api.tasks.Internal;
15 import org.gradle.api.tasks.TaskAction;
17 import org.gradle.api.tasks.TaskAction;
16 import org.implab.gradle.containers.ContainerExtension;
18 import org.implab.gradle.containers.ContainerExtension;
17 import org.implab.gradle.containers.ExecuteMixin;
19 import org.implab.gradle.containers.ExecuteMixin;
18 import org.implab.gradle.containers.PropertiesMixin;
20 import org.implab.gradle.containers.PropertiesMixin;
21 import org.implab.gradle.containers.cli.DockerTraits;
19
22
20 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin, ExecuteMixin {
23 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin, ExecuteMixin {
21
24
22 private final Property<String> cliCmd;
25 private final Property<String> cliCmd;
23
26
24 private final DirectoryProperty workingDir;
27 private final DirectoryProperty workingDir;
25
28
26 public DockerCliTask() {
29 public DockerCliTask() {
27 cliCmd = property(String.class)
30 cliCmd = property(String.class)
28 .convention(getContainerExtension().getCliCmd());
31 .convention(getContainerExtension().getCliCmd());
29
32
30 workingDir = directoryProperty();
33 workingDir = directoryProperty();
31 }
34 }
32
35
33 @Internal
36 @Internal
34 protected Optional<String> getSubCommand() {
37 protected Optional<String> getSubCommand() {
35 return Optional.empty();
38 return Optional.empty();
36 }
39 }
37
40
38 @Internal
41 @Internal
39 protected Collection<String> getSubCommandArguments() {
42 protected Collection<String> getSubCommandArguments() {
40 return Collections.emptyList();
43 return Collections.emptyList();
41 }
44 }
42
45
43 @Input
46 @Input
44 public Property<String> getCliCmd() {
47 public Property<String> getCliCmd() {
45 return cliCmd;
48 return cliCmd;
46 }
49 }
47
50
48 public void setCliCmd(String cliCmd) {
51 public void setCliCmd(String cliCmd) {
49 this.cliCmd.set(cliCmd);
52 this.cliCmd.set(cliCmd);
50 }
53 }
51
54
52 @Internal
55 @Internal
53 protected ContainerExtension getContainerExtension() {
56 protected ContainerExtension getContainerExtension() {
54 return getProject().getExtensions().getByType(ContainerExtension.class);
57 return getProject().getExtensions().getByType(ContainerExtension.class);
55 }
58 }
56
59
57 @TaskAction
60 @TaskAction
58 public void Run() throws Exception {
61 public void Run() throws Exception {
59 Execute();
62 Execute();
60 }
63 }
61
64
62 public void preparingCommand(List<String> commandLine) {
65 public void preparingCommand(List<String> commandLine) {
63 commandLine.add(cliCmd.get());
66 commandLine.add(cliCmd.get());
64 getSubCommand().ifPresent(commandLine::add);
67 getSubCommand().ifPresent(commandLine::add);
65 getSubCommandArguments().forEach(commandLine::add);
68 getSubCommandArguments().forEach(commandLine::add);
66 }
69 }
67
70
68 @Override
71 @Override
69 @Internal
72 @Internal
70 public Optional<File> getWorkingDir() {
73 public Optional<File> getWorkingDir() {
71 return Optional.ofNullable(workingDir.get().getAsFile());
74 return workingDir
75 .map(Directory::getAsFile)
76 .map(Optional::of)
77 .getOrElse(Optional.empty());
72 }
78 }
73
79
74 protected void setWorkingDir(Provider<File> workingDir) {
80 protected void setWorkingDir(Provider<File> workingDir) {
75 this.workingDir.fileProvider(workingDir);
81 this.workingDir.fileProvider(workingDir);
76 }
82 }
77
83
78 protected void setWorkingDir(File workingDir) {
84 protected void setWorkingDir(File workingDir) {
79 this.workingDir.set(workingDir);
85 this.workingDir.set(workingDir);
80 }
86 }
87
88 protected DockerTraits docker() {
89 return new TaskDockerTraits();
81 }
90 }
91
92 class TaskDockerTraits extends DockerTraits {
93
94 @Override
95 public Logger getLogger() {
96 return DockerCliTask.this.getLogger();
97 }
98
99 @Override
100 public Optional<File> getWorkingDir() {
101 return DockerCliTask.this.getWorkingDir();
102 }
103
104 @Override
105 public String getCliCmd() {
106 return cliCmd.get();
107 }
108
109 }
110 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now