Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.

Commit 603e28c

Browse files
committed
#172 Sorting roles by dependencies before deploying
1 parent 232b2a7 commit 603e28c

File tree

14 files changed

+242
-16
lines changed

14 files changed

+242
-16
lines changed

src/main/java/com/marklogic/appdeployer/AppConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ public class AppConfig {
111111
// Controls whether replicas are deleted or not when undeploying a database
112112
private boolean deleteReplicas = true;
113113

114+
private boolean sortRolesByDependencies = true;
115+
114116
// As defined by the REST API
115117
private String modulePermissions = "rest-admin,read,rest-admin,update,rest-extension-user,execute";
116118

@@ -801,4 +803,12 @@ public String getAppServicesPassword() {
801803
public void setAppServicesPassword(String appServicesPassword) {
802804
this.appServicesPassword = appServicesPassword;
803805
}
806+
807+
public boolean isSortRolesByDependencies() {
808+
return sortRolesByDependencies;
809+
}
810+
811+
public void setSortRolesByDependencies(boolean sortRolesByDependencies) {
812+
this.sortRolesByDependencies = sortRolesByDependencies;
813+
}
804814
}

src/main/java/com/marklogic/appdeployer/ConfigDir.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ public File getSecurityDir() {
7676
return new File(baseDir, "security");
7777
}
7878

79+
public File getRolesDir() {
80+
return new File(getSecurityDir(), "roles");
81+
}
82+
7983
public File getServersDir() {
8084
return new File(baseDir, "servers");
8185
}

src/main/java/com/marklogic/appdeployer/DefaultAppConfigFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,16 @@ public AppConfig newAppConfig() {
420420
c.setResourceFilenamesToIgnore(values);
421421
}
422422

423+
/**
424+
* Version 2.9.0 of ml-app-deployer, by default, sorts role files by reading each file and looking at the role
425+
* dependencies. You can disable this behavior by setting this property to false.
426+
*/
427+
prop = getProperty("mlSortRolesByDependencies");
428+
if (prop != null) {
429+
logger.info("Sort roles by dependencies: " + prop);
430+
c.setSortRolesByDependencies(Boolean.parseBoolean(prop));
431+
}
432+
423433
return c;
424434
}
425435

