Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy configurations #3751

Merged
merged 18 commits into from
Dec 3, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ typings/
# dotenv environment variables file
.env
.env.test
!test.env

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,21 @@ public String getMessageString() {
public void setMessageFlowTracingState(int tracingState){
}

@Override
public Object getVariable(String s) {
return null;
}

@Override
public void setVariable(String s, Object o) {
//
}

@Override
public Set getVariableKeySet() {
return null;
}

public int getMessageFlowTracingState(){
return SynapseConstants.TRACING_OFF;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.wso2.micro.integrator.initializer.StartupFinalizer;
import org.wso2.micro.integrator.initializer.dashboard.HeartBeatComponent;
import org.wso2.micro.integrator.initializer.deployment.application.deployer.CappDeployer;
import org.wso2.micro.integrator.initializer.deployment.config.deployer.ConfigDeployer;
import org.wso2.micro.integrator.initializer.deployment.synapse.deployer.FileRegistryResourceDeployer;
import org.wso2.micro.integrator.initializer.deployment.synapse.deployer.SynapseAppDeployer;
import org.wso2.micro.integrator.initializer.deployment.user.store.deployer.UserStoreDeployer;
Expand Down Expand Up @@ -169,6 +170,7 @@ private void addCAppDeployer(DeploymentEngine deploymentEngine) {
cappDeployer.init(configCtx);

// Register application deployment handlers
cappDeployer.registerDeploymentHandler(new ConfigDeployer());
cappDeployer.registerDeploymentHandler(new FileRegistryResourceDeployer(
synapseEnvironmentService.getSynapseEnvironment().getSynapseConfiguration().getRegistry()));
cappDeployer.registerDeploymentHandler(new DataSourceCappDeployer());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
* Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. 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 org.wso2.micro.integrator.initializer.deployment.config.deployer;

import org.apache.axis2.deployment.DeploymentException;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.commons.property.PropertyHolder;
import org.apache.synapse.transport.nhttp.config.SslSenderTrustStoreHolder;
import org.wso2.micro.application.deployer.CarbonApplication;
import org.wso2.micro.application.deployer.config.ApplicationConfiguration;
import org.wso2.micro.application.deployer.config.Artifact;
import org.wso2.micro.application.deployer.config.CappFile;
import org.wso2.micro.application.deployer.handler.AppDeploymentHandler;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

public class ConfigDeployer implements AppDeploymentHandler {

private static final Log log = LogFactory.getLog(ConfigDeployer.class);

private static final String PROPERTY_TYPE = "config/property";

private static final String LOCAL_CONFIG_FILE_NAME = "config.properties";
private static final String GLOBAL_CONFIG_FILE_NAME = "file.properties";
private Properties globalProperties;

public static final char URL_SEPARATOR_CHAR = '/';

public ConfigDeployer() {
}

@Override
public void deployArtifacts(CarbonApplication carbonApp, AxisConfiguration axisConfig)
throws DeploymentException {
if (log.isDebugEnabled()) {
log.debug("Deploying properties - " + carbonApp.getAppName());
}
ApplicationConfiguration appConfig = carbonApp.getAppConfig();
List<Artifact.Dependency> deps = appConfig.getApplicationArtifact().getDependencies();

List<Artifact> artifacts = new ArrayList<Artifact>();
for (Artifact.Dependency dep : deps) {
if (dep.getArtifact() != null) {
artifacts.add(dep.getArtifact());
}
}
deployConfigArtifacts(artifacts);
}

@Override
public void undeployArtifacts(CarbonApplication carbonApp, AxisConfiguration axisConfig)
throws DeploymentException {

}

private void deployConfigArtifacts(List<Artifact> artifacts) {
artifacts.stream().filter(artifact -> PROPERTY_TYPE.equals(artifact.getType())).forEach(artifact -> {
if (log.isDebugEnabled()) {
log.debug("Deploying config artifact: " + artifact.getName());
}
writePropertyToMap(artifact);
});
}

private void writePropertyToMap(Artifact artifact) {
// get the file path of the registry config file
List<CappFile> files = artifact.getFiles();
if (files.size() == 1) {
Path confFolder = Paths.get(getHome(), "conf");
Path globalPropertiesFilePath = confFolder.resolve(GLOBAL_CONFIG_FILE_NAME) ;
Path serverConfPropertyPath = confFolder.resolve(LOCAL_CONFIG_FILE_NAME);
String configFilePath = artifact.getExtractedPath() + File.separator + LOCAL_CONFIG_FILE_NAME;
processConfFile(artifact.getName(), configFilePath, globalPropertiesFilePath.toString(),
serverConfPropertyPath.toString());
} else {
log.error("config/property type must have a single file which declares " +
"config. But " + files.size() + " files found.");
}
}

private void processConfFile(String integrationName, String configFilePath, String globalPropertiesFilePath,
String serverConfPropertyPath) {
File configFile = new File(configFilePath);
// Load capp conf property file
Properties configProperties = loadPropertiesFromFile(configFile);
// Load global conf property file
this.globalProperties = loadPropertiesFromFile(new File(globalPropertiesFilePath));
// Load sever conf property file
Properties serverConfigProperties = loadPropertiesFromFile(new File(serverConfPropertyPath));

Properties newServerConfigProperties = new Properties();
if (serverConfigProperties.isEmpty() && configProperties.isEmpty() ) {
if (log.isDebugEnabled()) {
log.debug(String.format("No configuration is used in the integration[%s]", integrationName));
}
} else {
for (Map.Entry<Object, Object> entry : serverConfigProperties.entrySet()) {
String key = entry.getKey().toString();
String type = entry.getValue().toString();
if (configProperties.containsKey(key)) {
type = configProperties.getProperty(key);
configProperties.remove(key);
}
newServerConfigProperties.setProperty(key, type);
processConfigProperties(key, type);
}
for (Map.Entry<Object, Object> entry : configProperties.entrySet()) {
String key = entry.getKey().toString();
String type = entry.getValue().toString();
newServerConfigProperties.setProperty(key, type);
processConfigProperties(key, type);
}
writeServerConfFile(serverConfPropertyPath, newServerConfigProperties);
}
}

private void processConfigProperties(String key, String type) {
String value = getValueOfKey(key);
if (value != null) {
if (Objects.equals(type, "cert")) {
deployCert(key, value);
}
if (PropertyHolder.getInstance().hasKey(key)) {
String oldValue = PropertyHolder.getInstance().getPropertyValue(key);
if (!Objects.equals(oldValue, value)) {
log.info(String.format("The value of the key:[%s] has been " +
"replaced with the new value.", key));
}
}
PropertyHolder.getInstance().setProperty(key, value);
} else {
log.error(String.format("The value of the key:[%s] is not found.", key));
}
}

private void deployCert(String key, String path) {
// Load the truststore properties
char[] password = SslSenderTrustStoreHolder.getInstance().getPassword().toCharArray();
String type = SslSenderTrustStoreHolder.getInstance().getType();
Path trustStorePath = Paths.get(getHome(), SslSenderTrustStoreHolder.getInstance().getLocation());
try (FileInputStream trustStoreStream = new FileInputStream(trustStorePath.toFile())) {
KeyStore trustStore = KeyStore.getInstance(type);
trustStore.load(trustStoreStream, password);
if (!trustStore.containsAlias(key)) {
// Load the certificate file
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
try (FileInputStream certStream = new FileInputStream(path)) {
Certificate cert = certFactory.generateCertificate(certStream);
// Add the certificate to the truststore
trustStore.setCertificateEntry(key, cert);
log.info("Certificate added with alias: " + key);
}
// Save the truststore with the new certificate
try (FileOutputStream outputStream = new FileOutputStream(trustStorePath.toFile())) {
trustStore.store(outputStream, password);
log.info("Truststore updated successfully at: " + trustStorePath);
}
} else {
log.info(String.format("The trust store already contains a certificate " +
"with the alias [%s].", key));
}
} catch (FileNotFoundException e) {
log.error(String.format("File not found for importing the certificate: %s", key));
} catch (IOException e) {
log.error(String.format("Certificate import failed: %s", key));
} catch (CertificateException e) {
log.error(String.format("An error occurred while processing the certificate: %s", key));
} catch (KeyStoreException e) {
log.error(String.format("An error occurred while processing the truststore: %s", key));
} catch (NoSuchAlgorithmException e) {
log.error(String.format("An error occurred while loading the certificate: %s", key));
}
}

private void writeServerConfFile(String file, Properties newServerConfigProperties) {
try (FileWriter writer = new FileWriter(file)) {
Enumeration<?> propertyNames = newServerConfigProperties.propertyNames();
while (propertyNames.hasMoreElements()) {
String key = (String) propertyNames.nextElement();
String value = newServerConfigProperties.getProperty(key);
writer.write(key + ":" + value + "\n");
}
} catch (IOException e) {
log.error("Failed to add the config.properties file to the server conf folder: "
+ e.getMessage());
}
}

private Properties loadPropertiesFromFile(File file) {
Properties properties = new Properties();
if (file.exists()) {
try (FileInputStream serverConfigFileReader = new FileInputStream(file)) {
properties.load(serverConfigFileReader);
} catch (IOException e) {
log.error("Error occurred while loading properties from file:" + e.getMessage());
}
}
return properties;
}

private String getValueOfKey(String key) {
String value = System.getenv(key);
if (value == null) {
value = System.getProperty(key);
if (value == null) {
value = this.globalProperties.getProperty(key);
}
}
return value;
}

private String getHome() {
String carbonHome = System.getProperty("carbon.home");
if (carbonHome == null || "".equals(carbonHome) || ".".equals(carbonHome)) {
carbonHome = getSystemDependentPath(new File(".").getAbsolutePath());
}
return carbonHome;
}

private String getSystemDependentPath(String path) {
return path.replace(URL_SEPARATOR_CHAR, File.separatorChar);
}
}
40 changes: 40 additions & 0 deletions distribution/src/scripts/micro-integrator.bat
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ if ""%1""==""car"" goto setCar
if ""%1""==""-car"" goto setCar
if ""%1""==""--car"" goto setCar

@REM Check if the argument starts with --env-file=
set "envfile=%~1"
if "!envfile:~0,11!"=="--env-file=" (
set "file_path=!envfile:~11!"
call :export_env_file "!file_path!"
)
shift
goto setupArgs

Expand Down Expand Up @@ -133,6 +139,40 @@ echo Stopping the Micro Integrator Server
taskkill /F /PID %processId%
goto end

:export_env_file
setlocal EnableDelayedExpansion

set "file_path=%~1"

REM Check if the file exists
if not exist "!file_path!" (
echo Error: File '!file_path!' not found.
exit /b 1
)

REM Read each line in the file
for /f "usebackq tokens=1,* delims==" %%A in ("!file_path!") do (
set "line=%%A"

REM Ignore lines that start with '#' (comments) or are empty
if not "!line!"=="" (
if "!line:~0,1!" neq "#" (
set "key=%%A"
set "value=%%B"

REM Trim surrounding whitespace from key and value
for /f "tokens=* delims= " %%i in ("!key!") do set "key=%%i"
for /f "tokens=* delims= " %%i in ("!value!") do set "value=%%i"

REM Set the environment variable
setx "!key!" "!value!" >nul
set "!key!=!value!"
)
)
)
echo Environment variables loaded from !file_path!.
exit /b 0

rem ----- commandLifecycle -----------------------------------------------------
:commandLifecycle
goto findJdk
Expand Down
Loading
Loading