198 lines
6.7 KiB
Go
198 lines
6.7 KiB
Go
package oci
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/oracle/oci-go-sdk/v65/common"
|
|
"github.com/oracle/oci-go-sdk/v65/core"
|
|
)
|
|
|
|
// ReservedIP 是保留公网 IP 摘要;绑定目标尽力反查,失败时对应字段为空。
|
|
type ReservedIP struct {
|
|
ID string `json:"id"`
|
|
DisplayName string `json:"displayName"`
|
|
IPAddress string `json:"ipAddress"`
|
|
LifecycleState string `json:"lifecycleState"`
|
|
AssignedInstanceID string `json:"assignedInstanceId"`
|
|
AssignedInstanceName string `json:"assignedInstanceName"`
|
|
TimeCreated *time.Time `json:"timeCreated"`
|
|
}
|
|
|
|
// ListReservedIPs 实现 Client:列出区间内全部保留公网 IP(REGION 作用域);
|
|
// compartmentID 为空时用生效 compartment。
|
|
func (c *RealClient) ListReservedIPs(ctx context.Context, cred Credentials, region, compartmentID string) ([]ReservedIP, error) {
|
|
vn, err := c.vcnClient(cred, region)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cc, err := c.computeClient(cred, region)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]ReservedIP, 0, 4)
|
|
var page *string
|
|
for {
|
|
resp, err := vn.ListPublicIps(ctx, core.ListPublicIpsRequest{
|
|
Scope: core.ListPublicIpsScopeRegion,
|
|
Lifetime: core.ListPublicIpsLifetimeReserved,
|
|
CompartmentId: hostCompartment(cred, compartmentID),
|
|
Page: page,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list reserved ips: %w", err)
|
|
}
|
|
for i := range resp.Items {
|
|
out = append(out, c.toReservedIP(ctx, vn, cc, resp.Items[i]))
|
|
}
|
|
if resp.OpcNextPage == nil {
|
|
break
|
|
}
|
|
page = resp.OpcNextPage
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// toReservedIP 转换 SDK 对象并尽力反查绑定的实例。
|
|
func (c *RealClient) toReservedIP(ctx context.Context, vn core.VirtualNetworkClient, cc core.ComputeClient, p core.PublicIp) ReservedIP {
|
|
var created *time.Time
|
|
if p.TimeCreated != nil {
|
|
created = &p.TimeCreated.Time
|
|
}
|
|
r := ReservedIP{
|
|
ID: deref(p.Id),
|
|
DisplayName: deref(p.DisplayName),
|
|
IPAddress: deref(p.IpAddress),
|
|
LifecycleState: string(p.LifecycleState),
|
|
TimeCreated: created,
|
|
}
|
|
if p.AssignedEntityType == core.PublicIpAssignedEntityTypePrivateIp && p.AssignedEntityId != nil {
|
|
r.AssignedInstanceID, r.AssignedInstanceName = resolveIPAssignee(ctx, vn, cc, *p.AssignedEntityId)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// resolveIPAssignee 由私有 IP 反查所属实例;任一步失败即放弃,不影响列表主体。
|
|
func resolveIPAssignee(ctx context.Context, vn core.VirtualNetworkClient, cc core.ComputeClient, privateIPID string) (string, string) {
|
|
priv, err := vn.GetPrivateIp(ctx, core.GetPrivateIpRequest{PrivateIpId: &privateIPID})
|
|
if err != nil || priv.VnicId == nil {
|
|
return "", ""
|
|
}
|
|
atts, err := cc.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
|
CompartmentId: priv.CompartmentId,
|
|
VnicId: priv.VnicId,
|
|
})
|
|
if err != nil || len(atts.Items) == 0 || atts.Items[0].InstanceId == nil {
|
|
return "", ""
|
|
}
|
|
instanceID := *atts.Items[0].InstanceId
|
|
inst, err := cc.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &instanceID})
|
|
if err != nil {
|
|
return instanceID, ""
|
|
}
|
|
return instanceID, deref(inst.DisplayName)
|
|
}
|
|
|
|
// CreateReservedIP 实现 Client:在指定 compartment 创建保留公网 IP(空为生效 compartment)。
|
|
func (c *RealClient) CreateReservedIP(ctx context.Context, cred Credentials, region, compartmentID, displayName string) (ReservedIP, error) {
|
|
vn, err := c.vcnClient(cred, region)
|
|
if err != nil {
|
|
return ReservedIP{}, err
|
|
}
|
|
details := core.CreatePublicIpDetails{
|
|
CompartmentId: hostCompartment(cred, compartmentID),
|
|
Lifetime: core.CreatePublicIpDetailsLifetimeReserved,
|
|
}
|
|
if displayName != "" {
|
|
details.DisplayName = &displayName
|
|
}
|
|
resp, err := vn.CreatePublicIp(ctx, core.CreatePublicIpRequest{CreatePublicIpDetails: details})
|
|
if err != nil {
|
|
return ReservedIP{}, fmt.Errorf("create reserved ip: %w", err)
|
|
}
|
|
cc, err := c.computeClient(cred, region)
|
|
if err != nil {
|
|
return ReservedIP{}, err
|
|
}
|
|
return c.toReservedIP(ctx, vn, cc, resp.PublicIp), nil
|
|
}
|
|
|
|
// AssignReservedIP 实现 Client:绑定保留 IP 到实例主私有 IP;instanceID 为空表示解绑。
|
|
// 目标私有 IP 已有公网 IP 时自动释放(临时删除、其他保留解绑),地址会变更,调用方需提示用户。
|
|
func (c *RealClient) AssignReservedIP(ctx context.Context, cred Credentials, region, publicIPID, instanceID string) error {
|
|
vn, err := c.vcnClient(cred, region)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target := common.String("")
|
|
if instanceID != "" {
|
|
priv, err := c.primaryPrivateIP(ctx, cred, region, instanceID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := detachExistingPublicIP(ctx, vn, priv.Id); err != nil {
|
|
return err
|
|
}
|
|
target = priv.Id
|
|
}
|
|
_, err = vn.UpdatePublicIp(ctx, core.UpdatePublicIpRequest{
|
|
PublicIpId: &publicIPID,
|
|
UpdatePublicIpDetails: core.UpdatePublicIpDetails{PrivateIpId: target},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("assign reserved ip: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AssignReservedIPToVnic 实现 Client:绑定保留 IP 到指定 VNIC 的主私有 IP(次要网卡场景);
|
|
// 该私有 IP 已有公网 IP 时自动释放(临时删除、其他保留解绑)。
|
|
func (c *RealClient) AssignReservedIPToVnic(ctx context.Context, cred Credentials, region, publicIPID, vnicID string) error {
|
|
vn, err := c.vcnClient(cred, region)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
priv, err := vnicPrimaryPrivateIP(ctx, vn, vnicID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := detachExistingPublicIP(ctx, vn, priv.Id); err != nil {
|
|
return err
|
|
}
|
|
_, err = vn.UpdatePublicIp(ctx, core.UpdatePublicIpRequest{
|
|
PublicIpId: &publicIPID,
|
|
UpdatePublicIpDetails: core.UpdatePublicIpDetails{PrivateIpId: priv.Id},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("assign reserved ip to vnic: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// vnicPrimaryPrivateIP 取 VNIC 的主私有 IP。
|
|
func vnicPrimaryPrivateIP(ctx context.Context, vn core.VirtualNetworkClient, vnicID string) (core.PrivateIp, error) {
|
|
resp, err := vn.ListPrivateIps(ctx, core.ListPrivateIpsRequest{VnicId: &vnicID})
|
|
if err != nil {
|
|
return core.PrivateIp{}, fmt.Errorf("list private ips: %w", err)
|
|
}
|
|
for _, p := range resp.Items {
|
|
if p.IsPrimary != nil && *p.IsPrimary {
|
|
return p, nil
|
|
}
|
|
}
|
|
return core.PrivateIp{}, fmt.Errorf("assign reserved ip: vnic has no primary private ip")
|
|
}
|
|
|
|
// DeleteReservedIP 实现 Client:删除保留公网 IP(已绑定的会先被 OCI 拒绝)。
|
|
func (c *RealClient) DeleteReservedIP(ctx context.Context, cred Credentials, region, publicIPID string) error {
|
|
vn, err := c.vcnClient(cred, region)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := vn.DeletePublicIp(ctx, core.DeletePublicIpRequest{PublicIpId: &publicIPID}); err != nil {
|
|
return fmt.Errorf("delete reserved ip: %w", err)
|
|
}
|
|
return nil
|
|
}
|