264 lines
7.3 KiB
Go
264 lines
7.3 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
func (h *ociConfigHandler) availabilityDomains(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
ads, err := h.svc.AvailabilityDomains(c.Request.Context(), id, c.Query("region"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, ads)
|
|
}
|
|
|
|
// ---- 实例 ----
|
|
|
|
type createInstanceRequest struct {
|
|
Region string `json:"region"`
|
|
CompartmentID string `json:"compartmentId"` // 为空时建在租户根
|
|
AvailabilityDomain string `json:"availabilityDomain"` // 为空时默认使用第一个可用域
|
|
DisplayName string `json:"displayName" binding:"required"`
|
|
Shape string `json:"shape" binding:"required"`
|
|
Count int `json:"count"` // 批量创建数量,缺省 1,上限 10
|
|
Ocpus float32 `json:"ocpus"`
|
|
MemoryInGBs float32 `json:"memoryInGBs"`
|
|
ImageID string `json:"imageId"`
|
|
BootVolumeID string `json:"bootVolumeId"`
|
|
BootVolumeSizeGBs int64 `json:"bootVolumeSizeGBs"`
|
|
BootVolumeVpusPerGB int64 `json:"bootVolumeVpusPerGB"`
|
|
SubnetID string `json:"subnetId"` // 为空时自动创建 VCN 与子网
|
|
AssignPublicIP bool `json:"assignPublicIp"`
|
|
AssignIpv6 bool `json:"assignIpv6"`
|
|
SSHPublicKey string `json:"sshPublicKey"`
|
|
RootPassword string `json:"rootPassword"`
|
|
GenerateRootPassword bool `json:"generateRootPassword"` // 服务端生成随机 root 密码并写入 TAG RootPassword
|
|
UserData string `json:"userData"`
|
|
}
|
|
|
|
type updateInstanceRequest struct {
|
|
Region string `json:"region"`
|
|
DisplayName string `json:"displayName"`
|
|
Shape string `json:"shape"` // 更换 shape,运行中实例会自动重启
|
|
Ocpus float32 `json:"ocpus"`
|
|
MemoryInGBs float32 `json:"memoryInGBs"`
|
|
}
|
|
|
|
type instanceActionRequest struct {
|
|
Region string `json:"region"`
|
|
Action string `json:"action" binding:"required"`
|
|
}
|
|
|
|
func (h *ociConfigHandler) listInstances(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
instances, err := h.svc.Instances(c.Request.Context(), id, c.Query("region"), c.Query("compartmentId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, instances)
|
|
}
|
|
|
|
func (h *ociConfigHandler) createInstance(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req createInstanceRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
count := req.Count
|
|
if count == 0 {
|
|
count = 1
|
|
}
|
|
instances, failures, err := h.svc.CreateInstances(c.Request.Context(), id, oci.CreateInstanceInput{
|
|
Region: req.Region,
|
|
CompartmentID: req.CompartmentID,
|
|
AvailabilityDomain: req.AvailabilityDomain,
|
|
DisplayName: req.DisplayName,
|
|
Shape: req.Shape,
|
|
Ocpus: req.Ocpus,
|
|
MemoryInGBs: req.MemoryInGBs,
|
|
ImageID: req.ImageID,
|
|
BootVolumeID: req.BootVolumeID,
|
|
BootVolumeSizeGBs: req.BootVolumeSizeGBs,
|
|
BootVolumeVpusPerGB: req.BootVolumeVpusPerGB,
|
|
SubnetID: req.SubnetID,
|
|
AssignPublicIP: req.AssignPublicIP,
|
|
AssignIpv6: req.AssignIpv6,
|
|
SSHPublicKey: req.SSHPublicKey,
|
|
RootPassword: req.RootPassword,
|
|
GenerateRootPassword: req.GenerateRootPassword,
|
|
UserData: req.UserData,
|
|
}, count)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
// 全部失败视为执行失败,部分成功仍返回 201 并附带逐台失败信息
|
|
status := http.StatusCreated
|
|
if len(instances) == 0 && len(failures) > 0 {
|
|
status = http.StatusInternalServerError
|
|
}
|
|
// nil 切片会序列化为 null,显式空数组免去前端判空
|
|
if instances == nil {
|
|
instances = []oci.Instance{}
|
|
}
|
|
if failures == nil {
|
|
failures = []string{}
|
|
}
|
|
c.JSON(status, gin.H{"instances": instances, "errors": failures})
|
|
}
|
|
|
|
func (h *ociConfigHandler) getInstance(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
instance, err := h.svc.Instance(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, instance)
|
|
}
|
|
|
|
func (h *ociConfigHandler) updateInstance(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req updateInstanceRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
instance, err := h.svc.UpdateInstance(c.Request.Context(), id, c.Param("instanceId"), oci.UpdateInstanceInput{
|
|
Region: req.Region,
|
|
DisplayName: req.DisplayName,
|
|
Shape: req.Shape,
|
|
Ocpus: req.Ocpus,
|
|
MemoryInGBs: req.MemoryInGBs,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, instance)
|
|
}
|
|
|
|
func (h *ociConfigHandler) terminateInstance(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
preserve := c.Query("preserveBootVolume") == "true"
|
|
if err := h.svc.TerminateInstance(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"), preserve); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *ociConfigHandler) instanceAction(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req instanceActionRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
instance, err := h.svc.InstanceAction(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.Action)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, instance)
|
|
}
|
|
|
|
// ---- 引导卷 ----
|
|
|
|
type updateBootVolumeRequest struct {
|
|
Region string `json:"region"`
|
|
DisplayName string `json:"displayName"`
|
|
SizeInGBs int64 `json:"sizeInGBs"`
|
|
VpusPerGB int64 `json:"vpusPerGB"`
|
|
}
|
|
|
|
func (h *ociConfigHandler) listBootVolumes(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
volumes, err := h.svc.BootVolumes(c.Request.Context(), id, c.Query("region"), c.Query("availabilityDomain"), c.Query("compartmentId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, volumes)
|
|
}
|
|
|
|
func (h *ociConfigHandler) getBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
volume, err := h.svc.BootVolume(c.Request.Context(), id, c.Query("region"), c.Param("bootVolumeId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, volume)
|
|
}
|
|
|
|
func (h *ociConfigHandler) updateBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req updateBootVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
volume, err := h.svc.UpdateBootVolume(c.Request.Context(), id, c.Param("bootVolumeId"), oci.UpdateBootVolumeInput{
|
|
Region: req.Region,
|
|
DisplayName: req.DisplayName,
|
|
SizeInGBs: req.SizeInGBs,
|
|
VpusPerGB: req.VpusPerGB,
|
|
})
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, volume)
|
|
}
|
|
|
|
func (h *ociConfigHandler) deleteBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DeleteBootVolume(c.Request.Context(), id, c.Query("region"), c.Param("bootVolumeId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|