Lemoe před 4 roky
rodič
revize
579887fc35
3 změnil soubory, kde provedl 663 přidání a 30 odebrání
  1. 52 14
      README.md
  2. 27 16
      main.go
  3. 584 0
      shell/oci-help.sh

+ 52 - 14
README.md

@@ -1,16 +1,18 @@
+# 甲骨文实例抢购教程
+
 ## 获取用户 OCID 和租户 OCID
 登录甲骨文网站
 1. 点击右上角头像 -> 点击类似 oracleidentitycloudservice/[email protected] 的条目,进入后即可看到用户的 OCID。
 2. 点击右上角头像 -> 点击类似 租户:xxx 的条目, 进入页面后即可获取租户的 OCID。
 
-## 获取其它参数
-### 方法1
+## 获取创建实例需要的参数
+### 方法
 登录甲骨文网站,创建实例--保存为堆栈(Save as stack),下载 Terraform 配置,解压得到 main.tf 用文本编辑器打开即可。
-### 方法2
+### 方法
 登录甲骨文网站,创建实例,按F12打开浏览器开发者工具,点击创建实例发起请求,在开发者工具的网络中找到对应的网络请求获取参数。
 
-## 安装并配置 OCI
-### 安装
+## 安装并配置 OCI-Cli
+### 安装 OCI-Cli
 #### Linux
 ```
 bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
@@ -28,7 +30,7 @@ brew install oci-cli
 oci -v
 ```
 
-### 配置
+### 配置 OCI-Cli
 执行下面命令配置 OCI
 ```
 oci setup config
@@ -49,18 +51,54 @@ cat ~/.oci/oci_api_key_public.pem
 oci iam availability-domain list
 ```
 
