Polish "Add support for Docker Compose"

See gh-1417
This commit is contained in:
Stephane Nicoll
2023-05-31 14:39:36 +02:00
parent 8430cccecf
commit 9995d6a2e9
26 changed files with 807 additions and 856 deletions

View File

@@ -0,0 +1,63 @@
/*
* 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.docker.compose;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import io.spring.initializr.generator.container.docker.compose.ComposeFile;
import io.spring.initializr.generator.container.docker.compose.ComposeFileWriter;
import io.spring.initializr.generator.io.IndentingWriter;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.project.contributor.ProjectContributor;
/**
* A {@link ProjectContributor} that creates a 'compose.yaml' file through a
* {@link ComposeFile}.
*
* @author Moritz Halbritter
* @author Stephane Nicoll
*/
public class ComposeProjectContributor implements ProjectContributor {
private final ComposeFile composeFile;
private final IndentingWriterFactory indentingWriterFactory;
private final ComposeFileWriter composeFileWriter;
public ComposeProjectContributor(ComposeFile composeFile, IndentingWriterFactory indentingWriterFactory) {
this.composeFile = composeFile;
this.indentingWriterFactory = indentingWriterFactory;
this.composeFileWriter = new ComposeFileWriter();
}
@Override
public void contribute(Path projectRoot) throws IOException {
Path file = Files.createFile(projectRoot.resolve("compose.yaml"));
writeComposeFile(Files.newBufferedWriter(file));
}
void writeComposeFile(Writer out) throws IOException {
try (IndentingWriter writer = this.indentingWriterFactory.createIndentingWriter("yaml", out)) {
this.composeFileWriter.writeTo(writer, this.composeFile);
}
}
}

View File

