138 lines
4.5 KiB
Go
138 lines
4.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// ReservedIPs 列出指定区间的保留公网 IP(compartmentID 空为生效 compartment)。
|
|
func (s *OciConfigService) ReservedIPs(ctx context.Context, id uint, region, compartmentID string) ([]oci.ReservedIP, error) {
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.client.ListReservedIPs(ctx, cred, region, compartmentID)
|
|
}
|
|
|
|
// CreateReservedIP 在指定区间创建保留公网 IP。
|
|
func (s *OciConfigService) CreateReservedIP(ctx context.Context, id uint, region, compartmentID, displayName string) (oci.ReservedIP, error) {
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return oci.ReservedIP{}, err
|
|
}
|
|
return s.client.CreateReservedIP(ctx, cred, region, compartmentID, displayName)
|
|
}
|
|
|
|
// AssignReservedIP 绑定保留 IP:vnicID 非空绑到该网卡主私有 IP,
|
|
// 否则绑实例主网卡(instanceID 为空表示解绑)。
|
|
func (s *OciConfigService) AssignReservedIP(ctx context.Context, id uint, region, publicIPID, instanceID, vnicID string) error {
|
|
if publicIPID == "" {
|
|
return fmt.Errorf("assign reserved ip: publicIpId is required")
|
|
}
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if vnicID != "" {
|
|
return s.client.AssignReservedIPToVnic(ctx, cred, region, publicIPID, vnicID)
|
|
}
|
|
return s.client.AssignReservedIP(ctx, cred, region, publicIPID, instanceID)
|
|
}
|
|
|
|
// DeleteReservedIP 删除保留公网 IP。
|
|
func (s *OciConfigService) DeleteReservedIP(ctx context.Context, id uint, region, publicIPID string) error {
|
|
if publicIPID == "" {
|
|
return fmt.Errorf("delete reserved ip: publicIpId is required")
|
|
}
|
|
cred, err := s.credentialsByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.client.DeleteReservedIP(ctx, cred, region, publicIPID)
|
|
}
|
|
|
|
// 保留 IP 后台绑定的轮询节奏与超时上限。
|
|
const (
|
|
reservedBindInterval = 10 * time.Second
|
|
reservedBindTimeout = 8 * time.Minute
|
|
)
|
|
|
|
// goBind 启动保留 IP 后台绑定 goroutine 并纳入 WaitGroup,供 Stop 等待收尾。
|
|
func (s *OciConfigService) goBind(fn func()) {
|
|
s.bindWG.Add(1)
|
|
go func() {
|
|
defer s.bindWG.Done()
|
|
fn()
|
|
}()
|
|
}
|
|
|
|
// Stop 通知在途的保留 IP 绑定 goroutine 提前退出并等待全部收尾;仅关服时调用一次。
|
|
func (s *OciConfigService) Stop() {
|
|
close(s.bindStop)
|
|
s.bindWG.Wait()
|
|
}
|
|
|
|
// bindReservedIPToVnicWhenReady 等次要 VNIC 就绪后绑定保留 IP;附加网卡的后台编排,
|
|
// 失败只记日志(附加请求本身已成功返回)。
|
|
func (s *OciConfigService) bindReservedIPToVnicWhenReady(cred oci.Credentials, region, instanceID, attachmentID, publicIPID string) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), reservedBindTimeout)
|
|
defer cancel()
|
|
for {
|
|
vnics, err := s.client.ListInstanceVnics(ctx, cred, region, instanceID)
|
|
if err == nil {
|
|
if vnicID := readyVnicID(vnics, attachmentID); vnicID != "" {
|
|
if err := s.client.AssignReservedIPToVnic(ctx, cred, region, publicIPID, vnicID); err != nil {
|
|
log.Printf("[reserved-ip] bind %s to vnic %s failed: %v", publicIPID, vnicID, err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Printf("[reserved-ip] bind %s timed out waiting for attachment %s", publicIPID, attachmentID)
|
|
return
|
|
case <-s.bindStop:
|
|
return
|
|
case <-time.After(reservedBindInterval):
|
|
}
|
|
}
|
|
}
|
|
|
|
// readyVnicID 从网卡列表找出指定附加关系中已就绪的 VNIC。
|
|
func readyVnicID(vnics []oci.Vnic, attachmentID string) string {
|
|
for _, v := range vnics {
|
|
if v.AttachmentID == attachmentID && v.VnicID != "" && v.LifecycleState == "ATTACHED" {
|
|
return v.VnicID
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// bindReservedIPWhenReady 等实例 RUNNING 后绑定保留 IP;创建实例的后台编排,
|
|
// 失败只记日志(创建请求本身已成功返回)。
|
|
func (s *OciConfigService) bindReservedIPWhenReady(cred oci.Credentials, region, instanceID, publicIPID string) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), reservedBindTimeout)
|
|
defer cancel()
|
|
for {
|
|
inst, err := s.client.GetInstance(ctx, cred, region, instanceID)
|
|
if err == nil && inst.LifecycleState == "RUNNING" {
|
|
if err := s.client.AssignReservedIP(ctx, cred, region, publicIPID, instanceID); err != nil {
|
|
log.Printf("[reserved-ip] bind %s to instance %s failed: %v", publicIPID, instanceID, err)
|
|
}
|
|
return
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Printf("[reserved-ip] bind %s timed out waiting for instance %s", publicIPID, instanceID)
|
|
return
|
|
case <-s.bindStop:
|
|
return
|
|
case <-time.After(reservedBindInterval):
|
|
}
|
|
}
|
|
}
|