From bec7010d43c6f2ba45ab858bc3039f94789d0ab6 Mon Sep 17 00:00:00 2001 From: LamGC Date: Sun, 5 Sep 2021 14:36:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E5=81=9A=20Oracle=20Identity?= =?UTF-8?q?=20=E7=9B=B8=E5=85=B3=E7=BB=84=E4=BB=B6,=20=E4=B8=BA=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E6=8F=90=E4=BE=9B=E8=AE=BF=E9=97=AE=20Oracle=20Accoun?= =?UTF-8?q?t=20=E7=9A=84=E7=BB=84=E4=BB=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重做 OracleIdentityManager, 增加 OracleAccount 与 OracleClients 对象, 使脚本可以访问 SDK, 执行更多操作. 调整 ComputeInstance 与 InstanceNetwork, SDK Client 不再直接创建, 而是使用 OracleClients 中的客户端单例. --- .../oracle/sentry/ApplicationInitiation.java | 20 ++-- .../oracle/sentry/ComputeInstanceManager.java | 37 +++--- .../oracle/sentry/common/LazyLoader.java | 48 ++++++++ .../sentry/oci/account/OracleAccount.java | 88 ++++++++++++++ .../account/OracleAccountManager.java} | 99 ++++------------ .../sentry/oci/account/OracleClients.java | 110 ++++++++++++++++++ .../sentry/oci/compute/ComputeInstance.java | 44 +++---- .../sentry/oci/compute/InstanceNetwork.java | 2 +- .../sentry/script/ScriptComponents.java | 4 +- .../oracle/sentry/common/LazyLoaderTest.java | 63 ++++++++++ .../sentry/script/ScriptManagerTest.java | 3 +- 11 files changed, 386 insertions(+), 132 deletions(-) create mode 100644 src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java create mode 100644 src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccount.java rename src/main/java/net/lamgc/oracle/sentry/{OracleIdentityManager.java => oci/account/OracleAccountManager.java} (61%) create mode 100644 src/main/java/net/lamgc/oracle/sentry/oci/account/OracleClients.java create mode 100644 src/test/java/net/lamgc/oracle/sentry/common/LazyLoaderTest.java diff --git a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java index 6029d47..913a130 100644 --- a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java +++ b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java @@ -1,7 +1,8 @@ package net.lamgc.oracle.sentry; 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.ScriptLoggerFactory; import net.lamgc.oracle.sentry.script.ScriptManager; @@ -48,8 +49,8 @@ class ApplicationInitiation { private String sshIdentityPath; @Bean("oracle.identity.manager") - public OracleIdentityManager initialOracleIdentityManager() throws IOException { - OracleIdentityManager oracleUserManager = new OracleIdentityManager(); + public OracleAccountManager initialOracleAccountManager() throws IOException { + OracleAccountManager oracleUserManager = new OracleAccountManager(); log.info("正在加载 Oracle API 身份配置..."); log.debug("Oracle API 身份配置查找路径: \"{}\", 匹配表达式: {}", identityDirectory, identityFilePattern); File identityDir = new File(identityDirectory); @@ -68,15 +69,15 @@ class ApplicationInitiation { @Bean("oracle.compute.instance.manager") @Autowired - public ComputeInstanceManager initialComputeInstanceManager(OracleIdentityManager identityManager) throws IOException { + public ComputeInstanceManager initialComputeInstanceManager(OracleAccountManager accountManager) throws IOException { ComputeInstanceManager instanceManager = new ComputeInstanceManager(); int addTotal = 0; - for (AuthenticationDetailsProvider provider : identityManager.getProviders()) { - String identityName = identityManager.getIdentityName(provider.getUserId()); + for (OracleAccount account : accountManager.getAccounts()) { + String identityName = account.name(); log.info("正在加载用户 {} 所拥有的所有实例...", identityName); int addCount; try { - addCount = instanceManager.addComputeInstanceFromUser(provider); + addCount = instanceManager.addComputeInstanceFromUser(account); } catch (Exception e) { log.error("加载实例时发生异常.", e); continue; @@ -93,11 +94,12 @@ class ApplicationInitiation { @Bean("sentry.script.manager") @Autowired - public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager) { + public ScriptManager initialScriptManager(ComputeInstanceManager instanceManager, OracleAccountManager accountManager) { ScriptComponents context = new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create() .build()), instanceManager, - new ScriptLoggerFactory() + new ScriptLoggerFactory(), + accountManager ); ScriptManager manager = new ScriptManager(new File(scriptsLocation), context); diff --git a/src/main/java/net/lamgc/oracle/sentry/ComputeInstanceManager.java b/src/main/java/net/lamgc/oracle/sentry/ComputeInstanceManager.java index 402fe33..19f0a9b 100644 --- a/src/main/java/net/lamgc/oracle/sentry/ComputeInstanceManager.java +++ b/src/main/java/net/lamgc/oracle/sentry/ComputeInstanceManager.java @@ -1,20 +1,21 @@ 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.requests.ListInstancesRequest; 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.requests.ListCompartmentsRequest; 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.ssh.SshAuthIdentityProvider; import java.io.File; 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.stream.Collectors; @@ -64,37 +65,37 @@ public final class ComputeInstanceManager { public Set getInstancesByUserId(String userId) { Objects.requireNonNull(userId); return instanceMap.values().stream() - .filter(computeInstance -> computeInstance.getUserId().equals(userId)) + .filter(computeInstance -> computeInstance.getFromAccount().id().equals(userId)) .collect(Collectors.toSet()); } /** * 添加某一用户的所有计算实例. - * @param provider 用户身份提供器. + * @param account Oracle 云账号对象. * @return 返回已成功添加的实例数量. * @throws NullPointerException 如果 provider 为 {@code null} 则抛出异常. */ - public int addComputeInstanceFromUser(AuthenticationDetailsProvider provider) { - Objects.requireNonNull(provider); - IdentityClient identityClient = new IdentityClient(provider); - ComputeClient computeClient = new ComputeClient(provider); - ListCompartmentsResponse listCompartments = identityClient.listCompartments(ListCompartmentsRequest.builder() - .compartmentId(provider.getTenantId()) - .build()); + public int addComputeInstanceFromUser(OracleAccount account) { + Objects.requireNonNull(account); + ListCompartmentsResponse listCompartments = account.clients().identity() + .listCompartments(ListCompartmentsRequest.builder() + .compartmentId(account.tenantId()) + .build()); int addCount = 0; Set compartmentIds = listCompartments.getItems().stream() .map(Compartment::getId).collect(Collectors.toSet()); - compartmentIds.add(provider.getTenantId()); + compartmentIds.add(account.tenantId()); for (String compartmentId : compartmentIds) { - ListInstancesResponse listInstances = computeClient.listInstances(ListInstancesRequest.builder() - .compartmentId(compartmentId) - .build()); + ListInstancesResponse listInstances = account.clients().compute() + .listInstances(ListInstancesRequest.builder() + .compartmentId(compartmentId) + .build()); for (Instance instance : listInstances.getItems()) { if (instance.getLifecycleState() == Instance.LifecycleState.Terminated) { continue; } ComputeInstance computeInstance = new ComputeInstance(this, instance.getId(), - provider.getUserId(), compartmentId, instance.getImageId(), provider); + compartmentId, instance.getImageId(), account); addComputeInstance(computeInstance); addCount ++; diff --git a/src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java b/src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java new file mode 100644 index 0000000..24a8898 --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/common/LazyLoader.java @@ -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; + +/** + * 惰性加载器. + *

该加载器只会在第一次获取对象时初始化. + *

如果不用, 可能会导致内存消耗增加, 尤其是在管理大量帐号的情况下. + *

对象在本加载器中为单例, 任何通过同一个加载器获取的对象都是同一个对象. + * @author LamGC + * @param 对象类型. + */ +public final class LazyLoader { + + private final Supplier supplier; + private final AtomicReference object = new AtomicReference<>(null); + + /** + * 构建惰性加载器. + * @param supplier 对象提供器. + */ + public LazyLoader(Supplier supplier) { + this.supplier = Objects.requireNonNull(supplier); + } + + /** + * 获取对象. + *

如果是首次调用本方法, 本方法将通过 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(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccount.java b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccount.java new file mode 100644 index 0000000..99905a1 --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccount.java @@ -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. + *

该 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()); + } + +} diff --git a/src/main/java/net/lamgc/oracle/sentry/OracleIdentityManager.java b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccountManager.java similarity index 61% rename from src/main/java/net/lamgc/oracle/sentry/OracleIdentityManager.java rename to src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccountManager.java index bb3c3e7..7c7a185 100644 --- a/src/main/java/net/lamgc/oracle/sentry/OracleIdentityManager.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleAccountManager.java @@ -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.Throwables; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import com.oracle.bmc.ConfigFileReader; import com.oracle.bmc.Region; import com.oracle.bmc.auth.AuthenticationDetailsProvider; import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider; 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.LoggerFactory; @@ -28,25 +23,19 @@ import java.util.function.Supplier; import java.util.stream.Collectors; /** - * Oracle 身份管理器. + * Oracle 云账号管理器. * @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. * Key: Identity Id * Value {@link AuthenticationDetailsProvider} */ - private final Map identityMap = new ConcurrentHashMap<>(); - /** - * 用户名 Map. - * key Identity Id - * Value: Username - */ - private final Map identityNameMap = new ConcurrentHashMap<>(); + private final Map accountMap = new ConcurrentHashMap<>(); /** * 从目录扫描匹配的配置文件并加载. @@ -69,14 +58,14 @@ public final class OracleIdentityManager { int loadedCount = 0; for (File configFile : configFiles) { try { - AuthenticationDetailsProvider provider = loadFromConfigFile(configFile); - if (provider == null) { + OracleAccount account = loadFromConfigFile(configFile); + if (account == null) { continue; } loadedCount ++; log.info("已成功加载身份配置文件.\n\tUserId: {}\n\tUsername: {}\n\tPath: {}", - provider.getUserId(), - getIdentityName(provider.getUserId()), + account.id(), + account.name(), configFile.getCanonicalPath()); } catch (Exception e) { log.error("加载身份配置文件时发生异常.(Path: {})\n{}", configFile.getCanonicalPath(), Throwables.getStackTraceAsString(e)); @@ -92,7 +81,7 @@ public final class OracleIdentityManager { * @return 返回已成功加载后, 配置文件对应的身份配置提供器. * @throws IOException 如果读取文件发生问题时将抛出该异常. */ - public AuthenticationDetailsProvider loadFromConfigFile(File identityConfig) throws IOException { + public OracleAccount loadFromConfigFile(File identityConfig) throws IOException { if (!identityConfig.exists()) { throw new FileNotFoundException(identityConfig.getAbsolutePath()); } @@ -122,10 +111,14 @@ public final class OracleIdentityManager { .build(); // 尝试获取身份所属用户名, 以此检查该身份配置是否正确. - String identityName = getIdentityName0(provider); - identityNameMap.put(provider.getUserId(), identityName); - identityMap.put(provider.getUserId(), provider); - return provider; + OracleAccount oracleAccount = new OracleAccount(provider); + String accountName = oracleAccount.name(); + if (accountName == null) { + 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) { @@ -144,35 +137,6 @@ public final class OracleIdentityManager { 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 获取指定身份提供器. * @param userId 用户 Id. @@ -180,37 +144,20 @@ public final class OracleIdentityManager { * @throws NullPointerException 当 userId 为 {@code null} 时抛出该异常. * @throws NoSuchElementException 指定的 UserId 未找到对应 Provider 时抛出该异常. */ - public AuthenticationDetailsProvider getProviderByUserId(String userId) { + public OracleAccount getAccountByUserId(String userId) { Objects.requireNonNull(userId); - if (!identityMap.containsKey(userId)) { + if (!accountMap.containsKey(userId)) { throw new NoSuchElementException(userId); } - return identityMap.get(userId); - } - - /** - * 导出身份信息. - *

不包含私钥. - * @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 accountMap.get(userId); } /** * 获取所有身份提供器. * @return 返回包含所有身份提供器的集合对象. */ - public Set getProviders() { - return identityMap.values().stream().collect(Collectors.toUnmodifiableSet()); + public Set getAccounts() { + return accountMap.values().stream().collect(Collectors.toUnmodifiableSet()); } } diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleClients.java b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleClients.java new file mode 100644 index 0000000..b581876 --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/oci/account/OracleClients.java @@ -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, 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); + } + + /** + * 获取块存储客户端. + *

仅限计算实例的存储. + * @return 返回块存储客户端. + */ + public BlockstorageClient blockStorage() { + return getInstance(BlockstorageClient.class); + } + + /** + * 获取身份客户端. + *

用于访问身份相关 API. + * @return 返回身份客户端. + */ + public IdentityClient identity() { + return getInstance(IdentityClient.class); + } + + /** + * 获取对象存储客户端. + *

不包括计算实例的存储. + * @return 获取对象存储客户端. + */ + public ObjectStorageClient objectStorage() { + return getInstance(ObjectStorageClient.class); + } + + /** + * 获取实例. + * @param type 实例类. + * @param 实例类型. + * @return 返回对象. + */ + @SuppressWarnings("unchecked") + private 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 对象类型. + */ + private void registryLazyLoader(Class type, Supplier supplier) { + Objects.requireNonNull(type); + Objects.requireNonNull(supplier); + LAZY_LOADER_MAP.put(type, new LazyLoader<>(supplier)); + } + +} diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ComputeInstance.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ComputeInstance.java index 2068771..d2b6181 100644 --- a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ComputeInstance.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ComputeInstance.java @@ -1,6 +1,5 @@ package net.lamgc.oracle.sentry.oci.compute; -import com.oracle.bmc.auth.AuthenticationDetailsProvider; import com.oracle.bmc.core.ComputeClient; import com.oracle.bmc.core.model.Instance; 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.InstanceActionResponse; 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.SshAuthInfo; @@ -24,11 +24,10 @@ public final class ComputeInstance { private final ComputeInstanceManager instanceManager; private final String instanceId; - private final String userId; private final String compartmentId; private final String imageId; - private final AuthenticationDetailsProvider authProvider; private final InstanceNetwork network; + private final OracleAccount fromAccount; private final ComputeClient computeClient; @@ -36,22 +35,20 @@ public final class ComputeInstance { * 构造一个计算实例对象. * @param instanceManager 实例所属的管理器. * @param instanceId 实例 Id. - * @param userId 所属用户 Id. * @param compartmentId 实例所在区域的 Id. * @param imageId 镜像 Id. - * @param provider 所属用户的身份配置提供器. + * @param fromAccount 所属用户的身份配置提供器. */ - public ComputeInstance(ComputeInstanceManager instanceManager, String instanceId, String userId, - String compartmentId, String imageId, AuthenticationDetailsProvider provider) { + public ComputeInstance(ComputeInstanceManager instanceManager, String instanceId, + String compartmentId, String imageId, OracleAccount fromAccount) { this.instanceManager = instanceManager; this.instanceId = instanceId; - this.userId = userId; this.compartmentId = compartmentId; this.imageId = imageId; - this.authProvider = provider; + this.fromAccount = fromAccount; - computeClient = new ComputeClient(provider); this.network = new InstanceNetwork(this); + this.computeClient = fromAccount.clients().compute(); } /** @@ -63,14 +60,6 @@ public final class ComputeInstance { return instanceId; } - /** - * 获取所属用户的 Id. - * @return 返回用户 Id. - */ - public String getUserId() { - return userId; - } - /** * 获取服务器所属区域的 Id. *

使用的资源必须要处于同一区域, 例如 IP 资源, 磁盘. @@ -88,8 +77,8 @@ public final class ComputeInstance { */ public BootImage getImage() { GetImageResponse image = computeClient.getImage(GetImageRequest.builder() - .imageId(imageId) - .build()); + .imageId(imageId) + .build()); return new BootImage(image.getImage()); } @@ -170,10 +159,6 @@ public final class ComputeInstance { return computeClient; } - AuthenticationDetailsProvider getAuthProvider() { - return authProvider; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -183,12 +168,12 @@ public final class ComputeInstance { return false; } 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 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); } + /** + * 获取实例所属的 Oracle 云帐号对象. + * @return 返回实例所属帐号对象. + */ + public OracleAccount getFromAccount() { + return fromAccount; + } } diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/InstanceNetwork.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/InstanceNetwork.java index 003261c..d1fd456 100644 --- a/src/main/java/net/lamgc/oracle/sentry/oci/compute/InstanceNetwork.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/InstanceNetwork.java @@ -22,7 +22,7 @@ public class InstanceNetwork { InstanceNetwork(ComputeInstance instance) { this.instance = instance; - this.vcnClient = new VirtualNetworkClient(this.instance.getAuthProvider()); + this.vcnClient = instance.getFromAccount().clients().network(); } /** diff --git a/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java index c1f17e9..abee22f 100644 --- a/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java +++ b/src/main/java/net/lamgc/oracle/sentry/script/ScriptComponents.java @@ -1,6 +1,7 @@ package net.lamgc.oracle.sentry.script; import net.lamgc.oracle.sentry.ComputeInstanceManager; +import net.lamgc.oracle.sentry.oci.account.OracleAccountManager; import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient; /** @@ -12,7 +13,8 @@ import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient; public final record ScriptComponents( ScriptHttpClient HTTP, ComputeInstanceManager InstanceManager, - ScriptLoggerFactory loggerFactory + ScriptLoggerFactory loggerFactory, + OracleAccountManager AccountManager ) { } diff --git a/src/test/java/net/lamgc/oracle/sentry/common/LazyLoaderTest.java b/src/test/java/net/lamgc/oracle/sentry/common/LazyLoaderTest.java new file mode 100644 index 0000000..87b22c6 --- /dev/null +++ b/src/test/java/net/lamgc/oracle/sentry/common/LazyLoaderTest.java @@ -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 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 loader = new LazyLoader<>(Singleton::new); + AtomicBoolean start = new AtomicBoolean(false); + int threadNum = Runtime.getRuntime().availableProcessors(); + List 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); + } + + +} \ No newline at end of file diff --git a/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java b/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java index ef333e9..911935f 100644 --- a/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java +++ b/src/test/java/net/lamgc/oracle/sentry/script/ScriptManagerTest.java @@ -1,6 +1,7 @@ package net.lamgc.oracle.sentry.script; import net.lamgc.oracle.sentry.ComputeInstanceManager; +import net.lamgc.oracle.sentry.oci.account.OracleAccountManager; import net.lamgc.oracle.sentry.script.tools.http.ScriptHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.jupiter.api.Test; @@ -13,7 +14,7 @@ class ScriptManagerTest { public void loadScriptTest() { ScriptManager manager = new ScriptManager(new File("./run/scripts"), new ScriptComponents(new ScriptHttpClient(HttpClientBuilder.create().build()), - new ComputeInstanceManager(), new ScriptLoggerFactory())); + new ComputeInstanceManager(), new ScriptLoggerFactory(), new OracleAccountManager())); manager.loadScripts();