@@ -14,35 +14,35 @@
* limitations under the License.
*/
package io.spring.initializr.generator.spring.container.dockercompose;
package io.spring.initializr.generator.spring.container.docker.compose;
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.container.docker.compose.ComposeFile;
import io.spring.initializr.generator.container.docker.compose.ComposeService;
import io.spring.initializr.generator.spring.container.docker.compose.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.
* A {@link HelpDocumentCustomizer} that provide additional information about the
* {@link ComposeService compose services} that are defined for the project.
*
* @author Moritz Halbritter
*/
class DockerComposeHelpDocumentCustomizer implements HelpDocumentCustomizer {
public class DockerComposeHelpDocumentCustomizer implements HelpDocumentCustomizer {
private final DockerComposeFile composeFile;
private final ComposeFile composeFile;
DockerComposeHelpDocumentCustomizer(DockerComposeFile composeFile) {
public DockerComposeHelpDocumentCustomizer(ComposeFile 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()) {
if (this.composeFile.services().isEmpty()) {
model.put("serviceTable", null);
document.getWarnings()
.addItem(
@@ -50,10 +50,10 @@ class DockerComposeHelpDocumentCustomizer implements HelpDocumentCustomizer {
}
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()));
}
this.composeFile.services()
.values()
.forEach((service) -> 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);

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.generator.spring.container.dockercompose;
package io.spring.initializr.generator.spring.container.docker.compose;
import java.util.ArrayList;
import java.util.List;

View File

@@ -17,4 +17,4 @@
/**
* Support for Docker Compose.
*/
package io.spring.initializr.generator.spring.container.dockercompose;
package io.spring.initializr.generator.spring.container.docker.compose;

View File

@@ -1,70 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -1,42 +0,0 @@
/*
* 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;
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,254 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.docker.compose;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Path;
import io.spring.initializr.generator.container.docker.compose.ComposeFile;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.io.SimpleIndentStrategy;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ComposeProjectContributor}.
*
* @author Moritz Halbritter
* @author Stephane Nicoll
*/
class ComposeProjectContributorTests {
@Test
void composeFileIsContributedInProjectStructure(@TempDir Path projectDir) throws IOException {
ComposeFile compose = new ComposeFile();
compose.services().add("test", (service) -> service.image("my-image:1.2.3"));
new ComposeProjectContributor(compose, IndentingWriterFactory.withDefaultSettings()).contribute(projectDir);
Path composeFile = projectDir.resolve("compose.yaml");
assertThat(composeFile).isRegularFile();
}
@Test
void composeFileIsContributedUsingYamlContentId() throws IOException {
IndentingWriterFactory indentingWriterFactory = IndentingWriterFactory.create(new SimpleIndentStrategy(" "),
(factory) -> factory.indentingStrategy("yaml", new SimpleIndentStrategy("\t")));
ComposeFile composeFile = new ComposeFile();
composeFile.services()
.add("test", (service) -> service.imageAndTag("image:1.3.3").environment("a", "aa").environment("b", "bb"));
assertThat(generateComposeFile(composeFile, indentingWriterFactory)).isEqualToIgnoringNewLines("""
services:
test:
image: 'image:1.3.3'
environment:
- 'a=aa'
- 'b=bb'
""");
}
private String generateComposeFile(ComposeFile composeFile, IndentingWriterFactory indentingWriterFactory)
throws IOException {
StringWriter writer = new StringWriter();
new ComposeProjectContributor(composeFile, indentingWriterFactory).writeComposeFile(writer);
return writer.toString();
}
}

View File

@@ -14,12 +14,13 @@
* limitations under the License.
*/
package io.spring.initializr.generator.spring.container.dockercompose;
package io.spring.initializr.generator.spring.container.docker.compose;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import io.spring.initializr.generator.container.docker.compose.ComposeFile;
import io.spring.initializr.generator.io.template.MustacheTemplateRenderer;
import io.spring.initializr.generator.io.text.MustacheSection;
import io.spring.initializr.generator.io.text.Section;
@@ -38,17 +39,18 @@ class DockerComposeHelpDocumentCustomizerTests {
private DockerComposeHelpDocumentCustomizer customizer;
private DockerComposeFile dockerComposeFile;
private ComposeFile dockerComposeFile;
@BeforeEach
void setUp() {
this.dockerComposeFile = new DockerComposeFile();
this.dockerComposeFile = new ComposeFile();
this.customizer = new DockerComposeHelpDocumentCustomizer(this.dockerComposeFile);
}
@Test
void addsDockerComposeSection() throws IOException {
this.dockerComposeFile.addService(DockerComposeServiceHelper.service());
this.dockerComposeFile.services()
.add("test", (service) -> service.imageAndTag("image-1:1.2.3").imageWebsite("https:/example.com/image-1"));
HelpDocument helpDocument = helpDocument();
this.customizer.customize(helpDocument);
assertThat(helpDocument.getSections()).hasSize(1);
@@ -63,9 +65,9 @@ class DockerComposeHelpDocumentCustomizerTests {
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/) |
| Service name | Image | Tag | Website |
| ------------ | --------- | ------- | ------------------------------------- |
| test | `image-1` | `1.2.3` | [Website](https:/example.com/image-1) |
Please review the tags of the used images and set them to the same as you're running in production.""");

View File

@@ -14,20 +14,32 @@
* limitations under the License.
*/
package io.spring.initializr.generator.spring.container.dockercompose;
package io.spring.initializr.generator.spring.container.docker.compose;
import io.spring.initializr.generator.spring.container.dockercompose.Markdown.MarkdownTable;
import io.spring.initializr.generator.spring.container.docker.compose.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}.
* Tests for {@link Markdown}.
*
* @author Moritz Halbritter
*/
class MarkdownTableTests {
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/)");
}
@Test
void shouldFormatCorrectly() {

View File

@@ -1,58 +0,0 @@
/*
* 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));
}
}

View File

@@ -1,53 +0,0 @@
/*
* 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:");
}
}

View File

@@ -1,71 +0,0 @@
/*
* 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);
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* 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();
}
}

View File

@@ -1,116 +0,0 @@
/*
* 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);
}
}

View File

@@ -1,42 +0,0 @@
/*
* 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/)");
}
}