mirror of
https://gitee.com/dcren/initializr.git
synced 2025-10-22 03:27:38 +08:00
Add support for Docker Compose
This commit adds the necessary infrastructure to design and contribute a compose file to the project. ComposeHelpDocumentCustomizer can be used to write out a section into the help file containing a table listing all the services with their images and tags. See gh-1417
This commit is contained in:

committed by
Stephane Nicoll

parent
8f73f6c286
commit
8430cccecf
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Docker Compose file.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
public class DockerComposeFile {
|
||||||
|
|
||||||
|
// TreeMap to sort services by name
|
||||||
|
private final Map<String, DockerComposeService> services = new TreeMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link DockerComposeService service} to the file. If a service with the same
|
||||||
|
* {@link DockerComposeService#getName() name} already exists, it is replaced.
|
||||||
|
* @param service the service to add
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DockerComposeFile addService(DockerComposeService service) {
|
||||||
|
this.services.put(service.getName(), service);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the service with the given name.
|
||||||
|
* @param name the name
|
||||||
|
* @return the service or {@code null} if no service with the given name exists
|
||||||
|
*/
|
||||||
|
public DockerComposeService getService(String name) {
|
||||||
|
return this.services.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all services.
|
||||||
|
* @return all services
|
||||||
|
*/
|
||||||
|
public Collection<DockerComposeService> getServices() {
|
||||||
|
return Collections.unmodifiableCollection(this.services.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(PrintWriter writer) {
|
||||||
|
writer.println("services:");
|
||||||
|
for (DockerComposeService service : this.services.values()) {
|
||||||
|
service.write(writer, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for customizing a project's {@link DockerComposeFile}. Invoked with an
|
||||||
|
* {@link Ordered order} of {@code 0} by default, considering overriding
|
||||||
|
* {@link #getOrder()} to customize this behaviour.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DockerComposeFileCustomizer extends Ordered {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customizes the given {@link DockerComposeFile}.
|
||||||
|
* @param composeFile the compose file to customize
|
||||||
|
*/
|
||||||
|
void customize(DockerComposeFile composeFile);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default int getOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.spring.container.dockercompose.Markdown.MarkdownTable;
|
||||||
|
import io.spring.initializr.generator.spring.documentation.HelpDocument;
|
||||||
|
import io.spring.initializr.generator.spring.documentation.HelpDocumentCustomizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide additional information in the {@link HelpDocument} if the
|
||||||
|
* {@link DockerComposeFile} isn't empty.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeHelpDocumentCustomizer implements HelpDocumentCustomizer {
|
||||||
|
|
||||||
|
private final DockerComposeFile composeFile;
|
||||||
|
|
||||||
|
DockerComposeHelpDocumentCustomizer(DockerComposeFile composeFile) {
|
||||||
|
this.composeFile = composeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(HelpDocument document) {
|
||||||
|
Collection<DockerComposeService> services = this.composeFile.getServices();
|
||||||
|
Map<String, Object> model = new HashMap<>();
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
model.put("serviceTable", null);
|
||||||
|
document.getWarnings()
|
||||||
|
.addItem(
|
||||||
|
"No Docker Compose services found. As of now, the application won't start! Please add at least one service to the `compose.yaml` file.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MarkdownTable serviceTable = Markdown.table("Service name", "Image", "Tag", "Website");
|
||||||
|
for (DockerComposeService service : services) {
|
||||||
|
serviceTable.addRow(service.getName(), Markdown.code(service.getImage()),
|
||||||
|
Markdown.code(service.getImageTag()), Markdown.link("Website", service.getImageWebsite()));
|
||||||
|
}
|
||||||
|
model.put("serviceTable", serviceTable.toMarkdown());
|
||||||
|
}
|
||||||
|
document.addSection("documentation/docker-compose", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.project.contributor.ProjectContributor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ProjectContributor} which contributes a 'compose.yaml' file through a
|
||||||
|
* {@link DockerComposeFile}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeProjectContributor implements ProjectContributor {
|
||||||
|
|
||||||
|
private final DockerComposeFile composeFile;
|
||||||
|
|
||||||
|
DockerComposeProjectContributor(DockerComposeFile composeFile) {
|
||||||
|
this.composeFile = composeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contribute(Path projectRoot) throws IOException {
|
||||||
|
Path file = Files.createFile(projectRoot.resolve("compose.yaml"));
|
||||||
|
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(file))) {
|
||||||
|
this.composeFile.write(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for contributions specific to the Docker Compose parts of a project.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
@ProjectGenerationConfiguration
|
||||||
|
public class DockerComposeProjectGenerationConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DockerComposeFile composeFile(ObjectProvider<DockerComposeFileCustomizer> composeFileCustomizers) {
|
||||||
|
DockerComposeFile composeFile = new DockerComposeFile();
|
||||||
|
composeFileCustomizers.orderedStream().forEach((customizer) -> customizer.customize(composeFile));
|
||||||
|
return composeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DockerComposeProjectContributor dockerComposeProjectContributor(DockerComposeFile composeFile) {
|
||||||
|
return new DockerComposeProjectContributor(composeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DockerComposeHelpDocumentCustomizer dockerComposeHelpDocumentCustomizer(DockerComposeFile composeFile) {
|
||||||
|
return new DockerComposeHelpDocumentCustomizer(composeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Docker Compose service.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
public final class DockerComposeService {
|
||||||
|
|
||||||
|
private static final String INDENT = " ";
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String image;
|
||||||
|
|
||||||
|
private final String imageTag;
|
||||||
|
|
||||||
|
private final String imageWebsite;
|
||||||
|
|
||||||
|
private final Map<String, String> environment;
|
||||||
|
|
||||||
|
private final Set<Integer> ports;
|
||||||
|
|
||||||
|
private DockerComposeService(Builder builder) {
|
||||||
|
this.name = builder.name;
|
||||||
|
this.image = builder.image;
|
||||||
|
this.imageTag = builder.imageTag;
|
||||||
|
this.imageWebsite = builder.imageWebsite;
|
||||||
|
this.environment = builder.environment;
|
||||||
|
this.ports = builder.ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImage() {
|
||||||
|
return this.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageTag() {
|
||||||
|
return this.imageTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageWebsite() {
|
||||||
|
return this.imageWebsite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getEnvironment() {
|
||||||
|
return Collections.unmodifiableMap(this.environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getPorts() {
|
||||||
|
return Collections.unmodifiableSet(this.ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DockerComposeService service = (DockerComposeService) o;
|
||||||
|
return Objects.equals(this.name, service.name) && Objects.equals(this.image, service.image)
|
||||||
|
&& Objects.equals(this.imageTag, service.imageTag)
|
||||||
|
&& Objects.equals(this.imageWebsite, service.imageWebsite)
|
||||||
|
&& Objects.equals(this.environment, service.environment) && Objects.equals(this.ports, service.ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(this.name, this.image, this.imageTag, this.imageWebsite, this.environment, this.ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DockerComposeService{" + "name='" + this.name + '\'' + ", image='" + this.image + '\'' + ", imageTag='"
|
||||||
|
+ this.imageTag + '\'' + ", imageWebsite='" + this.imageWebsite + '\'' + ", environment="
|
||||||
|
+ this.environment + ", ports=" + this.ports + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(PrintWriter writer, int indentation) {
|
||||||
|
int currentIndent = indentation;
|
||||||
|
println(writer, this.name + ":", currentIndent);
|
||||||
|
currentIndent++;
|
||||||
|
println(writer, "image: '%s:%s'".formatted(this.image, this.imageTag), currentIndent);
|
||||||
|
if (!this.environment.isEmpty()) {
|
||||||
|
writeEnvironment(writer, currentIndent);
|
||||||
|
}
|
||||||
|
if (!this.ports.isEmpty()) {
|
||||||
|
writePorts(writer, currentIndent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writePorts(PrintWriter writer, int currentIndent) {
|
||||||
|
println(writer, "ports:", currentIndent);
|
||||||
|
for (Integer port : this.ports) {
|
||||||
|
println(writer, "- '%d'".formatted(port), currentIndent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeEnvironment(PrintWriter writer, int currentIndent) {
|
||||||
|
println(writer, "environment:", currentIndent);
|
||||||
|
for (Map.Entry<String, String> env : this.environment.entrySet()) {
|
||||||
|
println(writer, "- '%s=%s'".formatted(env.getKey(), env.getValue()), currentIndent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void println(PrintWriter writer, String value, int indentation) {
|
||||||
|
writer.write(INDENT.repeat(indentation));
|
||||||
|
writer.println(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new {@link Builder} with the given image. The name is automatically
|
||||||
|
* deduced.
|
||||||
|
* @param image the image
|
||||||
|
* @param tag the image tag
|
||||||
|
* @return a new builder
|
||||||
|
*/
|
||||||
|
public static Builder withImage(String image, String tag) {
|
||||||
|
// See https://github.com/docker/compose/pull/1624
|
||||||
|
String name = image.replaceAll("[^a-zA-Z0-9._\\-]", "_");
|
||||||
|
return new Builder(name, image, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new {@link Builder} with the given image. The name is automatically
|
||||||
|
* deduced.
|
||||||
|
* @param imageAndTag the image and tag in the format {@code image:tag}
|
||||||
|
* @return a new builder
|
||||||
|
*/
|
||||||
|
public static Builder withImage(String imageAndTag) {
|
||||||
|
String[] split = imageAndTag.split(":", 2);
|
||||||
|
if (split.length == 1) {
|
||||||
|
return withImage(split[0], "latest");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return withImage(split[0], split[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a {@link Builder} with the given service.
|
||||||
|
* @param service the service to initialize from
|
||||||
|
* @return a new builder
|
||||||
|
*/
|
||||||
|
public static Builder from(DockerComposeService service) {
|
||||||
|
return new Builder(service.name, service.image, service.imageTag).imageWebsite(service.imageWebsite)
|
||||||
|
.environment(service.environment)
|
||||||
|
.ports(service.ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for {@link DockerComposeService}.
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String image;
|
||||||
|
|
||||||
|
private String imageTag;
|
||||||
|
|
||||||
|
private String imageWebsite;
|
||||||
|
|
||||||
|
private final Map<String, String> environment = new TreeMap<>();
|
||||||
|
|
||||||
|
private final Set<Integer> ports = new TreeSet<>();
|
||||||
|
|
||||||
|
private Builder(String name, String image, String imageTag) {
|
||||||
|
this.name = name;
|
||||||
|
this.image = image;
|
||||||
|
this.imageTag = imageTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder image(String image) {
|
||||||
|
this.image = image;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder imageTag(String imageTag) {
|
||||||
|
this.imageTag = imageTag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder imageWebsite(String imageWebsite) {
|
||||||
|
this.imageWebsite = imageWebsite;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder environment(String key, String value) {
|
||||||
|
this.environment.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder environment(Map<String, String> environment) {
|
||||||
|
this.environment.putAll(environment);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ports(Collection<Integer> ports) {
|
||||||
|
this.ports.addAll(ports);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ports(int... ports) {
|
||||||
|
return ports(Arrays.stream(ports).boxed().toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the {@link DockerComposeService} instance.
|
||||||
|
* @return the built instance
|
||||||
|
*/
|
||||||
|
public DockerComposeService build() {
|
||||||
|
return new DockerComposeService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for Markdown.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
final class Markdown {
|
||||||
|
|
||||||
|
private Markdown() {
|
||||||
|
// Static class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given string as code.
|
||||||
|
* @param code the input string
|
||||||
|
* @return string formatted as code
|
||||||
|
*/
|
||||||
|
static String code(String code) {
|
||||||
|
return "`%s`".formatted(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Markdown link.
|
||||||
|
* @param text text of the link
|
||||||
|
* @param url url of the link
|
||||||
|
* @return the formatted link in Markdown
|
||||||
|
*/
|
||||||
|
static String link(String text, String url) {
|
||||||
|
return "[%s](%s)".formatted(text, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Markdown table.
|
||||||
|
* @param headerCaptions captions of the header
|
||||||
|
* @return the Markdown table
|
||||||
|
*/
|
||||||
|
static MarkdownTable table(String... headerCaptions) {
|
||||||
|
return new MarkdownTable(headerCaptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Markdown table.
|
||||||
|
* <p>
|
||||||
|
* The formatted table is pretty-printed, all the columns are padded with spaces to
|
||||||
|
* have a consistent look.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
static class MarkdownTable {
|
||||||
|
|
||||||
|
private final List<String> headerCaptions;
|
||||||
|
|
||||||
|
private final List<List<String>> rows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new table with the given header captions.
|
||||||
|
* @param headerCaptions the header captions
|
||||||
|
*/
|
||||||
|
MarkdownTable(String... headerCaptions) {
|
||||||
|
this.headerCaptions = List.of(headerCaptions);
|
||||||
|
this.rows = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new row with the given cells.
|
||||||
|
* @param cells the cells to add
|
||||||
|
* @throws IllegalArgumentException if the cell size doesn't match the number of
|
||||||
|
* header captions
|
||||||
|
*/
|
||||||
|
void addRow(String... cells) {
|
||||||
|
if (cells.length != this.headerCaptions.size()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Expected %d cells, got %d".formatted(this.headerCaptions.size(), cells.length));
|
||||||
|
}
|
||||||
|
this.rows.add(List.of(cells));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the whole table as Markdown.
|
||||||
|
* @return the table formatted as Markdown.
|
||||||
|
*/
|
||||||
|
String toMarkdown() {
|
||||||
|
int[] columnMaxLengths = calculateMaxColumnLengths();
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
writeHeader(result, columnMaxLengths);
|
||||||
|
writeHeaderSeparator(result, columnMaxLengths);
|
||||||
|
writeRows(result, columnMaxLengths);
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeader(StringBuilder result, int[] columnMaxLengths) {
|
||||||
|
for (int i = 0; i < this.headerCaptions.size(); i++) {
|
||||||
|
result.append((i > 0) ? " " : "| ")
|
||||||
|
.append(pad(this.headerCaptions.get(i), columnMaxLengths[i]))
|
||||||
|
.append(" |");
|
||||||
|
}
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeaderSeparator(StringBuilder result, int[] columnMaxLengths) {
|
||||||
|
for (int i = 0; i < this.headerCaptions.size(); i++) {
|
||||||
|
result.append((i > 0) ? " " : "| ").append("-".repeat(columnMaxLengths[i])).append(" |");
|
||||||
|
}
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRows(StringBuilder result, int[] columnMaxLengths) {
|
||||||
|
for (List<String> row : this.rows) {
|
||||||
|
for (int i = 0; i < row.size(); i++) {
|
||||||
|
result.append((i > 0) ? " " : "| ").append(pad(row.get(i), columnMaxLengths[i])).append(" |");
|
||||||
|
}
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] calculateMaxColumnLengths() {
|
||||||
|
int[] columnMaxLengths = new int[this.headerCaptions.size()];
|
||||||
|
for (int i = 0; i < this.headerCaptions.size(); i++) {
|
||||||
|
columnMaxLengths[i] = this.headerCaptions.get(i).length();
|
||||||
|
}
|
||||||
|
for (List<String> row : this.rows) {
|
||||||
|
for (int i = 0; i < row.size(); i++) {
|
||||||
|
String cell = row.get(i);
|
||||||
|
if (cell.length() > columnMaxLengths[i]) {
|
||||||
|
columnMaxLengths[i] = cell.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columnMaxLengths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String pad(String input, int length) {
|
||||||
|
return input + " ".repeat(length - input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support for Docker Compose.
|
||||||
|
*/
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
@@ -0,0 +1,16 @@
|
|||||||
|
### Docker Compose support
|
||||||
|
|
||||||
|
This project contains a Docker Compose file named `compose.yaml`.
|
||||||
|
|
||||||
|
{{#serviceTable}}
|
||||||
|
In this file, the following services have been defined:
|
||||||
|
|
||||||
|
{{serviceTable}}
|
||||||
|
|
||||||
|
Please review the tags of the used images and set them to the same as you're running in production.
|
||||||
|
{{/serviceTable}}
|
||||||
|
{{^serviceTable}}
|
||||||
|
However, no services were found. As of now, the application won't start!
|
||||||
|
|
||||||
|
Please make sure to add at least one service in the `compose.yaml` file.
|
||||||
|
{{/serviceTable}}
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerComposeFile}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeFileTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void write() {
|
||||||
|
DockerComposeFile file = new DockerComposeFile();
|
||||||
|
file.addService(DockerComposeServiceHelper.service(1));
|
||||||
|
file.addService(DockerComposeServiceHelper.service(2));
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
file.write(new PrintWriter(writer));
|
||||||
|
assertThat(writer.toString()).isEqualToIgnoringNewLines("""
|
||||||
|
services:
|
||||||
|
service-1:
|
||||||
|
image: 'image-1:image-tag-1'
|
||||||
|
service-2:
|
||||||
|
image: 'image-2:image-tag-2'
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void servicesAreOrderedByName() {
|
||||||
|
DockerComposeFile file = new DockerComposeFile();
|
||||||
|
file.addService(DockerComposeServiceHelper.service(2));
|
||||||
|
file.addService(DockerComposeServiceHelper.service(1));
|
||||||
|
assertThat(file.getServices()).containsExactly(DockerComposeServiceHelper.service(1),
|
||||||
|
DockerComposeServiceHelper.service(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.io.template.MustacheTemplateRenderer;
|
||||||
|
import io.spring.initializr.generator.io.text.MustacheSection;
|
||||||
|
import io.spring.initializr.generator.io.text.Section;
|
||||||
|
import io.spring.initializr.generator.spring.documentation.HelpDocument;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerComposeHelpDocumentCustomizer}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeHelpDocumentCustomizerTests {
|
||||||
|
|
||||||
|
private DockerComposeHelpDocumentCustomizer customizer;
|
||||||
|
|
||||||
|
private DockerComposeFile dockerComposeFile;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
this.dockerComposeFile = new DockerComposeFile();
|
||||||
|
this.customizer = new DockerComposeHelpDocumentCustomizer(this.dockerComposeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addsDockerComposeSection() throws IOException {
|
||||||
|
this.dockerComposeFile.addService(DockerComposeServiceHelper.service());
|
||||||
|
HelpDocument helpDocument = helpDocument();
|
||||||
|
this.customizer.customize(helpDocument);
|
||||||
|
assertThat(helpDocument.getSections()).hasSize(1);
|
||||||
|
Section section = helpDocument.getSections().get(0);
|
||||||
|
assertThat(section).isInstanceOf(MustacheSection.class);
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
helpDocument.write(new PrintWriter(stringWriter));
|
||||||
|
assertThat(stringWriter.toString()).isEqualToIgnoringNewLines("""
|
||||||
|
### Docker Compose support
|
||||||
|
|
||||||
|
This project contains a Docker Compose file named `compose.yaml`.
|
||||||
|
|
||||||
|
In this file, the following services have been defined:
|
||||||
|
|
||||||
|
| Service name | Image | Tag | Website |
|
||||||
|
| ------------ | --------- | ------------- | --------------------------------- |
|
||||||
|
| service-1 | `image-1` | `image-tag-1` | [Website](https://service-1.org/) |
|
||||||
|
|
||||||
|
|
||||||
|
Please review the tags of the used images and set them to the same as you're running in production.""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addsWarningIfNoServicesAreDefined() throws IOException {
|
||||||
|
HelpDocument helpDocument = helpDocument();
|
||||||
|
this.customizer.customize(helpDocument);
|
||||||
|
assertThat(helpDocument.getWarnings().getItems()).containsExactly(
|
||||||
|
"No Docker Compose services found. As of now, the application won't start! Please add at least one service to the `compose.yaml` file.");
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
helpDocument.write(new PrintWriter(stringWriter));
|
||||||
|
assertThat(stringWriter.toString()).isEqualToIgnoringNewLines(
|
||||||
|
"""
|
||||||
|
# Read Me First
|
||||||
|
The following was discovered as part of building this project:
|
||||||
|
|
||||||
|
* No Docker Compose services found. As of now, the application won't start! Please add at least one service to the `compose.yaml` file.
|
||||||
|
|
||||||
|
### Docker Compose support
|
||||||
|
|
||||||
|
This project contains a Docker Compose file named `compose.yaml`.
|
||||||
|
|
||||||
|
However, no services were found. As of now, the application won't start!
|
||||||
|
|
||||||
|
Please make sure to add at least one service in the `compose.yaml` file.""");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HelpDocument helpDocument() {
|
||||||
|
return new HelpDocument(new MustacheTemplateRenderer("/templates"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerComposeProjectContributor}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeProjectContributorTests {
|
||||||
|
|
||||||
|
private DockerComposeProjectContributor contributor;
|
||||||
|
|
||||||
|
private DockerComposeFile dockerComposeFile;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
this.dockerComposeFile = new DockerComposeFile();
|
||||||
|
this.contributor = new DockerComposeProjectContributor(this.dockerComposeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writesComposeYamlFile(@TempDir Path tempDir) throws IOException {
|
||||||
|
this.dockerComposeFile.addService(DockerComposeServiceHelper.service());
|
||||||
|
this.contributor.contribute(tempDir);
|
||||||
|
assertThat(tempDir.resolve("compose.yaml")).content(StandardCharsets.UTF_8).startsWith("services:");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerComposeProjectGenerationConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeProjectGenerationConfigurationTests {
|
||||||
|
|
||||||
|
private final ApplicationContextRunner runner = new ApplicationContextRunner()
|
||||||
|
.withUserConfiguration(DockerComposeProjectGenerationConfiguration.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void providesBeans() {
|
||||||
|
this.runner.run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(DockerComposeFile.class);
|
||||||
|
assertThat(context).hasSingleBean(DockerComposeProjectContributor.class);
|
||||||
|
assertThat(context).hasSingleBean(DockerComposeHelpDocumentCustomizer.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void callsCustomizers() {
|
||||||
|
DockerComposeService service = DockerComposeServiceHelper.service(3);
|
||||||
|
DockerComposeFileCustomizer customizer = (composeFile) -> composeFile.addService(service);
|
||||||
|
this.runner.withBean(DockerComposeFileCustomizer.class, () -> customizer).run((context) -> {
|
||||||
|
DockerComposeFile composeFile = context.getBean(DockerComposeFile.class);
|
||||||
|
assertThat(composeFile.getServices()).containsExactly(service);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class Services {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DockerComposeService service1() {
|
||||||
|
return DockerComposeServiceHelper.service(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
DockerComposeService service2() {
|
||||||
|
return DockerComposeServiceHelper.service(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for {@link DockerComposeService}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
final class DockerComposeServiceHelper {
|
||||||
|
|
||||||
|
private DockerComposeServiceHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link DockerComposeService}.
|
||||||
|
* @return a new {@link DockerComposeService}
|
||||||
|
*/
|
||||||
|
static DockerComposeService service() {
|
||||||
|
return service(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link DockerComposeService} with the given suffix.
|
||||||
|
* @param suffix the suffix
|
||||||
|
* @return a new {@link DockerComposeService}
|
||||||
|
*/
|
||||||
|
static DockerComposeService service(int suffix) {
|
||||||
|
return DockerComposeService.withImage("image-" + suffix, "image-tag-" + suffix)
|
||||||
|
.name("service-" + suffix)
|
||||||
|
.imageWebsite("https://service-" + suffix + ".org/")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DockerComposeService}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class DockerComposeServiceTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldWriteToPrinter() {
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("elasticsearch:8.6.1")
|
||||||
|
.imageWebsite("https://www.docker.elastic.co/r/elasticsearch")
|
||||||
|
.environment("ELASTIC_PASSWORD", "secret")
|
||||||
|
.environment("discovery.type", "single-node")
|
||||||
|
.ports(9200, 9300)
|
||||||
|
.build();
|
||||||
|
service.write(new PrintWriter(stringWriter), 0);
|
||||||
|
String content = stringWriter.toString();
|
||||||
|
assertThat(content).isEqualToIgnoringNewLines("""
|
||||||
|
elasticsearch:
|
||||||
|
image: 'elasticsearch:8.6.1'
|
||||||
|
environment:
|
||||||
|
- 'ELASTIC_PASSWORD=secret'
|
||||||
|
- 'discovery.type=single-node'
|
||||||
|
ports:
|
||||||
|
- '9200'
|
||||||
|
- '9300'
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nameIsDeduced() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("elasticsearch:8.6.1").build();
|
||||||
|
assertThat(service.getName()).isEqualTo("elasticsearch");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void tagIsSetToLatestIfNotGiven() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("redis").build();
|
||||||
|
assertThat(service.getImage()).isEqualTo("redis");
|
||||||
|
assertThat(service.getImageTag()).isEqualTo("latest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removesIllegalCharsFromDecudedName() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("SOME._-name<>;|\uD83C\uDF31").build();
|
||||||
|
assertThat(service.getName()).isEqualTo("SOME._-name_____");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void portsAreSorted() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("redis").ports(5, 3, 4, 2, 1).build();
|
||||||
|
assertThat(service.getPorts()).containsExactly(1, 2, 3, 4, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void environmentIsSorted() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("redis")
|
||||||
|
.environment("z", "zz")
|
||||||
|
.environment("a", "aa")
|
||||||
|
.build();
|
||||||
|
assertThat(service.getEnvironment()).containsExactly(entry("a", "aa"), entry("z", "zz"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void builderFrom() {
|
||||||
|
DockerComposeService service = DockerComposeService.withImage("elasticsearch", "8.6.1")
|
||||||
|
.imageWebsite("https://hub.docker.com/_/redis")
|
||||||
|
.environment(Map.of("some", "value"))
|
||||||
|
.ports(6379)
|
||||||
|
.build();
|
||||||
|
DockerComposeService service2 = DockerComposeService.from(service).build();
|
||||||
|
assertThat(service).isEqualTo(service2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void equalsAndHashcode() {
|
||||||
|
DockerComposeService service1 = DockerComposeService.withImage("redis").build();
|
||||||
|
DockerComposeService service2 = DockerComposeService.withImage("elasticsearch:8.6.1").build();
|
||||||
|
DockerComposeService service3 = DockerComposeService.withImage("redis").build();
|
||||||
|
assertThat(service1).isEqualTo(service3);
|
||||||
|
assertThat(service1).hasSameHashCodeAs(service3);
|
||||||
|
assertThat(service3).isEqualTo(service1);
|
||||||
|
assertThat(service3).hasSameHashCodeAs(service1);
|
||||||
|
assertThat(service1).isNotEqualTo(service2);
|
||||||
|
assertThat(service2).isNotEqualTo(service1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import io.spring.initializr.generator.spring.container.dockercompose.Markdown.MarkdownTable;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link MarkdownTable}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class MarkdownTableTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFormatCorrectly() {
|
||||||
|
MarkdownTable table = new MarkdownTable("a", "b1", "c22", "d333");
|
||||||
|
table.addRow("0", "1", "2", "3");
|
||||||
|
table.addRow("4", "5", "6", "7");
|
||||||
|
String markdown = table.toMarkdown();
|
||||||
|
assertThat(markdown).isEqualToIgnoringNewLines("""
|
||||||
|
| a | b1 | c22 | d333 |
|
||||||
|
| - | -- | --- | ---- |
|
||||||
|
| 0 | 1 | 2 | 3 |
|
||||||
|
| 4 | 5 | 6 | 7 |
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rowIsBiggerThanHeading() {
|
||||||
|
MarkdownTable table = new MarkdownTable("a", "b", "c", "d");
|
||||||
|
table.addRow("0.0", "1.1", "2.2", "3.3");
|
||||||
|
table.addRow("4.4", "5.5", "6.6", "7.7");
|
||||||
|
String markdown = table.toMarkdown();
|
||||||
|
assertThat(markdown).isEqualToIgnoringNewLines("""
|
||||||
|
| a | b | c | d |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 0.0 | 1.1 | 2.2 | 3.3 |
|
||||||
|
| 4.4 | 5.5 | 6.6 | 7.7 |
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void throwsIfCellsDifferFromHeader() {
|
||||||
|
MarkdownTable table = new MarkdownTable("a", "b", "c", "d");
|
||||||
|
assertThatThrownBy(() -> table.addRow("1")).isInstanceOf(IllegalArgumentException.class)
|
||||||
|
.hasMessage("Expected 4 cells, got 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.spring.initializr.generator.spring.container.dockercompose;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Markdown}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class MarkdownTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFormatCode() {
|
||||||
|
String code = Markdown.code("c = a + b");
|
||||||
|
assertThat(code).isEqualTo("`c = a + b`");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldFormatLink() {
|
||||||
|
String link = Markdown.link("Spring Website", "https://spring.io/");
|
||||||
|
assertThat(link).isEqualTo("[Spring Website](https://spring.io/)");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -6,4 +6,6 @@
|
|||||||
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
|
<suppress files=".+Application\.java" checks="HideUtilityClassConstructor"/>
|
||||||
<suppress files="[\\/]initializr-service-sample[\\/]" checks="JavadocType"/>
|
<suppress files="[\\/]initializr-service-sample[\\/]" checks="JavadocType"/>
|
||||||
<suppress files="[\\/]initializr-service-sample[\\/]" checks="ImportControl"/>
|
<suppress files="[\\/]initializr-service-sample[\\/]" checks="ImportControl"/>
|
||||||
|
<suppress files="initializr-generator-spring/src/test/java/io/spring/initializr/generator/spring/container/dockercompose/DockerComposeServiceTests.java" checks="SpringLeadingWhitespace"/>
|
||||||
|
<suppress files="initializr-generator-spring/src/test/java/io/spring/initializr/generator/spring/container/dockercompose/DockerComposeFileTests." checks="SpringLeadingWhitespace"/>
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
Reference in New Issue
Block a user