403 lines
12 KiB
Go
403 lines
12 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// ---- 实例 IP ----
|
|
|
|
// @Summary 更换实例主网卡临时公网 IP
|
|
// @Description 旧临时 IP 删除、旧保留 IP 自动解绑(资源保留在账户),随后分配新临时 IP
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body object true "请求体(见接口说明)"
|
|
// @Success 200 {object} publicIpResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/change-public-ip [post]
|
|
func (h *ociConfigHandler) changePublicIP(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req struct {
|
|
Region string `json:"region"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
ip, err := h.svc.ChangeInstancePublicIP(c.Request.Context(), id, req.Region, c.Param("instanceId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"publicIp": ip})
|
|
}
|
|
|
|
// @Summary 更换 VNIC 临时公网 IP
|
|
// @Description 旧临时 IP 删除、旧保留 IP 自动解绑(资源保留在账户),随后分配新临时 IP
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vnicId path string true "VNIC OCID"
|
|
// @Param body body object true "请求体:region"
|
|
// @Success 200 {object} publicIpResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vnics/{vnicId}/change-public-ip [post]
|
|
func (h *ociConfigHandler) changeVnicPublicIP(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req struct {
|
|
Region string `json:"region"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
ip, err := h.svc.ChangeVnicPublicIP(c.Request.Context(), id, req.Region, c.Param("vnicId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"publicIp": ip})
|
|
}
|
|
|
|
// @Summary 实例添加 IPv6 地址
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body object true "请求体(见接口说明)"
|
|
// @Success 201 {object} ipv6AddressResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/ipv6-addresses [post]
|
|
func (h *ociConfigHandler) addInstanceIpv6(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req struct {
|
|
Region string `json:"region"`
|
|
Address string `json:"address"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
address, err := h.svc.AddInstanceIpv6(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.Address)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, gin.H{"ipv6Address": address})
|
|
}
|
|
|
|
// @Summary 移除实例 IPv6 地址
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/ipv6-addresses [delete]
|
|
func (h *ociConfigHandler) deleteInstanceIpv6(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
err := h.svc.DeleteInstanceIpv6(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"), c.Query("address"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// ---- VNIC 管理 ----
|
|
|
|
type attachVnicRequest struct {
|
|
Region string `json:"region"`
|
|
oci.AttachVnicInput
|
|
}
|
|
|
|
// @Summary 实例 VNIC 列表
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Success 200 {array} oci.Vnic
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/vnics [get]
|
|
func (h *ociConfigHandler) listInstanceVnics(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
vnics, err := h.svc.InstanceVnics(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, vnics)
|
|
}
|
|
|
|
// @Summary 附加 VNIC
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body attachVnicRequest true "请求体"
|
|
// @Success 201 {object} oci.Vnic
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/vnics [post]
|
|
func (h *ociConfigHandler) attachVnic(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req attachVnicRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
vnic, err := h.svc.AttachVnic(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.AttachVnicInput)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, vnic)
|
|
}
|
|
|
|
// @Summary 分离 VNIC
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param attachmentId path string true "attachmentId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vnic-attachments/{attachmentId} [delete]
|
|
func (h *ociConfigHandler) detachVnic(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DetachVnic(c.Request.Context(), id, c.Query("region"), c.Param("attachmentId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// addVnicIpv6 为指定 VNIC 添加 IPv6;body.address 留空自动分配。
|
|
//
|
|
// @Summary 为指定 VNIC 添加 IPv6
|
|
// @Tags 计算
|
|
// @Param id path int true "配置 ID"
|
|
// @Param vnicId path string true "vnicId"
|
|
// @Param body body object true "请求体(见接口说明)"
|
|
// @Success 201 {object} addressResponse
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/vnics/{vnicId}/ipv6-addresses [post]
|
|
func (h *ociConfigHandler) addVnicIpv6(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req struct {
|
|
Region string `json:"region"`
|
|
Address string `json:"address"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
addr, err := h.svc.AddVnicIpv6(c.Request.Context(), id, req.Region, c.Param("vnicId"), req.Address)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, gin.H{"address": addr})
|
|
}
|
|
|
|
// ---- 引导卷挂载 ----
|
|
|
|
type attachBootVolumeRequest struct {
|
|
Region string `json:"region"`
|
|
BootVolumeID string `json:"bootVolumeId" binding:"required"`
|
|
}
|
|
|
|
// @Summary 引导卷附件列表
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Success 200 {array} oci.BootVolumeAttachment
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/boot-volume-attachments [get]
|
|
func (h *ociConfigHandler) listBootVolumeAttachments(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
atts, err := h.svc.BootVolumeAttachments(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, atts)
|
|
}
|
|
|
|
// @Summary 附加引导卷
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body attachBootVolumeRequest true "请求体"
|
|
// @Success 201 {object} oci.BootVolumeAttachment
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/boot-volume-attachments [post]
|
|
func (h *ociConfigHandler) attachBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req attachBootVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
att, err := h.svc.AttachBootVolume(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.BootVolumeID)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, att)
|
|
}
|
|
|
|
// @Summary 更换实例引导卷
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body attachBootVolumeRequest true "请求体"
|
|
// @Success 200 {object} oci.BootVolumeAttachment
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/replace-boot-volume [post]
|
|
func (h *ociConfigHandler) replaceBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req attachBootVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
att, err := h.svc.ReplaceBootVolume(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.BootVolumeID)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, att)
|
|
}
|
|
|
|
// @Summary 分离引导卷
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param attachmentId path string true "attachmentId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/boot-volume-attachments/{attachmentId} [delete]
|
|
func (h *ociConfigHandler) detachBootVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DetachBootVolume(c.Request.Context(), id, c.Query("region"), c.Param("attachmentId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// ---- 块卷挂载 ----
|
|
|
|
type attachVolumeRequest struct {
|
|
Region string `json:"region"`
|
|
VolumeID string `json:"volumeId" binding:"required"`
|
|
ReadOnly bool `json:"readOnly"`
|
|
}
|
|
|
|
// @Summary 块存储卷列表
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Success 200 {array} oci.BlockVolume
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/volumes [get]
|
|
func (h *ociConfigHandler) listBlockVolumes(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
volumes, err := h.svc.BlockVolumes(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)
|
|
}
|
|
|
|
// @Summary 卷附件列表
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Success 200 {array} oci.VolumeAttachment
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/volume-attachments [get]
|
|
func (h *ociConfigHandler) listVolumeAttachments(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
atts, err := h.svc.VolumeAttachments(c.Request.Context(), id, c.Query("region"), c.Param("instanceId"))
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, atts)
|
|
}
|
|
|
|
// @Summary 附加块存储卷
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param instanceId path string true "instanceId"
|
|
// @Param body body attachVolumeRequest true "请求体"
|
|
// @Success 201 {object} oci.VolumeAttachment
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/instances/{instanceId}/volume-attachments [post]
|
|
func (h *ociConfigHandler) attachVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
var req attachVolumeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
att, err := h.svc.AttachVolume(c.Request.Context(), id, req.Region, c.Param("instanceId"), req.VolumeID, req.ReadOnly)
|
|
if err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, att)
|
|
}
|
|
|
|
// @Summary 分离块存储卷
|
|
// @Tags 存储
|
|
// @Param id path int true "配置 ID"
|
|
// @Param attachmentId path string true "attachmentId"
|
|
// @Success 204 "无内容"
|
|
// @Security BearerAuth
|
|
// @Router /api/v1/oci-configs/{id}/volume-attachments/{attachmentId} [delete]
|
|
func (h *ociConfigHandler) detachVolume(c *gin.Context) {
|
|
id, ok := pathID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := h.svc.DetachVolume(c.Request.Context(), id, c.Query("region"), c.Param("attachmentId")); err != nil {
|
|
respondError(c, err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|