-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Backport 2.6][#8637][PLAT-1672]: Adding APIs to schedule External us…
…er-defined scripts. Summary: - Added APIs to schedule/stop/modify external scripts in Platform - Scripts are defined on the universe level and a universe can only have a single external script scheduled. - These scripts are stored in RunTimeConfig DB in this phase but will be shifted to a more secured DB in the next phase. - These APIs are highly sensitive and can be exploited to affect the Platform, Currently, only ATS will be accessing these APIs. These APIs send data in Multipart/From data format and this API can be accessed using X-AUTH-TOKEN as well as X-AUTH-YW-API-TOKEN. Schedule external script: ``` POST /api/v1/customers/:cUUID/universes/:uniUUID/schedule_script { script: FileUpload, cornExpression: ${cornExpression} scriptParameter: {"param1": "va1l", "param2": "val2"} timeLimitMins: ${timeLimitMins} } ``` example ``` { script: FileUpload, cronExperssion: 5 * * * *, scriptParameter: {"restore_time": "2021-08-08 12:00:00", "platform_version": "2.6.7"} timeLimitMins: 4 } ``` Update existing external scheduled script ``` PUT /api/v1/customers/:cUUID/universes/:uniUUID/update_scheduled_script { script: FileUpload, cornExpression: ${cornExpression} scriptParameter: {"param1": "val1", "param2": "val2"} timeLimitMins: ${timeLimitMins} } ``` stop scheduled external scheduled script ``` PUT /api/v1/customers/:cUUID/universes/:uniUUID/stop_scheduled_script ``` Original commit: D12324 / 717ef62 Test Plan: Jenkins: rebase: 2.6 - Added unit tests. - Tested on a local setup by running self-generated scripts and an infinitely long-running script Reviewers: arnav, hkandala, sanketh Reviewed By: hkandala, sanketh Subscribers: yugaware, jenkins-bot, hsu Differential Revision: https://phabricator.dev.yugabyte.com/D12981
- Loading branch information
Showing
15 changed files
with
762 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
managed/src/main/java/com/yugabyte/yw/commissioner/tasks/ExternalScript.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.yugabyte.yw.commissioner.tasks; | ||
|
||
import com.yugabyte.yw.commissioner.AbstractTaskBase; | ||
import com.yugabyte.yw.commissioner.SubTaskGroup; | ||
import com.yugabyte.yw.commissioner.SubTaskGroupQueue; | ||
import com.yugabyte.yw.commissioner.tasks.subtasks.RunExternalScript; | ||
|
||
public class ExternalScript extends AbstractTaskBase { | ||
|
||
public RunExternalScript.Params params() { | ||
return (RunExternalScript.Params) taskParams; | ||
} | ||
|
||
@Override | ||
public void run() { | ||
createThreadpool(); | ||
try { | ||
SubTaskGroupQueue subTaskGroupQueue = new SubTaskGroupQueue(userTaskUUID); | ||
RunExternalScript task = createTask(RunExternalScript.class); | ||
task.initialize(params()); | ||
SubTaskGroup subTaskGroup = new SubTaskGroup("RunExternalScript", executor); | ||
subTaskGroup.addTask(task); | ||
subTaskGroupQueue.add(subTaskGroup); | ||
subTaskGroupQueue.run(); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} finally { | ||
executor.shutdownNow(); | ||
} | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
managed/src/main/java/com/yugabyte/yw/commissioner/tasks/subtasks/RunExternalScript.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package com.yugabyte.yw.commissioner.tasks.subtasks; | ||
|
||
import static com.yugabyte.yw.controllers.ScheduleScriptController.PLT_EXT_SCRIPT_CONTENT; | ||
import static com.yugabyte.yw.controllers.ScheduleScriptController.PLT_EXT_SCRIPT_PARAM; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.google.inject.Inject; | ||
import com.yugabyte.yw.commissioner.AbstractTaskBase; | ||
import com.yugabyte.yw.common.ShellProcessHandler; | ||
import com.yugabyte.yw.common.ShellResponse; | ||
import com.yugabyte.yw.common.Util; | ||
import com.yugabyte.yw.common.config.impl.RuntimeConfig; | ||
import com.yugabyte.yw.common.config.impl.SettableRuntimeConfigFactory; | ||
import com.yugabyte.yw.forms.AbstractTaskParams; | ||
import com.yugabyte.yw.models.Universe; | ||
import com.yugabyte.yw.models.Users; | ||
import java.nio.charset.Charset; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.OutputStreamWriter; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
public class RunExternalScript extends AbstractTaskBase { | ||
@Inject private ShellProcessHandler shellProcessHandler; | ||
|
||
@Inject private SettableRuntimeConfigFactory sConfigFactory; | ||
|
||
@Inject private play.Configuration appConfig; | ||
|
||
private static final String TEMP_SCRIPT_FILE_NAME = "tempScript_"; | ||
private static final String SCRIPT_DIR = "tmp_external_scripts/"; | ||
private static final String SCRIPT_STORE_DIR = "/tmp_external_scripts"; | ||
|
||
public static class Params extends AbstractTaskParams { | ||
public String platformUrl; | ||
public String timeLimitMins; | ||
public UUID customerUUID; | ||
public UUID universeUUID; | ||
public UUID userUUID; | ||
} | ||
|
||
public Params params() { | ||
return (Params) taskParams; | ||
} | ||
|
||
@Override | ||
public void run() { | ||
File tempScriptFile = null; | ||
try { | ||
Universe universe = Universe.getOrBadRequest(params().universeUUID); | ||
RuntimeConfig<Universe> config = sConfigFactory.forUniverse(universe); | ||
|
||
List<String> keys = Arrays.asList(PLT_EXT_SCRIPT_CONTENT, PLT_EXT_SCRIPT_PARAM); | ||
Map<String, String> configKeysMap = null; | ||
try { | ||
// Extracting the set of keys in synchronized way as they are interconnected and During the | ||
// scheduled script update the task should not extract partially updated multi keys. | ||
configKeysMap = Util.getLockedMultiKeyConfig(config, keys); | ||
} catch (Exception e) { | ||
throw new RuntimeException( | ||
"External Script Task failed as the schedule is stopped and this is a old task"); | ||
} | ||
|
||
// Create a temporary file to store script and make it executable. | ||
String devopsHome = appConfig.getString("yb.devops.home"); | ||
File directory = new File(devopsHome + SCRIPT_STORE_DIR); | ||
if (!directory.exists()) { | ||
directory.mkdir(); | ||
} | ||
tempScriptFile = | ||
File.createTempFile( | ||
TEMP_SCRIPT_FILE_NAME + params().universeUUID.toString(), ".py", directory); | ||
|
||
FileOutputStream file = new FileOutputStream(tempScriptFile.getAbsoluteFile()); | ||
OutputStreamWriter output = new OutputStreamWriter(file, Charset.forName("UTF-8")); | ||
output.write(configKeysMap.get(PLT_EXT_SCRIPT_CONTENT)); | ||
output.close(); | ||
tempScriptFile.setExecutable(true); | ||
|
||
// Add the commands to the script. | ||
List<String> commandList = new ArrayList<>(); | ||
commandList.add(SCRIPT_DIR + tempScriptFile.getName()); | ||
commandList.add("--universe_name"); | ||
commandList.add(universe.name); | ||
commandList.add("--universe_uuid"); | ||
commandList.add(params().universeUUID.toString()); | ||
commandList.add("--platform_url"); | ||
commandList.add(params().platformUrl); | ||
commandList.add("--auth_token"); | ||
commandList.add(Users.getOrBadRequest(params().userUUID).createAuthToken()); | ||
|
||
String scriptParam = configKeysMap.get(PLT_EXT_SCRIPT_PARAM); | ||
if (!StringUtils.isEmpty(scriptParam)) { | ||
final ObjectMapper mapper = new ObjectMapper(); | ||
JsonNode jsonNode = mapper.readTree(scriptParam); | ||
Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields(); | ||
while (fields.hasNext()) { | ||
Map.Entry<String, JsonNode> field = fields.next(); | ||
String fieldName = field.getKey(); | ||
String fieldValue = field.getValue().asText(); | ||
if (!StringUtils.isEmpty(fieldName) && !StringUtils.isEmpty(fieldValue)) { | ||
commandList.add("--" + fieldName); | ||
commandList.add(fieldValue); | ||
} | ||
} | ||
} | ||
|
||
String description = String.join(" ", commandList); | ||
|
||
// Execute the command. | ||
ShellResponse shellResponse = | ||
shellProcessHandler.run(commandList, new HashMap<>(), description); | ||
processShellResponse(shellResponse); | ||
} catch (Exception e) { | ||
LOG.error("Error executing task {}, error='{}'", getName(), e.getMessage(), e); | ||
throw new RuntimeException(e); | ||
} finally { | ||
// Delete temporary file if exists. | ||
if (tempScriptFile != null && tempScriptFile.exists()) { | ||
tempScriptFile.delete(); | ||
} | ||
} | ||
LOG.info("Finished {} task.", getName()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.