-## 开始创建实例
-根据系统类型下载对应的可执行文件 https://github.com/lemoex/oci-help/releases/latest
+## Telegram Bot 消息提醒
+如果需要通过 Telegram Bot 发送抢购实例成功的提醒,需要创建机器人获取 token 并获取用户的 id。
+1. 通过 [BotFather](https://t.me/BotFather) 创建一个 Bot 获取 token。 
+2. 通过 [IDBot](https://t.me/myidbot) 获取用户 id。
+
+## 开始抢购实例
+### 方法一(使用编译好的 go 可执行程序抢购实例)
+根据系统类型下载对应的可执行文件 [下载地址](https://github.com/lemoex/oci-help/releases/latest)。  
 解压后,执行下面命令运行程序
 ```
 ./oci-help
 ```
-输入 2 并回车,根据第一步获取的参数,输入后即可开始创建实例。
+Telegram Bot 消息提醒配置:  
+运行程序后,在主菜单选择第 3 项 [ Telegram Bot 消息提醒 ],接着选择第 1 项设置 [ token 和用户 id ]。  
+开始抢购实例:   
+主菜单选择第 2 项 [ 新建抢购实例任务 ],根据提示输入对应的参数即可开始抢购实例。
 
-## Telegram 消息提醒配置
-通过 [BotFather](https://t.me/BotFather) 创建一个 Bot 获取 token,
-通过 [IDBot](https://t.me/myidbot) 获取用户 id。  
-执行下面命令运行程序,选择第 3 项,接着选择第 1 项设置 token 和用户 id,输入 token 和用户 id 即可。
+> 当程序意外停止或者手动终止后,运行程序后在主菜单选择第 1 项 [ 历史抢购实例任务 ] 可以查看之前添加的抢购实例任务,输入对应的序号即可开始抢购实例。
+
+### 方法二
+下载 [oci-help.sh](https://github.com/lemoex/oci-help/blob/main/shell/oci-help.sh) 脚本。
+用文本编辑器打开 oci-help.sh,根据上面获取到的参数修改脚本。保存后通过下面命令运行脚本开始新建或者升级实例。
 ```
-./oci-help
+# 新建实例
+./oci-help.sh --launch
+# 升级实例
+./oci-help.sh --update
 ```
+
+## 使用 screen 在后台运行
+### 在 screen 会话中运行程序
+```
+# 在 screen 中运行 oci-help
+screen -S oci-help ./oci-help
+
+# 在 screen 中运行新建实例命令
+screen -S oci-help ./oci-help.sh --launch
+# 在 screen 中运行升级实例命令
+screen -S oci-help ./oci-help.sh --update
+```
+
+### 从 screen 会话中分离
+按住 Ctrl 键不松,依次按字母 A 和 D 键,即可从当前screen中分离;或者直接关闭终端窗口。
+
+### 重新连接 screen 会话
+```
+screen -r oci-help
+```
+
+### 终止 screen 会话
+在 screen 会话中 按下 Ctrl 不松,按下字母 D 键,即可终止该 screen 会话。

+ 27 - 16
main.go

@@ -27,7 +27,11 @@ var name string
 type Result struct {
 	Status  json.Number `json:"status"`
 	Message string      `json:"message"`
-	Data    interface{} `json:"data"`
+	Data    Data        `json:"data"`
+}
+type Data struct {
+	InstanceName string `json:"display-name"`
+	Shape        string `json:"shape"`
 }
 
 type Node struct {
@@ -61,10 +65,10 @@ func main() {
 
 func mainMenu() {
 	cmdClear()
-	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "欢迎使用甲骨文新建实例脚本")
-	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "1.", "查看新建记录")
-	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "2.", "开始新建实例")
-	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "3.", "Telegram bot 消息提醒")
+	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "欢迎使用甲骨文实例抢购脚本")
+	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "1.", "历史抢购实例任务")
+	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "2.", "新建抢购实例任务")
+	fmt.Printf("\033[1;36;40m%s\033[0m %s\n", "3.", "Telegram Bot 消息提醒")
 	fmt.Println("")
 	fmt.Print("请选择[1-3]: ")
 	var num int
@@ -82,13 +86,13 @@ func mainMenu() {
 func loadNode() {
 	cmdClear()
 	sections := cfg.Sections()
-	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "新建实例历史记录")
+	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "历史抢购实例任务")
 	for i := 1; i < len(sections); i++ {
 		fmt.Printf("\033[1;36;40m%d.\033[0m %s\n", i, sections[i].Name())
 	}
 	fmt.Printf("\n\033[1;36;40m%d.\033[0m %s\n", 0, "返回主菜单")
 	var num int
-	fmt.Print("\n请输入序号, 开始新建实例: ")
+	fmt.Print("\n请输入序号, 开始抢购实例: ")
 	fmt.Scanln(&num)
 	if num <= 0 || num >= len(sections) {
 		mainMenu()
@@ -120,7 +124,7 @@ func addNode() {
 		min_time   string
 		max_time   string
 	)
-	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "开始新建实例, 请按要求输入以下参数")
+	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "新建抢购实例任务, 请按要求输入以下参数")
 	fmt.Print("请随便输入一个名称(不能有空格): ")
 	fmt.Scanln(&name)
 	fmt.Print("请输入[availabilityDomain|availability_domain]: ")
@@ -174,7 +178,7 @@ func addNode() {
 
 func setTelegramBot() {
 	cmdClear()
-	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "Telegram bot 消息提醒配置")
+	fmt.Printf("\n\033[1;32;40m%s\033[0m\n\n", "Telegram Bot 消息提醒配置")
 	fmt.Println("Telegram Bot Token:", tg_token)
 	fmt.Println("Telegram User ID:", tg_chatId)
 	fmt.Printf("\n\033[1;36;40m%s\033[0m %s\n", "1.", "设置token和用户id")
@@ -214,7 +218,7 @@ func sendMessage(name, text string) {
 
 func launchInstance(node *Node) {
 	name = node.Name
-	text := "开始创建实例"
+	text := "开始抢购实例"
 	printGreen(text)
 	sendMessage(node.Name, text)
 	cmd := "oci"
@@ -233,17 +237,24 @@ func launchInstance(node *Node) {
 	}
 
 	for {
-		out, _ := exec.Command(cmd, args...).CombinedOutput()
+		out, err := exec.Command(cmd, args...).CombinedOutput()
 		ret := string(out)
+		if err != nil {
+			text = "抢购失败, Command failed. " + err.Error() + "\n" + ret
+			printRed(text)
+			sendMessage(node.Name, text)
+			return
+		}
+
 		pos := strings.Index(ret, "{")
 		if pos != -1 {
 			ret = ret[pos:]
 		}
 
 		var result Result
-		err := json.Unmarshal([]byte(ret), &result)
+		err = json.Unmarshal([]byte(ret), &result)
 		if err != nil {
-			text = "出现异常, " + ret
+			text = "抢购失败, Unmarshal failed. " + "\n" + ret
 			printRed(text)
 			sendMessage(node.Name, text)
 			return
@@ -253,13 +264,13 @@ func launchInstance(node *Node) {
 		case "500", "429":
 			printNone(result.Message)
 		default:
-			if result.Data != nil {
-				text = "实例创建成功"
+			if result.Data != (Data{}) {
+				text = "抢购成功, 实例名称: [" + result.Data.InstanceName + "]"
 				printGreen(text)
 				sendMessage(node.Name, text)
 				return
 			}
-			text = "出现异常, " + result.Message
+			text = "抢购失败, " + result.Message
 			printRed(text)
 			sendMessage(node.Name, text)
 			return

+ 584 - 0
shell/oci-help.sh

@@ -0,0 +1,584 @@
+#!/usr/bin/env sh
+trap 'onCtrlC' INT
+
+
+############################################################
+#
+#   甲骨文ARM实例自动新建/升级脚本
+#
+############################################################
+
+
+#====== 新建实例配置相关 ======#
+# 区域ID                 [availability_domain]
+Available_Domain='xxxx:AP-xxxxx-1-AD-1'
+# 镜像                   [source_id]
+Image_ID='ocid1.image.oc1.ap-xxxxx-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+# 子网ID                 [subnet_id]
+Subnet_ID='ocid1.subnet.oc1.ap-xxxxx-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+# 公钥                   [ssh_authorized_keys]
+SSH_Key_PUB="ssh-rsa xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ssh-key-xxxx-xx-xx"
+# 租户ID                 [compartment_id]
+Compartment_ID='ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
+# 配置                   [shape]
+Shape='VM.Standard.A1.Flex'
+# CPU数目
+CPU=1
+# 内存大小(GB)
+RAM=6
+# 引导卷大小(GB)
+HD=50
+# 实例名称
+Instance_Name="instance-xxxx-xxxx"
+
+
+#====== 升级实例配置相关 ======#
+# 升级的实例OCID          [实例详细信息页面的OCID]
+_Instance_ID="ocid1.instance.oc1.ap-xxxxx-1.xxxxxxxxxxxxxx"
+# 升级到CPU个数
+_CPU=4
+# 升级到内存大小(GB)
+_RAM=24
+
+
+#====== 新建/升级实例时间间隔 ======#
+# 指定一个时间范围,随机生成时间间隔。
+min_Time=5
+max_Time=30
+
+
+#====== OCI个人资料名称 ======#
+# 执行 oci setup config 配置oci时,「Enter the name of the profile you would like to create:」输入的名称,不输入直接回车名称默认为 DEFAULT。
+profile="DEFAULT"
+
+
+#====== Telegram bot 消息提醒配置相关 ======#
+# 发送消息提醒。0: 不发送;1: 发送
+SEND_MSG=1
+# Telegram bot token, 通过 BotFather(https://t.me/BotFather) 创建一个 Bot 获取 token。
+TOKEN=xxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+# 接收消息的Telegram ID, 通过 IDBot(https://t.me/myidbot) 获取个人 Telegram ID。
+CHAT_ID=xxxxxxxxx
+# 使用代理访问Telegram bot发送消息的API。0: 不使用;1: 使用。
+PROXY=0
+# Socks 代理
+PROXY_URL=socks5://127.0.0.1:1080
+# Http 代理
+#PROXY_URL=http://127.0.0.1:1087
+
+
+
+
+###============================== 以下区域无需修改 ==============================###
+PROJECT="甲骨文 OCI 新建/升级实例"
+VER=1.0.0
+PROJECT_ENTRY="$0"
+LOG_DIR=./log
+LOG_FILE=$LOG_DIR/OCI.log
+NO_TIMESTAMP=0
+# 保存日志到文件。0:不保存;1:保存
+SAVE_LOG=1
+
+# Telegram bot 发送消息 API 
+URL="https://api.telegram.org/bot${TOKEN}/sendMessage"
+
+
+#################################################################################
+
+# 新建实例
+oci_launch_instance() {
+    oci compute instance launch --profile $profile \
+    --availability-domain $Available_Domain \
+    --image-id $Image_ID \
+    --subnet-id $Subnet_ID \
+    --shape $Shape \
+    --assign-public-ip true \
+    --metadata '{"ssh_authorized_keys": "'"${SSH_Key_PUB}"'"}' \
+    --compartment-id $Compartment_ID \
+    --shape-config '{"ocpus":'$CPU',"memory_in_gbs":'$RAM'}' \
+    --boot-volume-size-in-gbs $HD \
+    --display-name $Instance_Name
+}
+launch_instance() {
+    msg_text="开始新建实例「${Instance_Name}: ${CPU}C${RAM}G」"
+    info "$msg_text"
+    sendMessage "$msg_text"
+    while [ true ]; do
+        _warn "正在尝试新建实例..."
+        ret=$(oci_launch_instance 2>&1)
+        #ret=${ret#*:}
+        ret=${ret#*ServiceError:}
+        status=$(echo "${ret}" | jq '.status' 2> /dev/null)
+        message=$(echo "${ret}" | jq '.message' 2> /dev/null)
+
+        #oci_launch_instance > ${LOG_DIR}/result.json 2>&1
+        #sed -i '' '1d' ${LOG_DIR}/result.json
+        #status="$(cat ${LOG_DIR}/result.json | jq '.status')"
+        #message="$(cat ${LOG_DIR}/result.json | jq '.message')"
+        #_info "$status, $message"
+
+        msg_text="Message: ${message}, Status: ${status}"
+        case "${status}" in
+        500)
+            debug "$msg_text"
+            ;;
+        429)
+            debug "$msg_text"
+            ;;
+        502)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        503)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        400)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        401)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        404)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        409)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        *)
+            if [ -n "$(echo "$ret" | grep -i "data")" ]; then
+                # 实例新建成功
+                text_success="实例「${Instance_Name}: ${CPU}C${RAM}G」新建成功, 实例详细信息请查看[success.json]."
+                info "${text_success}"
+                sendMessage "${text_success}"
+                echo "$ret" > ./success.json 2>&1
+                sleep 3s
+                break
+                exit 0
+            else
+                local text_error="脚本已停止, $ret"
+                error "$text_error"
+                sendMessage "$text_error"
+                break
+                exit
+            fi
+            ;;
+        esac
+        local interval=$(random_range $min_Time $max_Time)
+        sleep $interval
+    done
+}
+
+# 升级实例
+oci_update_instance() {
+    oci compute instance update --profile ${profile} \
+	--instance-id ${_Instance_ID} \
+	--shape-config '{"ocpus":'${_CPU}',"memory_in_gbs":'${_RAM}'}' \
+	--force
+}
+update_instance() {
+    msg_text="开始升级实例到「${_CPU} Core CPU, ${_RAM} GB RAM」"
+    info "$msg_text"
+    sendMessage "$msg_text"
+    while [ true ]; do
+        _warn "正在尝试升级实例..."
+        ret=$(oci_update_instance 2>&1)
+        ret=${ret#*ServiceError:}
+        status=$(echo "${ret}" | jq '.status' 2> /dev/null)
+        message=$(echo "${ret}" | jq '.message' 2> /dev/null)
+        msg_text="Message: ${message}, Status: ${status}"
+        case "${status}" in
+        500)
+            debug "$msg_text"
+            ;;
+        429)
+            debug "$msg_text"
+            ;;
+        502)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        503)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        400)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        401)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        404)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        409)
+            error "$msg_text"
+            sendMessage "脚本已停止, ${msg_text}"
+            break
+            ;;
+        *)
+            if [ -n "$(echo "$ret" | grep -i "data")" ]; then
+                text_success="实例已成功升级到「${_CPU} Core CPU, ${_RAM} GB RAM」, 实例详细信息请查看[success.json]."
+                info "${text_success}"
+                sendMessage "${text_success}"
+                echo "$ret" > ./success.json 2>&1
+                sleep 3s
+                break
+                exit 0
+            else
+                local text_error="脚本已停止, $ret"
+                error "$text_error"
+                sendMessage "$text_error"
+                break
+                exit
+            fi
+            ;;
+        esac
+        local interval=$(random_range $min_Time $max_Time)
+        sleep $interval
+    done
+}
+
+# 生成指定范围随机数
+random_range() {
+    local min=$1
+    local max=$2
+    echo $((RANDOM % ($max - $min) + $min))
+}
+
+sendMessage() {
+    if [ 1 -eq $SEND_MSG ]; then
+        if [ 1 -eq $PROXY ]; then
+            result=$(curl --connect-timeout 10 --max-time 10 -s -S -x $PROXY_URL -X POST $URL -d parse_mode=Markdown -d chat_id=${CHAT_ID} -d text="*甲骨文信息*%0A${1}" 2>&1)
+            if [ 0 -eq $? ]; then
+                info "Telegram 消息提醒发送成功"
+            else    
+                error "Telegram 消息提醒发送失败, $result"
+            fi
+        else
+            result=$(curl --connect-timeout 10 --max-time 10 -s -S -X POST $URL -d parse_mode=Markdown -d chat_id=${CHAT_ID} -d text="*甲骨文信息*%0A${1}" 2>&1)
+            if [ 0 -eq $? ]; then
+                info "Telegram 消息提醒发送成功"
+            else    
+                error "Telegram 消息提醒发送失败, $result"
+            fi
+        fi
+    fi
+}
+
+onCtrlC() {
+    error "检测到「Ctrl + C」,正在终止脚本..."
+    sendMessage "脚本已停止运行。"
+    exit 0
+}
+
+version() {
+    echo "$PROJECT"
+    echo "v$VER"
+}
+
+showhelp() {
+    version
+    echo "Usage: $PROJECT_ENTRY <command> ... [parameters ...]
+Commands:
+    -h, --help               Show this help message.
+    -v, --version            Show version info.
+    --launch                 Create instance.
+    --update                 Update instance.
+
+
+Parameters:
+    --available-domain       区域ID
+    --image-id               系统镜像ID
+    --subnet-id              子网ID
+    --shape                  配置类型
+    --shape-config           配置参数:CPU个数、内存大小(GB)
+    --boot-volume-size       引导卷大小(GB)
+    --ssh-key-pub            SSH公钥
+    --compartment-id         租户ID
+    --instance-name          实例名称
+    --instance-id            实例OCID,升级实例需要。
+    --profile                配置oci时指定的别名,默认为DEFAULT。
+                             当一台机器上面为多个甲骨文账号配置oci时,
+                             需要指定不同的别名区分。
+"
+}
+
+_printf_black() {
+    printf '\33[1;30m%b\33[0m' "$1"
+}
+_printf_red() {
+    printf '\33[1;31m%b\33[0m' "$1"
+}
+_printf_green() {
+    printf '\33[1;32m%b\33[0m' "$1"
+}
+_printf_yellow() {
+    printf '\33[1;33m%b\33[0m' "$1"
+}
+_printf_blue() {
+    printf '\33[1;34m%b\33[0m' "$1"
+}
+_printf_purple() {
+    printf '\33[1;35m%b\33[0m' "$1"
+}
+_printf_skyBlue() {
+    printf '\33[1;36m%b\33[0m' "$1"
+}
+_printf_white() {
+    printf '\33[1;37m%b\33[0m' "$1"
+}
+_printf_normal() {
+    printf -- "%b" "$1"
+}
+
+_error() {
+    if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
+        printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] " >&2
+    fi
+    if [ -z "$2" ]; then
+        _printf_red "$1" >&2
+    else
+        _printf_red "$1='$2'" >&2
+    fi
+    printf "\n" >&2
+    return 1
+}
+_warn() {
+    _exitstatus="$?"  
+    if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
+        printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] " >&2
+    fi
+    if [ -z "$2" ]; then
+        _printf_yellow "$1" >&2
+    else
+        _printf_yellow "$1='$2'" >&2
+    fi
+    printf "\n" >&2
+    # return the saved exit status
+    return "$_exitstatus"
+}
+_info() {
+    _exitstatus="$?"
+    if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
+        printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] "
+    fi
+    if [ -z "$2" ]; then
+        _printf_green "$1"
+    else
+        _printf_green "$1='$2'"
+    fi
+    printf "\n"
+    return "$_exitstatus"
+}
+_debug() {
+    _exitstatus="$?"
+    if [ -z "$NO_TIMESTAMP" ] || [ "$NO_TIMESTAMP" = "0" ]; then
+        printf -- "%s" "[$(date '+%Y-%m-%d %H:%M:%S')] "
+    fi
+    if [ -z "$2" ]; then
+        _printf_normal "$1"
+    else
+        _printf_normal "$1='$2'"
+    fi
+    printf "\n"
+    return "$_exitstatus"
+}
+
+error() {
+    if [ $SAVE_LOG -eq 1 ]; then
+        _error "$1" 2>&1 | tee -a $LOG_FILE
+        return
+    fi
+    _error "$1"
+}
+warn() {
+    if [ $SAVE_LOG -eq 1 ]; then
+        _warn "$1" 2>&1 | tee -a $LOG_FILE
+        return
+    fi
+    _warn "$1"
+}
+info() {
+    if [ $SAVE_LOG -eq 1 ]; then
+        _info "$1" 2>&1 | tee -a $LOG_FILE
+        return
+    fi
+    _info "$1"
+}
+debug() {
+    if [ $SAVE_LOG -eq 1 ]; then
+        _debug "$1" 2>&1 | tee -a $LOG_FILE
+        return
+    fi
+    _debug "$1"
+}
+
+install_JQ() {
+    _warn "正在安装JQ..."
+    if [ `uname` = 'Darwin' ]; then
+        if [ "$(command -v brew)" ]; then
+            # 使用brew安装jq
+            brew install jq
+        else
+            # brew未安装
+            _error "请手动安装Homebrew"
+            exit
+        fi
+    elif [ $(uname) = 'Linux' ]; then
+        source /etc/os-release
+        case $ID in
+        debian | ubuntu)
+            sudo apt-get update -y
+            sudo apt-get install jq -y
+            ;;
+        centos)
+            sudo yum install epel-release -y
+            sudo yum install jq -y
+            ;;
+        *)
+            _error "请手动安装jq"
+            exit
+            ;;
+        esac
+    else
+        _error "请手动安装jq"
+        exit
+    fi
+}
+
+_init() {
+    _info "${PROJECT} 脚本正在启动..."
+    if ! [ -d ./log/ ]; then
+        _info "创建日志目录"
+        mkdir ${LOG_DIR}
+    fi
+    # 检查oci命令行工具是否安装
+    if [ -z "$(command -v oci)" ]; then
+        _error "oci命令行工具未安装, 请手动安装"
+        exit
+    fi
+    # 检查jq是否安装
+    if [ -z "$(command -v jq)" ]; then
+        install_JQ
+    fi
+}
+
+_process() {
+    _CMD=""
+    while [ ${#} -gt 0 ]; do
+        case "${1}" in
+        --help | -h)
+            showhelp
+            return
+            ;;
+        --version | -v)
+            version
+            return
+            ;;
+        --launch)
+            _CMD="launch"
+            ;;
+        --update)
+            _CMD="update"
+            ;;
+        --available-domain)
+            Available_Domain=$2
+            shift
+            ;;
+        --image-id)
+            Image_ID=$2
+            shift
+            ;;
+        --subnet-id)
+            Subnet_ID=$2
+            shift
+            ;;
+        --shape)
+            Shape=$2
+            shift
+            ;;
+        --ssh-key-pub)
+            SSH_Key_PUB=$2
+            shift
+            ;;
+        --compartment-id)
+            Compartment_ID=$2
+            shift
+            ;;
+        --shape-config)
+            CPU=$2
+            RAM=$3
+            _CPU=$2
+            _RAM=$3
+            shift 2
+            ;;
+        --boot-volume-size)
+            HD=$2
+            shift
+            ;;
+        --instance-name)
+            Instance_Name=$2
+            shift
+            ;;
+        --profile)
+            profile=$2
+            shift
+            ;;
+        --instance-id)
+            _Instance_ID=$2
+            shift
+            ;;
+        *)
+            _error "Unknown parameter : $1"
+            return 1
+            ;;
+        esac
+        shift 1
+    done
+
+    _init
+
+    case "${_CMD}" in
+    launch) launch_instance ;;
+    update) update_instance ;;
+    *)
+        if [ "$_CMD" ]; then
+            _error "Invalid command: $_CMD"
+        fi
+        showhelp
+        return 1
+        ;;
+    esac
+}
+
+_startswith() {
+    _str="$1"
+    _sub="$2"
+    echo "$_str" | grep "^$_sub" >/dev/null 2>&1
+}
+
+main() {
+    [ -z "$1" ] && showhelp && return
+    if _startswith "$1" '-'; then _process "$@"; else "$@"; fi
+}
+
+main "$@"