docs: 补充文档.

This commit is contained in:
LamGC 2021-08-19 19:10:12 +08:00
parent 882eabbc71
commit c92c491bd8
Signed by: LamGC
GPG Key ID: 6C5AE2A913941E1D
22 changed files with 219 additions and 6 deletions

View File

@ -19,6 +19,10 @@ public class ApplicationMain {
@SuppressWarnings("AlibabaConstantFieldShouldBeUpperCase") @SuppressWarnings("AlibabaConstantFieldShouldBeUpperCase")
private final static Object mainThreadWaiter = new Object(); private final static Object mainThreadWaiter = new Object();
/**
* 程序入口.
* @param args 程序参数.
*/
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args); SpringApplication.run(ApplicationMain.class, args);

View File

@ -37,6 +37,10 @@ public class ComputeInstanceManager {
sshIdentityProvider.loadAuthInfo(); sshIdentityProvider.loadAuthInfo();
} }
/**
* 获取实例 SSH 认证配置提供器.
* @return 返回 SSH 认证配置提供器.
*/
public SshAuthIdentityProvider getSshIdentityProvider() { public SshAuthIdentityProvider getSshIdentityProvider() {
return sshIdentityProvider; return sshIdentityProvider;
} }

View File

@ -10,6 +10,9 @@ import org.springframework.stereotype.Component;
@Component("sentry.constants") @Component("sentry.constants")
public final class Constants { public final class Constants {
/**
* 本类唯一实例, 请不要进行设置.
*/
public static Constants instance; public static Constants instance;
private Constants() { private Constants() {
@ -21,6 +24,10 @@ public final class Constants {
private String firstConnectionPolicy; private String firstConnectionPolicy;
/**
* 获取 SSH 首次连接策略.
* @return 返回策略值.
*/
@NonNull @NonNull
public String getFirstConnectionPolicy() { public String getFirstConnectionPolicy() {
return firstConnectionPolicy; return firstConnectionPolicy;

View File

@ -87,7 +87,9 @@ public final class OracleIdentityManager {
/** /**
* 通过配置文件加载身份信息. * 通过配置文件加载身份信息.
* <p> 加载成功后, 将会注册到身份管理器中.
* @param identityConfig 身份信息文件. * @param identityConfig 身份信息文件.
* @return 返回已成功加载后, 配置文件对应的身份配置提供器.
* @throws IOException 如果读取文件发生问题时将抛出该异常. * @throws IOException 如果读取文件发生问题时将抛出该异常.
*/ */
public AuthenticationDetailsProvider loadFromConfigFile(File identityConfig) throws IOException { public AuthenticationDetailsProvider loadFromConfigFile(File identityConfig) throws IOException {

View File

@ -4,10 +4,20 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/**
* 输入流包装器.
* <p> 准确来说只是屏蔽了 {@link InputStream#close()} 而已,
* 尝试修复 SSH 命令执行会话可能会关闭设置的输入流的问题.
* @author LamGC
*/
public class InputStreamWrapper extends InputStream { public class InputStreamWrapper extends InputStream {
private final InputStream source; private final InputStream source;
/**
* 包装一个输入流.
* @param source 输入源.
*/
public InputStreamWrapper(InputStream source) { public InputStreamWrapper(InputStream source) {
this.source = source; this.source = source;
} }

View File

@ -3,10 +3,20 @@ package net.lamgc.oracle.sentry.common;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/**
* 输出流包装器.
* <p> 准确来说只是屏蔽了 {@link OutputStream#close()} 而已,
* 尝试修复 SSH 命令执行会话可能会关闭设置的输出流的问题.
* @author LamGC
*/
public class OutputStreamWrapper extends OutputStream { public class OutputStreamWrapper extends OutputStream {
private final OutputStream target; private final OutputStream target;
/**
* 包装一个输出流.
* @param target 目标输出流.
*/
public OutputStreamWrapper(OutputStream target) { public OutputStreamWrapper(OutputStream target) {
this.target = target; this.target = target;
} }

View File

@ -1,5 +1,10 @@
package net.lamgc.oracle.sentry.oci.compute; package net.lamgc.oracle.sentry.oci.compute;
/**
* 实例动作.
* <p> 可对实例执行的操作.
* @author LamGC
*/
public enum InstanceAction { public enum InstanceAction {
/** /**
* 启动实例. * 启动实例.
@ -30,6 +35,10 @@ public enum InstanceAction {
this.actionValue = actionValue; this.actionValue = actionValue;
} }
/**
* 获取动作的 API 调用值.
* @return 返回 API 所规定的对应值.
*/
public String getActionValue() { public String getActionValue() {
return actionValue; return actionValue;
} }

View File

@ -71,6 +71,7 @@ public final class CommandExecSession implements Closeable {
/** /**
* 设置输入流. * 设置输入流.
* <p> 设置待执行命令的输入流. * <p> 设置待执行命令的输入流.
* @param in 待设置的输入流
*/ */
public void setIn(InputStream in) { public void setIn(InputStream in) {
channelExec.setIn(new InputStreamWrapper(in)); channelExec.setIn(new InputStreamWrapper(in));
@ -79,6 +80,7 @@ public final class CommandExecSession implements Closeable {
/** /**
* 设置标准输出流. * 设置标准输出流.
* <p> 对应待执行命令的 Stdout. * <p> 对应待执行命令的 Stdout.
* @param out 设置标准输出的输出流.
*/ */
public void setOut(OutputStream out) { public void setOut(OutputStream out) {
channelExec.setOut(new OutputStreamWrapper(out)); channelExec.setOut(new OutputStreamWrapper(out));
@ -87,6 +89,7 @@ public final class CommandExecSession implements Closeable {
/** /**
* 设置错误输出流. * 设置错误输出流.
* <p> 如果命令使用到, 错误信息会从该输出流输出. * <p> 如果命令使用到, 错误信息会从该输出流输出.
* @param err 设置错误输出的输出流.
*/ */
public void setErr(OutputStream err) { public void setErr(OutputStream err) {
channelExec.setErr(new OutputStreamWrapper(err)); channelExec.setErr(new OutputStreamWrapper(err));

View File

@ -16,6 +16,12 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/**
* 实例 SSH 客户端.
* <p> 包装并简化了 SSH 会话的创建流程.
* @author LamGC
*/
@SuppressWarnings("unused")
public class InstanceSsh implements AutoCloseable { public class InstanceSsh implements AutoCloseable {
private final static Logger log = LoggerFactory.getLogger(InstanceSsh.class); private final static Logger log = LoggerFactory.getLogger(InstanceSsh.class);
@ -24,6 +30,11 @@ public class InstanceSsh implements AutoCloseable {
private final SshAuthInfo authInfo; private final SshAuthInfo authInfo;
private final SshClient sshClient; private final SshClient sshClient;
/**
* 创建连接实例用的 SSH 客户端.
* @param instance SSH 客户端对应的计算实例.
* @param authInfo SSH 认证配置.
*/
public InstanceSsh(ComputeInstance instance, SshAuthInfo authInfo) { public InstanceSsh(ComputeInstance instance, SshAuthInfo authInfo) {
this.instance = Objects.requireNonNull(instance); this.instance = Objects.requireNonNull(instance);
this.authInfo = Objects.requireNonNull(authInfo); this.authInfo = Objects.requireNonNull(authInfo);
@ -43,6 +54,12 @@ public class InstanceSsh implements AutoCloseable {
sshClient.start(); sshClient.start();
} }
/**
* 创建 SSH 会话.
* <p> 允许创建多个 SSH 会话.
* @return 返回新的 SSH 会话.
* @throws IOException 会话创建失败时将抛出异常.
*/
public SshSession createSession() throws IOException { public SshSession createSession() throws IOException {
Set<String> instancePublicIps = instance.network().getInstancePublicIp(); Set<String> instancePublicIps = instance.network().getInstancePublicIp();
if (instancePublicIps.stream().findFirst().isEmpty()) { if (instancePublicIps.stream().findFirst().isEmpty()) {
@ -75,7 +92,6 @@ public class InstanceSsh implements AutoCloseable {
} }
} }
@Override @Override
public void close() { public void close() {
sshClient.stop(); sshClient.stop();

View File

@ -12,10 +12,18 @@ public class PasswordAuthInfo extends SshAuthInfo {
return AuthType.PASSWORD; return AuthType.PASSWORD;
} }
/**
* 获取 SSH 登录密码.
* @return 返回登录密码.
*/
public String getPassword() { public String getPassword() {
return password; return password;
} }
/**
* 设置 SSH 登录密码.
* @param password 新的登录密码.
*/
public void setPassword(String password) { public void setPassword(String password) {
this.password = password; this.password = password;
} }

View File

@ -5,6 +5,10 @@ import org.apache.sshd.common.config.keys.FilePasswordProvider;
import java.io.File; import java.io.File;
import java.security.KeyPair; import java.security.KeyPair;
/**
* 公钥登录认证配置.
* @author LamGC
*/
public class PublicKeyAuthInfo extends SshAuthInfo{ public class PublicKeyAuthInfo extends SshAuthInfo{
private File privateKeyPath; private File privateKeyPath;
@ -15,18 +19,36 @@ public class PublicKeyAuthInfo extends SshAuthInfo{
return AuthType.PUBLIC_KEY; return AuthType.PUBLIC_KEY;
} }
/**
* 获取私钥路径.
* <p> 注意: 该路径由 SSH 认证配置文件提供, 不保证私钥的存在.
* @return 返回私钥所在路径.
*/
public File getPrivateKeyPath() { public File getPrivateKeyPath() {
return privateKeyPath; return privateKeyPath;
} }
/**
* 设置私钥路径.
* @param privateKeyPath 私钥路径.
*/
public void setPrivateKeyPath(File privateKeyPath) { public void setPrivateKeyPath(File privateKeyPath) {
this.privateKeyPath = privateKeyPath; this.privateKeyPath = privateKeyPath;
} }
/**
* 获取私钥密码.
* @return 如果有, 返回非 {@code null} .
*/
public String getKeyPassword() { public String getKeyPassword() {
return keyPassword; return keyPassword;
} }
/**
* 设置私钥密码.
* <p> 如果私钥存在密码但未提供密码, 将无法使用私钥验证会话.
* @param keyPassword 私钥密码.
*/
public void setKeyPassword(String keyPassword) { public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword; this.keyPassword = keyPassword;
} }

View File

@ -51,7 +51,11 @@ public final class SshAuthIdentityProvider {
.build()); .build());
private final AtomicBoolean needSave = new AtomicBoolean(false); private final AtomicBoolean needSave = new AtomicBoolean(false);
/**
* 创建 SSH 认证配置提供器.
* @param instanceManager 所属实例管理器.
* @param identityJson 认证配置文件对象.
*/
public SshAuthIdentityProvider(ComputeInstanceManager instanceManager, File identityJson) { public SshAuthIdentityProvider(ComputeInstanceManager instanceManager, File identityJson) {
this.instanceManager = instanceManager; this.instanceManager = instanceManager;
this.identityJsonFile = identityJson; this.identityJsonFile = identityJson;
@ -69,6 +73,11 @@ public final class SshAuthIdentityProvider {
}, 60, 10, TimeUnit.SECONDS); }, 60, 10, TimeUnit.SECONDS);
} }
/**
* 添加 SSH 认证配置.
* @param instanceId 配置对应的实例 Id.
* @param authInfo SSH 认证配置对象.
*/
public void addSshAuthIdentity(String instanceId, SshAuthInfo authInfo) { public void addSshAuthIdentity(String instanceId, SshAuthInfo authInfo) {
authInfoMap.put(instanceId, authInfo); authInfoMap.put(instanceId, authInfo);
} }
@ -157,6 +166,7 @@ public final class SshAuthIdentityProvider {
/** /**
* 获取所有不存在 SSH 配置的实例 Id. * 获取所有不存在 SSH 配置的实例 Id.
* @return 返回所有不存在对应 SSH 认证配置的实例 Id.
*/ */
private Set<String> checkForMissingInstances() { private Set<String> checkForMissingInstances() {
Set<String> instanceIdSet = instanceManager.getComputeInstances().stream() Set<String> instanceIdSet = instanceManager.getComputeInstances().stream()

View File

@ -29,14 +29,28 @@ public abstract class SshAuthInfo {
*/ */
public abstract AuthType getType(); public abstract AuthType getType();
/**
* 获取 SSH 登录用户名.
* @return 返回 SSH 登录用户名.
*/
public String getUsername() { public String getUsername() {
return username; return username;
} }
/**
* 获取服务器公钥.
* <p> 用于认证服务器身份, 在首次登录成功后设置.
* @return 如果之前认证成功并保存过, 则不为 {@code null}, 否则需要进行首次连接确认.
*/
public PublicKey getServerKey() { public PublicKey getServerKey() {
return serverKey; return serverKey;
} }
/**
* 设置服务器公钥.
* <p> 如果本对象有关联的 {@link SshAuthIdentityProvider}, 则会通知 Provider 保存 SSH 认证配置文件.
* @param serverKey 服务器公钥.
*/
public void setServerKey(PublicKey serverKey) { public void setServerKey(PublicKey serverKey) {
this.serverKey = serverKey; this.serverKey = serverKey;
if (this.provider != null) { if (this.provider != null) {
@ -44,14 +58,27 @@ public abstract class SshAuthInfo {
} }
} }
/**
* 设置 SSH 登录用户名.
* @param username 登录 SSH 的用户名.
*/
public void setUsername(String username) { public void setUsername(String username) {
this.username = username; this.username = username;
} }
/**
* 设置 SSH 认证配置提供器.
* <p> 设置后, 可在首次连接认证通过后, 保存服务器公钥到文件中.
* @param provider 所属提供器对象.
*/
void setProvider(SshAuthIdentityProvider provider) { void setProvider(SshAuthIdentityProvider provider) {
this.provider = provider; this.provider = provider;
} }
/**
* 认证类型.
* <p> 如果没有所需认证类型, 就是没支持.
*/
public enum AuthType { public enum AuthType {
/** /**
* 密码认证. * 密码认证.
@ -68,6 +95,10 @@ public abstract class SshAuthInfo {
this.targetClass = targetClass; this.targetClass = targetClass;
} }
/**
* 获取类型所属的认证配置类.
* @return 返回认证配置类.
*/
public Class<? extends SshAuthInfo> getTargetClass() { public Class<? extends SshAuthInfo> getTargetClass() {
return targetClass; return targetClass;
} }

View File

@ -23,6 +23,10 @@ import java.util.Collections;
*/ */
public final class SshAuthInfoSerializer implements JsonSerializer<SshAuthInfo>, JsonDeserializer<SshAuthInfo> { public final class SshAuthInfoSerializer implements JsonSerializer<SshAuthInfo>, JsonDeserializer<SshAuthInfo> {
/**
* 本类唯一实例.
* <p> 序列化器支持多用.
*/
public final static SshAuthInfoSerializer INSTANCE = new SshAuthInfoSerializer(); public final static SshAuthInfoSerializer INSTANCE = new SshAuthInfoSerializer();
private SshAuthInfoSerializer() {} private SshAuthInfoSerializer() {}

View File

@ -23,6 +23,11 @@ public final class ScriptManager {
private final Map<ScriptInfo, Script> scripts = new ConcurrentHashMap<>(); private final Map<ScriptInfo, Script> scripts = new ConcurrentHashMap<>();
/**
* 创建新的脚本管理器.
* @param scriptsLocation 脚本加载路径.
* @param components 脚本组件.
*/
public ScriptManager(File scriptsLocation, ScriptComponents components) { public ScriptManager(File scriptsLocation, ScriptComponents components) {
this.scriptsLocation = scriptsLocation; this.scriptsLocation = scriptsLocation;
this.context = components; this.context = components;

View File

@ -19,11 +19,21 @@ public class GroovyDslDelegate implements Script {
private final ScriptHttpClient HTTP; private final ScriptHttpClient HTTP;
private final ComputeInstanceManager InstanceManager; private final ComputeInstanceManager InstanceManager;
/**
* 构建一个 DSL Delegate, 并传入可操作对象.
* @param httpClient Http 客户端.
* @param instanceManager 实例管理器.
*/
public GroovyDslDelegate(ScriptHttpClient httpClient, ComputeInstanceManager instanceManager) { public GroovyDslDelegate(ScriptHttpClient httpClient, ComputeInstanceManager instanceManager) {
HTTP = httpClient; HTTP = httpClient;
InstanceManager = instanceManager; InstanceManager = instanceManager;
} }
/**
* 注册触发器.
* @param triggerName 触发器名称.
* @param closure 待执行闭包.
*/
private void trigger(String triggerName, Closure<?> closure){ private void trigger(String triggerName, Closure<?> closure){
DefaultGroovyMethods.with(GroovyTriggerProvider.INSTANCE.getTriggerByName(triggerName), closure); DefaultGroovyMethods.with(GroovyTriggerProvider.INSTANCE.getTriggerByName(triggerName), closure);
} }

View File

@ -19,6 +19,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* Groovy 脚本加载器.
* @author LamGC * @author LamGC
*/ */
@SuppressWarnings("MapOrSetKeyShouldOverrideHashCodeEquals") @SuppressWarnings("MapOrSetKeyShouldOverrideHashCodeEquals")
@ -30,6 +31,10 @@ public class GroovyScriptLoader implements ScriptLoader {
private final Map<Script, ScriptInfo> scriptInfoMap = new ConcurrentHashMap<>(); private final Map<Script, ScriptInfo> scriptInfoMap = new ConcurrentHashMap<>();
/**
* 构造一个新的脚本加载器.
* <p> 每个加载器所使用的 {@link GroovyClassLoader} 实例是不一样的.
*/
public GroovyScriptLoader() { public GroovyScriptLoader() {
CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
compilerConfiguration.setScriptBaseClass(DelegatingScript.class.getName()); compilerConfiguration.setScriptBaseClass(DelegatingScript.class.getName());

View File

@ -18,6 +18,9 @@ public class GroovyTriggerProvider {
private final Map<String, ServiceLoader.Provider<GroovyTrigger>> triggerProviderMap = new ConcurrentHashMap<>(); private final Map<String, ServiceLoader.Provider<GroovyTrigger>> triggerProviderMap = new ConcurrentHashMap<>();
/**
* Trigger Provider 唯一实例.
*/
public final static GroovyTriggerProvider INSTANCE = new GroovyTriggerProvider(); public final static GroovyTriggerProvider INSTANCE = new GroovyTriggerProvider();
private GroovyTriggerProvider() { private GroovyTriggerProvider() {
@ -40,6 +43,12 @@ public class GroovyTriggerProvider {
}); });
} }
/**
* 通过 Trigger 名称获取新的 Trigger.
* @param triggerName Trigger 名称.
* @return 返回指定 Trigger 的新实例.
* @throws NoSuchElementException 当指定的 Trigger 名称没有对应 Trigger 时抛出该异常.
*/
public GroovyTrigger getTriggerByName(String triggerName) { public GroovyTrigger getTriggerByName(String triggerName) {
if (!triggerProviderMap.containsKey(triggerName.toLowerCase())) { if (!triggerProviderMap.containsKey(triggerName.toLowerCase())) {
throw new NoSuchElementException("The specified trigger could not be found: " + triggerName); throw new NoSuchElementException("The specified trigger could not be found: " + triggerName);

View File

@ -13,6 +13,11 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface TriggerName { public @interface TriggerName {
/**
* Trigger 名称.
* <p> 需保证唯一性.
* @return 返回 Trigger 名称.
*/
String value(); String value();
} }

View File

@ -10,7 +10,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/** /**
* * Http 访问对象.
* <p> 该对象可以复用.
* @author LamGC * @author LamGC
*/ */
public class HttpAccess { public class HttpAccess {
@ -23,12 +24,23 @@ public class HttpAccess {
this.url = url; this.url = url;
} }
/**
* Get 方法发起 Http 请求.
* @return 返回 Http 响应对象.
* @throws IOException 当请求发送失败时抛出异常.
*/
public HttpAccessResponse get() throws IOException { public HttpAccessResponse get() throws IOException {
HttpGet request = new HttpGet(url); HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request); HttpResponse response = client.execute(request);
return new HttpAccessResponse(response); return new HttpAccessResponse(response);
} }
/**
* Post 方法发起 Http 请求.
* @param body Post 请求体.
* @return 返回 Http 响应对象.
* @throws IOException 当请求发送失败时抛出异常.
*/
public HttpAccessResponse post(String body) throws IOException { public HttpAccessResponse post(String body) throws IOException {
HttpPost request = new HttpPost(url); HttpPost request = new HttpPost(url);
request.setEntity(new StringEntity(body, StandardCharsets.UTF_8)); request.setEntity(new StringEntity(body, StandardCharsets.UTF_8));

View File

@ -32,18 +32,35 @@ public final class HttpAccessResponse {
this.entity = response.getEntity(); this.entity = response.getEntity();
} }
/**
* 获取响应状态行.
* @return 返回响应状态行, 包括响应码和信息.
*/
public StatusLine getStatusLine() { public StatusLine getStatusLine() {
return statusLine; return statusLine;
} }
/**
* 获取语言.
* @return 返回 Locale 对象.
*/
public Locale getLocale() { public Locale getLocale() {
return locale; return locale;
} }
/**
* ResponseBody 转为字符串并返回.
* @return 返回字符串形式的响应体.
* @throws IOException 当接收失败时抛出异常.
*/
public String getContentToString() throws IOException { public String getContentToString() throws IOException {
return EntityUtils.toString(entity); return EntityUtils.toString(entity);
} }
/**
* 获取响应体实体, 可手动接收 Http Response Body.
* @return 返回 Http 实体.
*/
public HttpEntity getEntity() { public HttpEntity getEntity() {
return entity; return entity;
} }

View File

@ -2,10 +2,19 @@ package net.lamgc.oracle.sentry.script.tools.http;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
/**
* 创建脚本使用的 HttpClient 包装对象.
* <p> 可根据脚本需要优化和简化步骤.
* @author LamGC
*/
public class ScriptHttpClient { public class ScriptHttpClient {
private final HttpClient httpClient; private final HttpClient httpClient;
/**
* 包装并构造一个脚本 Http 客户端.
* @param httpClient 原始 Http 客户端.
*/
public ScriptHttpClient(HttpClient httpClient) { public ScriptHttpClient(HttpClient httpClient) {
this.httpClient = httpClient; this.httpClient = httpClient;
} }
@ -13,6 +22,7 @@ public class ScriptHttpClient {
/** /**
* 打开一个连接. * 打开一个连接.
* @param url 要访问的 Url. * @param url 要访问的 Url.
* @return 返回 Http 访问对象(可重复使用).
*/ */
public HttpAccess create(String url) { public HttpAccess create(String url) {
return new HttpAccess(httpClient, url); return new HttpAccess(httpClient, url);