186 lines
6.9 KiB
Go
186 lines
6.9 KiB
Go
package oci
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/oracle/oci-go-sdk/v65/core"
|
||
)
|
||
|
||
// 本文件实现 VCN 的级联删除:OCI 要求 VCN 删除前先清空其子网、网关、
|
||
// 非默认路由表 / 安全列表 / DHCP 选项,顺序参照控制台删除向导。
|
||
// 子网仍被 VNIC 占用(实例未终止)时 OCI 返回 409 IncorrectState,
|
||
// 该错误经 api 层提炼后提示用户先终止实例,不在此做实例级清理。
|
||
|
||
// deleteVcnCascade 依次清理关联资源后删除 VCN。
|
||
func deleteVcnCascade(ctx context.Context, vn core.VirtualNetworkClient, vcnID string) error {
|
||
vcnResp, err := vn.GetVcn(ctx, core.GetVcnRequest{VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("get vcn %s: %w", vcnID, err)
|
||
}
|
||
comp := deref(vcnResp.CompartmentId)
|
||
if err := deleteVcnSubnets(ctx, vn, comp, vcnID); err != nil {
|
||
return err
|
||
}
|
||
if err := clearVcnRouteTables(ctx, vn, comp, vcnID, deref(vcnResp.DefaultRouteTableId)); err != nil {
|
||
return err
|
||
}
|
||
if err := deleteVcnGateways(ctx, vn, comp, vcnID); err != nil {
|
||
return err
|
||
}
|
||
if err := deleteVcnSecondaries(ctx, vn, comp, vcnID, vcnResp.Vcn); err != nil {
|
||
return err
|
||
}
|
||
if _, err := vn.DeleteVcn(ctx, core.DeleteVcnRequest{VcnId: &vcnID}); err != nil {
|
||
return fmt.Errorf("delete vcn %s: %w", vcnID, err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// deleteVcnSubnets 删除 VCN 下全部子网并等待终止完成。
|
||
func deleteVcnSubnets(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
resp, err := vn.ListSubnets(ctx, core.ListSubnetsRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list subnets: %w", err)
|
||
}
|
||
for _, s := range resp.Items {
|
||
if s.LifecycleState == core.SubnetLifecycleStateTerminated ||
|
||
s.LifecycleState == core.SubnetLifecycleStateTerminating {
|
||
continue
|
||
}
|
||
if _, err := vn.DeleteSubnet(ctx, core.DeleteSubnetRequest{SubnetId: s.Id}); err != nil {
|
||
return fmt.Errorf("delete subnet %s: %w", deref(s.DisplayName), err)
|
||
}
|
||
}
|
||
return waitSubnetsGone(ctx, vn, compartmentID, vcnID)
|
||
}
|
||
|
||
// waitSubnetsGone 轮询等待 VCN 下子网全部终止,超时 60 秒。
|
||
func waitSubnetsGone(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
for i := 0; i < 30; i++ {
|
||
resp, err := vn.ListSubnets(ctx, core.ListSubnetsRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("wait subnets terminated: %w", err)
|
||
}
|
||
alive := 0
|
||
for _, s := range resp.Items {
|
||
if s.LifecycleState != core.SubnetLifecycleStateTerminated {
|
||
alive++
|
||
}
|
||
}
|
||
if alive == 0 {
|
||
return nil
|
||
}
|
||
select {
|
||
case <-ctx.Done():
|
||
return ctx.Err()
|
||
case <-time.After(2 * time.Second):
|
||
}
|
||
}
|
||
return fmt.Errorf("wait subnets terminated: timeout after 60s")
|
||
}
|
||
|
||
// clearVcnRouteTables 清空所有路由表规则(解除对网关的引用)并删除非默认路由表。
|
||
func clearVcnRouteTables(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID, defaultRtID string) error {
|
||
resp, err := vn.ListRouteTables(ctx, core.ListRouteTablesRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list route tables: %w", err)
|
||
}
|
||
for _, rt := range resp.Items {
|
||
if len(rt.RouteRules) > 0 {
|
||
if _, err := vn.UpdateRouteTable(ctx, core.UpdateRouteTableRequest{
|
||
RtId: rt.Id,
|
||
UpdateRouteTableDetails: core.UpdateRouteTableDetails{RouteRules: []core.RouteRule{}},
|
||
}); err != nil {
|
||
return fmt.Errorf("clear route table %s: %w", deref(rt.DisplayName), err)
|
||
}
|
||
}
|
||
if deref(rt.Id) != defaultRtID {
|
||
if _, err := vn.DeleteRouteTable(ctx, core.DeleteRouteTableRequest{RtId: rt.Id}); err != nil {
|
||
return fmt.Errorf("delete route table %s: %w", deref(rt.DisplayName), err)
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// deleteVcnGateways 删除 VCN 下的互联网 / NAT / 服务网关。
|
||
func deleteVcnGateways(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
if err := deleteInternetGateways(ctx, vn, compartmentID, vcnID); err != nil {
|
||
return err
|
||
}
|
||
if err := deleteNatGateways(ctx, vn, compartmentID, vcnID); err != nil {
|
||
return err
|
||
}
|
||
return deleteServiceGateways(ctx, vn, compartmentID, vcnID)
|
||
}
|
||
|
||
func deleteInternetGateways(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
resp, err := vn.ListInternetGateways(ctx, core.ListInternetGatewaysRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list internet gateways: %w", err)
|
||
}
|
||
for _, g := range resp.Items {
|
||
if _, err := vn.DeleteInternetGateway(ctx, core.DeleteInternetGatewayRequest{IgId: g.Id}); err != nil {
|
||
return fmt.Errorf("delete internet gateway %s: %w", deref(g.DisplayName), err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func deleteNatGateways(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
resp, err := vn.ListNatGateways(ctx, core.ListNatGatewaysRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list nat gateways: %w", err)
|
||
}
|
||
for _, g := range resp.Items {
|
||
if _, err := vn.DeleteNatGateway(ctx, core.DeleteNatGatewayRequest{NatGatewayId: g.Id}); err != nil {
|
||
return fmt.Errorf("delete nat gateway %s: %w", deref(g.DisplayName), err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func deleteServiceGateways(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string) error {
|
||
resp, err := vn.ListServiceGateways(ctx, core.ListServiceGatewaysRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list service gateways: %w", err)
|
||
}
|
||
for _, g := range resp.Items {
|
||
if _, err := vn.DeleteServiceGateway(ctx, core.DeleteServiceGatewayRequest{ServiceGatewayId: g.Id}); err != nil {
|
||
return fmt.Errorf("delete service gateway %s: %w", deref(g.DisplayName), err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// deleteVcnSecondaries 删除非默认安全列表与非默认 DHCP 选项。
|
||
func deleteVcnSecondaries(ctx context.Context, vn core.VirtualNetworkClient, compartmentID, vcnID string, vcn core.Vcn) error {
|
||
slResp, err := vn.ListSecurityLists(ctx, core.ListSecurityListsRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list security lists: %w", err)
|
||
}
|
||
for _, sl := range slResp.Items {
|
||
if deref(sl.Id) == deref(vcn.DefaultSecurityListId) {
|
||
continue
|
||
}
|
||
if _, err := vn.DeleteSecurityList(ctx, core.DeleteSecurityListRequest{SecurityListId: sl.Id}); err != nil {
|
||
return fmt.Errorf("delete security list %s: %w", deref(sl.DisplayName), err)
|
||
}
|
||
}
|
||
dhResp, err := vn.ListDhcpOptions(ctx, core.ListDhcpOptionsRequest{CompartmentId: &compartmentID, VcnId: &vcnID})
|
||
if err != nil {
|
||
return fmt.Errorf("list dhcp options: %w", err)
|
||
}
|
||
for _, d := range dhResp.Items {
|
||
if deref(d.Id) == deref(vcn.DefaultDhcpOptionsId) {
|
||
continue
|
||
}
|
||
if _, err := vn.DeleteDhcpOptions(ctx, core.DeleteDhcpOptionsRequest{DhcpId: d.Id}); err != nil {
|
||
return fmt.Errorf("delete dhcp options %s: %w", deref(d.DisplayName), err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|