##// END OF EJS Templates
Working version of containers plugin, extracted container-base plugin for basic types and configuration
cin -
r10:902127e66316 default
parent child
Show More
@@ -0,0 +1,49
1 package org.implab.gradle.containers;
2
3 import org.gradle.api.Plugin;
4 import org.gradle.api.Project;
5 import org.gradle.api.plugins.ExtraPropertiesExtension;
6 import org.implab.gradle.containers.tasks.BuildImage;
7 import org.implab.gradle.containers.tasks.PushImage;
8 import org.implab.gradle.containers.tasks.RunImage;
9 import org.implab.gradle.containers.tasks.SaveImage;
10 import org.implab.gradle.containers.tasks.TagImage;
11
12 public class ContainerBasePlugin implements Plugin<Project> {
13 public static final String CONTAINER_EXTENSION_NAME = "container";
14
15 ContainerExtension containerExtension;
16
17 ContainerExtension getContainerExtension() {
18 if (containerExtension == null)
19 throw new IllegalStateException();
20 return containerExtension;
21 }
22
23 void exportClasses(Project project, Class<?>... classes) {
24 ExtraPropertiesExtension extras = project.getExtensions().getExtraProperties();
25 for (var clazz : classes)
26 extras.set(clazz.getSimpleName(), clazz);
27 }
28
29 @Override
30 public void apply(Project project) {
31
32 containerExtension = project.getObjects().newInstance(ContainerExtension.class);
33
34 containerExtension.getImageAuthority()
35 .convention(project.provider(() -> (String) project.getProperties().get("imagesAuthority")));
36
37 containerExtension.getImageGroup()
38 .convention(project.provider(() -> (String) project.getProperties().get("imagesGroup")));
39
40 project.getExtensions().add(CONTAINER_EXTENSION_NAME, containerExtension);
41
42 exportClasses(
43 project,
44 BuildImage.class, PushImage.class, SaveImage.class, TagImage.class, RunImage.class);
45
46
47 }
48
49 }
@@ -0,0 +1,38
1 package org.implab.gradle.containers.cli;
2
3 import java.io.Serializable;
4 import java.util.Optional;
5
6 import com.fasterxml.jackson.annotation.JsonCreator;
7 import com.fasterxml.jackson.annotation.JsonProperty;
8
9 public class ImageRef implements Serializable {
10
11 private static final long serialVersionUID = 2023111901L;
12
13 final String tag;
14
15 final String id;
16
17 @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
18 public ImageRef(@JsonProperty("tag") String tag, @JsonProperty("id") String id) {
19 this.id = id;
20 this.tag = tag;
21 }
22
23 public String getId() {
24 return id;
25 }
26
27 public ImageRef withId(String id) {
28 return new ImageRef(tag, id);
29 }
30
31 public Optional<String> getTag() {
32 return Optional.ofNullable(tag);
33 }
34
35 public ImageRef withTag(String tag) {
36 return new ImageRef(tag, id);
37 }
38 }
@@ -0,0 +1,39
1 package org.implab.gradle.containers.dsl;
2
3 import java.util.concurrent.Callable;
4
5 import org.gradle.api.provider.ListProperty;
6 import org.gradle.api.tasks.Input;
7 import org.implab.gradle.containers.PropertiesMixin;
8
9 import groovy.lang.Closure;
10
11 public interface OptionsMixin extends PropertiesMixin {
12
13 @Input
14 ListProperty<String> getOptions();
15
16 default void option(String opt) {
17 getOptions().add(opt);
18 }
19
20 default void option(Callable<String> optionProvider) {
21 getOptions().add(provider(optionProvider));
22 }
23
24 default void option(Closure<String> optionProvider) {
25 getOptions().add(provider(optionProvider));
26 }
27
28 default void options(String ...opts) {
29 getOptions().addAll(opts);
30 }
31
32 default void options(Callable<Iterable<String>> optionsProvider) {
33 getOptions().addAll(provider(optionsProvider));
34 }
35
36 default void options(Closure<Iterable<String>> optionsProvider) {
37 getOptions().addAll(provider(optionsProvider));
38 }
39 }
@@ -5,6 +5,16 plugins {
5 id "ivy-publish"
5 id "ivy-publish"
6 }
6 }
7
7
8 dependencies {
9 implementation "com.fasterxml.jackson.core:jackson-core:2.13.5",
10 "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.5"
11 }
12
13 java {
14 targetCompatibility = 11
15 sourceCompatibility = 11
16 }
17
8
18
9 repositories {
19 repositories {
10 mavenCentral()
20 mavenCentral()
@@ -16,11 +26,18 gradlePlugin {
16 plugins {
26 plugins {
17 containerPlugin {
27 containerPlugin {
18 id = 'org.implab.gradle-container'
28 id = 'org.implab.gradle-container'
19 displayName = "Container building plugin"
29 displayName = "Provdes convetional configuration to build a container image"
20 description = 'Build and publish container images with docker or podman. Simple wrapper around cli.'
30 description = 'Build and publish container images with docker or podman. Simple wrapper around cli.'
21 implementationClass = 'org.implab.gradle.containers.ContainerPlugin'
31 implementationClass = 'org.implab.gradle.containers.ContainerPlugin'
22 tags.set(['containers', 'image', 'docker', 'podman'])
32 tags.set(['containers', 'image', 'docker', 'podman'])
23 }
33 }
34 containerBasePlugin {
35 id = 'org.implab.gradle-container-base'
36 displayName = "Provides tasks to build manipulate container images"
37 description = 'Build and publish container images with docker or podman. Simple wrapper around cli.'
38 implementationClass = 'org.implab.gradle.containers.ContainerBasePlugin'
39 tags.set(['containers', 'image', 'docker', 'podman'])
40 }
24 }
41 }
25 }
42 }
26
43
@@ -1,156 +1,85
1 package org.implab.gradle.containers;
1 package org.implab.gradle.containers;
2
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
3 import java.util.Optional;
6 import java.util.Optional;
4
7
8 import javax.inject.Inject;
9
5 import org.gradle.api.Project;
10 import org.gradle.api.Project;
6 import org.gradle.api.file.Directory;
7 import org.gradle.api.file.DirectoryProperty;
11 import org.gradle.api.file.DirectoryProperty;
8 import org.gradle.api.file.ProjectLayout;
12 import org.gradle.api.file.ProjectLayout;
9 import org.gradle.api.file.RegularFile;
10 import org.gradle.api.file.RegularFileProperty;
13 import org.gradle.api.file.RegularFileProperty;
11 import org.gradle.api.model.ObjectFactory;
12 import org.gradle.api.provider.Property;
14 import org.gradle.api.provider.Property;
13 import org.gradle.api.provider.Provider;
15 import org.gradle.api.provider.Provider;
14
16 import org.implab.gradle.containers.cli.ImageName;
15 public class ContainerExtension {
17 import org.implab.gradle.containers.cli.ImageRef;
16
18 import org.implab.gradle.containers.cli.Utils;
17 private final Property<String> cliCmd;
18
19
19 private final Property<String> imageAuthority;
20 public abstract class ContainerExtension {
20
21 private final Property<String> imageGroup;
22
21
23 private final Property<String> imageShortName;
22 public abstract Property<String> getCliCmd();
24
25 private final Property<String> imageTag;
26
23
27 private final Property<ImageName> imageName;
24 public abstract DirectoryProperty getContextDirectory();
28
25
29 private final DirectoryProperty contextDir;
26 public abstract RegularFileProperty getImageIdFile();
30
31 private final RegularFileProperty imageIdFile;
32
27
33 public ContainerExtension(ObjectFactory factory, ProjectLayout layout, Project project) {
28 /**
34 contextDir = factory.directoryProperty();
29 * Specifies the name of the registry where the image is located
35 contextDir.convention(layout.getBuildDirectory().dir("context"));
30 * {@code registry.my-company.org}
36
31 */
37 imageIdFile = factory.fileProperty();
32 public abstract Property<String> getImageAuthority();
38 imageIdFile.convention(layout.getBuildDirectory().file("imageId"));
39
40 cliCmd = factory.property(String.class);
41 cliCmd.set("docker");
42
33
43 imageAuthority = factory.property(String.class);
34 /**
44 imageGroup = factory.property(String.class);
35 * Specified the path of the image like {@code my-company}
45 imageShortName = factory.property(String.class);
36 */
46
37 public abstract Property<String> getImageGroup();
47 imageShortName.convention(project.getName());
48
38
49 imageTag = factory.property(String.class);
39 public abstract Property<ImageName> getImageName();
50 imageTag.set(project
51 .provider(() -> Optional.ofNullable(project.getVersion()).map(v -> v.toString()).orElse("latest")));
52
53 Provider<String> imageRepository = imageGroup.map(g -> g + "/" + imageShortName.get()).orElse(imageShortName);
54
40
55 imageName = factory.property(ImageName.class);
41 /**
56 imageName.convention(project.provider(
42 * Specifies local image part like {@code httpd}
57 () -> new ImageName().authority(imageAuthority.get()).name(imageRepository.get()).tag(imageTag.get())));
43 */
58 }
44 public abstract Property<String> getImageLocalName();
59
60 public Property<String> getCliCmd() {
61 return cliCmd;
62 }
63
45
64 public void setCliCmd(String cliCmd) {
46 /**
65 this.cliCmd.set(cliCmd);
47 * This property is deprecated use imageLocalName
66 }
48 */
67
49 @Deprecated
68 public DirectoryProperty getContextDirectory() {
50 public Property<String> getImageShortName() {
69 return contextDir;
51 return getImageLocalName();
70 }
71
72 public void setContextDirectory(Directory dir) {
73 contextDir.set(dir);
74 }
52 }
75
53
76 public void setContextDirectory(Provider<Directory> dir) {
54 public abstract Property<String> getImageTag();
77 contextDir.set(dir);
78 }
79
80 public RegularFileProperty getImageIdFile() {
81 return imageIdFile;
82 }
83
84 public void setImageIdFile(RegularFile file) {
85 imageIdFile.set(file);
86 }
87
88 public void setImageIdFile(Provider<RegularFile> file) {
89 imageIdFile.set(file);
90 }
91
55
92 public Property<String> getImageAuthority() {
56 @Inject
93 return imageAuthority;
57 public ContainerExtension(ProjectLayout layout, Project project) {
94 }
58 getContextDirectory().convention(layout.getBuildDirectory().dir("context"));
95
96 public void setImageAuthority(String value) {
97 imageAuthority.set(value);
98 }
99
59
100 public void setImageAuthority(Provider<String> value) {
60 getImageIdFile().convention(layout.getBuildDirectory().file("iid.json"));
101 imageAuthority.set(value);
102 }
103
61
104 public Property<String> getImageGroup() {
62 getCliCmd().set("docker");
105 return imageGroup;
106 }
107
108 public void setImageGroup(String value) {
109 imageGroup.set(value);
110 }
111
63
112 public void setImageGroup(Provider<String> value) {
64 getImageLocalName().convention(project.getName());
113 imageGroup.set(value);
114 }
115
116 public Property<ImageName> getImageName() {
117 return imageName;
118 }
119
65
120 public void setImageName(ImageName name) {
66 getImageTag().set(project
121 imageName.set(name);
67 .provider(() -> Optional.ofNullable(project.getVersion()).map(v -> v.toString()).orElse("latest")));
122 }
123
124 public void setImageName(Provider<ImageName> name) {
125 imageName.set(name);
126 }
127
68
128 public Property<String> getImageShortName() {
69 Provider<String> imageRepository = getImageGroup().map(g -> g + "/" + getImageLocalName().get())
129 return imageShortName;
70 .orElse(getImageLocalName());
130 }
131
132 public void setImageShortName(String name) {
133 imageShortName.set(name);
134 }
135
71
136 public void setImageShortName(Provider<String> name) {
72 getImageName().convention(project.provider(
137 imageShortName.set(name);
73 () -> new ImageName().authority(getImageAuthority().get()).name(imageRepository.get())
138 }
74 .tag(getImageTag().get())));
139
140 public Property<String> getImageTag() {
141 return imageTag;
142 }
143
144 public void setImageTag(String tag) {
145 imageTag.set(tag);
146 }
147
148 public void setImageTag(Provider<String> tag) {
149 imageTag.set(tag);
150 }
75 }
151
76
152 public ImageName createImageName() {
77 public ImageName createImageName() {
153 return new ImageName();
78 return new ImageName();
154 }
79 }
155
80
81 public ImageRef readImageRef(File file) throws FileNotFoundException, IOException {
82 return Utils.readImageRef(file);
83 }
84
156 } No newline at end of file
85 }
@@ -1,52 +1,36
1 package org.implab.gradle.containers;
1 package org.implab.gradle.containers;
2
2
3 import org.gradle.api.GradleException;
3 import org.gradle.api.Plugin;
4 import org.gradle.api.Plugin;
4 import org.gradle.api.Project;
5 import org.gradle.api.Project;
5 import org.gradle.api.artifacts.Dependency;
6 import org.gradle.api.artifacts.Dependency;
6 import org.gradle.api.file.ProjectLayout;
7 import org.gradle.api.file.ProjectLayout;
7 import org.gradle.api.model.ObjectFactory;
8 import org.gradle.api.plugins.ExtraPropertiesExtension;
9 import org.gradle.api.tasks.Copy;
8 import org.gradle.api.tasks.Copy;
10 import org.gradle.api.tasks.Delete;
9 import org.gradle.api.tasks.Delete;
11 import org.gradle.api.tasks.TaskProvider;
10 import org.gradle.api.tasks.TaskProvider;
11 import org.implab.gradle.containers.cli.ImageName;
12 import org.implab.gradle.containers.tasks.BuildImage;
12 import org.implab.gradle.containers.tasks.BuildImage;
13 import org.implab.gradle.containers.tasks.PushImage;
13 import org.implab.gradle.containers.tasks.PushImage;
14 import org.implab.gradle.containers.tasks.RunImage;
15 import org.implab.gradle.containers.tasks.SaveImage;
14 import org.implab.gradle.containers.tasks.SaveImage;
16 import org.implab.gradle.containers.tasks.TagImage;
17
15
18 public class ContainerPlugin implements Plugin<Project> {
16 public class ContainerPlugin implements Plugin<Project> {
19
17
20 public static final String BUILD_GROUP = "build";
18 public static final String BUILD_GROUP = "build";
21
19
22 public static final String CONTAINER_EXTENSION_NAME = "container";
23
24 public static final String ARCHIVE_CONFIGURATION = "archive";
20 public static final String ARCHIVE_CONFIGURATION = "archive";
25
21
26 private ContainerExtension containerExtension;
22 ContainerExtension containerExtension;
27
23
28 void exportClasses(Project project, Class<?>... classes) {
29 ExtraPropertiesExtension extras = project.getExtensions().getExtraProperties();
30 for (var clazz : classes)
31 extras.set(clazz.getSimpleName(), clazz);
32 }
33
34 public void apply(Project project) {
24 public void apply(Project project) {
35 ObjectFactory factory = project.getObjects();
36 ProjectLayout layout = project.getLayout();
25 ProjectLayout layout = project.getLayout();
37 containerExtension = new ContainerExtension(factory, layout, project);
38
26
39 containerExtension.getImageAuthority()
27 project.getPluginManager().apply(ContainerBasePlugin.class);
40 .convention(project.provider(() -> (String) project.getProperties().get("imagesAuthority")));
41
28
42 containerExtension.getImageGroup()
29 var basePlugin = project.getPlugins().findPlugin(ContainerBasePlugin.class);
43 .convention(project.provider(() -> (String) project.getProperties().get("imagesGroup")));
30 if (basePlugin == null)
31 throw new GradleException("The container-base plugin fails to be applied");
44
32
45 project.getExtensions().add(CONTAINER_EXTENSION_NAME, containerExtension);
33 var containerExtension = basePlugin.getContainerExtension();
46
47 exportClasses(
48 project,
49 BuildImage.class, PushImage.class, SaveImage.class, TagImage.class, RunImage.class);
50
34
51 project.getConfigurations().create(Dependency.DEFAULT_CONFIGURATION, c -> {
35 project.getConfigurations().create(Dependency.DEFAULT_CONFIGURATION, c -> {
52 c.setCanBeConsumed(true);
36 c.setCanBeConsumed(true);
@@ -85,17 +69,14 public class ContainerPlugin implements
85
69
86 project.getTasks().register("pushImage", PushImage.class, t -> {
70 project.getTasks().register("pushImage", PushImage.class, t -> {
87 t.dependsOn(buildImageTask);
71 t.dependsOn(buildImageTask);
88 t.getImageName().set(buildImageTask.flatMap(b -> b.getImageName()));
72 t.getImageName().set(buildImageTask.flatMap(BuildImage::getImageName));
89 });
73 });
90
74
91 project.getTasks().register("saveImage", SaveImage.class, t -> {
75 project.getTasks().register("saveImage", SaveImage.class, t -> {
92 t.dependsOn(buildImageTask);
76 t.dependsOn(buildImageTask);
93 t.getImage().set(buildImageTask.flatMap(b -> b.getImageName()));
77 t.getExportImages().add(buildImageTask.flatMap(BuildImage::getImageName).map(ImageName::toString));
94 });
78 });
95
79
96 project.getArtifacts().add(Dependency.DEFAULT_CONFIGURATION, buildImageTask.flatMap(x -> x.getImageIdFile()),
80 project.getArtifacts().add(Dependency.DEFAULT_CONFIGURATION, buildImageTask);
97 t -> {
98 t.builtBy(buildImageTask);
99 });
100 }
81 }
101 }
82 }
@@ -7,56 +7,131 import java.util.ArrayList;
7 import java.util.Arrays;
7 import java.util.Arrays;
8 import java.util.List;
8 import java.util.List;
9 import java.util.Optional;
9 import java.util.Optional;
10
10 import org.gradle.api.logging.Logger;
11 import org.gradle.api.logging.Logger;
11
12
12 public abstract class DockerTraits {
13 public abstract class DockerTraits {
13
14
15 public final String BUILD_COMMAND = "build";
16 public final String PUSH_COMMAND = "push";
17 public final String RUN_COMMAND = "run";
18 public final String SAVE_COMMAND = "save";
19 public final String INSPECT_COMMAND = "inspect";
20 public final String IMAGE_COMMAND = "image";
21 public final String TAG_COMMAND = "tag";
22
14 public abstract Logger getLogger();
23 public abstract Logger getLogger();
15
24
16 public abstract Optional<File> getWorkingDir();
25 public abstract Optional<File> getWorkingDir();
17
26
18 public abstract String getCliCmd();
27 public abstract String getCliCmd();
19
28
20 boolean execute(ProcessBuilder builder, int code) throws IOException, InterruptedException {
29 Process startProcess(ProcessBuilder builder) throws IOException {
21 var proc = builder.start();
30 getLogger().info("Starting: {}", builder.command());
31 return builder.start();
32 }
33
34 protected boolean checkRetCode(Process proc, int code) throws InterruptedException {
35 if (getLogger().isInfoEnabled()) {
36 Utils.redirectIO(proc.getInputStream(), getLogger()::info);
37 Utils.redirectIO(proc.getErrorStream(), getLogger()::info);
38 }
39
22 return proc.waitFor() == code;
40 return proc.waitFor() == code;
23 }
41 }
24
42
43 protected void complete(Process proc) throws InterruptedException, IOException {
44 if (getLogger().isInfoEnabled())
45 Utils.redirectIO(proc.getInputStream(), getLogger()::info);
46
47 if (getLogger().isErrorEnabled())
48 Utils.redirectIO(proc.getErrorStream(), getLogger()::error);
49
50 var code = proc.waitFor();
51 if (code != 0) {
52 getLogger().error("The process exited with code {}", code);
53 throw new IOException("The process exited with error code " + code);
54 }
55 }
56
25 protected ProcessBuilder builder(String... args) {
57 protected ProcessBuilder builder(String... args) {
26 var command = new ArrayList<String>(args.length + 1);
58 var argsList = new ArrayList<String>(args.length + 1);
59 Arrays.stream(args).forEach(argsList::add);
60
61 return builder(argsList);
62 }
63
64 protected ProcessBuilder builder(List<String> args) {
65 var command = new ArrayList<String>(args.size() + 1);
66
27 command.add(getCliCmd());
67 command.add(getCliCmd());
28 Arrays.stream(args).forEach(command::add);
68 args.forEach(command::add);
29
69
30 var builder = new ProcessBuilder(command);
70 var builder = new ProcessBuilder(command);
31
71
32 getWorkingDir().ifPresent(builder::directory);
72 getWorkingDir().ifPresent(builder::directory);
33 return builder();
73 return builder;
74 }
75
76 public void buildImage(String imageName, File contextDirectory, List<String> options)
77 throws IOException, InterruptedException {
78 var args = new ArrayList<String>();
79 args.add(BUILD_COMMAND);
80 args.addAll(options);
81 args.add("-t");
82 args.add(imageName);
83 args.add(contextDirectory.getAbsolutePath());
84 complete(startProcess(builder(args)));
85 }
86
87 public void pushImage(String image, List<String> options) throws InterruptedException, IOException {
88 var args = new ArrayList<String>();
89 args.add(PUSH_COMMAND);
90 args.addAll(options);
91 args.add(image);
92 complete(startProcess(builder(args)));
34 }
93 }
35
94
36 protected ProcessBuilder builder(List<String> command) {
95 public void runImage(String image, List<String> options, List<String> command)
37 var builder = new ProcessBuilder(command);
96 throws InterruptedException, IOException {
38
97 var args = new ArrayList<String>();
39 getWorkingDir().ifPresent(builder::directory);
98 args.add(RUN_COMMAND);
40 return builder();
99 args.addAll(options);
100 args.add(image);
101 args.addAll(command);
102 complete(startProcess(builder(args)));
41 }
103 }
42
104
43 public void build(List<String> args) {
105 public void saveImage(List<String> images, File output) throws InterruptedException, IOException {
44
106 if (output.exists())
107 output.delete();
108
109 var args = new ArrayList<String>();
110 args.add(SAVE_COMMAND);
111 args.add("-o");
112 args.add(output.getAbsolutePath());
113 images.forEach(args::add);
114
115 complete(startProcess(builder(args)));
116 }
117
118 public void tagImage(String source, String target) throws InterruptedException, IOException {
119 complete(startProcess(builder(TAG_COMMAND, source, target)));
45 }
120 }
46
121
47 public boolean imageExists(String imageId) throws InterruptedException, IOException {
122 public boolean imageExists(String imageId) throws InterruptedException, IOException {
48 getLogger().info("Check image {} exists", imageId);
123 getLogger().info("Check image {} exists", imageId);
49
124
50 var builder = builder("image", "inspect", "--format", "image-exists", imageId);
125 return checkRetCode(
51
126 startProcess(builder(IMAGE_COMMAND, INSPECT_COMMAND, "--format", "image-exists", imageId)),
52 return execute(builder, 0);
127 0);
53 }
128 }
54
129
55 public boolean imageExists(File imageIdFile) {
130 public boolean imageExists(File imageIdFile) {
56 if (imageIdFile.exists()) {
131 if (imageIdFile.exists()) {
57 try {
132 try {
58 var imageId = Files.readString(imageIdFile.toPath());
133 var imageId = Utils.readImageRef(imageIdFile);
59 return imageExists(imageId);
134 return imageExists(imageId.getId());
60 } catch (IOException | InterruptedException e) {
135 } catch (IOException | InterruptedException e) {
61 getLogger().error("Failed to read imageId {}: {}", imageIdFile, e);
136 getLogger().error("Failed to read imageId {}: {}", imageIdFile, e);
62 return false;
137 return false;
@@ -1,10 +1,13
1 package org.implab.gradle.containers;
1 package org.implab.gradle.containers.cli;
2
2
3 import java.io.Serializable;
3 import java.io.Serializable;
4 import java.util.Optional;
4 import java.util.Optional;
5
5
6 import com.fasterxml.jackson.annotation.JsonCreator;
7 import com.fasterxml.jackson.annotation.JsonProperty;
8
6 public class ImageName implements Serializable {
9 public class ImageName implements Serializable {
7
10
8 private static final long serialVersionUID = -1990105923537254552L;
11 private static final long serialVersionUID = -1990105923537254552L;
9
12
10 final String authority;
13 final String authority;
@@ -19,7 +22,9 public class ImageName implements Serial
19 tag = null;
22 tag = null;
20 }
23 }
21
24
22 private ImageName(String authority, String name, String tag) {
25 @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
26 private ImageName(@JsonProperty("authority") String authority, @JsonProperty("name") String name,
27 @JsonProperty("tag") String tag) {
23 this.authority = authority;
28 this.authority = authority;
24 this.name = name;
29 this.name = name;
25 this.tag = tag;
30 this.tag = tag;
@@ -59,7 +64,7 public class ImageName implements Serial
59
64
60 Optional.ofNullable(authority).ifPresent(s -> sb.append(s).append("/"));
65 Optional.ofNullable(authority).ifPresent(s -> sb.append(s).append("/"));
61 Optional.ofNullable(name).ifPresent(s -> sb.append(s));
66 Optional.ofNullable(name).ifPresent(s -> sb.append(s));
62 Optional.ofNullable(tag).ifPresent(s-> sb.append(":").append(s));
67 Optional.ofNullable(tag).ifPresent(s -> sb.append(":").append(s));
63
68
64 return sb.toString();
69 return sb.toString();
65 }
70 }
@@ -3,15 +3,25 package org.implab.gradle.containers.cli
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.FileInputStream;
6 import java.io.FileNotFoundException;
7 import java.io.FileOutputStream;
7 import java.io.FileOutputStream;
8 import java.io.FileReader;
8 import java.io.IOException;
9 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.InputStream;
10 import java.io.OutputStream;
11 import java.io.OutputStream;
11 import java.nio.file.Files;
12 import java.io.Reader;
12 import java.util.Scanner;
13 import java.util.Scanner;
14 import java.util.stream.StreamSupport;
15 import java.nio.file.Files;
16 import java.util.List;
17 import org.gradle.api.Action;
18 import org.gradle.internal.impldep.org.bouncycastle.util.Iterable;
13
19
14 import org.gradle.api.Action;
20 import com.fasterxml.jackson.core.exc.StreamWriteException;
21 import com.fasterxml.jackson.databind.DatabindException;
22 import com.fasterxml.jackson.databind.ObjectMapper;
23 import com.fasterxml.jackson.databind.SerializationFeature;
24 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
15
25
16 import groovy.json.JsonGenerator;
26 import groovy.json.JsonGenerator;
17 import groovy.json.JsonOutput;
27 import groovy.json.JsonOutput;
@@ -53,6 +63,42 public final class Utils {
53 return out.toString();
63 return out.toString();
54 }
64 }
55
65
66 public static <T> T readJson(final Reader reader, Class<T> type) throws IOException {
67 ObjectMapper objectMapper = new ObjectMapper()
68 .registerModule(new Jdk8Module());
69 return objectMapper.readValue(reader, type);
70 }
71
72 public static void writeJson(final File file, Object value)
73 throws StreamWriteException, DatabindException, IOException {
74 ObjectMapper objectMapper = new ObjectMapper()
75 .enable(SerializationFeature.INDENT_OUTPUT)
76 .registerModule(new Jdk8Module());
77 objectMapper.writeValue(file, value);
78 }
79
80 public static ImageRef readImageRef(final File file) throws FileNotFoundException, IOException {
81 try (var reader = new FileReader(file)) {
82 return readJson(reader, ImageRef.class);
83 }
84 }
85
86 public static String readAll(final File src) throws IOException {
87 return Files.readString(src.toPath());
88 }
89
90 public static List<String> readAll(final Iterable<? extends File> files) {
91 return StreamSupport.stream(files.spliterator(), false)
92 .map(file -> {
93 try {
94 return Utils.readAll(file);
95 } catch (IOException e) {
96 throw new RuntimeException(e);
97 }
98 })
99 .toList();
100 }
101
56 public static String readAll(final InputStream src, String charset) throws IOException {
102 public static String readAll(final InputStream src, String charset) throws IOException {
57 ByteArrayOutputStream out = new ByteArrayOutputStream();
103 ByteArrayOutputStream out = new ByteArrayOutputStream();
58 src.transferTo(out);
104 src.transferTo(out);
@@ -85,7 +131,7 public final class Utils {
85 public static <T> Action<T> wrapClosure(Closure<?> closure) {
131 public static <T> Action<T> wrapClosure(Closure<?> closure) {
86 return x -> {
132 return x -> {
87 closure.setDelegate(x);
133 closure.setDelegate(x);
88 closure.setResolveStrategy(Closure.DELEGATE_FIRST);
134 closure.setResolveStrategy(Closure.OWNER_FIRST);
89 closure.call(x);
135 closure.call(x);
90 };
136 };
91 }
137 }
@@ -1,4 +1,4
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.dsl;
2
2
3 public class MountSpec {
3 public class MountSpec {
4
4
@@ -1,4 +1,4
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.dsl;
2
2
3 import java.util.ArrayList;
3 import java.util.ArrayList;
4
4
@@ -1,40 +1,36
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;
7 import java.util.ArrayList;
3 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.HashMap;
4 import java.util.HashMap;
11 import java.util.List;
5 import java.util.List;
12 import java.util.Map;
6 import java.util.Map;
13 import java.util.Optional;
14
7
15 import org.apache.tools.ant.taskdefs.UpToDate;
16 import org.gradle.api.Action;
8 import org.gradle.api.Action;
9 import org.gradle.api.file.Directory;
17 import org.gradle.api.file.DirectoryProperty;
10 import org.gradle.api.file.DirectoryProperty;
18 import org.gradle.api.file.RegularFile;
11 import org.gradle.api.file.RegularFile;
19 import org.gradle.api.file.RegularFileProperty;
12 import org.gradle.api.file.RegularFileProperty;
20 import org.gradle.api.provider.ListProperty;
21 import org.gradle.api.provider.MapProperty;
13 import org.gradle.api.provider.MapProperty;
22 import org.gradle.api.provider.Property;
14 import org.gradle.api.provider.Property;
15 import org.gradle.api.provider.Provider;
23 import org.gradle.api.tasks.Input;
16 import org.gradle.api.tasks.Input;
24 import org.gradle.api.tasks.InputDirectory;
17 import org.gradle.api.tasks.InputDirectory;
18 import org.gradle.api.tasks.Internal;
19 import org.gradle.api.tasks.Optional;
25 import org.gradle.api.tasks.OutputFile;
20 import org.gradle.api.tasks.OutputFile;
26 import org.gradle.api.tasks.SkipWhenEmpty;
21 import org.gradle.api.tasks.SkipWhenEmpty;
27 import org.gradle.api.tasks.TaskAction;
22 import org.gradle.api.tasks.TaskAction;
28 import org.implab.gradle.containers.ImageName;
23 import java.io.File;
24
25 import org.implab.gradle.containers.cli.ImageName;
26 import org.implab.gradle.containers.cli.ImageRef;
29 import org.implab.gradle.containers.cli.Utils;
27 import org.implab.gradle.containers.cli.Utils;
30 import org.implab.gradle.containers.dsl.MapPropertyEntry;
28 import org.implab.gradle.containers.dsl.MapPropertyEntry;
29 import org.implab.gradle.containers.dsl.OptionsMixin;
31
30
32 import groovy.lang.Closure;
31 import groovy.lang.Closure;
33
32
34 public abstract class BuildImage extends DockerCliTask {
33 public abstract class BuildImage extends DockerCliTask implements OptionsMixin {
35
36 public final String BUILD_COMMAND = "build";
37
38 @InputDirectory
34 @InputDirectory
39 @SkipWhenEmpty
35 @SkipWhenEmpty
40 public abstract DirectoryProperty getContextDirectory();
36 public abstract DirectoryProperty getContextDirectory();
@@ -43,19 +39,21 public abstract class BuildImage extends
43 public abstract MapProperty<String, String> getBuildArgs();
39 public abstract MapProperty<String, String> getBuildArgs();
44
40
45 @Input
41 @Input
46 public abstract ListProperty<String> getExtraCommandArgs();
42 @Optional
47
48 @Input
49 @org.gradle.api.tasks.Optional
50 public abstract Property<String> getBuildTarget();
43 public abstract Property<String> getBuildTarget();
51
44
52 @Input
45 @Input
53 public abstract Property<ImageName> getImageName();
46 public abstract Property<ImageName> getImageName();
54
47
55 @OutputFile
48 @Internal
56 public abstract RegularFileProperty getImageIdFile();
49 public abstract RegularFileProperty getImageIdFile();
57
50
58 protected BuildImage() {
51 @OutputFile
52 public Provider<File> getImageIdFileOutput() {
53 return getImageIdFile().map(RegularFile::getAsFile);
54 }
55
56 public BuildImage() {
59 getOutputs().upToDateWhen(task -> getImageIdFile()
57 getOutputs().upToDateWhen(task -> getImageIdFile()
60 .map(RegularFile::getAsFile)
58 .map(RegularFile::getAsFile)
61 .map(docker()::imageExists)
59 .map(docker()::imageExists)
@@ -66,6 +64,7 public abstract class BuildImage extends
66 getBuildArgs().putAll(provider(() -> {
64 getBuildArgs().putAll(provider(() -> {
67 Map<String, String> args = new HashMap<>();
65 Map<String, String> args = new HashMap<>();
68 spec.execute(args);
66 spec.execute(args);
67 getLogger().info("add buildArgs {}", args);
69 return args;
68 return args;
70 }));
69 }));
71 }
70 }
@@ -79,12 +78,14 public abstract class BuildImage extends
79 }
78 }
80
79
81 @TaskAction
80 @TaskAction
82 public void Run() throws Exception {
81 public void run() throws Exception {
83 List<String> args = new ArrayList<>();
82 List<String> args = new ArrayList<>();
84
83
85 args.addAll(List.of(
84 // create a temp file to store image id
86 "-t", getImageName().get().toString(),
85 var iidFile = new File(this.getTemporaryDir(), "iid");
87 "--iidfile", getImageIdFile().getAsFile().get().toString()));
86
87 // specify where to write image id
88 args.addAll(List.of("--iidfile", iidFile.getAbsolutePath()));
88
89
89 getBuildArgs().get().forEach((k, v) -> {
90 getBuildArgs().get().forEach((k, v) -> {
90 args.add("--build-arg");
91 args.add("--build-arg");
@@ -98,45 +99,18 public abstract class BuildImage extends
98 }
99 }
99
100
100 // add extra parameters
101 // add extra parameters
101 getExtraCommandArgs().getOrElse(Collections.emptyList())
102 getOptions().get().forEach(args::add);
102 .forEach(args::add);
103
104 args.add(getContextDirectory().getAsFile().get().toString());
105
106 docker().build(args);
107 }
108
109
103
110 @Override
104 var imageTag = getImageName().map(ImageName::toString).get();
111 protected Optional<String> getSubCommand() {
112 return Optional.of(BUILD_COMMAND);
113 }
114
115 @Override
116 protected Collection<String> getSubCommandArguments() {
117 List<String> args = new ArrayList<>();
118
105
119 args.addAll(List.of(
106 // build image
120 "-t", getImageName().get().toString(),
107 docker().buildImage(
121 "--iidfile", getImageIdFile().getAsFile().get().toString()));
108 imageTag,
122
109 getContextDirectory().map(Directory::getAsFile).get(),
123 getBuildArgs().get().forEach((k, v) -> {
110 args);
124 args.add("--build-arg");
125 args.add(String.format("%s=%s", k, v));
126 });
127
111
128 // add --target if specified for multi-stage build
112 // read the built image id and store image ref metadata
129 if (getBuildTarget().isPresent()) {
113 var imageRef = new ImageRef(imageTag, Utils.readAll(iidFile));
130 args.add("--target");
114 Utils.writeJson(getImageIdFileOutput().get(), imageRef);
131 args.add(getBuildTarget().get());
132 }
133
134 // add extra parameters
135 getExtraCommandArgs().getOrElse(Collections.emptyList())
136 .forEach(args::add);
137
138 args.add(getContextDirectory().getAsFile().get().toString());
139
140 return args;
141 }
115 }
142 }
116 }
@@ -1,9 +1,6
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;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.Optional;
4 import java.util.Optional;
8
5
9 import org.gradle.api.DefaultTask;
6 import org.gradle.api.DefaultTask;
@@ -11,80 +8,31 import org.gradle.api.file.Directory;
11 import org.gradle.api.file.DirectoryProperty;
8 import org.gradle.api.file.DirectoryProperty;
12 import org.gradle.api.logging.Logger;
9 import org.gradle.api.logging.Logger;
13 import org.gradle.api.provider.Property;
10 import org.gradle.api.provider.Property;
14 import org.gradle.api.provider.Provider;
15 import org.gradle.api.tasks.Input;
11 import org.gradle.api.tasks.Input;
16 import org.gradle.api.tasks.Internal;
12 import org.gradle.api.tasks.Internal;
17 import org.gradle.api.tasks.TaskAction;
18 import org.implab.gradle.containers.ContainerExtension;
13 import org.implab.gradle.containers.ContainerExtension;
19 import org.implab.gradle.containers.ExecuteMixin;
20 import org.implab.gradle.containers.PropertiesMixin;
14 import org.implab.gradle.containers.PropertiesMixin;
21 import org.implab.gradle.containers.cli.DockerTraits;
15 import org.implab.gradle.containers.cli.DockerTraits;
22
16
23 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin, ExecuteMixin {
17 public abstract class DockerCliTask extends DefaultTask implements PropertiesMixin {
24
25 private final Property<String> cliCmd;
26
27 private final DirectoryProperty workingDir;
28
29 public DockerCliTask() {
30 cliCmd = property(String.class)
31 .convention(getContainerExtension().getCliCmd());
32
33 workingDir = directoryProperty();
34 }
35
36 @Internal
37 protected Optional<String> getSubCommand() {
38 return Optional.empty();
39 }
40
41 @Internal
42 protected Collection<String> getSubCommandArguments() {
43 return Collections.emptyList();
44 }
45
18
46 @Input
19 @Input
47 public Property<String> getCliCmd() {
20 public abstract Property<String> getCliCmd();
48 return cliCmd;
49 }
50
21
51 public void setCliCmd(String cliCmd) {
22 @Input
52 this.cliCmd.set(cliCmd);
23 @org.gradle.api.tasks.Optional
53 }
24 public abstract DirectoryProperty getWorkingDirectory();
54
25
55 @Internal
26 @Internal
56 protected ContainerExtension getContainerExtension() {
27 protected ContainerExtension getContainerExtension() {
57 return getProject().getExtensions().getByType(ContainerExtension.class);
28 return getProject().getExtensions().getByType(ContainerExtension.class);
58 }
29 }
59
30
60 @TaskAction
31 public DockerCliTask() {
61 public void Run() throws Exception {
32 getCliCmd().convention(getContainerExtension().getCliCmd());
62 Execute();
63 }
64
65 public void preparingCommand(List<String> commandLine) {
66 commandLine.add(cliCmd.get());
67 getSubCommand().ifPresent(commandLine::add);
68 getSubCommandArguments().forEach(commandLine::add);
69 }
70
33
71 @Override
72 @Internal
73 public Optional<File> getWorkingDir() {
74 return workingDir
75 .map(Directory::getAsFile)
76 .map(Optional::of)
77 .getOrElse(Optional.empty());
78 }
34 }
79
35
80 protected void setWorkingDir(Provider<File> workingDir) {
81 this.workingDir.fileProvider(workingDir);
82 }
83
84 protected void setWorkingDir(File workingDir) {
85 this.workingDir.set(workingDir);
86 }
87
88 protected DockerTraits docker() {
36 protected DockerTraits docker() {
89 return new TaskDockerTraits();
37 return new TaskDockerTraits();
90 }
38 }
@@ -98,12 +46,15 public abstract class DockerCliTask exte
98
46
99 @Override
47 @Override
100 public Optional<File> getWorkingDir() {
48 public Optional<File> getWorkingDir() {
101 return DockerCliTask.this.getWorkingDir();
49 return getWorkingDirectory()
50 .map(Directory::getAsFile)
51 .map(Optional::of)
52 .getOrElse(Optional.empty());
102 }
53 }
103
54
104 @Override
55 @Override
105 public String getCliCmd() {
56 public String getCliCmd() {
106 return cliCmd.get();
57 return DockerCliTask.this.getCliCmd().get();
107 }
58 }
108
59
109 }
60 }
@@ -1,19 +1,16
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.util.ArrayList;
3 import java.io.IOException;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.concurrent.Callable;
7
4
8 import org.gradle.api.provider.ListProperty;
5 import org.gradle.api.provider.ListProperty;
9 import org.gradle.api.provider.Property;
6 import org.gradle.api.provider.Property;
10 import org.gradle.api.tasks.Input;
7 import org.gradle.api.tasks.Input;
11 import org.gradle.api.tasks.Optional;
8 import org.gradle.api.tasks.Optional;
12 import org.implab.gradle.containers.ImageName;
9 import org.gradle.api.tasks.TaskAction;
10 import org.implab.gradle.containers.cli.ImageName;
11 import org.implab.gradle.containers.dsl.OptionsMixin;
13
12
14 public abstract class PushImage extends DockerCliTask {
13 public abstract class PushImage extends DockerCliTask implements OptionsMixin {
15
16 public final String PUSH_COMMAND = "push";
17
14
18 @Input
15 @Input
19 public abstract Property<ImageName> getImageName();
16 public abstract Property<ImageName> getImageName();
@@ -26,23 +23,11 public abstract class PushImage extends
26 @Optional
23 @Optional
27 public abstract ListProperty<String> getOptions();
24 public abstract ListProperty<String> getOptions();
28
25
29 public PushImage option(Callable<String> provider) {
26 @TaskAction
30 getOptions().add(getProject().provider(provider));
27 public void run() throws InterruptedException, IOException {
31 return this;
32 }
33
28
34 @Override
29 docker().pushImage(
35 protected java.util.Optional<String> getSubCommand() {
30 getImageName().get().toString(),
36 return java.util.Optional.of(PUSH_COMMAND);
31 getOptions().get());
37 }
38
39 @Override
40 protected Collection<String> getSubCommandArguments() {
41 List<String> args = new ArrayList<>();
42
43 args.addAll(getOptions().get());
44 args.add(getImageName().get().toString());
45
46 return args;
47 }
32 }
48 }
33 }
@@ -1,23 +1,16
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.util.ArrayList;
3 import java.io.IOException;
4 import java.util.Collection;
5 import java.util.List;
6
7 import org.gradle.api.Action;
4 import org.gradle.api.Action;
8 import org.gradle.api.provider.ListProperty;
5 import org.gradle.api.provider.ListProperty;
9 import org.gradle.api.provider.Property;
6 import org.gradle.api.provider.Property;
10 import org.gradle.api.tasks.Input;
7 import org.gradle.api.tasks.Input;
11 import org.gradle.api.tasks.Optional;
8 import org.gradle.api.tasks.Optional;
12 import org.implab.gradle.containers.ImageName;
9 import org.implab.gradle.containers.cli.ImageName;
13
10 import org.implab.gradle.containers.dsl.OptionsMixin;
14 public abstract class RunImage extends DockerCliTask {
11 import org.implab.gradle.containers.dsl.VolumeSpec;
15
12
16 public final String RUN_COMMAND = "run";
13 public abstract class RunImage extends DockerCliTask implements OptionsMixin {
17
18 @Input
19 @Optional
20 public abstract ListProperty<String> getOptions();
21
14
22 @Input
15 @Input
23 public abstract Property<ImageName> getImageName();
16 public abstract Property<ImageName> getImageName();
@@ -25,22 +18,7 public abstract class RunImage extends D
25 @Input
18 @Input
26 @Optional
19 @Optional
27 public abstract ListProperty<String> getCommandLine();
20 public abstract ListProperty<String> getCommandLine();
28
21
29 @Override
30 protected java.util.Optional<String> getSubCommand() {
31 return java.util.Optional.of(RUN_COMMAND);
32 }
33
34 @Override
35 protected Collection<String> getSubCommandArguments() {
36 List<String> args = new ArrayList<String>();
37
38 args.addAll(getOptions().get());
39 args.add(getImageName().get().toString());
40 args.addAll(getCommandLine().get());
41
42 return args;
43 }
44
22
45 public void volume(Action<VolumeSpec> configure) {
23 public void volume(Action<VolumeSpec> configure) {
46 getOptions().add("-v");
24 getOptions().add("-v");
@@ -51,4 +29,15 public abstract class RunImage extends D
51 }));
29 }));
52 }
30 }
53
31
32 void commandLine(String ...args) {
33 getCommandLine().addAll(args);
34 }
35
36 public void run() throws InterruptedException, IOException {
37 docker().runImage(
38 getImageName().get().toString(),
39 getOptions().get(),
40 getCommandLine().get());
41 }
42
54 }
43 }
@@ -1,25 +1,27
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.io.File;
4 import java.io.IOException;
3 import java.util.ArrayList;
5 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.Optional;
6 import java.util.Optional;
7
7
8 import org.gradle.api.file.DirectoryProperty;
8 import org.gradle.api.file.DirectoryProperty;
9 import org.gradle.api.file.FileCollection;
9 import org.gradle.api.file.RegularFile;
10 import org.gradle.api.file.RegularFile;
11 import org.gradle.api.provider.ListProperty;
10 import org.gradle.api.provider.Property;
12 import org.gradle.api.provider.Property;
11 import org.gradle.api.provider.Provider;
13 import org.gradle.api.provider.Provider;
12 import org.gradle.api.tasks.Input;
14 import org.gradle.api.tasks.Input;
13 import org.gradle.api.tasks.Internal;
15 import org.gradle.api.tasks.Internal;
14 import org.gradle.api.tasks.OutputFile;
16 import org.gradle.api.tasks.OutputFile;
17 import org.gradle.api.tasks.TaskAction;
15 import org.gradle.api.tasks.TaskExecutionException;
18 import org.gradle.api.tasks.TaskExecutionException;
16 import org.implab.gradle.containers.ImageName;
19 import org.implab.gradle.containers.cli.Utils;
17
20
18 public abstract class SaveImage extends DockerCliTask {
21 public abstract class SaveImage extends DockerCliTask {
19 public final String SAVE_COMMAND = "save";
20
22
21 @Input
23 @Input
22 public abstract Property<ImageName> getImage();
24 public abstract ListProperty<String> getExportImages();
23
25
24 @OutputFile
26 @OutputFile
25 public Provider<RegularFile> getArchiveFile() {
27 public Provider<RegularFile> getArchiveFile() {
@@ -44,6 +46,29 public abstract class SaveImage extends
44 @Internal
46 @Internal
45 public abstract Property<String> getArchiveExtension();
47 public abstract Property<String> getArchiveExtension();
46
48
49 String readImageRefTag(File file) throws Exception {
50 getLogger().info("Reading image ref from {}", file);
51 var imageRef = Utils.readImageRef(file);
52 return imageRef.getTag().orElseThrow(() -> new Exception("The image tag is required to save image"));
53 }
54
55 public void imageRef(File file) {
56 getExportImages().add(provider(() -> {
57 return readImageRefTag(file);
58 }));
59 }
60
61 public void imageRefs(FileCollection files) {
62 dependsOn(files);
63 getExportImages().addAll(provider(() -> {
64 var tags = new ArrayList<String>();
65 for (File file : files) {
66 tags.add(readImageRefTag(file));
67 }
68 return tags;
69 }));
70 }
71
47 public SaveImage() {
72 public SaveImage() {
48 getArchiveFileName().convention(provider(this::conventionalArchiveFileName));
73 getArchiveFileName().convention(provider(this::conventionalArchiveFileName));
49 getArchiveBaseName().convention(provider(this::conventionalArchiveBaseName));
74 getArchiveBaseName().convention(provider(this::conventionalArchiveBaseName));
@@ -77,16 +102,10 public abstract class SaveImage extends
77 return String.join("-", parts) + "." + getArchiveExtension().get();
102 return String.join("-", parts) + "." + getArchiveExtension().get();
78 }
103 }
79
104
80 @Override
105 @TaskAction
81 protected Optional<String> getSubCommand() {
106 public void run() throws InterruptedException, IOException {
82 return Optional.of(SAVE_COMMAND);
107 docker().saveImage(
83 }
108 getExportImages().get(),
84
109 getArchiveFile().map(RegularFile::getAsFile).get());
85 @Override
86 protected Collection<String> getSubCommandArguments() {
87 return List.of(
88 getImage().get().toString(),
89 getArchiveFile().get().getAsFile().toString()
90 );
91 }
110 }
92 }
111 }
@@ -1,51 +1,22
1 package org.implab.gradle.containers.tasks;
1 package org.implab.gradle.containers.tasks;
2
2
3 import java.util.ArrayList;
3 import java.io.IOException;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.concurrent.Callable;
7
8 import org.gradle.api.provider.ListProperty;
9 import org.gradle.api.provider.Property;
4 import org.gradle.api.provider.Property;
10 import org.gradle.api.tasks.Input;
5 import org.gradle.api.tasks.Input;
11 import org.gradle.api.tasks.Optional;
6 import org.gradle.api.tasks.TaskAction;
12 import org.implab.gradle.containers.ImageName;
7 import org.implab.gradle.containers.cli.ImageName;
13
8
14 public abstract class TagImage extends DockerCliTask {
9 public abstract class TagImage extends DockerCliTask {
15 public final String TAG_COMMAND = "tag";
16
17 @Input
10 @Input
18 public abstract Property<ImageName> getSrcImage();
11 public abstract Property<ImageName> getSrcImage();
19
12
20 @Input
13 @Input
21 public abstract Property<ImageName> getDestImage();
14 public abstract Property<ImageName> getDestImage();
22
15
23 @Input
16 @TaskAction
24 @Optional
17 public void run() throws InterruptedException, IOException {
25 public abstract Property<String> getTransport();
18 docker().tagImage(
26
19 getSrcImage().map(ImageName::toString).get(),
27 @Input
20 getDestImage().map(ImageName::toString).get());
28 @Optional
29 public abstract ListProperty<String> getOptions();
30
31 public TagImage option(Callable<String> provider) {
32 getOptions().add(getProject().provider(provider));
33 return this;
34 }
35
36 @Override
37 protected java.util.Optional<String> getSubCommand() {
38 return java.util.Optional.of(TAG_COMMAND);
39 }
40
41 @Override
42 protected Collection<String> getSubCommandArguments() {
43 List<String> args = new ArrayList<>();
44
45 args.addAll(getOptions().get());
46 args.add(getSrcImage().get().toString());
47 args.add(getDestImage().get().toString());
48
49 return args;
50 }
21 }
51 }
22 }
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