src/main/java/com/marklogic/appdeployer/command/AbstractResourceCommand.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected void processExecuteOnResourceDir(CommandContext context, File resource
3939
if (logger.isInfoEnabled()) {
4040
logger.info("Processing files in directory: " + resourceDir.getAbsolutePath());
4141
}
42-
for (File f : listFilesInDirectory(resourceDir)) {
42+
for (File f : listFilesInDirectory(resourceDir, context)) {
4343
if (logger.isInfoEnabled()) {
4444
logger.info("Processing file: " + f.getAbsolutePath());
4545
}
@@ -49,6 +49,19 @@ protected void processExecuteOnResourceDir(CommandContext context, File resource
4949
}
5050
}
5151

52+
/**
53+
* Defaults to the parent method. This was extracted so that a subclass can override it and have access to the
54+
* CommandContext, which allows for reading in the contents of each file and replacing tokens, which may impact the
55+
* order in which the files are processed.
56+
*
57+
* @param resourceDir
58+
* @param context
59+
* @return
60+
*/
61+
protected File[] listFilesInDirectory(File resourceDir, CommandContext context) {
62+
return listFilesInDirectory(resourceDir);
63+
}
64+
5265
/**
5366
* Subclasses can override this to add functionality after a resource has been saved.
5467
*
Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,102 @@
11
package com.marklogic.appdeployer.command.security;
22

3-
import java.io.File;
4-
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.node.ArrayNode;
55
import com.marklogic.appdeployer.command.AbstractResourceCommand;
66
import com.marklogic.appdeployer.command.CommandContext;
77
import com.marklogic.appdeployer.command.SortOrderConstants;
8+
import com.marklogic.mgmt.PayloadParser;
89
import com.marklogic.mgmt.ResourceManager;
10+
import com.marklogic.mgmt.api.security.Role;
911
import com.marklogic.mgmt.security.RoleManager;
12+
import com.marklogic.rest.util.Fragment;
13+
14+
import java.io.File;
15+
import java.util.*;
1016

1117
public class DeployRolesCommand extends AbstractResourceCommand {
1218

13-
public DeployRolesCommand() {
14-
setExecuteSortOrder(SortOrderConstants.DEPLOY_ROLES);
15-
setUndoSortOrder(SortOrderConstants.DELETE_ROLES);
16-
}
19+
public DeployRolesCommand() {
20+
setExecuteSortOrder(SortOrderConstants.DEPLOY_ROLES);
21+
setUndoSortOrder(SortOrderConstants.DELETE_ROLES);
22+
}
23+
24+
/**
25+
* Overriding this so we can list the files based on role dependencies.
26+
*
27+
* @param dir
28+
* @return
29+
*/
30+
@Override
31+
protected File[] listFilesInDirectory(File dir, CommandContext context) {
32+
File[] files = super.listFilesInDirectory(dir);
33+
34+
if (context.getAppConfig().isSortRolesByDependencies()) {
35+
if (logger.isInfoEnabled()) {
36+
logger.info("Sorting role files by role dependencies");
37+
}
38+
List<RoleFile> roleFiles = sortFilesBasedOnRoleDependencies(files, context);
39+
files = new File[files.length];
40+
for (int i = 0; i < roleFiles.size(); i++) {
41+
files[i] = roleFiles.get(i).file;
42+
}
43+
}
44+
45+
return files;
46+
}
47+
48+
protected List<RoleFile> sortFilesBasedOnRoleDependencies(File[] files, CommandContext context) {
49+
List<RoleFile> roleFiles = new ArrayList<>();
50+
PayloadParser parser = new PayloadParser();
51+
for (File f : files) {
52+
RoleFile rf = new RoleFile(f);
53+
String payload = copyFileToString(f, context);
54+
if (parser.isJsonPayload(payload)) {
55+
JsonNode json = parser.parseJson(payload);
56+
rf.role.setRoleName(json.get("role-name").asText());
57+
if (json.has("role")) {
58+
ArrayNode roles = (ArrayNode) json.get("role");
59+
Iterator<JsonNode> iter = roles.elements();
60+
while (iter.hasNext()) {
61+
rf.role.getRole().add(iter.next().asText());
62+
}
63+
}
64+
} else {
65+
Fragment frag = new Fragment(payload);
66+
rf.role.setRoleName(frag.getElementValue("/node()/m:role-name"));
67+
rf.role.setRole(frag.getElementValues("/node()/m:roles/m:role"));
68+
}
69+
roleFiles.add(rf);
70+
}
71+
72+
Collections.sort(roleFiles);
73+
return roleFiles;
74+
}
75+
76+
protected File[] getResourceDirs(CommandContext context) {
77+
return new File[]{context.getAppConfig().getConfigDir().getRolesDir()};
78+
}
79+
80+
@Override
81+
protected ResourceManager getResourceManager(CommandContext context) {
82+
return new RoleManager(context.getManageClient());
83+
}
84+
85+
}
86+
87+
class RoleFile implements Comparable<RoleFile> {
1788

18-
protected File[] getResourceDirs(CommandContext context) {
19-
return new File[] { new File(context.getAppConfig().getConfigDir().getSecurityDir(), "roles") };
20-
}
89+
File file;
90+
Role role;
2191

22-
@Override
23-
protected ResourceManager getResourceManager(CommandContext context) {
24-
return new RoleManager(context.getManageClient());
25-
}
92+
public RoleFile(File file) {
93+
this.file = file;
94+
this.role = new Role();
95+
this.role.setRole(new ArrayList<String>());
96+
}
2697

98+
@Override
99+
public int compareTo(RoleFile o) {
100+
return this.role.compareTo(o.role);
101+
}
27102
}

src/main/java/com/marklogic/mgmt/api/security/Role.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import com.marklogic.mgmt.api.Resource;
99
import com.marklogic.mgmt.security.RoleManager;
1010

11-
public class Role extends Resource {
11+
public class Role extends Resource implements Comparable<Role> {
1212

1313
private String roleName;
1414
private String description;
@@ -27,7 +27,27 @@ public Role(API api, String roleName) {
2727
this.roleName = roleName;
2828
}
2929

30-
public void addExternalName(String name) {
30+
@Override
31+
public int compareTo(Role other) {
32+
if (other == null) {
33+
return 0;
34+
}
35+
if (this.role == null || this.role.isEmpty()) {
36+
return -1;
37+
}
38+
if (other.role == null || other.role.isEmpty()) {
39+
return 1;
40+
}
41+
if (other.role.contains(this.roleName)) {
42+
return -1;
43+
}
44+
if (this.role.contains(other.roleName)) {
45+
return 1;
46+
}
47+
return -1;
48+
}
49+
50+
public void addExternalName(String name) {
3151
if (externalName == null) {
3252
externalName = new ArrayList<>();
3353
}

src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ public void allProperties() {
118118
p.setProperty("mlReplicaForestFastDataDirectory", "/var/fast");
119119
p.setProperty("mlReplicaForestLargeDataDirectory", "/var/large");
120120

121+
p.setProperty("mlSortRolesByDependencies", "false");
122+
121123
sut = new DefaultAppConfigFactory(new SimplePropertySource(p));
122124
AppConfig config = sut.newAppConfig();
123125

@@ -170,6 +172,8 @@ public void allProperties() {
170172
assertEquals("/var/data", config.getReplicaForestDataDirectory());
171173
assertEquals("/var/fast", config.getReplicaForestFastDataDirectory());
172174
assertEquals("/var/large", config.getReplicaForestLargeDataDirectory());
175+
176+
assertFalse(config.isSortRolesByDependencies());
173177
}
174178

175179
/**
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.marklogic.appdeployer.command.security;
2+
3+
import com.marklogic.appdeployer.AbstractAppDeployerTest;
4+
import com.marklogic.appdeployer.command.CommandContext;
5+
import com.marklogic.mgmt.security.RoleManager;
6+
import org.junit.Before;
7+
import org.junit.Test;
8+
9+
import java.io.File;
10+
11+
public class DeployRolesWithDependenciesTest extends AbstractAppDeployerTest {
12+
13+
@Before
14+
public void setup() {
15+
appConfig.getConfigDir().setBaseDir(new File("src/test/resources/sample-app/roles-with-dependencies"));
16+
}
17+
18+
@Test
19+
public void testSorting() {
20+
DeployRolesCommand command = new DeployRolesCommand();
21+
File[] files = command.listFilesInDirectory(appConfig.getConfigDir().getRolesDir(),
22+
new CommandContext(appConfig, manageClient, null));
23+
24+
assertEquals("role3.json", files[0].getName());
25+
assertEquals("role2.xml", files[1].getName());
26+
assertEquals("role1.json", files[2].getName());
27+
assertEquals("role4.xml", files[3].getName());
28+
assertEquals("role5.json", files[4].getName());
29+
assertEquals("role0.json", files[5].getName());
30+
}
31+
32+
@Test
33+
public void test() {
34+
initializeAppDeployer(new DeployRolesCommand());
35+
36+
try {
37+
appDeployer.deploy(appConfig);
38+
39+
RoleManager mgr = new RoleManager(manageClient);
40+
for (int i = 0; i <= 5; i++) {
41+
assertTrue(mgr.exists("sample-app-role" + i));
42+
}
43+
} finally {
44+
undeploySampleApp();
45+
}
46+
}
47+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"role-name": "sample-app-role0",
3+
"role": [
4+
"sample-app-role1",
5+
"sample-app-role2",
6+
"sample-app-role3",
7+
"sample-app-role4",
8+
"sample-app-role5"
9+
]
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"role-name": "sample-app-role1",
3+
"role": [
4+
"sample-app-role2",
5+
"sample-app-role3"
6+
]
7+
}

0 commit comments

Comments
 (0)