Skip to content

Commit 080fdff

Browse files
committed
support multiple cloud manager environments with optional cloudManager.target assignment
1 parent 5a46804 commit 080fdff

8 files changed

Lines changed: 249 additions & 143 deletions

File tree

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/AbstractCloudManagerMojo.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@
4040
abstract class AbstractCloudManagerMojo extends AbstractMojo {
4141

4242
/**
43-
* Selected environments to generate. It's only allowed to define a single environment for this mojo,
44-
* but to be compatible with the other CONGA plugins it's uses the same semantic for defining multiple
45-
* environments.
43+
* Selected environments to generate.
4644
*/
4745
@Parameter(property = "conga.environments")
4846
private String[] environments;
@@ -77,7 +75,7 @@ protected File getTargetDir() {
7775
* @return Environment directory
7876
* @throws MojoExecutionException if no or multiple directories found
7977
*/
80-
protected File getEnvironmentDir() throws MojoExecutionException {
78+
protected List<File> getEnvironmentDir() throws MojoExecutionException {
8179
List<File> directories = null;
8280
Set<String> selectedEnvironments = toSet(this.environments);
8381
if (configurationDir.exists() && configurationDir.isDirectory()) {
@@ -92,11 +90,7 @@ protected File getEnvironmentDir() throws MojoExecutionException {
9290
if (directories == null || directories.isEmpty()) {
9391
throw new MojoExecutionException("No matching environment directory found in " + getCanonicalPath(configurationDir));
9492
}
95-
if (directories.size() > 1) {
96-
throw new MojoExecutionException("Multiple environments found in " + getCanonicalPath(configurationDir)
97-
+ " - please specify a single environment via the 'environments' parameter.");
98-
}
99-
return directories.get(0);
93+
return directories;
10094
}
10195

10296
/**

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/CloudManagerAllPackageMojo.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.File;
2525
import java.io.IOException;
2626
import java.util.List;
27+
import java.util.Set;
2728

2829
import org.apache.maven.plugin.MojoExecutionException;
2930
import org.apache.maven.plugin.MojoFailureException;
@@ -71,23 +72,31 @@ public final class CloudManagerAllPackageMojo extends AbstractCloudManagerMojo {
7172
@Parameter(property = "conga.cloudManager.allPackage.skip", defaultValue = "false")
7273
private boolean skip;
7374

75+
private static final String CLOUDMANAGER_TARGET_NONE = "none";
76+
7477
@Override
7578
public void execute() throws MojoExecutionException, MojoFailureException {
7679
if (skip) {
7780
return;
7881
}
7982

80-
File environmentDir = getEnvironmentDir();
81-
List<File> nodeDirs = getNodeDirs(environmentDir);
82-
ModelParser modelParser = new ModelParser();
83-
for (File nodeDir : nodeDirs) {
84-
buildAllPackage(nodeDir, modelParser);
83+
List<File> environmentDirs = getEnvironmentDir();
84+
for (File environmentDir : environmentDirs) {
85+
List<File> nodeDirs = getNodeDirs(environmentDir);
86+
ModelParser modelParser = new ModelParser();
87+
for (File nodeDir : nodeDirs) {
88+
Set<String> cloudManagerTarget = modelParser.getCloudManagerTarget(nodeDir);
89+
if (!cloudManagerTarget.contains(CLOUDMANAGER_TARGET_NONE)) {
90+
buildAllPackage(environmentDir, nodeDir, cloudManagerTarget, modelParser);
91+
}
92+
}
8593
}
8694
}
8795

88-
private void buildAllPackage(File nodeDir, ModelParser modelParser) throws MojoFailureException {
96+
private void buildAllPackage(File environmentDir, File nodeDir, Set<String> cloudManagerTarget,
97+
ModelParser modelParser) throws MojoExecutionException {
8998
String groupName = this.group;
90-
String packageName = nodeDir.getName() + "-" + this.name;
99+
String packageName = environmentDir.getName() + "." + nodeDir.getName() + "." + this.name;
91100

92101
List<ContentPackageFile> contentPackages = modelParser.getContentPackagesForNode(nodeDir);
93102
File targetFile = new File(getTargetDir(), packageName + ".zip");
@@ -98,15 +107,15 @@ private void buildAllPackage(File nodeDir, ModelParser modelParser) throws MojoF
98107
.logger(getLog());
99108

100109
try {
101-
if (builder.build(contentPackages)) {
110+
if (builder.build(contentPackages, cloudManagerTarget)) {
102111
getLog().info("Generated " + getCanonicalPath(targetFile));
103112
}
104113
else {
105114
getLog().debug("Skipped " + getCanonicalPath(targetFile) + " - no valid package.");
106115
}
107116
}
108117
catch (IOException ex) {
109-
throw new MojoFailureException("Unable to generate " + getCanonicalPath(targetFile), ex);
118+
throw new MojoExecutionException("Unable to generate " + getCanonicalPath(targetFile), ex);
110119
}
111120
}
112121

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/CloudManagerDispatcherConfigMojo.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,27 @@ public void execute() throws MojoExecutionException, MojoFailureException {
6565
return;
6666
}
6767

68-
File environmentDir = getEnvironmentDir();
69-
List<File> nodeDirs = getNodeDirs(environmentDir);
70-
ModelParser modelParser = new ModelParser();
71-
for (File nodeDir : nodeDirs) {
72-
if (modelParser.hasRole(nodeDir, ROLE_AEM_DISPATCHER_CLOUD)) {
73-
buildDispatcherConfig(nodeDir);
68+
int dispatcherNodeCount = 0;
69+
List<File> environmentDirs = getEnvironmentDir();
70+
for (File environmentDir : environmentDirs) {
71+
List<File> nodeDirs = getNodeDirs(environmentDir);
72+
ModelParser modelParser = new ModelParser();
73+
for (File nodeDir : nodeDirs) {
74+
if (modelParser.hasRole(nodeDir, ROLE_AEM_DISPATCHER_CLOUD)) {
75+
buildDispatcherConfig(environmentDir, nodeDir);
76+
dispatcherNodeCount++;
77+
}
7478
}
7579
}
80+
81+
if (dispatcherNodeCount > 1) {
82+
throw new MojoFailureException("More than one node with role '" + ROLE_AEM_DISPATCHER_CLOUD + "' found - "
83+
+ "AEM Cloud service supports only a single dispatcher configuration.");
84+
}
7685
}
7786

78-
private void buildDispatcherConfig(File nodeDir) throws MojoFailureException {
79-
File targetFile = new File(getTargetDir(), nodeDir.getName() + ".dispatcher-config.zip");
87+
private void buildDispatcherConfig(File environmentDir, File nodeDir) throws MojoExecutionException {
88+
File targetFile = new File(getTargetDir(), environmentDir.getName() + "." + nodeDir.getName() + ".dispatcher-config.zip");
8089

8190
try {
8291
String basePath = toZipDirectoryPath(nodeDir);
@@ -86,7 +95,7 @@ private void buildDispatcherConfig(File nodeDir) throws MojoFailureException {
8695
zipArchiver.createArchive();
8796
}
8897
catch (ArchiverException | IOException ex) {
89-
throw new MojoFailureException("Unable to build file " + targetFile.getPath() + ": " + ex.getMessage(), ex);
98+
throw new MojoExecutionException("Unable to build file " + targetFile.getPath() + ": " + ex.getMessage(), ex);
9099
}
91100
}
92101

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/allpackage/AllPackageBuilder.java

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Enumeration;
3333
import java.util.List;
3434
import java.util.Properties;
35+
import java.util.Set;
3536
import java.util.stream.Collectors;
3637
import java.util.zip.ZipEntry;
3738
import java.util.zip.ZipFile;
@@ -63,6 +64,8 @@ public final class AllPackageBuilder {
6364
private boolean autoDependenciesSeparateMutable;
6465
private Log log;
6566

67+
private static final String RUNMODE_DEFAULT = "$default$";
68+
6669
/**
6770
* @param targetFile Target file
6871
* @param groupName Group name
@@ -112,10 +115,20 @@ private Log getLog() {
112115
/**
113116
* Build "all" content package.
114117
* @param contentPackages Content packages (invalid will be filtered out)
118+
* @param cloudManagerTarget Target environments/run modes the packages should be attached to
115119
* @return true if "all" package was generated, false if not valid package was found.
116120
* @throws IOException I/O exception
117121
*/
118-
public boolean build(List<ContentPackageFile> contentPackages) throws IOException {
122+
public boolean build(List<ContentPackageFile> contentPackages, Set<String> cloudManagerTarget) throws IOException {
123+
124+
// collect list of cloud manager environment run modes
125+
List<String> environmentRunmodes = new ArrayList<>();
126+
if (cloudManagerTarget.isEmpty()) {
127+
environmentRunmodes.add(RUNMODE_DEFAULT);
128+
}
129+
else {
130+
environmentRunmodes.addAll(cloudManagerTarget);
131+
}
119132

120133
// generate warnings for each invalid content packages that is skipped
121134
contentPackages.stream()
@@ -142,27 +155,29 @@ public boolean build(List<ContentPackageFile> contentPackages) throws IOExceptio
142155

143156
// build content package
144157
// if auto dependencies is active: build separate "dependency chains" between mutable and immutable packages
145-
List<ContentPackageFile> previousPackages = new ArrayList<>();
146158
try (ContentPackage contentPackage = builder.build(targetFile)) {
147-
for (ContentPackageFile pkg : validContentPackages) {
148-
String path = buildPackagePath(pkg, rootPath);
149-
150-
// get last previous package
151-
// if autoDependenciesSeparateMutable active only that of the same mutability type
152-
ContentPackageFile previousPkg = previousPackages.stream()
153-
.filter(item -> !autoDependenciesSeparateMutable || mutableMatches(item, pkg))
154-
.reduce((first, second) -> second)
155-
.orElse(null);
156-
157-
if (autoDependencies && previousPkg != null) {
158-
// wire previous package in package dependency
159-
addFileWithDependency(contentPackage, path, pkg, previousPkg);
160-
}
161-
else {
162-
// add package file directly
163-
contentPackage.addFile(path, pkg.getFile());
159+
for (String environmentRunmode : environmentRunmodes) {
160+
List<ContentPackageFile> previousPackages = new ArrayList<>();
161+
for (ContentPackageFile pkg : validContentPackages) {
162+
String path = buildPackagePath(pkg, rootPath, environmentRunmode);
163+
164+
// get last previous package
165+
// if autoDependenciesSeparateMutable active only that of the same mutability type
166+
ContentPackageFile previousPkg = previousPackages.stream()
167+
.filter(item -> !autoDependenciesSeparateMutable || mutableMatches(item, pkg))
168+
.reduce((first, second) -> second)
169+
.orElse(null);
170+
171+
if (autoDependencies && previousPkg != null) {
172+
// wire previous package in package dependency
173+
addFileWithDependency(contentPackage, path, pkg, previousPkg);
174+
}
175+
else {
176+
// add package file directly
177+
contentPackage.addFile(path, pkg.getFile());
178+
}
179+
previousPackages.add(pkg);
164180
}
165-
previousPackages.add(pkg);
166181
}
167182
}
168183

@@ -201,15 +216,18 @@ private static String buildRootPath(String groupName, String packageName) {
201216
* @param rootPath Root path
202217
* @return Package path
203218
*/
204-
private static String buildPackagePath(ContentPackageFile pkg, String rootPath) {
205-
String runModeSuffix = "";
219+
private static String buildPackagePath(ContentPackageFile pkg, String rootPath, String environmentRunmode) {
220+
StringBuilder runModeSuffix = new StringBuilder();
206221
if (RunModeUtil.isOnlyAuthor(pkg)) {
207-
runModeSuffix = "." + RUNMODE_AUTHOR;
222+
runModeSuffix.append(".").append(RUNMODE_AUTHOR);
208223
}
209224
else if (RunModeUtil.isOnlyPublish(pkg)) {
210-
runModeSuffix = "." + RUNMODE_PUBLISH;
225+
runModeSuffix.append(".").append(RUNMODE_PUBLISH);
226+
}
227+
if (!StringUtils.equals(environmentRunmode, RUNMODE_DEFAULT)) {
228+
runModeSuffix.append(".").append(environmentRunmode);
211229
}
212-
return rootPath + "/" + pkg.getPackageType() + "/install" + runModeSuffix + "/" + pkg.getFile().getName();
230+
return rootPath + "/" + pkg.getPackageType() + "/install" + runModeSuffix.toString() + "/" + pkg.getFile().getName();
213231
}
214232

215233
/**

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/model/ModelParser.java

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,16 @@
2828
import java.io.InputStreamReader;
2929
import java.io.Reader;
3030
import java.util.ArrayList;
31+
import java.util.LinkedHashSet;
3132
import java.util.List;
3233
import java.util.Map;
34+
import java.util.Set;
3335

3436
import org.apache.commons.lang3.CharEncoding;
3537
import org.apache.commons.lang3.StringUtils;
3638
import org.yaml.snakeyaml.Yaml;
3739

40+
import io.wcm.devops.conga.model.util.MapExpander;
3841
import io.wcm.devops.conga.plugins.aem.postprocessor.ContentPackagePropertiesPostProcessor;
3942

4043
/**
@@ -66,6 +69,54 @@ public List<ContentPackageFile> getContentPackagesForNode(File nodeDir) {
6669
return collectPackages(data, nodeDir);
6770
}
6871

72+
/**
73+
* Checks if the node has the given node role assigned.
74+
* @param nodeDir Node directory
75+
* @param roleName Node role name
76+
* @return true if role is assigned
77+
*/
78+
@SuppressWarnings("unchecked")
79+
public boolean hasRole(File nodeDir, String roleName) {
80+
Map<String, Object> data = getModelData(nodeDir);
81+
List<Map<String, Object>> roles = (List<Map<String, Object>>)data.get("roles");
82+
for (Map<String, Object> role : roles) {
83+
if (StringUtils.equals((String)role.get("role"), roleName)) {
84+
return true;
85+
}
86+
}
87+
return false;
88+
}
89+
90+
/**
91+
* Collects all assigned "cloudManager.target" values (lists or single values) to any role from the node.
92+
* @param nodeDir Node directory
93+
* @return List of cloud manager environment names or "none"
94+
*/
95+
@SuppressWarnings("unchecked")
96+
public Set<String> getCloudManagerTarget(File nodeDir) {
97+
Set<String> targets = new LinkedHashSet<>();
98+
Map<String, Object> data = getModelData(nodeDir);
99+
List<Map<String, Object>> roles = (List<Map<String, Object>>)data.get("roles");
100+
for (Map<String, Object> role : roles) {
101+
Map<String, Object> config = (Map<String, Object>)role.get("config");
102+
if (config != null) {
103+
Object targetValue = MapExpander.getDeep(config, "cloudManager.target");
104+
if (targetValue != null) {
105+
if (targetValue instanceof String) {
106+
targets.add((String)targetValue);
107+
}
108+
else if (targetValue instanceof List) {
109+
targets.addAll((List<String>)targetValue);
110+
}
111+
else {
112+
throw new RuntimeException("Invalid cloudManager.target value: " + targetValue);
113+
}
114+
}
115+
}
116+
}
117+
return targets;
118+
}
119+
69120
private Map<String, Object> getModelData(File nodeDir) {
70121
File modelFile = new File(nodeDir, MODEL_FILE);
71122
if (!modelFile.exists() || !modelFile.isFile()) {
@@ -113,22 +164,4 @@ private ContentPackageFile toContentPackageFile(Map<String, Object> fileData,
113164
return new ContentPackageFile(file, fileData, roleData);
114165
}
115166

116-
/**
117-
* Checks if the node has the given node role assigned.
118-
* @param nodeDir Node directory
119-
* @param roleName Node role name
120-
* @return true if role is assigned
121-
*/
122-
@SuppressWarnings("unchecked")
123-
public boolean hasRole(File nodeDir, String roleName) {
124-
Map<String, Object> data = getModelData(nodeDir);
125-
List<Map<String, Object>> roles = (List<Map<String, Object>>)data.get("roles");
126-
for (Map<String, Object> role : roles) {
127-
if (StringUtils.equals((String)role.get("role"), roleName)) {
128-
return true;
129-
}
130-
}
131-
return false;
132-
}
133-
134167
}

0 commit comments

Comments
 (0)