Skip to content
This repository was archived by the owner on Sep 16, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion src/main/java/com/marklogic/mgmt/admin/AdminManager.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.marklogic.mgmt.admin;

import java.io.FileOutputStream;
import java.net.URI;

import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
Expand Down Expand Up @@ -126,7 +129,7 @@ public boolean execute() {
* This used to be much more complex - the code first got the latest restart timestamp and then waited for a new
* value. But based on the "delay()" method implementation in marklogic-samplestack, we can just keep catching
* exceptions until the call to get the restart timestamp works.
*
*
* @param action
*/
public void invokeActionRequiringRestart(ActionRequiringRestart action) {
Expand Down Expand Up @@ -215,4 +218,65 @@ public void setWaitForRestartCheckInterval(int waitForRestartCheckInterval) {
public void setWaitForRestartLimit(int waitForRestartLimit) {
this.waitForRestartLimit = waitForRestartLimit;
}


/**
* Part of the steps required to join a cluster. This posts the host config of the host that wants to
* join the cluster, to one of the cluster hosts. The cluster host returns a zip of the cluster config
* for the joining host to use
* @param joiningHostConfig - Output of getServerConfig of the joining host
* @param group - The group in the cluster to join
* @param zone - String that will get stored as the zone (optional)
* @return An array of bytes that represent a zip file of the cluster config
* @throws Exception
*/
public byte[] postJoiningHostConfig(Fragment joiningHostConfig, String group, String zone) throws Exception {

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("group", group);
if(zone != null && !zone.isEmpty()){
map.add("zone", zone);
}
map.add("server-config", joiningHostConfig.getPrettyXml());

HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<MultiValueMap<String, String>>(map, headers);

URI url = adminConfig.buildUri("/admin/v1/cluster-config");
ResponseEntity<byte[]> bytes = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
return bytes.getBody();
}

/**
* Final step of adding a host to a cluster
* Takes the zip file created from calling postJoiningHostConfig, which is the cluster config,
* and posts it to the joining host
* @param clusterConfigZipBytes Array of bytes that represent a zip file of the cluster config
*/
public void postClustConfigToJoiningHost(byte[] clusterConfigZipBytes) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-type", "application/zip");

URI clusterConfigUri = adminConfig.buildUri("/admin/v1/cluster-config");

HttpEntity<Resource> resourceEntity = new HttpEntity<Resource>(new ByteArrayResource(clusterConfigZipBytes), headers);
ResponseEntity<String> response = restTemplate.exchange(clusterConfigUri, HttpMethod.POST, resourceEntity, String.class);
if(response.getStatusCode().value() == 202){
waitForRestart();
}
}

/**
* Instructs the server referred to by this AdminManager to leave the cluster it belongs to.
* Note that once it does so, the server will need to be initialized again
*/
public void leaveCluster() {
ResponseEntity<String> response = restTemplate.exchange(adminConfig.buildUri("/admin/v1/host-config"), HttpMethod.DELETE, null, String.class);
if(response.getStatusCode().value() == 202){
waitForRestart();
}
}

}
65 changes: 65 additions & 0 deletions src/main/java/com/marklogic/mgmt/clusters/ClusterManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.marklogic.mgmt.AbstractManager;
import com.marklogic.mgmt.ManageClient;
import com.marklogic.mgmt.admin.ActionRequiringRestart;
import com.marklogic.mgmt.admin.AdminConfig;
import com.marklogic.mgmt.admin.AdminManager;
import com.marklogic.rest.util.Fragment;

Expand Down Expand Up @@ -35,4 +36,68 @@ public String getVersion() {
Fragment f = manageClient.getXml("/manage/v2");
return f.getElementValue("/c:local-cluster-default/c:version");
}

/**
* Adds a host to the cluster represented by adminManager
* @param adminManager AdminManager for a host already in the cluster
* @param hostname hostname of the joining host
* @param username admin username of the joining host
* @param password admin password of the joining host
* @param group name of the group within the cluster to add the joining host to
* @throws Exception
*/
public void addHost(AdminManager adminManager, String hostname, String username, String password, String group) throws Exception {
addHost(adminManager, hostname, username, password, group, null);
}

/**
* Adds a host to the cluster represented by adminManager
* @param adminManager AdminManager for a host already in the cluster
* @param hostname hostname of the joining host
* @param username admin username of the joining host
* @param password admin password of the joining host
* @param group name of the group within the cluster to add the joining host to
* @param zone zone information for the joining host (optional)
* @throws Exception
*/
public void addHost(AdminManager adminManager, String hostname, String username, String password, String group, String zone) throws Exception{
AdminConfig joiningHostAdminConfig = new AdminConfig(hostname, 8001, username, password);
AdminManager joiningHostAdminManager = new AdminManager(joiningHostAdminConfig);

// get the joining host's configuration
Fragment fragment = joiningHostAdminManager.getServerConfig();

// Make sure host has previously been initialized. If it hasn't, the host-id from the getServerConfig will
// be empty
String hostId = fragment.getElementValue("node()/m:host-id");
if(hostId.isEmpty()){
logger.error("New host [" + hostname + "] has not been initialized. Please initialize the host first.");
return;
}

// Send the joining host's config to the bootstrap host and receive
// the cluster config data needed to complete the join.
byte[] clusterConfigZipBytes = adminManager.postJoiningHostConfig(fragment, group, zone);
if(clusterConfigZipBytes == null){
logger.error("Error sending new host's config to cluster and receiving updated cluster configuration");
return;
}

// Final step of adding a host to the cluster. Post the zipped cluster config to the
// joining host
joiningHostAdminManager.postClustConfigToJoiningHost(clusterConfigZipBytes);

}

/**
* Removes the specified host from the cluster. Assumes connection information is of the host to remove
* @param hostname hostname of the server to remove from the cluster
*/
public void removeHost(String hostname){
AdminConfig adminConfig = new AdminConfig(hostname, 8001, manageClient.getManageConfig().getUsername(), manageClient.getManageConfig().getPassword());
AdminManager adminManager = new AdminManager(adminConfig);
adminManager.leaveCluster();

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.marklogic.mgmt.clusters;

import com.marklogic.mgmt.ManageClient;
import com.marklogic.mgmt.ManageConfig;
import com.marklogic.mgmt.admin.AdminConfig;
import com.marklogic.mgmt.admin.AdminManager;
import com.marklogic.mgmt.hosts.HostManager;
import org.junit.Assert;

/**
* Basic test to test adding a new host to a cluster and then removing it.
*
* Note: The new host must have already been initialized
* Note: After running this test, the new host will have been removed from the cluster and
* will need to be re-initialized before this test can be run again.
*/
public class AddAndRemoveHostDebug {

public static void main(String[] args) throws Exception{
String existingHost = args[0];
String existingHostPassword = args[1];
String joiningHost = args[2];
String joiningHostPassword = args[3];
String group = "Default";
String zone = "Don't let me get in my zone";

ManageConfig manageConfig = new ManageConfig(existingHost, 8002, "admin", existingHostPassword);
ManageClient manageClient = new ManageClient(manageConfig);

HostManager hostManager = new HostManager(manageClient);

Assert.assertEquals("Expecting only one host to start out with", 1, hostManager.getHostIds().size());

// Add joining host to the cluster
ClusterManager clusterManager = new ClusterManager(manageClient);
AdminConfig adminConfig = new AdminConfig(existingHost, 8001, "admin", existingHostPassword);
AdminManager adminManager = new AdminManager(adminConfig);
clusterManager.addHost(adminManager, joiningHost, "admin", joiningHostPassword, "Default");

Assert.assertEquals("Expecting two hosts after the add", 2, hostManager.getHostIds().size());
System.out.println("After adding a host, the cluster now has two hosts");

// Remove joining host from cluster
clusterManager.removeHost(joiningHost);

Assert.assertEquals("Expecting 1 host after removing", 1, hostManager.getHostIds().size());
System.out.println("After removing a host, the cluster now has one host");


}
}