mirror of
https://github.com/LamGC/Oracle-Sentry.git
synced 2025-07-04 06:17:27 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
46ae196fe9
|
|||
e5b08bc12a
|
|||
bec7010d43
|
|||
6b2bd0a59d
|
|||
749ea644f1
|
|||
e6a81784a3
|
|||
b5c3af1c19
|
|||
13d90595b7
|
|||
3641593210
|
|||
f5881ddbfe
|
|||
2c1e9606e1
|
|||
166c63fb93
|
|||
69def56d91
|
|||
cfbe0315f0
|
|||
ead42e2b00
|
|||
8dc7da3f3f
|
|||
8658104f7f
|
|||
2cd679bcaf
|
|||
2f225d27fe
|
|||
c2aa02cae3
|
|||
284f0229f7
|
20
build.gradle
20
build.gradle
@ -1,12 +1,13 @@
|
|||||||
|
//file:noinspection GradlePackageUpdate SpringBoot 的版本由插件管理.
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '2.5.3'
|
id 'org.springframework.boot' version '2.5.4'
|
||||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||||
id 'jacoco'
|
id 'jacoco'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'net.lamgc.oracle'
|
group 'net.lamgc.oracle'
|
||||||
version '0.1.0'
|
version '0.2.0'
|
||||||
compileJava.sourceCompatibility = JavaVersion.VERSION_16
|
compileJava.sourceCompatibility = JavaVersion.VERSION_16
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -14,17 +15,18 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
def ociSdkVer = '2.3.2'
|
def ociSdkVer = '2.4.0'
|
||||||
def sshdVer = '2.7.0'
|
def sshdVer = '2.7.0'
|
||||||
def bouncyCastleVer = '1.69'
|
def bouncyCastleVer = '1.69'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter'
|
implementation 'org.springframework.boot:spring-boot-starter'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
|
||||||
implementation 'org.slf4j:slf4j-api:1.7.31'
|
implementation 'org.slf4j:slf4j-api:1.7.32'
|
||||||
|
|
||||||
implementation "com.oracle.oci.sdk:oci-java-sdk-core:${ociSdkVer}"
|
implementation "com.oracle.oci.sdk:oci-java-sdk-core:${ociSdkVer}"
|
||||||
implementation "com.oracle.oci.sdk:oci-java-sdk-identity:${ociSdkVer}"
|
implementation "com.oracle.oci.sdk:oci-java-sdk-identity:${ociSdkVer}"
|
||||||
|
implementation "com.oracle.oci.sdk:oci-java-sdk-objectstorage:${ociSdkVer}"
|
||||||
|
|
||||||
implementation "org.apache.sshd:sshd-core:${sshdVer}"
|
implementation "org.apache.sshd:sshd-core:${sshdVer}"
|
||||||
implementation "org.apache.sshd:sshd-sftp:${sshdVer}"
|
implementation "org.apache.sshd:sshd-sftp:${sshdVer}"
|
||||||
@ -32,14 +34,16 @@ dependencies {
|
|||||||
implementation "org.bouncycastle:bcpg-jdk15on:${bouncyCastleVer}"
|
implementation "org.bouncycastle:bcpg-jdk15on:${bouncyCastleVer}"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk15on:${bouncyCastleVer}"
|
implementation "org.bouncycastle:bcpkix-jdk15on:${bouncyCastleVer}"
|
||||||
|
|
||||||
|
implementation 'cglib:cglib:3.3.0'
|
||||||
|
|
||||||
implementation "net.i2p.crypto:eddsa:0.3.0"
|
implementation "net.i2p.crypto:eddsa:0.3.0"
|
||||||
|
|
||||||
implementation 'org.codehaus.groovy:groovy-all:3.0.7'
|
implementation 'org.codehaus.groovy:groovy-all:3.0.8'
|
||||||
|
|
||||||
implementation 'com.google.code.gson:gson:2.8.7'
|
implementation 'com.google.code.gson:gson:2.8.8'
|
||||||
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package net.lamgc.oracle.sentry;
|
package net.lamgc.oracle.sentry;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
import net.lamgc.oracle.sentry.oci.account.OracleAccount;
|
||||||
|
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptComponents;
|
import net.lamgc.oracle.sentry.script.ScriptComponents;
|
||||||
|
import net.lamgc.oracle.sentry.script.ScriptLoggerFactory;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptManager;
|
import net.lamgc.oracle.sentry.script.ScriptManager;
|
||||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
@ -47,8 +49,8 @@ class ApplicationInitiation {
|
|||||||
private String sshIdentityPath;
|
private String sshIdentityPath;
|
||||||
|
|
||||||
@Bean("oracle.identity.manager")
|
@Bean("oracle.identity.manager")
|
||||||
public OracleIdentityManager initialOracleIdentityManager() throws IOException {
|
public OracleAccountManager initialOracleAccountManager() throws IOException {
|
||||||
OracleIdentityManager oracleUserManager = new OracleIdentityManager();
|
OracleAccountManager oracleUserManager = new OracleAccountManager();
|
||||||
log.info("正在加载 Oracle API 身份配置...");
|
log.info("正在加载 Oracle API 身份配置...");
|
||||||
log.debug("Oracle API 身份配置查找路径: \"{}\", 匹配表达式: {}", identityDirectory, identityFilePattern);
|
log.debug("Oracle API 身份配置查找路径: \"{}\", 匹配表达式: {}", identityDirectory, identityFilePattern);
|
||||||
File identityDir = new File(identityDirectory);
|
File identityDir = new File(identityDirectory);
|
||||||
@ -67,15 +69,15 @@ class ApplicationInitiation {
|
|||||||
|
|
||||||
@Bean("oracle.compute.instance.manager")
|
@Bean("oracle.compute.instance.manager")
|
||||||
@Autowired
|
@Autowired
|
||||||
public ComputeInstanceManager initialComputeInstanceManager(OracleIdentityManager identityManager) throws IOException {
|
public ComputeInstanceManager initialComputeInstanceManager(OracleAccountManager accountManager) throws IOException {
|
||||||
ComputeInstanceManager instanceManager = new ComputeInstanceManager();
|
ComputeInstanceManager instanceManager = new ComputeInstanceManager();
|
||||||
int addTotal = 0;
|
int addTotal = 0;
|
||||||
for (AuthenticationDetailsProvider provider : identityManager.getProviders()) {
|
for (OracleAccount account : accountManager.getAccounts()) {
|
||||||
String identityName = identityManager.getIdentityName(provider.getUserId());
|
String identityName = account.name();
|
||||||
log.info("正在加载用户 {} 所拥有的所有实例...", identityName);
|
log.info("正在加载用户 {} 所拥有的所有实例...", identityName);
|
||||||
int addCount;
|
int addCount;
|
||||||
try {
|
try {
|
||||||
addCount = instanceManager.addComputeInstanceFromUser(provider);
|
addCount = instanceManager.addComputeInstanceFromUser(account);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("加载实例时发生异常.", e);
|
log.error("加载实例时发生异常.", e);
|
||||||
continue;
|
continue;
|
||||||
@ -92,10 +94,13 @@ class ApplicationInitiation {
|
|||||||
|
|
||||||
@Bean("sentry.script.manager")
|
@Bean("sentry.script.manager")
|
||||||
@Autowired
|
@Autowired
|
||||||
public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager) {
|
public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager, OracleAccountManager accountManager) {
|
||||||
ScriptComponents context = new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create()
|
ScriptComponents context = new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create()
|
||||||
.build()),
|
.build()),
|
||||||
instanceManager);
|
instanceManager,
|
||||||
|
new ScriptLoggerFactory(),
|
||||||
|
accountManager
|
||||||
|
);
|
||||||
|
|
||||||
ScriptManager manager = new ScriptManager(new File(scriptsLocation), context);
|
ScriptManager manager = new ScriptManager(new File(scriptsLocation), context);
|
||||||
manager.loadScripts();
|
manager.loadScripts();
|
||||||
@ -104,6 +109,7 @@ class ApplicationInitiation {
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
@SuppressWarnings({"MismatchedReadAndWriteOfArray", "RedundantOperationOnEmptyContainer"})
|
||||||
private void initialEnvironment() throws IOException {
|
private void initialEnvironment() throws IOException {
|
||||||
String[] directors = new String[] {
|
String[] directors = new String[] {
|
||||||
"./config",
|
"./config",
|
||||||
@ -112,7 +118,7 @@ class ApplicationInitiation {
|
|||||||
};
|
};
|
||||||
|
|
||||||
String[] files = new String[] {
|
String[] files = new String[] {
|
||||||
sshIdentityPath
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String directory : directors) {
|
for (String directory : directors) {
|
||||||
|
@ -12,7 +12,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ApplicationMain {
|
class ApplicationMain {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(ApplicationMain.class);
|
private final static Logger log = LoggerFactory.getLogger(ApplicationMain.class);
|
||||||
|
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
package net.lamgc.oracle.sentry;
|
package net.lamgc.oracle.sentry;
|
||||||
|
|
||||||
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
|
||||||
import com.oracle.bmc.core.ComputeClient;
|
|
||||||
import com.oracle.bmc.core.model.Instance;
|
import com.oracle.bmc.core.model.Instance;
|
||||||
import com.oracle.bmc.core.requests.ListInstancesRequest;
|
import com.oracle.bmc.core.requests.ListInstancesRequest;
|
||||||
import com.oracle.bmc.core.responses.ListInstancesResponse;
|
import com.oracle.bmc.core.responses.ListInstancesResponse;
|
||||||
import com.oracle.bmc.identity.IdentityClient;
|
|
||||||
import com.oracle.bmc.identity.model.Compartment;
|
import com.oracle.bmc.identity.model.Compartment;
|
||||||
import com.oracle.bmc.identity.requests.ListCompartmentsRequest;
|
import com.oracle.bmc.identity.requests.ListCompartmentsRequest;
|
||||||
import com.oracle.bmc.identity.responses.ListCompartmentsResponse;
|
import com.oracle.bmc.identity.responses.ListCompartmentsResponse;
|
||||||
|
import net.lamgc.oracle.sentry.oci.account.OracleAccount;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstance;
|
import net.lamgc.oracle.sentry.oci.compute.ComputeInstance;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ssh.SshAuthIdentityProvider;
|
import net.lamgc.oracle.sentry.oci.compute.ssh.SshAuthIdentityProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ import java.util.stream.Collectors;
|
|||||||
* 计算实例管理器.
|
* 计算实例管理器.
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
*/
|
*/
|
||||||
public class ComputeInstanceManager {
|
public final class ComputeInstanceManager {
|
||||||
|
|
||||||
private final Map<String, ComputeInstance> instanceMap = new ConcurrentHashMap<>();
|
private final Map<String, ComputeInstance> instanceMap = new ConcurrentHashMap<>();
|
||||||
private SshAuthIdentityProvider sshIdentityProvider;
|
private SshAuthIdentityProvider sshIdentityProvider;
|
||||||
@ -64,37 +65,37 @@ public class ComputeInstanceManager {
|
|||||||
public Set<ComputeInstance> getInstancesByUserId(String userId) {
|
public Set<ComputeInstance> getInstancesByUserId(String userId) {
|
||||||
Objects.requireNonNull(userId);
|
Objects.requireNonNull(userId);
|
||||||
return instanceMap.values().stream()
|
return instanceMap.values().stream()
|
||||||
.filter(computeInstance -> computeInstance.getUserId().equals(userId))
|
.filter(computeInstance -> computeInstance.getFromAccount().id().equals(userId))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加某一用户的所有计算实例.
|
* 添加某一用户的所有计算实例.
|
||||||
* @param provider 用户身份提供器.
|
* @param account Oracle 云账号对象.
|
||||||
* @return 返回已成功添加的实例数量.
|
* @return 返回已成功添加的实例数量.
|
||||||
* @throws NullPointerException 如果 provider 为 {@code null} 则抛出异常.
|
* @throws NullPointerException 如果 provider 为 {@code null} 则抛出异常.
|
||||||
*/
|
*/
|
||||||
public int addComputeInstanceFromUser(AuthenticationDetailsProvider provider) {
|
public int addComputeInstanceFromUser(OracleAccount account) {
|
||||||
Objects.requireNonNull(provider);
|
Objects.requireNonNull(account);
|
||||||
IdentityClient identityClient = new IdentityClient(provider);
|
ListCompartmentsResponse listCompartments = account.clients().identity()
|
||||||
ComputeClient computeClient = new ComputeClient(provider);
|
.listCompartments(ListCompartmentsRequest.builder()
|
||||||
ListCompartmentsResponse listCompartments = identityClient.listCompartments(ListCompartmentsRequest.builder()
|
.compartmentId(account.tenantId())
|
||||||
.compartmentId(provider.getTenantId())
|
.build());
|
||||||
.build());
|
|
||||||
int addCount = 0;
|
int addCount = 0;
|
||||||
Set<String> compartmentIds = listCompartments.getItems().stream()
|
Set<String> compartmentIds = listCompartments.getItems().stream()
|
||||||
.map(Compartment::getId).collect(Collectors.toSet());
|
.map(Compartment::getId).collect(Collectors.toSet());
|
||||||
compartmentIds.add(provider.getTenantId());
|
compartmentIds.add(account.tenantId());
|
||||||
for (String compartmentId : compartmentIds) {
|
for (String compartmentId : compartmentIds) {
|
||||||
ListInstancesResponse listInstances = computeClient.listInstances(ListInstancesRequest.builder()
|
ListInstancesResponse listInstances = account.clients().compute()
|
||||||
.compartmentId(compartmentId)
|
.listInstances(ListInstancesRequest.builder()
|
||||||
.build());
|
.compartmentId(compartmentId)
|
||||||
|
.build());
|
||||||
for (Instance instance : listInstances.getItems()) {
|
for (Instance instance : listInstances.getItems()) {
|
||||||
if (instance.getLifecycleState() == Instance.LifecycleState.Terminated) {
|
if (instance.getLifecycleState() == Instance.LifecycleState.Terminated) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ComputeInstance computeInstance = new ComputeInstance(this, instance.getId(),
|
ComputeInstance computeInstance = new ComputeInstance(this, instance.getId(),
|
||||||
provider.getUserId(), compartmentId, instance.getImageId(), provider);
|
compartmentId, instance.getImageId(), account);
|
||||||
|
|
||||||
addComputeInstance(computeInstance);
|
addComputeInstance(computeInstance);
|
||||||
addCount ++;
|
addCount ++;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.lamgc.oracle.sentry;
|
package net.lamgc.oracle.sentry;
|
||||||
|
|
||||||
|
import net.lamgc.oracle.sentry.oci.compute.ssh.ConfiguredForwardingFilter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -23,6 +25,8 @@ public final class Constants {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private String firstConnectionPolicy;
|
private String firstConnectionPolicy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfiguredForwardingFilter forwardingFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 SSH 首次连接策略.
|
* 获取 SSH 首次连接策略.
|
||||||
@ -32,4 +36,12 @@ public final class Constants {
|
|||||||
public String getFirstConnectionPolicy() {
|
public String getFirstConnectionPolicy() {
|
||||||
return firstConnectionPolicy;
|
return firstConnectionPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已配置的转发过滤器.
|
||||||
|
* @return 返回转发过滤器.
|
||||||
|
*/
|
||||||
|
public ConfiguredForwardingFilter getForwardingFilter() {
|
||||||
|
return forwardingFilter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
48
src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java
Normal file
48
src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package net.lamgc.oracle.sentry.common;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 惰性加载器.
|
||||||
|
* <p> 该加载器只会在第一次获取对象时初始化.
|
||||||
|
* <p> 如果不用, 可能会导致内存消耗增加, 尤其是在管理大量帐号的情况下.
|
||||||
|
* <p> 对象在本加载器中为单例, 任何通过同一个加载器获取的对象都是同一个对象.
|
||||||
|
* @author LamGC
|
||||||
|
* @param <T> 对象类型.
|
||||||
|
*/
|
||||||
|
public final class LazyLoader<T> {
|
||||||
|
|
||||||
|
private final Supplier<T> supplier;
|
||||||
|
private final AtomicReference<T> object = new AtomicReference<>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建惰性加载器.
|
||||||
|
* @param supplier 对象提供器.
|
||||||
|
*/
|
||||||
|
public LazyLoader(Supplier<T> supplier) {
|
||||||
|
this.supplier = Objects.requireNonNull(supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象.
|
||||||
|
* <p> 如果是首次调用本方法, 本方法将通过 Supplier 获取一个对象, 缓存对象后返回.
|
||||||
|
* @return 返回惰性加载的对象.
|
||||||
|
*/
|
||||||
|
public T getInstance() {
|
||||||
|
if (object.get() == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (object.get() == null) {
|
||||||
|
T newInstance = supplier.get();
|
||||||
|
if (newInstance == null) {
|
||||||
|
throw new NullPointerException("Supplier is not allowed to return null.");
|
||||||
|
}
|
||||||
|
return object.compareAndSet(null, newInstance) ? newInstance : object.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.lamgc.oracle.sentry.common;
|
package net.lamgc.oracle.sentry.common.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
@ -1,4 +1,4 @@
|
|||||||
package net.lamgc.oracle.sentry.common;
|
package net.lamgc.oracle.sentry.common.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
@ -0,0 +1,53 @@
|
|||||||
|
package net.lamgc.oracle.sentry.common.logging;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.Level;
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.filter.Filter;
|
||||||
|
import ch.qos.logback.core.spi.FilterReply;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 级别范围过滤器.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class LevelRangeFilter extends Filter<ILoggingEvent> {
|
||||||
|
|
||||||
|
private Level maxLevel;
|
||||||
|
private Level minLevel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterReply decide(ILoggingEvent event) {
|
||||||
|
int level = event.getLevel().levelInt;
|
||||||
|
if (level > maxLevel.levelInt || level < minLevel.levelInt) {
|
||||||
|
return FilterReply.DENY;
|
||||||
|
}
|
||||||
|
return FilterReply.NEUTRAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最高等级.
|
||||||
|
* <p> 仅限 XML 配置文件设置.
|
||||||
|
* @param maxLevel 最高等级(包括).
|
||||||
|
*/
|
||||||
|
public void setMaxLevel(String maxLevel) {
|
||||||
|
this.maxLevel = Level.toLevel(maxLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最低等级.
|
||||||
|
* @param minLevel 允许的最低等级(包括).
|
||||||
|
*/
|
||||||
|
public void setMinLevel(String minLevel) {
|
||||||
|
this.minLevel = Level.toLevel(minLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
if (maxLevel != null && minLevel != null) {
|
||||||
|
if (maxLevel.levelInt < minLevel.levelInt) {
|
||||||
|
throw new IllegalArgumentException("The maximum level cannot be less than the minimum level.");
|
||||||
|
}
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package net.lamgc.oracle.sentry.common.logging;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.filter.Filter;
|
||||||
|
import ch.qos.logback.core.spi.FilterReply;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class MarkerFilter extends Filter<ILoggingEvent> {
|
||||||
|
|
||||||
|
private String markerName;
|
||||||
|
private FilterReply onMatch = FilterReply.NEUTRAL;
|
||||||
|
private FilterReply onMismatch = FilterReply.DENY;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FilterReply decide(ILoggingEvent event) {
|
||||||
|
return event.getMarker() != null && event.getMarker().getName().equals(markerName) ? onMatch : onMismatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置要匹配的 {@link Marker} 名称.
|
||||||
|
* @param markerName Marker 名称.
|
||||||
|
*/
|
||||||
|
public void setMarkerName(String markerName) {
|
||||||
|
this.markerName = markerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果匹配, 需要执行的操作.
|
||||||
|
* @see FilterReply
|
||||||
|
* @param onMatch 操作名称.
|
||||||
|
*/
|
||||||
|
public void setOnMatch(String onMatch) {
|
||||||
|
this.onMatch = FilterReply.valueOf(onMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果不匹配, 需要执行的操作.
|
||||||
|
* @see FilterReply
|
||||||
|
* @param onMismatch 操作名称.
|
||||||
|
*/
|
||||||
|
public void setOnMismatch(String onMismatch) {
|
||||||
|
this.onMismatch = FilterReply.valueOf(onMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
if (markerName != null && onMatch != null && onMismatch != null) {
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package net.lamgc.oracle.sentry.common.logging;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.filter.Filter;
|
||||||
|
import ch.qos.logback.core.spi.FilterReply;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public class NoMarkerFilter extends Filter<ILoggingEvent> {
|
||||||
|
@Override
|
||||||
|
public FilterReply decide(ILoggingEvent event) {
|
||||||
|
return event.getMarker() != null ? FilterReply.DENY : FilterReply.NEUTRAL;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package net.lamgc.oracle.sentry.oci.account;
|
||||||
|
|
||||||
|
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
||||||
|
import com.oracle.bmc.identity.model.User;
|
||||||
|
import com.oracle.bmc.identity.requests.GetUserRequest;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oracle 云账户.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public final class OracleAccount {
|
||||||
|
|
||||||
|
private final AuthenticationDetailsProvider provider;
|
||||||
|
private final OracleClients clients;
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
OracleAccount(AuthenticationDetailsProvider provider) {
|
||||||
|
this.provider = Objects.requireNonNull(provider);
|
||||||
|
this.clients = new OracleClients(provider);
|
||||||
|
this.user = clients.identity().getUser(GetUserRequest.builder()
|
||||||
|
.userId(provider.getUserId())
|
||||||
|
.build()).getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取帐号 Id.
|
||||||
|
* @return 返回帐号 OCID.
|
||||||
|
*/
|
||||||
|
public String id() {
|
||||||
|
return provider.getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取租户 Id.
|
||||||
|
* <p> 该 Id 同时也是根区间 Id.
|
||||||
|
* @return 返回租户 Id.
|
||||||
|
*/
|
||||||
|
public String tenantId() {
|
||||||
|
return provider.getTenantId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户名.
|
||||||
|
* @return 返回用户名称.
|
||||||
|
*/
|
||||||
|
public String name() {
|
||||||
|
return this.user.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户说明信息.
|
||||||
|
* @return 返回设定的用户说明信息.
|
||||||
|
*/
|
||||||
|
public String description() {
|
||||||
|
return this.user.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取该账户所属的 API 客户端集合.
|
||||||
|
* @return 返回该账户所属的甲骨文 API 客户端集.
|
||||||
|
*/
|
||||||
|
public OracleClients clients() {
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AuthenticationDetailsProvider thatProvider = ((OracleAccount) o).provider;
|
||||||
|
return provider.getUserId().equals(thatProvider.getUserId()) &&
|
||||||
|
provider.getTenantId().equals(thatProvider.getTenantId()) &&
|
||||||
|
provider.getFingerprint().equals(thatProvider.getFingerprint()) &&
|
||||||
|
provider.getKeyId().equals(thatProvider.getKeyId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(provider.getUserId(), provider.getTenantId(), provider.getFingerprint(), provider.getKeyId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,12 @@
|
|||||||
package net.lamgc.oracle.sentry;
|
package net.lamgc.oracle.sentry.oci.account;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.oracle.bmc.ConfigFileReader;
|
import com.oracle.bmc.ConfigFileReader;
|
||||||
import com.oracle.bmc.Region;
|
import com.oracle.bmc.Region;
|
||||||
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
||||||
import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
|
import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
|
||||||
import com.oracle.bmc.auth.SimplePrivateKeySupplier;
|
import com.oracle.bmc.auth.SimplePrivateKeySupplier;
|
||||||
import com.oracle.bmc.identity.IdentityClient;
|
|
||||||
import com.oracle.bmc.identity.requests.GetUserRequest;
|
|
||||||
import com.oracle.bmc.identity.responses.GetUserResponse;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -28,25 +23,19 @@ import java.util.function.Supplier;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Oracle 身份管理器.
|
* Oracle 云账号管理器.
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
*/
|
*/
|
||||||
public final class OracleIdentityManager {
|
public final class OracleAccountManager {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(OracleIdentityManager.class);
|
private final static Logger log = LoggerFactory.getLogger(OracleAccountManager.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证身份 Map.
|
* 认证身份 Map.
|
||||||
* Key: Identity Id
|
* Key: Identity Id
|
||||||
* Value {@link AuthenticationDetailsProvider}
|
* Value {@link AuthenticationDetailsProvider}
|
||||||
*/
|
*/
|
||||||
private final Map<String, AuthenticationDetailsProvider> identityMap = new ConcurrentHashMap<>();
|
private final Map<String, OracleAccount> accountMap = new ConcurrentHashMap<>();
|
||||||
/**
|
|
||||||
* 用户名 Map.
|
|
||||||
* key Identity Id
|
|
||||||
* Value: Username
|
|
||||||
*/
|
|
||||||
private final Map<String, String> identityNameMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从目录扫描匹配的配置文件并加载.
|
* 从目录扫描匹配的配置文件并加载.
|
||||||
@ -69,14 +58,14 @@ public final class OracleIdentityManager {
|
|||||||
int loadedCount = 0;
|
int loadedCount = 0;
|
||||||
for (File configFile : configFiles) {
|
for (File configFile : configFiles) {
|
||||||
try {
|
try {
|
||||||
AuthenticationDetailsProvider provider = loadFromConfigFile(configFile);
|
OracleAccount account = loadFromConfigFile(configFile);
|
||||||
if (provider == null) {
|
if (account == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
loadedCount ++;
|
loadedCount ++;
|
||||||
log.info("已成功加载身份配置文件.\n\tUserId: {}\n\tUsername: {}\n\tPath: {}",
|
log.info("已成功加载身份配置文件.\n\tUserId: {}\n\tUsername: {}\n\tPath: {}",
|
||||||
provider.getUserId(),
|
account.id(),
|
||||||
getIdentityName(provider.getUserId()),
|
account.name(),
|
||||||
configFile.getCanonicalPath());
|
configFile.getCanonicalPath());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("加载身份配置文件时发生异常.(Path: {})\n{}", configFile.getCanonicalPath(), Throwables.getStackTraceAsString(e));
|
log.error("加载身份配置文件时发生异常.(Path: {})\n{}", configFile.getCanonicalPath(), Throwables.getStackTraceAsString(e));
|
||||||
@ -92,7 +81,7 @@ public final class OracleIdentityManager {
|
|||||||
* @return 返回已成功加载后, 配置文件对应的身份配置提供器.
|
* @return 返回已成功加载后, 配置文件对应的身份配置提供器.
|
||||||
* @throws IOException 如果读取文件发生问题时将抛出该异常.
|
* @throws IOException 如果读取文件发生问题时将抛出该异常.
|
||||||
*/
|
*/
|
||||||
public AuthenticationDetailsProvider loadFromConfigFile(File identityConfig) throws IOException {
|
public OracleAccount loadFromConfigFile(File identityConfig) throws IOException {
|
||||||
if (!identityConfig.exists()) {
|
if (!identityConfig.exists()) {
|
||||||
throw new FileNotFoundException(identityConfig.getAbsolutePath());
|
throw new FileNotFoundException(identityConfig.getAbsolutePath());
|
||||||
}
|
}
|
||||||
@ -122,10 +111,14 @@ public final class OracleIdentityManager {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 尝试获取身份所属用户名, 以此检查该身份配置是否正确.
|
// 尝试获取身份所属用户名, 以此检查该身份配置是否正确.
|
||||||
String identityName = getIdentityName0(provider);
|
OracleAccount oracleAccount = new OracleAccount(provider);
|
||||||
identityNameMap.put(provider.getUserId(), identityName);
|
String accountName = oracleAccount.name();
|
||||||
identityMap.put(provider.getUserId(), provider);
|
if (accountName == null) {
|
||||||
return provider;
|
throw new NullPointerException("Failed to obtain the account name. The identity configuration may be incorrect.");
|
||||||
|
}
|
||||||
|
log.debug("已成功通过身份配置获取用户名称: {}", accountName);
|
||||||
|
accountMap.put(oracleAccount.id(), oracleAccount);
|
||||||
|
return oracleAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIdentityProfileConfig(ConfigFileReader.ConfigFile config) {
|
private boolean checkIdentityProfileConfig(ConfigFileReader.ConfigFile config) {
|
||||||
@ -144,35 +137,6 @@ public final class OracleIdentityManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取身份所属用户的名称.
|
|
||||||
* @param provider 身份提供器.
|
|
||||||
* @return 返回用户名.
|
|
||||||
*/
|
|
||||||
private String getIdentityName0(AuthenticationDetailsProvider provider) {
|
|
||||||
IdentityClient identityClient = new IdentityClient(provider);
|
|
||||||
GetUserResponse user = identityClient.getUser(GetUserRequest.builder()
|
|
||||||
.userId(provider.getUserId())
|
|
||||||
.build());
|
|
||||||
return user.getUser().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取身份信息所属的用户名.
|
|
||||||
* @param userId 身份信息所属的用户 Id.
|
|
||||||
* @return 返回用户名.
|
|
||||||
* @throws NullPointerException 当 userId 为 {@code null} 时抛出该异常.
|
|
||||||
* @throws NoSuchElementException 指定的 UserId 未找到对应用户名时抛出该异常.
|
|
||||||
*/
|
|
||||||
public String getIdentityName(String userId) {
|
|
||||||
Objects.requireNonNull(userId);
|
|
||||||
if (!identityMap.containsKey(userId)) {
|
|
||||||
throw new NoSuchElementException(userId);
|
|
||||||
}
|
|
||||||
return identityNameMap.get(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过 UserId 获取指定身份提供器.
|
* 通过 UserId 获取指定身份提供器.
|
||||||
* @param userId 用户 Id.
|
* @param userId 用户 Id.
|
||||||
@ -180,37 +144,20 @@ public final class OracleIdentityManager {
|
|||||||
* @throws NullPointerException 当 userId 为 {@code null} 时抛出该异常.
|
* @throws NullPointerException 当 userId 为 {@code null} 时抛出该异常.
|
||||||
* @throws NoSuchElementException 指定的 UserId 未找到对应 Provider 时抛出该异常.
|
* @throws NoSuchElementException 指定的 UserId 未找到对应 Provider 时抛出该异常.
|
||||||
*/
|
*/
|
||||||
public AuthenticationDetailsProvider getProviderByUserId(String userId) {
|
public OracleAccount getAccountByUserId(String userId) {
|
||||||
Objects.requireNonNull(userId);
|
Objects.requireNonNull(userId);
|
||||||
if (!identityMap.containsKey(userId)) {
|
if (!accountMap.containsKey(userId)) {
|
||||||
throw new NoSuchElementException(userId);
|
throw new NoSuchElementException(userId);
|
||||||
}
|
}
|
||||||
return identityMap.get(userId);
|
return accountMap.get(userId);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出身份信息.
|
|
||||||
* <p> 不包含私钥.
|
|
||||||
* @return 返回 Json 形式的身份信息数组.
|
|
||||||
*/
|
|
||||||
public JsonArray exportIdentityInfo() {
|
|
||||||
JsonArray identityInfoArray = new JsonArray(identityMap.size());
|
|
||||||
for (AuthenticationDetailsProvider provider : identityMap.values()) {
|
|
||||||
JsonObject identity = new JsonObject();
|
|
||||||
identity.addProperty("UserId", provider.getUserId());
|
|
||||||
identity.addProperty("TenantId", provider.getTenantId());
|
|
||||||
identity.addProperty("Fingerprint", provider.getFingerprint());
|
|
||||||
identityInfoArray.add(identity);
|
|
||||||
}
|
|
||||||
return identityInfoArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有身份提供器.
|
* 获取所有身份提供器.
|
||||||
* @return 返回包含所有身份提供器的集合对象.
|
* @return 返回包含所有身份提供器的集合对象.
|
||||||
*/
|
*/
|
||||||
public Set<AuthenticationDetailsProvider> getProviders() {
|
public Set<OracleAccount> getAccounts() {
|
||||||
return identityMap.values().stream().collect(Collectors.toUnmodifiableSet());
|
return accountMap.values().stream().collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package net.lamgc.oracle.sentry.oci.account;
|
||||||
|
|
||||||
|
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
||||||
|
import com.oracle.bmc.core.BlockstorageClient;
|
||||||
|
import com.oracle.bmc.core.ComputeClient;
|
||||||
|
import com.oracle.bmc.core.VirtualNetworkClient;
|
||||||
|
import com.oracle.bmc.identity.IdentityClient;
|
||||||
|
import com.oracle.bmc.objectstorage.ObjectStorageClient;
|
||||||
|
import net.lamgc.oracle.sentry.common.LazyLoader;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲骨文 SDK 客户端.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public class OracleClients {
|
||||||
|
|
||||||
|
private final Map<Class<?>, LazyLoader<?>> LAZY_LOADER_MAP = new ConcurrentHashMap<>();
|
||||||
|
private final AuthenticationDetailsProvider provider;
|
||||||
|
|
||||||
|
OracleClients(AuthenticationDetailsProvider provider) {
|
||||||
|
this.provider = Objects.requireNonNull(provider);
|
||||||
|
initialLazyLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialLazyLoad() {
|
||||||
|
registryLazyLoader(ComputeClient.class, () -> new ComputeClient(provider));
|
||||||
|
registryLazyLoader(VirtualNetworkClient.class, () -> new VirtualNetworkClient(provider));
|
||||||
|
registryLazyLoader(BlockstorageClient.class, () -> new BlockstorageClient(provider));
|
||||||
|
registryLazyLoader(IdentityClient.class, () -> new IdentityClient(provider));
|
||||||
|
registryLazyLoader(ObjectStorageClient.class, () -> new ObjectStorageClient(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取计算类客户端.
|
||||||
|
* @return 获取计算类客户端对象.
|
||||||
|
*/
|
||||||
|
public ComputeClient compute() {
|
||||||
|
return getInstance(ComputeClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网络客户端.
|
||||||
|
* @return 返回 VCN 操作客户端.
|
||||||
|
*/
|
||||||
|
public VirtualNetworkClient network() {
|
||||||
|
return getInstance(VirtualNetworkClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取块存储客户端.
|
||||||
|
* <p> 仅限计算实例的存储.
|
||||||
|
* @return 返回块存储客户端.
|
||||||
|
*/
|
||||||
|
public BlockstorageClient blockStorage() {
|
||||||
|
return getInstance(BlockstorageClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取身份客户端.
|
||||||
|
* <p> 用于访问身份相关 API.
|
||||||
|
* @return 返回身份客户端.
|
||||||
|
*/
|
||||||
|
public IdentityClient identity() {
|
||||||
|
return getInstance(IdentityClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象存储客户端.
|
||||||
|
* <p> 不包括计算实例的存储.
|
||||||
|
* @return 获取对象存储客户端.
|
||||||
|
*/
|
||||||
|
public ObjectStorageClient objectStorage() {
|
||||||
|
return getInstance(ObjectStorageClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实例.
|
||||||
|
* @param type 实例类.
|
||||||
|
* @param <T> 实例类型.
|
||||||
|
* @return 返回对象.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T getInstance(Class<?> type) {
|
||||||
|
Objects.requireNonNull(type);
|
||||||
|
if (!LAZY_LOADER_MAP.containsKey(type)) {
|
||||||
|
throw new NoSuchElementException("No lazy loader of this type was found: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) LAZY_LOADER_MAP.get(type).getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册惰性加载器.
|
||||||
|
* @param type 对象类.
|
||||||
|
* @param supplier 对象提供器.
|
||||||
|
* @param <T> 对象类型.
|
||||||
|
*/
|
||||||
|
private <T> void registryLazyLoader(Class<? extends T> type, Supplier<T> supplier) {
|
||||||
|
Objects.requireNonNull(type);
|
||||||
|
Objects.requireNonNull(supplier);
|
||||||
|
LAZY_LOADER_MAP.put(type, new LazyLoader<>(supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package net.lamgc.oracle.sentry.oci.compute;
|
package net.lamgc.oracle.sentry.oci.compute;
|
||||||
|
|
||||||
import com.oracle.bmc.auth.AuthenticationDetailsProvider;
|
|
||||||
import com.oracle.bmc.core.ComputeClient;
|
import com.oracle.bmc.core.ComputeClient;
|
||||||
import com.oracle.bmc.core.model.Instance;
|
import com.oracle.bmc.core.model.Instance;
|
||||||
import com.oracle.bmc.core.requests.GetImageRequest;
|
import com.oracle.bmc.core.requests.GetImageRequest;
|
||||||
@ -10,6 +9,7 @@ import com.oracle.bmc.core.responses.GetImageResponse;
|
|||||||
import com.oracle.bmc.core.responses.GetInstanceResponse;
|
import com.oracle.bmc.core.responses.GetInstanceResponse;
|
||||||
import com.oracle.bmc.core.responses.InstanceActionResponse;
|
import com.oracle.bmc.core.responses.InstanceActionResponse;
|
||||||
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
||||||
|
import net.lamgc.oracle.sentry.oci.account.OracleAccount;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ssh.InstanceSsh;
|
import net.lamgc.oracle.sentry.oci.compute.ssh.InstanceSsh;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ssh.SshAuthInfo;
|
import net.lamgc.oracle.sentry.oci.compute.ssh.SshAuthInfo;
|
||||||
|
|
||||||
@ -24,11 +24,10 @@ public final class ComputeInstance {
|
|||||||
private final ComputeInstanceManager instanceManager;
|
private final ComputeInstanceManager instanceManager;
|
||||||
|
|
||||||
private final String instanceId;
|
private final String instanceId;
|
||||||
private final String userId;
|
|
||||||
private final String compartmentId;
|
private final String compartmentId;
|
||||||
private final String imageId;
|
private final String imageId;
|
||||||
private final AuthenticationDetailsProvider authProvider;
|
|
||||||
private final InstanceNetwork network;
|
private final InstanceNetwork network;
|
||||||
|
private final OracleAccount fromAccount;
|
||||||
|
|
||||||
private final ComputeClient computeClient;
|
private final ComputeClient computeClient;
|
||||||
|
|
||||||
@ -36,22 +35,20 @@ public final class ComputeInstance {
|
|||||||
* 构造一个计算实例对象.
|
* 构造一个计算实例对象.
|
||||||
* @param instanceManager 实例所属的管理器.
|
* @param instanceManager 实例所属的管理器.
|
||||||
* @param instanceId 实例 Id.
|
* @param instanceId 实例 Id.
|
||||||
* @param userId 所属用户 Id.
|
|
||||||
* @param compartmentId 实例所在区域的 Id.
|
* @param compartmentId 实例所在区域的 Id.
|
||||||
* @param imageId 镜像 Id.
|
* @param imageId 镜像 Id.
|
||||||
* @param provider 所属用户的身份配置提供器.
|
* @param fromAccount 所属用户的身份配置提供器.
|
||||||
*/
|
*/
|
||||||
public ComputeInstance(ComputeInstanceManager instanceManager, String instanceId, String userId,
|
public ComputeInstance(ComputeInstanceManager instanceManager, String instanceId,
|
||||||
String compartmentId, String imageId, AuthenticationDetailsProvider provider) {
|
String compartmentId, String imageId, OracleAccount fromAccount) {
|
||||||
this.instanceManager = instanceManager;
|
this.instanceManager = instanceManager;
|
||||||
this.instanceId = instanceId;
|
this.instanceId = instanceId;
|
||||||
this.userId = userId;
|
|
||||||
this.compartmentId = compartmentId;
|
this.compartmentId = compartmentId;
|
||||||
this.imageId = imageId;
|
this.imageId = imageId;
|
||||||
this.authProvider = provider;
|
this.fromAccount = fromAccount;
|
||||||
|
|
||||||
computeClient = new ComputeClient(provider);
|
|
||||||
this.network = new InstanceNetwork(this);
|
this.network = new InstanceNetwork(this);
|
||||||
|
this.computeClient = fromAccount.clients().compute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,14 +60,6 @@ public final class ComputeInstance {
|
|||||||
return instanceId;
|
return instanceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所属用户的 Id.
|
|
||||||
* @return 返回用户 Id.
|
|
||||||
*/
|
|
||||||
public String getUserId() {
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务器所属区域的 Id.
|
* 获取服务器所属区域的 Id.
|
||||||
* <p> 使用的资源必须要处于同一区域, 例如 IP 资源, 磁盘.
|
* <p> 使用的资源必须要处于同一区域, 例如 IP 资源, 磁盘.
|
||||||
@ -88,8 +77,8 @@ public final class ComputeInstance {
|
|||||||
*/
|
*/
|
||||||
public BootImage getImage() {
|
public BootImage getImage() {
|
||||||
GetImageResponse image = computeClient.getImage(GetImageRequest.builder()
|
GetImageResponse image = computeClient.getImage(GetImageRequest.builder()
|
||||||
.imageId(imageId)
|
.imageId(imageId)
|
||||||
.build());
|
.build());
|
||||||
return new BootImage(image.getImage());
|
return new BootImage(image.getImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,10 +159,6 @@ public final class ComputeInstance {
|
|||||||
return computeClient;
|
return computeClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationDetailsProvider getAuthProvider() {
|
|
||||||
return authProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
@ -183,12 +168,12 @@ public final class ComputeInstance {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ComputeInstance that = (ComputeInstance) o;
|
ComputeInstance that = (ComputeInstance) o;
|
||||||
return instanceId.equals(that.instanceId) && userId.equals(that.userId) && compartmentId.equals(that.compartmentId);
|
return instanceId.equals(that.instanceId) && fromAccount.equals(that.fromAccount) && compartmentId.equals(that.compartmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(instanceId, userId, compartmentId);
|
return Objects.hash(instanceId, fromAccount, compartmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,4 +186,11 @@ public final class ComputeInstance {
|
|||||||
.getAuthInfoByInstanceId(instanceId);
|
.getAuthInfoByInstanceId(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实例所属的 Oracle 云帐号对象.
|
||||||
|
* @return 返回实例所属帐号对象.
|
||||||
|
*/
|
||||||
|
public OracleAccount getFromAccount() {
|
||||||
|
return fromAccount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import java.util.NoSuchElementException;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 实例网络操作类.
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
*/
|
*/
|
||||||
public class InstanceNetwork {
|
public class InstanceNetwork {
|
||||||
@ -22,7 +23,7 @@ public class InstanceNetwork {
|
|||||||
|
|
||||||
InstanceNetwork(ComputeInstance instance) {
|
InstanceNetwork(ComputeInstance instance) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.vcnClient = new VirtualNetworkClient(this.instance.getAuthProvider());
|
this.vcnClient = instance.getFromAccount().clients().network();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
||||||
|
|
||||||
import net.lamgc.oracle.sentry.common.InputStreamWrapper;
|
import net.lamgc.oracle.sentry.common.io.InputStreamWrapper;
|
||||||
import net.lamgc.oracle.sentry.common.OutputStreamWrapper;
|
import net.lamgc.oracle.sentry.common.io.OutputStreamWrapper;
|
||||||
import org.apache.sshd.client.channel.ChannelExec;
|
import org.apache.sshd.client.channel.ChannelExec;
|
||||||
import org.apache.sshd.client.channel.ClientChannelEvent;
|
import org.apache.sshd.client.channel.ClientChannelEvent;
|
||||||
|
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
||||||
|
|
||||||
|
import org.apache.sshd.common.session.Session;
|
||||||
|
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||||
|
import org.apache.sshd.server.forward.ForwardingFilter;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已配置转发过滤器.
|
||||||
|
* <p> 根据应用配置, 选择是否允许转发指定类型的流量.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
@Component("sentry.script.ssh.forwarding.filter")
|
||||||
|
public class ConfiguredForwardingFilter implements ForwardingFilter {
|
||||||
|
|
||||||
|
@Value("oracle.ssh.forwarding.X11.enable")
|
||||||
|
private String x11Enabled;
|
||||||
|
|
||||||
|
@Value("oracle.ssh.forwarding.tcp.enable")
|
||||||
|
private String tcpEnabled;
|
||||||
|
|
||||||
|
@Value("oracle.ssh.forwarding.agent.enable")
|
||||||
|
private String agentEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canForwardAgent(Session session, String requestType) {
|
||||||
|
return Boolean.parseBoolean(agentEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canListen(SshdSocketAddress address, Session session) {
|
||||||
|
return Boolean.parseBoolean(tcpEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
|
||||||
|
return Boolean.parseBoolean(tcpEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canForwardX11(Session session, String requestType) {
|
||||||
|
return Boolean.parseBoolean(x11Enabled);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import net.lamgc.oracle.sentry.Constants;
|
||||||
import net.lamgc.oracle.sentry.oci.compute.ComputeInstance;
|
import net.lamgc.oracle.sentry.oci.compute.ComputeInstance;
|
||||||
import org.apache.sshd.client.SshClient;
|
import org.apache.sshd.client.SshClient;
|
||||||
import org.apache.sshd.client.future.AuthFuture;
|
import org.apache.sshd.client.future.AuthFuture;
|
||||||
@ -40,6 +41,7 @@ public class InstanceSsh implements AutoCloseable {
|
|||||||
this.authInfo = Objects.requireNonNull(authInfo);
|
this.authInfo = Objects.requireNonNull(authInfo);
|
||||||
|
|
||||||
sshClient = SshClient.setUpDefaultClient();
|
sshClient = SshClient.setUpDefaultClient();
|
||||||
|
sshClient.setForwardingFilter(Constants.instance.getForwardingFilter());
|
||||||
sshClient.setServerKeyVerifier(new OracleInstanceServerKeyVerifier(instance, authInfo));
|
sshClient.setServerKeyVerifier(new OracleInstanceServerKeyVerifier(instance, authInfo));
|
||||||
if (authInfo instanceof PublicKeyAuthInfo info) {
|
if (authInfo instanceof PublicKeyAuthInfo info) {
|
||||||
sshClient.setKeyIdentityProvider(new FileKeyPairProvider(info.getPrivateKeyPath().toPath()));
|
sshClient.setKeyIdentityProvider(new FileKeyPairProvider(info.getPrivateKeyPath().toPath()));
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
||||||
|
|
||||||
import org.apache.sshd.client.session.ClientSession;
|
import org.apache.sshd.client.session.ClientSession;
|
||||||
|
import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker;
|
||||||
|
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||||
import org.apache.sshd.sftp.client.SftpClientFactory;
|
import org.apache.sshd.sftp.client.SftpClientFactory;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@ -45,6 +47,35 @@ public class SshSession implements Closeable {
|
|||||||
return new SftpSession(factory.createSftpClient(clientSession));
|
return new SftpSession(factory.createSftpClient(clientSession));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建本地 TCP 转发隧道.
|
||||||
|
* <p> 该隧道为方向为 "本地->远端" (本地发起连接转发至远端端口).
|
||||||
|
* @param localPort 本地监听端口.
|
||||||
|
* @param remotePort 远端目标端口.
|
||||||
|
* @return 返回 TCP 转发通道对象, 可获取通道信息和关闭通道.
|
||||||
|
* @throws IOException 当操作失败时抛出该异常.
|
||||||
|
*/
|
||||||
|
public TcpForwardingChannel createLocalTcpForwarding(int localPort, int remotePort) throws IOException {
|
||||||
|
ExplicitPortForwardingTracker tracker = clientSession
|
||||||
|
.createLocalPortForwardingTracker(localPort, new SshdSocketAddress(remotePort));
|
||||||
|
return new TcpForwardingChannel(tracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建远端 TCP 转发隧道.
|
||||||
|
* <p> 该隧道为方向为 "本地<-远端" (远端服务器发起连接转发至本地端口).
|
||||||
|
* @param remotePort 远端监听端口号, 该端口为远端服务连接转发的端口号.
|
||||||
|
* @param localPort 本地连接端口号, 该端口为本地服务端的端口号.
|
||||||
|
* @return 返回 Tcp 转发通道对象, 用于管理转发通道.
|
||||||
|
* @throws IOException 当操作失败时抛出异常.
|
||||||
|
*/
|
||||||
|
public TcpForwardingChannel createRemoteTcpForwarding(int remotePort, int localPort) throws IOException {
|
||||||
|
ExplicitPortForwardingTracker tracker =
|
||||||
|
clientSession.createRemotePortForwardingTracker(
|
||||||
|
new SshdSocketAddress(remotePort), new SshdSocketAddress(localPort));
|
||||||
|
return new TcpForwardingChannel(tracker);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭 SSH 连接会话, 该连接会话所属的其他会话将会一同被关闭.
|
* 关闭 SSH 连接会话, 该连接会话所属的其他会话将会一同被关闭.
|
||||||
* @throws IOException 关闭失败时抛出异常,
|
* @throws IOException 关闭失败时抛出异常,
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package net.lamgc.oracle.sentry.oci.compute.ssh;
|
||||||
|
|
||||||
|
import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker;
|
||||||
|
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP 隧道.
|
||||||
|
* <p> 可通过该对象管理隧道.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public class TcpForwardingChannel implements AutoCloseable {
|
||||||
|
|
||||||
|
private final ExplicitPortForwardingTracker tracker;
|
||||||
|
|
||||||
|
TcpForwardingChannel(ExplicitPortForwardingTracker tracker) {
|
||||||
|
this.tracker = tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为本地转发.
|
||||||
|
* @return 如果是本地转发, 则为 {@code true}, 否则返回 {@code false}, 代表远端转发.
|
||||||
|
*/
|
||||||
|
public boolean isLocalForwarding() {
|
||||||
|
return tracker.isLocalForwarding();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隧道是否已打开.
|
||||||
|
* @return 如果已经打开, 返回 {@code true}, 否则返回 {@code false} 表示已关闭.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
return tracker.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地地址.
|
||||||
|
* @return 获取本地连接地址.
|
||||||
|
*/
|
||||||
|
public SshdSocketAddress getLocalAddress() {
|
||||||
|
return tracker.getLocalAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取远端地址.
|
||||||
|
* @return 获取远端地址.
|
||||||
|
*/
|
||||||
|
public SshdSocketAddress getRemoteAddress() {
|
||||||
|
return tracker.getRemoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取监听绑定地址.
|
||||||
|
* @return 返回监听绑定地址.
|
||||||
|
*/
|
||||||
|
public SshdSocketAddress getBoundAddress() {
|
||||||
|
return tracker.getBoundAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
tracker.close();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package net.lamgc.oracle.sentry.script;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本组件工厂.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public interface ScriptComponentFactory<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并获取实例.
|
||||||
|
* @param info 脚本信息.
|
||||||
|
* @return 返回对象.
|
||||||
|
*/
|
||||||
|
T getInstance(ScriptInfo info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象属性名.
|
||||||
|
* @return 返回建议的对象属性名, {@link ScriptLoader} 并不一定遵守.
|
||||||
|
*/
|
||||||
|
String getPropertyName();
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package net.lamgc.oracle.sentry.script;
|
package net.lamgc.oracle.sentry.script;
|
||||||
|
|
||||||
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
||||||
|
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,7 +12,9 @@ import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
|||||||
*/
|
*/
|
||||||
public final record ScriptComponents(
|
public final record ScriptComponents(
|
||||||
ScriptHttpClient HTTP,
|
ScriptHttpClient HTTP,
|
||||||
ComputeInstanceManager InstanceManager
|
ComputeInstanceManager InstanceManager,
|
||||||
|
ScriptLoggerFactory loggerFactory,
|
||||||
|
OracleAccountManager AccountManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
package net.lamgc.oracle.sentry.script;
|
||||||
|
|
||||||
|
import net.sf.cglib.proxy.Enhancer;
|
||||||
|
import net.sf.cglib.proxy.MethodInterceptor;
|
||||||
|
import net.sf.cglib.proxy.MethodProxy;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.MarkerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本日志记录器工厂.
|
||||||
|
* <p> 通过 CGLIB 无缝为脚本设置 {@link Marker} 以将脚本日志输出到特定文件中.
|
||||||
|
* @author LamGC
|
||||||
|
*/
|
||||||
|
public class ScriptLoggerFactory implements ScriptComponentFactory<Logger> {
|
||||||
|
|
||||||
|
private final static Marker SCRIPT_MARKER = MarkerFactory.getMarker("Script");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Logger getInstance(ScriptInfo info) {
|
||||||
|
Logger realLogger = LoggerFactory.getLogger(info.getGroup() + ":" + info.getName());
|
||||||
|
Enhancer enhancer = new Enhancer();
|
||||||
|
enhancer.setSuperclass(Logger.class);
|
||||||
|
enhancer.setCallback(new LoggerProxyImpl(realLogger));
|
||||||
|
return (Logger) enhancer.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPropertyName() {
|
||||||
|
return "Log";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LoggerProxyImpl implements MethodInterceptor {
|
||||||
|
|
||||||
|
private final static Set<String> PROXY_METHOD_NAMES = Set.of(
|
||||||
|
"trace", "debug", "info", "warn", "error",
|
||||||
|
"isTraceEnabled",
|
||||||
|
"isDebugEnabled",
|
||||||
|
"isInfoEnabled",
|
||||||
|
"isWarnEnabled",
|
||||||
|
"isErrorEnabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
private final static ConcurrentHashMap<Method, Method> METHOD_CACHE_MAP =
|
||||||
|
new ConcurrentHashMap<>(PROXY_METHOD_NAMES.size(), 1);
|
||||||
|
|
||||||
|
private final Logger targetLog;
|
||||||
|
private final Class<? extends Logger> logClass;
|
||||||
|
|
||||||
|
public LoggerProxyImpl(Logger targetLog) {
|
||||||
|
this.targetLog = targetLog;
|
||||||
|
logClass = targetLog.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||||
|
if (METHOD_CACHE_MAP.contains(method)) {
|
||||||
|
return METHOD_CACHE_MAP.get(method).invoke(targetLog, insertParameterToArray(args, SCRIPT_MARKER));
|
||||||
|
} else if (PROXY_METHOD_NAMES.contains(method.getName())) {
|
||||||
|
Class<?>[] types = method.getParameterTypes();
|
||||||
|
if (types.length != 0 && !Marker.class.isAssignableFrom(types[0])) {
|
||||||
|
Class<?>[] realMethodParamTypes = insertTypeToArray(types, Marker.class);
|
||||||
|
Method realMethod = logClass.getDeclaredMethod(method.getName(), realMethodParamTypes);
|
||||||
|
METHOD_CACHE_MAP.put(method, realMethod);
|
||||||
|
realMethod.invoke(targetLog, insertParameterToArray(args, SCRIPT_MARKER));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proxy.invoke(targetLog, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] insertParameterToArray(Object[] arr, Object newElement) {
|
||||||
|
if (arr.length == 0) {
|
||||||
|
return new Object[] {newElement};
|
||||||
|
}
|
||||||
|
Object[] newArr = new Object[arr.length + 1];
|
||||||
|
newArr[0] = newElement;
|
||||||
|
System.arraycopy(arr, 0, newArr, 1, arr.length);
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?>[] insertTypeToArray(Class<?>[] arr, Class<?> newElement) {
|
||||||
|
if (arr.length == 0) {
|
||||||
|
return new Class<?>[] {newElement};
|
||||||
|
}
|
||||||
|
Class<?>[] newArr = new Class<?>[arr.length + 1];
|
||||||
|
newArr[0] = newElement;
|
||||||
|
System.arraycopy(arr, 0, newArr, 1, arr.length);
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,10 +2,9 @@ package net.lamgc.oracle.sentry.script.groovy;
|
|||||||
|
|
||||||
import groovy.lang.Closure;
|
import groovy.lang.Closure;
|
||||||
import groovy.lang.DelegatesTo;
|
import groovy.lang.DelegatesTo;
|
||||||
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
|
||||||
import net.lamgc.oracle.sentry.script.Script;
|
import net.lamgc.oracle.sentry.script.Script;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptInfo;
|
import net.lamgc.oracle.sentry.script.ScriptInfo;
|
||||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
import net.lamgc.oracle.sentry.script.groovy.trigger.GroovyTrigger;
|
||||||
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
|
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,20 +15,28 @@ import org.codehaus.groovy.runtime.DefaultGroovyMethods;
|
|||||||
public class GroovyDslDelegate implements Script {
|
public class GroovyDslDelegate implements Script {
|
||||||
|
|
||||||
private final GroovyScriptInfo scriptInfo = new GroovyScriptInfo();
|
private final GroovyScriptInfo scriptInfo = new GroovyScriptInfo();
|
||||||
|
private final GroovyScriptLoader scriptLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建一个 DSL Delegate, 并传入可操作对象.
|
* 构建一个 DSL Delegate, 并传入可操作对象.
|
||||||
|
* @param scriptLoader 该脚本所属的加载器.
|
||||||
*/
|
*/
|
||||||
public GroovyDslDelegate() {
|
public GroovyDslDelegate(GroovyScriptLoader scriptLoader) {
|
||||||
|
this.scriptLoader = scriptLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册触发器.
|
* 注册触发器.
|
||||||
|
* <p> 注意: 如果脚本尚未初始化完成, 将无法注册触发器, 可通过 {@link #isInitialed()} 检查是否已经完成初始化.
|
||||||
* @param triggerName 触发器名称.
|
* @param triggerName 触发器名称.
|
||||||
* @param closure 待执行闭包.
|
* @param closure 待执行闭包.
|
||||||
*/
|
*/
|
||||||
private void trigger(String triggerName, Closure<?> closure){
|
private void trigger(String triggerName, Closure<?> closure){
|
||||||
DefaultGroovyMethods.with(GroovyTriggerProvider.INSTANCE.getTriggerByName(triggerName), closure);
|
if (!scriptLoader.isInitialed(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GroovyTrigger trigger = GroovyTriggerProvider.INSTANCE.getTriggerByName(triggerName);
|
||||||
|
DefaultGroovyMethods.with(trigger, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,4 +51,12 @@ public class GroovyDslDelegate implements Script {
|
|||||||
public ScriptInfo getScriptInfo() {
|
public ScriptInfo getScriptInfo() {
|
||||||
return scriptInfo;
|
return scriptInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查脚本当前是否已经初始化完成.
|
||||||
|
* @return 如果脚本已经初始化, 本方法将返回 {@code true}.
|
||||||
|
*/
|
||||||
|
public final boolean isInitialed() {
|
||||||
|
return scriptLoader.isInitialed(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.lamgc.oracle.sentry.script.groovy;
|
package net.lamgc.oracle.sentry.script.groovy;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import groovy.lang.Binding;
|
import groovy.lang.Binding;
|
||||||
import groovy.lang.GroovyClassLoader;
|
import groovy.lang.GroovyClassLoader;
|
||||||
@ -8,6 +9,7 @@ import net.lamgc.oracle.sentry.script.Script;
|
|||||||
import net.lamgc.oracle.sentry.script.ScriptComponents;
|
import net.lamgc.oracle.sentry.script.ScriptComponents;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptInfo;
|
import net.lamgc.oracle.sentry.script.ScriptInfo;
|
||||||
import net.lamgc.oracle.sentry.script.ScriptLoader;
|
import net.lamgc.oracle.sentry.script.ScriptLoader;
|
||||||
|
import net.lamgc.oracle.sentry.script.ScriptComponentFactory;
|
||||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -17,7 +19,9 @@ import java.io.IOException;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,8 +34,8 @@ public class GroovyScriptLoader implements ScriptLoader {
|
|||||||
private final static Logger log = LoggerFactory.getLogger(GroovyScriptLoader.class);
|
private final static Logger log = LoggerFactory.getLogger(GroovyScriptLoader.class);
|
||||||
|
|
||||||
private final GroovyClassLoader scriptClassLoader;
|
private final GroovyClassLoader scriptClassLoader;
|
||||||
|
|
||||||
private final Map<Script, ScriptInfo> scriptInfoMap = new ConcurrentHashMap<>();
|
private final Map<Script, ScriptInfo> scriptInfoMap = new ConcurrentHashMap<>();
|
||||||
|
private final Set<Script> initialedScript = new HashSet<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造一个新的脚本加载器.
|
* 构造一个新的脚本加载器.
|
||||||
@ -58,11 +62,17 @@ public class GroovyScriptLoader implements ScriptLoader {
|
|||||||
Constructor<? extends DelegatingScript> constructor =
|
Constructor<? extends DelegatingScript> constructor =
|
||||||
scriptClass.asSubclass(DelegatingScript.class).getConstructor();
|
scriptClass.asSubclass(DelegatingScript.class).getConstructor();
|
||||||
DelegatingScript newScriptObject = constructor.newInstance();
|
DelegatingScript newScriptObject = constructor.newInstance();
|
||||||
GroovyDslDelegate dslDelegate = new GroovyDslDelegate();
|
GroovyDslDelegate dslDelegate = new GroovyDslDelegate(this);
|
||||||
newScriptObject.setDelegate(dslDelegate);
|
newScriptObject.setDelegate(dslDelegate);
|
||||||
newScriptObject.setBinding(createBinding(context));
|
|
||||||
newScriptObject.run();
|
newScriptObject.run();
|
||||||
scriptInfoMap.put(dslDelegate, dslDelegate.getScriptInfo());
|
ScriptInfo scriptInfo = dslDelegate.getScriptInfo();
|
||||||
|
if (!checkScriptInfo(scriptInfo)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
initialedScript.add(dslDelegate);
|
||||||
|
newScriptObject.setBinding(createBinding(context, scriptInfo));
|
||||||
|
newScriptObject.run();
|
||||||
|
scriptInfoMap.put(dslDelegate, scriptInfo);
|
||||||
return dslDelegate;
|
return dslDelegate;
|
||||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
log.error("加载脚本时发生异常.(ScriptPath: {})\n{}", scriptFile.getAbsolutePath(), Throwables.getStackTraceAsString(e));
|
log.error("加载脚本时发生异常.(ScriptPath: {})\n{}", scriptFile.getAbsolutePath(), Throwables.getStackTraceAsString(e));
|
||||||
@ -75,18 +85,48 @@ public class GroovyScriptLoader implements ScriptLoader {
|
|||||||
return scriptInfoMap.get(script);
|
return scriptInfoMap.get(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Binding createBinding(ScriptComponents components) {
|
/**
|
||||||
|
* 检查脚本是否已经初始化完毕.
|
||||||
|
* @param script 脚本对象.
|
||||||
|
* @return 如果已初始化完毕, 返回 {@code true}.
|
||||||
|
*/
|
||||||
|
public boolean isInitialed(GroovyDslDelegate script) {
|
||||||
|
return initialedScript.contains(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Binding createBinding(ScriptComponents components, ScriptInfo info) {
|
||||||
Binding binding = new Binding();
|
Binding binding = new Binding();
|
||||||
for (Field field : components.getClass().getDeclaredFields()) {
|
for (Field field : components.getClass().getDeclaredFields()) {
|
||||||
try {
|
try {
|
||||||
String name = field.getName();
|
String name = field.getName();
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
Object o = field.get(components);
|
Object o = field.get(components);
|
||||||
binding.setProperty(name, o);
|
if (o instanceof ScriptComponentFactory factory) {
|
||||||
|
binding.setProperty(factory.getPropertyName(), factory.getInstance(info));
|
||||||
|
} else {
|
||||||
|
binding.setProperty(name, o);
|
||||||
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean checkScriptInfo(ScriptInfo info) {
|
||||||
|
if (Strings.isNullOrEmpty(info.getGroup())) {
|
||||||
|
log.warn("脚本信息缺少 {}, 跳过加载.", "Group");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(info.getName())) {
|
||||||
|
log.warn("脚本信息缺少 {}, 跳过加载.", "Name");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Strings.isNullOrEmpty(info.getVersion())) {
|
||||||
|
log.warn("脚本信息缺少 {}, 跳过加载.", "Version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,9 @@ public interface GroovyTrigger {
|
|||||||
*/
|
*/
|
||||||
void run(Closure<?> task);
|
void run(Closure<?> task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭触发器.
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package net.lamgc.oracle.sentry.script.groovy.trigger;
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import groovy.lang.Closure;
|
import groovy.lang.Closure;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@ -14,17 +13,21 @@ import java.util.concurrent.Executors;
|
|||||||
*/
|
*/
|
||||||
@TriggerName("once")
|
@TriggerName("once")
|
||||||
public class OnceTrigger implements GroovyTrigger {
|
public class OnceTrigger implements GroovyTrigger {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(OnceTrigger.class);
|
|
||||||
private final static ExecutorService EXECUTOR = Executors.newFixedThreadPool(
|
private final static ExecutorService EXECUTOR = Executors.newFixedThreadPool(
|
||||||
Runtime.getRuntime().availableProcessors(),
|
Runtime.getRuntime().availableProcessors(),
|
||||||
new ThreadFactoryBuilder()
|
new ThreadFactoryBuilder()
|
||||||
.setNameFormat("GroovyOnceExec-%d")
|
.setNameFormat("GroovyOnceExec-%d")
|
||||||
.setUncaughtExceptionHandler((t, e) -> log.error("脚本执行时发生未捕获异常.", e))
|
.setUncaughtExceptionHandler((t, e) -> LoggerFactory.getLogger(OnceTrigger.class)
|
||||||
|
.error("脚本执行时发生未捕获异常.", e))
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(Closure<?> task) {
|
public void run(Closure<?> task) {
|
||||||
EXECUTOR.execute(task);
|
EXECUTOR.execute(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
// Nothing.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,30 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
import org.springframework.scheduling.support.CronTrigger;
|
import org.springframework.scheduling.support.CronTrigger;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 时间触发器.
|
||||||
|
* <p> 通过设置 Cron 时间表达式, 可以达到定时触发的效果.
|
||||||
* @author LamGC
|
* @author LamGC
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@TriggerName("timer")
|
@TriggerName("timer")
|
||||||
public class TimerTrigger implements GroovyTrigger {
|
public class TimerTrigger implements GroovyTrigger {
|
||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(TimerTrigger.class);
|
|
||||||
|
|
||||||
private CronTrigger trigger;
|
|
||||||
private final static ThreadPoolTaskScheduler SCHEDULER = new ThreadPoolTaskScheduler();
|
private final static ThreadPoolTaskScheduler SCHEDULER = new ThreadPoolTaskScheduler();
|
||||||
static {
|
static {
|
||||||
SCHEDULER.setPoolSize(Runtime.getRuntime().availableProcessors());
|
SCHEDULER.setPoolSize(Runtime.getRuntime().availableProcessors());
|
||||||
SCHEDULER.setThreadFactory(new ThreadFactoryBuilder()
|
SCHEDULER.setThreadFactory(new ThreadFactoryBuilder()
|
||||||
.setNameFormat("Groovy-TimerTrigger-%d")
|
.setNameFormat("Groovy-TimerTrigger-%d")
|
||||||
.build());
|
.build());
|
||||||
SCHEDULER.setErrorHandler(t -> log.error("脚本执行时发生异常.", t));
|
SCHEDULER.setErrorHandler(t -> triggerLog().error("脚本执行时发生异常.", t));
|
||||||
|
SCHEDULER.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CronTrigger trigger;
|
||||||
|
private ScheduledFuture<?> future;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设定定时时间.
|
* 设定定时时间.
|
||||||
* <p> 只允许在第一次执行时设置.
|
* <p> 只允许在第一次执行时设置.
|
||||||
@ -39,26 +44,42 @@ public class TimerTrigger implements GroovyTrigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(Closure<?> runnable) {
|
public synchronized void run(Closure<?> runnable) {
|
||||||
|
if (future != null) {
|
||||||
|
triggerLog().warn("脚本存在多个 run 代码块, 已忽略.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (trigger == null) {
|
if (trigger == null) {
|
||||||
if (!log.isDebugEnabled()) {
|
if (!triggerLog().isDebugEnabled()) {
|
||||||
log.warn("脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).");
|
triggerLog().warn("脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).");
|
||||||
} else {
|
} else {
|
||||||
log.warn("{} - 脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).", this);
|
triggerLog().warn("{} - 脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).", this);
|
||||||
log.warn("{} - 脚本尚未设置 Cron 时间表达式, 任务将不会执行.\n{}", this, Throwables.getStackTraceAsString(new Exception()));
|
triggerLog().warn("{} - 脚本尚未设置 Cron 时间表达式, 任务将不会执行.\n{}", this, Throwables.getStackTraceAsString(new Exception()));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (runnable == null) {
|
} else if (runnable == null) {
|
||||||
if (!log.isDebugEnabled()) {
|
if (!triggerLog().isDebugEnabled()) {
|
||||||
log.warn("脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).");
|
triggerLog().warn("脚本尚未设置 Cron 时间表达式, 任务将不会执行(堆栈信息请检查调试级别日志).");
|
||||||
} else {
|
} else {
|
||||||
log.warn("{} - 脚本尚未设置任务动作, 任务将不会执行(堆栈信息请检查调试级别日志).", this);
|
triggerLog().warn("{} - 脚本尚未设置任务动作, 任务将不会执行(堆栈信息请检查调试级别日志).", this);
|
||||||
log.warn("{} - 脚本尚未设置任务动作, 任务将不会执行.\n{}", this, Throwables.getStackTraceAsString(new Exception()));
|
triggerLog().warn("{} - 脚本尚未设置任务动作, 任务将不会执行.\n{}", this, Throwables.getStackTraceAsString(new Exception()));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCHEDULER.schedule(new TimerTaskRunnable(runnable), trigger);
|
this.future = SCHEDULER.schedule(new TimerTaskRunnable(runnable), trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
if (this.future != null) {
|
||||||
|
future.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Logger triggerLog() {
|
||||||
|
return LoggerFactory.getLogger(TimerTrigger.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TimerTaskRunnable implements Runnable {
|
private static class TimerTaskRunnable implements Runnable {
|
||||||
|
@ -14,3 +14,14 @@ oracle:
|
|||||||
# 首次连接认证策略
|
# 首次连接认证策略
|
||||||
# 支持 confirm(询问) accept(接受) reject(拒绝)
|
# 支持 confirm(询问) accept(接受) reject(拒绝)
|
||||||
authenticationPolicy: 'confirm'
|
authenticationPolicy: 'confirm'
|
||||||
|
# SSH 转发设定
|
||||||
|
forwarding:
|
||||||
|
# X11 转发
|
||||||
|
X11:
|
||||||
|
enable: false
|
||||||
|
# SSH-Agent 转发
|
||||||
|
agent:
|
||||||
|
enable: false
|
||||||
|
# TCP 转发
|
||||||
|
tcp:
|
||||||
|
enable: true
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
<configuration debug="false">
|
<configuration debug="false">
|
||||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<target>System.out</target>
|
<target>System.out</target>
|
||||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
<filter class="net.lamgc.oracle.sentry.common.logging.NoMarkerFilter"/>
|
||||||
<level>INFO</level>
|
<filter class="net.lamgc.oracle.sentry.common.logging.LevelRangeFilter">
|
||||||
<onMatch>ACCEPT</onMatch>
|
<minLevel>DEBUG</minLevel>
|
||||||
<onMismatch>DENY</onMismatch>
|
<maxLevel>INFO</maxLevel>
|
||||||
</filter>
|
</filter>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>[%d{HH:mm:ss.SSS} %5level][%logger][%thread]: %msg%n</pattern>
|
<pattern>[%d{HH:mm:ss.SSS} %5level][%logger][%thread]: %msg%n</pattern>
|
||||||
@ -13,6 +13,7 @@
|
|||||||
</appender>
|
</appender>
|
||||||
<appender name="stderr" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="stderr" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<target>System.err</target>
|
<target>System.err</target>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.NoMarkerFilter"/>
|
||||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
<level>WARN</level>
|
<level>WARN</level>
|
||||||
</filter>
|
</filter>
|
||||||
@ -23,6 +24,7 @@
|
|||||||
|
|
||||||
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>./logs/latest.log</file>
|
<file>./logs/latest.log</file>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.NoMarkerFilter"/>
|
||||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
<level>TRACE</level>
|
<level>TRACE</level>
|
||||||
<onMatch>DENY</onMatch>
|
<onMatch>DENY</onMatch>
|
||||||
@ -37,11 +39,62 @@
|
|||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
|
<appender name="stdout_script" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<target>System.out</target>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.MarkerFilter">
|
||||||
|
<markerName>Script</markerName>
|
||||||
|
</filter>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.LevelRangeFilter">
|
||||||
|
<minLevel>DEBUG</minLevel>
|
||||||
|
<maxLevel>INFO</maxLevel>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>[%d{HH:mm:ss.SSS} %5level][Script][%logger]: %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<appender name="stderr_script" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<target>System.err</target>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.MarkerFilter">
|
||||||
|
<markerName>Script</markerName>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>WARN</level>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>[%d{HH:mm:ss.SSS} %5level][Script][%logger]: %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="logFile_script" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>./logs/latest-script.log</file>
|
||||||
|
<filter class="net.lamgc.oracle.sentry.common.logging.MarkerFilter">
|
||||||
|
<markerName>Script</markerName>
|
||||||
|
</filter>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>TRACE</level>
|
||||||
|
<onMatch>DENY</onMatch>
|
||||||
|
<onMismatch>ACCEPT</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<encoder>
|
||||||
|
<pattern>[%d{HH:mm:ss.SSS} %5level][Script][%logger]: %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>./logs/run-script-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
|
<maxHistory>30</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
<logger name="com.oracle.bmc" level="WARN"/>
|
<logger name="com.oracle.bmc" level="WARN"/>
|
||||||
|
<logger name="org.springframework" level="INFO"/>
|
||||||
|
<logger name="org.apache.http" level="INFO"/>
|
||||||
<logger name="com.oracle.bmc.http.ApacheConfigurator" level="ERROR"/>
|
<logger name="com.oracle.bmc.http.ApacheConfigurator" level="ERROR"/>
|
||||||
<root>
|
<root>
|
||||||
<appender-ref ref="stdout" />
|
<appender-ref ref="stdout" />
|
||||||
<appender-ref ref="stderr" />
|
<appender-ref ref="stderr" />
|
||||||
<appender-ref ref="logFile" />
|
<appender-ref ref="logFile" />
|
||||||
|
|
||||||
|
<appender-ref ref="stdout_script" />
|
||||||
|
<appender-ref ref="stderr_script" />
|
||||||
|
<appender-ref ref="logFile_script" />
|
||||||
</root>
|
</root>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package net.lamgc.oracle.sentry.common;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class LazyLoaderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public void lazyLoadTest() throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
LazyLoader<Object> loader = new LazyLoader<>(Object::new);
|
||||||
|
|
||||||
|
Field field = LazyLoader.class.getDeclaredField("object");
|
||||||
|
field.setAccessible(true);
|
||||||
|
AtomicReference reference = (AtomicReference) field.get(loader);
|
||||||
|
assertNotNull(reference);
|
||||||
|
assertNull(reference.get());
|
||||||
|
Object instance = loader.getInstance();
|
||||||
|
assertEquals(reference.get(), instance);
|
||||||
|
assertEquals(reference.get(), loader.getInstance());
|
||||||
|
assertEquals(instance, loader.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
|
public void multiThreadAccessTest() {
|
||||||
|
class Singleton {
|
||||||
|
private final static AtomicInteger constructNum = new AtomicInteger(0);
|
||||||
|
public Singleton() {
|
||||||
|
if (constructNum.incrementAndGet() > 1) {
|
||||||
|
fail("Multiple instances were generated.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final LazyLoader<Singleton> loader = new LazyLoader<>(Singleton::new);
|
||||||
|
AtomicBoolean start = new AtomicBoolean(false);
|
||||||
|
int threadNum = Runtime.getRuntime().availableProcessors();
|
||||||
|
List<Thread> threads = new ArrayList<>(threadNum);
|
||||||
|
for (int i = 0; i < threadNum; i++) {
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
while (!start.get()) {
|
||||||
|
}
|
||||||
|
assertNotNull(loader.getInstance());
|
||||||
|
});
|
||||||
|
threads.add(thread);
|
||||||
|
}
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
start.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package net.lamgc.oracle.sentry.script;
|
package net.lamgc.oracle.sentry.script;
|
||||||
|
|
||||||
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
import net.lamgc.oracle.sentry.ComputeInstanceManager;
|
||||||
|
import net.lamgc.oracle.sentry.oci.account.OracleAccountManager;
|
||||||
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -13,7 +14,7 @@ class ScriptManagerTest {
|
|||||||
public void loadScriptTest() {
|
public void loadScriptTest() {
|
||||||
ScriptManager manager = new ScriptManager(new File("./run/scripts"),
|
ScriptManager manager = new ScriptManager(new File("./run/scripts"),
|
||||||
new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create().build()),
|
new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create().build()),
|
||||||
new ComputeInstanceManager()));
|
new ComputeInstanceManager(), new ScriptLoggerFactory(), new OracleAccountManager()));
|
||||||
|
|
||||||
manager.loadScripts();
|
manager.loadScripts();
|
||||||
|
|
||||||
|
@ -7,4 +7,9 @@ public abstract class BaseTestTrigger implements GroovyTrigger {
|
|||||||
public void run(Closure<?> task) {
|
public void run(Closure<?> task) {
|
||||||
throw new UnsupportedOperationException("Unavailable trigger.");
|
throw new UnsupportedOperationException("Unavailable trigger.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user