blob: fe209126e876b5ec6435d5ee6728ae5d4947eaed [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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 com.epam.dlab.backendapi.core.commands;
import com.epam.dlab.exceptions.DlabException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.MoreObjects;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import java.io.IOException;
import java.util.*;
/**
* Parse command for emulate commands of Docker.
*/
public class CommandParserMock {
private ObjectMapper MAPPER = new ObjectMapper()
.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true)
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
private String command;
private String action;
private String resourceType;
private String imageType;
private String requestId;
private String responsePath;
private String name;
private String json;
private Map<String, String> envMap = new HashMap<>();
private Map<String, String> varMap = new HashMap<>();
private List<String> otherArgs = new ArrayList<>();
private Map<String, String> variables = new HashMap<>();
private boolean dockerCommand;
public CommandParserMock() {
}
public CommandParserMock(String command, String uuid) {
parse(command, uuid);
}
/**
* Return the name of docker command.
*/
public String getCommand() {
return command;
}
/**
* Return the name of docker action.
*/
public String getAction() {
return action;
}
/**
* Return the type of resource.
*/
public String getResourceType() {
return resourceType;
}
/**
* Return the image type.
*/
public String getImageType() {
return imageType;
}
/**
* Return the request id.
*/
public String getRequestId() {
return requestId;
}
/**
* Return the path for response files.
*/
public String getResponsePath() {
return responsePath;
}
/**
* Return name of docker container.
*/
public String getName() {
return name;
}
/**
* Return content of Json if present otherwise <b>null</b>.
*/
public String getJson() {
return json;
}
/**
* Return map of environment variables.
*/
public Map<String, String> getVariables() {
return variables;
}
/**
* Add argument to list.
*
* @param args list of arguments.
* @param arg argument.
*/
private void addArgToList(List<String> args, String arg) {
if (arg == null) {
return;
}
if (arg.length() > 1) {
if (arg.startsWith("'") && arg.endsWith("'")) {
arg = arg.substring(1, arg.length() - 1);
}
if (arg.startsWith("\"") && arg.endsWith("\"")) {
arg = arg.substring(1, arg.length() - 1);
}
}
arg = arg.trim();
if (arg.isEmpty()) {
return;
}
args.add(arg);
}
/**
* Extract arguments from command line.
*
* @param cmd command line.
* @return List of arguments.
*/
private List<String> extractArgs(String cmd) {
boolean isQuote = false;
boolean isDoubleQuote = false;
List<String> args = new ArrayList<>();
int pos = 0;
for (int i = 0; i < cmd.length(); i++) {
final char c = cmd.charAt(i);
if (c == '\'') {
isQuote = !isQuote;
continue;
}
if (c == '"') {
isDoubleQuote = !isDoubleQuote;
continue;
}
if (!isQuote && !isDoubleQuote && c == ' ') {
addArgToList(args, cmd.substring(pos, i));
pos = i + 1;
}
}
if (!isQuote && !isDoubleQuote) {
addArgToList(args, cmd.substring(pos));
}
return args;
}
/**
* Return the value of argument.
*
* @param args list of arguments.
* @param index index of named arguments
* @param argName name of argument.
*/
private String getArgValue(List<String> args, int index, String argName) {
if (!args.get(index).equals(argName)) {
return null;
}
args.remove(index);
if (index < args.size()) {
String value = args.get(index);
args.remove(index);
return value;
}
throw new DlabException("Argument \"" + argName + "\" detected but not have value");
}
/**
* Return pair name/value separated.
*
* @param argName name of argument.
* @param value value.
* @param separator separator.
*/
private Pair<String, String> getPair(String argName, String value, String separator) {
String[] array = value.split(separator);
if (array.length != 2) {
throw new DlabException("Invalid value for \"" + argName + "\": " + value);
}
return new ImmutablePair<>(array[0], array[1]);
}
/**
* Return name of docker image.
*
* @param args list of arguments.
* @throws if image name not found.
*/
public static String getImageName(List<String> args) {
for (String s : args) {
if (s.startsWith("docker.dlab-")) {
return s;
}
}
throw new DlabException("Name of docker image not found");
}
/**
* Extract Json properties from Json content.
*
* @param jsonContent Json content.
* @return
*/
private Map<String, String> getJsonVariables(String jsonContent) {
Map<String, String> vars = new HashMap<>();
if (jsonContent == null) {
return vars;
}
JsonNode json;
try {
json = MAPPER.readTree(jsonContent);
} catch (IOException e) {
throw new DlabException("Can't parse json content: " + e.getLocalizedMessage(), e);
}
Iterator<String> keys = json.fieldNames();
while (keys.hasNext()) {
String key = keys.next();
String value = getTextValue(json.get(key));
if (value != null) {
vars.put(key, value);
}
}
return vars;
}
/**
* Return the value of json property or <b>null</b>.
*
* @param jsonNode - Json node.
*/
private String getTextValue(JsonNode jsonNode) {
return jsonNode != null ? jsonNode.textValue() : null;
}
/**
* Parse command line.
*
* @param cmd command line.
*/
public void parse(String cmd, String uuid) {
command = null;
action = null;
resourceType = null;
imageType = null;
requestId = uuid;
responsePath = null;
name = null;
json = null;
envMap.clear();
varMap.clear();
otherArgs.clear();
variables.clear();
List<String> args = extractArgs(cmd);
dockerCommand = args.contains("docker");
int i = 0;
String s;
Pair<String, String> p;
while (i < args.size()) {
if ((s = getArgValue(args, i, "-v")) != null) {
p = getPair("-v", s, ":");
varMap.put(p.getValue(), p.getKey());
} else if ((s = getArgValue(args, i, "-e")) != null) {
p = getPair("-e", s, "=");
envMap.put(p.getKey(), p.getValue());
} else if ((s = getArgValue(args, i, "docker")) != null || (s = getArgValue(args, i, "python")) != null) {
command = s;
} else if ((s = getArgValue(args, i, "--action")) != null) {
action = s;
} else if ((s = getArgValue(args, i, "--name")) != null) {
name = s;
} else if ((s = getArgValue(args, i, "echo")) != null) {
if (s.equals("-e")) {
if (i >= args.size()) {
throw new DlabException("Argument \"echo -e\" detected but not have value");
}
s = args.get(i);
args.remove(i);
}
json = s;
} else if ((s = getArgValue(args, i, "--result_path")) != null) {
responsePath = s;
varMap.put("/response", responsePath);
args.remove(i);
} else {
i++;
}
}
if (args.size() > 0) {
otherArgs.addAll(args);
}
resourceType = envMap.get("conf_resource");
if (isDockerCommand()) {
imageType = getImageName(args);
imageType = imageType.replace("docker.dlab-", "").replace(":latest", "");
}
responsePath = varMap.get("/response");
variables.putAll(envMap);
variables.putAll(getJsonVariables(json));
variables.put("request_id", requestId);
variables.put("instance_id", "i-" + requestId.replace("-", "").substring(0, 17));
variables.put("cluster_id", "j-" + requestId.replace("-", "").substring(0, 13).toUpperCase());
variables.put("notebook_id", requestId.replace("-", "").substring(17, 22));
}
public boolean isDockerCommand() {
return dockerCommand;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("command", command)
.add("action", action)
.add("resourceType", resourceType)
.add("imageType", imageType)
.add("requestId", requestId)
.add("responsePath", responsePath)
.add("name", name)
.add("environment", envMap)
.add("variable", varMap)
.add("others", otherArgs)
.add("json", json)
.toString();
}
}