blob: 095b78c6a0aeb411cc4a417c2fef4fded2f28de1 [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.datalab.properties;
import com.epam.datalab.exceptions.DynamicChangePropertiesException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.epam.datalab.properties.ChangePropertiesConst.DEFAULT_NAME_PLACE;
import static com.epam.datalab.properties.ChangePropertiesConst.DEFAULT_VALUE_PLACE;
@Slf4j
public class ChangePropertiesService {
public String readFileAsString(String selfServicePropPath, String serviceName) {
try {
log.info("Trying to read {}, file from path {} :", serviceName, selfServicePropPath);
String currentConf = FileUtils.readFileToString(new File(selfServicePropPath), Charset.defaultCharset());
return hideSecretsAndRemoveLicence(currentConf);
} catch (IOException e) {
log.error(e.getMessage());
throw new DynamicChangePropertiesException(String.format("Failed while read file %s", serviceName));
}
}
public void writeFileFromString(String newPropFile, String serviceName, String servicePath) {
try {
changeCHMODE(serviceName, servicePath, ChangePropertiesConst.DEFAULT_CHMOD, ChangePropertiesConst.WRITE_CHMOD);
String oldFile = readFile(serviceName, servicePath);
BufferedWriter writer = new BufferedWriter(new FileWriter(servicePath));
log.info("Trying to overwrite {}, file for path {} :", serviceName, servicePath);
writer.write(addLicence());
writer.write(checkAndReplaceSecretIfEmpty(newPropFile, oldFile));
log.info("{} overwritten successfully", serviceName);
writer.close();
changeCHMODE(serviceName, servicePath, ChangePropertiesConst.WRITE_CHMOD, ChangePropertiesConst.DEFAULT_CHMOD);
} catch (IOException e) {
log.error("Failed to create writer with path {}", servicePath);
throw new DynamicChangePropertiesException(String.format("Failed during overwriting %s", serviceName));
}
}
private String readFile(String serviceName, String servicePath) {
String oldFile;
try {
oldFile = FileUtils.readFileToString(new File(servicePath), Charset.defaultCharset());
} catch (IOException e) {
log.error("Failed to read with path {}", servicePath);
throw new DynamicChangePropertiesException(String.format("Failed during overwriting %s", serviceName));
}
return oldFile;
}
public RestartAnswer restart(RestartForm restartForm) {
try {
boolean billing = restartForm.isBilling();
boolean provserv = restartForm.isProvserv();
boolean ui = restartForm.isUi();
String shCommand = buildSHRestartCommand(billing, provserv, ui);
log.info("Tying to restart ui: {}, provserv: {}, billing: {}, with command: {}", ui,
provserv, billing, shCommand);
Runtime.getRuntime().exec(shCommand).waitFor();
return RestartAnswer.builder()
.billingSuccess(billing)
.provservSuccess(provserv)
.endpoint(restartForm.getEndpoint())
.build();
} catch (IOException | InterruptedException e) {
log.error(e.getMessage());
return RestartAnswer.builder()
.billingSuccess(false)
.provservSuccess(false)
.endpoint(restartForm.getEndpoint())
.build();
}
}
private String hideSecretsAndRemoveLicence(String currentConf) {
Matcher passMatcher = Pattern.compile(ChangePropertiesConst.SECRET_REGEX).matcher(currentConf);
Matcher userMatcher = Pattern.compile(ChangePropertiesConst.USER_REGEX).matcher(currentConf);
List<String> secretsAndUsers = new ArrayList<>();
final String[] confWithReplacedSecretConf = {removeLicence(currentConf)};
while (passMatcher.find()) {
String[] secret = passMatcher.group().split(":");
if (!(secret[DEFAULT_VALUE_PLACE].isEmpty() ||
secret[DEFAULT_VALUE_PLACE].trim().isEmpty())) {
secretsAndUsers.add(secret[DEFAULT_NAME_PLACE] + ":" + secret[DEFAULT_VALUE_PLACE]);
}
}
while (userMatcher.find()) {
String[] user = userMatcher.group().split(":");
if (!(user[DEFAULT_VALUE_PLACE].isEmpty() ||
user[DEFAULT_VALUE_PLACE].trim().isEmpty()))
secretsAndUsers.add(user[DEFAULT_NAME_PLACE] + ":" + user[DEFAULT_VALUE_PLACE]);
}
secretsAndUsers.forEach(x -> {
String toReplace = x.split(":")[DEFAULT_NAME_PLACE] + ":" + ChangePropertiesConst.SECRET_REPLACEMENT_FORMAT;
confWithReplacedSecretConf[0] = confWithReplacedSecretConf[0].replace(x, toReplace);
});
return confWithReplacedSecretConf[0];
}
private String removeLicence(String conf) {
return conf.split(ChangePropertiesConst.LICENCE_REGEX)[conf.split(ChangePropertiesConst.LICENCE_REGEX).length - 1];
}
private void changeCHMODE(String serviceName, String path, String fromMode, String toMode) throws IOException {
try {
String command = String.format(ChangePropertiesConst.CHANGE_CHMOD_SH_COMMAND_FORMAT, toMode, path);
log.info("Trying to change chmod for file {} {}->{}", serviceName, fromMode, toMode);
log.info("Execute command: {}", command);
Runtime.getRuntime().exec(command).waitFor();
} catch (InterruptedException e) {
log.error("Failed change chmod for file {} {}->{}", serviceName, fromMode, toMode);
}
}
private String addLicence() {
return ChangePropertiesConst.LICENCE;
}
private String checkAndReplaceSecretIfEmpty(String newPropFile, String oldProf) {
Map<String, Queue<String>> emptySecretsAndUserNames = findEmptySecretAndNames(newPropFile);
return emptySecretsAndUserNames.isEmpty() ? newPropFile : replaceOldSecret(newPropFile, oldProf, emptySecretsAndUserNames);
}
private String replaceOldSecret(String newPropFile, String oldProf, Map<String, Queue<String>> emptySecrets) {
String fileWithReplacedEmptySecrets = newPropFile;
Matcher oldPassMatcher = Pattern.compile(ChangePropertiesConst.SECRET_REGEX).matcher(oldProf);
Matcher oldUserMatcher = Pattern.compile(ChangePropertiesConst.USER_REGEX).matcher(oldProf);
while (oldPassMatcher.find()) {
String[] s = oldPassMatcher.group().split(":");
if (emptySecrets.containsKey(s[ChangePropertiesConst.DEFAULT_NAME_PLACE])) {
String poll = emptySecrets.get(s[DEFAULT_NAME_PLACE]).poll();
if (poll != null) {
poll = poll.replace("*", "\\*");
String old = oldPassMatcher.group();
old = old.replace("$", "\\$");
old = old.replace("{", "\\}");
old = old.replace("}", "\\}");
fileWithReplacedEmptySecrets = fileWithReplacedEmptySecrets.replaceFirst(poll, old);
}
}
}
while (oldUserMatcher.find()) {
String[] s = oldUserMatcher.group().split(":");
if (emptySecrets.containsKey(s[ChangePropertiesConst.DEFAULT_NAME_PLACE])) {
String poll = emptySecrets.get(s[DEFAULT_NAME_PLACE]).poll();
if (poll != null) {
poll = poll.replace("*", "\\*");
String old = oldUserMatcher.group();
old = old.replace("$", "\\$");
old = old.replace("{", "\\}");
old = old.replace("}", "\\}");
fileWithReplacedEmptySecrets = fileWithReplacedEmptySecrets.replaceFirst(poll, old);
}
}
}
return fileWithReplacedEmptySecrets;
}
private Map<String, Queue<String>> findEmptySecretAndNames(String newPropFile) {
Matcher passMatcher = Pattern.compile(ChangePropertiesConst.SECRET_REGEX).matcher(newPropFile);
Matcher userNameMatcher = Pattern.compile(ChangePropertiesConst.USER_REGEX).matcher(newPropFile);
Map<String, Queue<String>> emptySecrets = new HashMap<>();
while (passMatcher.find()) {
String[] s = passMatcher.group().split(":");
if (s[DEFAULT_VALUE_PLACE].equals(ChangePropertiesConst.SECRET_REPLACEMENT_FORMAT)) {
if (emptySecrets.get(s[DEFAULT_NAME_PLACE]) == null) {
Queue<String> values = new ArrayDeque<>();
values.add(passMatcher.group());
emptySecrets.put(s[ChangePropertiesConst.DEFAULT_NAME_PLACE], values);
} else {
Queue<String> values = emptySecrets.get(s[DEFAULT_NAME_PLACE]);
values.add(passMatcher.group());
}
}
}
while (userNameMatcher.find()) {
String[] s = userNameMatcher.group().split(":");
if (s[DEFAULT_VALUE_PLACE].equals(ChangePropertiesConst.SECRET_REPLACEMENT_FORMAT)) {
if (emptySecrets.get(s[DEFAULT_NAME_PLACE]) == null) {
Queue<String> values = new ArrayDeque<>();
values.add(userNameMatcher.group());
emptySecrets.put(s[ChangePropertiesConst.DEFAULT_NAME_PLACE], values);
} else {
Queue<String> values = emptySecrets.get(s[DEFAULT_NAME_PLACE]);
values.add(userNameMatcher.group());
}
}
}
return emptySecrets;
}
private String buildSHRestartCommand(boolean billing, boolean provserv, boolean ui) {
StringBuilder stringBuilder = new StringBuilder(ChangePropertiesConst.SUPERVISORCTL_RESTART_SH_COMMAND);
if (billing) stringBuilder.append(ChangePropertiesConst.BILLING_SERVICE_SUPERVISORCTL_RUN_NAME);
if (provserv) stringBuilder.append(ChangePropertiesConst.PROVISIONING_SERVICE_SUPERVISORCTL_RUN_NAME);
if (ui) stringBuilder.append(ChangePropertiesConst.SELF_SERVICE_SUPERVISORCTL_RUN_NAME);
return stringBuilder.toString();
}
}