jenkins Job华为云EIP变更带宽

异步星轨
• 阅读 124

引言:

在数字化时代,云服务资源的弹性管理是企业降低运营成本、提高效率的关键手段。通过弹性公网IP(EIP)服务,企业可以实现按需计费,优化网络支出。然而,根据业务流量的不同阶段调整计费模式,则是提升成本效益的进阶策略。本人腾讯云快十年老用户乘机吐槽一下腾讯云(由于我在大有所为的某云上面已经简单实现了更改流程):
习惯了使用apiexplorer这样的工具生成代码进行修改,参考一下友商的:
jenkins Job华为云EIP变更带宽
然后我的腾讯云弹性公网IP ?EIP 对吧?
jenkins Job华为云EIP变更带宽
这是什么样的体验?完全搜索不到?关键词EIP 弹性公网 完全搜索不到......
jenkins Job华为云EIP变更带宽
jenkins Job华为云EIP变更带宽
最后我在这里找到了:
绑定弹性公网IP
jenkins Job华为云EIP变更带宽
你可以归于私有网络?是不是应该好歹独立一下.....
jenkins Job华为云EIP变更带宽
吐槽完毕,本文将详细介绍如何使用华为云Go SDK在流量高峰时自动调整EIP带宽设置,并在峰值过后恢复原计费模式。

业务背景:

考虑到一家在线互动应用提供商,主要架构是websockt 长链接,其流量在晚高峰时段飙升,观众涌入平台进行抢红包等互动活动。活动时常大概在一个小时。在流量高峰时,固定带宽的使用能保证用户的观看体验,且相对于按流量计费,成本更为可控。因此,我们面临一个机遇,通过智能化工具调整EIP的带宽计费模式,在需要时提供稳定的网络资源和更优的财务开支。

技术方案:

服务器场景搭建在华为云上,使用了cce kubernetes服务。绑定了应用型负载均衡(负载均衡有本身的EIP公网IP),为了实现这一目标,我们选择华为云的Elastic IP服务,它提供了一系列API,允许程序化管理EIP资源。通过Go语言编写的定时任务,我们可以精确控制带宽的调整时机和规模。在本方案中,使用华为云提供的SDK,我们将编写一个Go应用程序,当业务流量达到高峰时,自动将EIP的计费模式从按流量计费调整为按带宽计费,并固定带宽大小。一小时后,系统自动将设置恢复为按流量计费,以节省成本。

代码实现:

参照以下代码实例:UpdateBandwidth 更新带宽方法:
jenkins Job华为云EIP变更带宽

package main

import (
    "fmt"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // The AK and SK used for authentication are hard-coded or stored in plaintext, which has great security risks. It is recommended that the AK and SK be stored in ciphertext in configuration files or environment variables and decrypted during use to ensure security.
    // In this example, AK and SK are stored in environment variables for authentication. Before running this example, set environment variables CLOUD_SDK_AK and CLOUD_SDK_SK in the local environment
    ak := os.Getenv("CLOUD_SDK_AK")
    sk := os.Getenv("CLOUD_SDK_SK")

    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        Build()

    client := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            Build())

    request := &model.UpdatePublicipRequest{}
    request.Body = &model.UpdatePublicipsRequestBody{
    }
    response, err := client.UpdatePublicip(request)
    if err == nil {
        fmt.Printf("%+v\n", response)
    } else {
        fmt.Println(err)
    }
}

最终实现代码如下:

package main

import (
    "fmt"
    "time"

    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // Replace with your own AK/SK, Region, and Project ID.
    ak := "xxxxx"
    sk := "xxxxx"
    projectID := "xxxxx"

    // Authenticate using your Access Key and Secret Key.
    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        WithProjectId(projectID).
        Build()

    // Create EIP client configuration.
    eipClient := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            WithHttpConfig(config.DefaultHttpConfig()).
            Build(),
    )

    // Replace the bandwidthId with your actual bandwidth ID.
    sizeBandwidth := int32(xxx)
    sizeTraefikBandwidth := int32(xxxx)

    // Update bandwidth to 10 Mbps and set to bandwidth billing mode.
    if err := updateBandwidth(eipClient, bandwidthId, "", sizeBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
        fmt.Printf("Error updating bandwidth: %s\n", err)
        return
    }
    // Set a timer to switch back to traffic billing mode after 1 hour.
    time.AfterFunc(1*time.Hour, func() {
        if err := updateBandwidth(eipClient, bandwidthId, "", sizeTraefikBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("Error reverting bandwidth: %s\n", err)
            return
        }
    })

    // Block the main goroutine to not exit immediately.
    select {}
}

// Function to update the bandwidth of an EIP.
func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

初始化EIP客户端

首先,需设置客户端认证,这涉及到基础的Access Key(AK)和Secret Key(SK)配置:

ak := "REPLACE_WITH_YOUR_AK"
sk := "REPLACE_WITH_YOUR_SK"
projectID := "REPLACE_WITH_YOUR_PROJECT_ID"

    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        Build()

    client := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            Build())
...

务必保障这些敏感信息在环境变量或加密存储中,避免硬编码在应用程序中。

更新带宽函数

updateBandwidth 函数对EIP带宽大小和计费模式进行修改。

func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

这个函数构造了更新带宽的API请求,并处理响应或可能出现的错误。

使用协程和定时器

Go的并发模型让我们能够用协程(goroutines)和定时器轻松实现这个需求。

    go func() {
        if err := updateBandwidth(eipClient, bandwidthId, "xxxxx", sizeBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
            fmt.Printf("更新带宽时出错: %s\n", err)
            return
        }

        // 设置计时器在10分钟后切换回流量计费模式。
        timer := time.NewTimer(1 * time.Minute)
        <-timer.C

        if err := updateBandwidth(eipClient, bandwidthId, "xxxxxx", sizeTrafficBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("恢复带宽时出错: %s\n", err)
            return
        }
    }()

    // 使用通道阻塞主 goroutine 无限期。避免在空的 select 语句中旋转,这是不必要的。
    done := make(chan struct{})
    <-done
}

第一版代码

完整代码如下:

package main

import (
    "fmt"
    "time"

    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // Replace with your own AK/SK, Region, and Project ID.
    ak := "xxxxxx"
    sk := "xxxxxx"
    projectID := "xxxxxx"

    // Authenticate using your Access Key and Secret Key.
    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        WithProjectId(projectID).
        Build()

    // Create EIP client configuration.
    eipClient := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            WithHttpConfig(config.DefaultHttpConfig()).
            Build(),
    )

    // Replace the bandwidthId with your actual bandwidth ID.
    bandwidthId := "xxxx"
    sizeBandwidth := int32(xxx)
    sizeTrafficBandwidth := int32(xxx)

    // Update bandwidth to 10 Mbps and set to bandwidth billing mode.
    if err := updateBandwidth(eipClient, bandwidthId, "xxxx", sizeBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
        fmt.Printf("Error updating bandwidth: %s\n", err)
        return
    }
    // Set a timer to switch back to traffic billing mode after 1 hour.
    time.AfterFunc(1*time.Hour, func() {
        if err := updateBandwidth(eipClient, bandwidthId, "xxxxx", sizeBandwidth, model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("Error reverting bandwidth: %s\n", err)
            return
        }
    })

    // Block the main goroutine to not exit immediately.
    select {}
}

// Function to update the bandwidth of an EIP.
func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

运行main.go
jenkins Job华为云EIP变更带宽
可以在控制台查看代码操作,控制台对应实例生效!

继续完善代码:

上面的代码是用户传入bandwidthId,这关键一般用户不知道bandwidthId 阿?控制台用户来说一般能知道的是公网IP
jenkins Job华为云EIP变更带宽
我这里想到的是使用ListPublicips-查询弹性公网IP列表获取bandwidth_name bandwidth_id。 创建两个函数:getBandwidthIdByPublicIP getBandwidthNameByPublicIP

// 这里假设有一个通过公网IP获取带宽ID的函数
func getBandwidthIdByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthId != nil {
                return *publicip.BandwidthId, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}
func getBandwidthNameByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthName != nil {
                return *publicip.BandwidthName, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}

继续完善一下,通过运行main.go传入公网IP,固定带宽恢复按量计费后的带宽大小,以及修改为固定带宽的持续时间 。

go run main.go publicIP  sizeBandwidth sizeTrafficBandwidth timerMinutes
package main

import (
    "errors"
    "fmt"
    "os"
    "strconv"
    "time"

    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // Replace with your own AK/SK, Region, and Project ID.
    ak := "xxxxxx"
    sk := "xxxxxx"
    projectID := "xxxxxx"
    if len(os.Args) != 5 {
        fmt.Println("Usage: go run main.go <publicIP> <sizeBandwidth> <sizeTrafficBandwidth> <timerMinutes>")
        return
    }

    publicIP := os.Args[1]
    sizeBandwidth, err := strconv.Atoi(os.Args[2])
    if err != nil {
        fmt.Println("Invalid sizeBandwidth:", err)
        return
    }
    sizeTrafficBandwidth, err := strconv.Atoi(os.Args[3])
    if err != nil {
        fmt.Println("Invalid sizeTrafficBandwidth:", err)
        return
    }
    timerMinutes, err := strconv.Atoi(os.Args[4])
    if err != nil {
        fmt.Println("Invalid timerMinutes:", err)
        return
    }
    // Authenticate using your Access Key and Secret Key.
    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        WithProjectId(projectID).
        Build()

    // Create EIP client configuration.
    eipClient := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            WithHttpConfig(config.DefaultHttpConfig()).
            Build(),
    )

    bandwidthId, err := getBandwidthIdByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    bandwidthName, err := getBandwidthNameByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    go func() {
        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
            fmt.Printf("更新带宽时出错: %s\n", err)
            return
        }

        // 设置计时器在10分钟后切换回流量计费模式。
        timer := time.NewTimer(time.Duration(timerMinutes) * time.Minute)
        <-timer.C

        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeTrafficBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("恢复带宽时出错: %s\n", err)
            return
        }
    }()

    // 使用通道阻塞主 goroutine 无限期。避免在空的 select 语句中旋转,这是不必要的。
    done := make(chan struct{})
    <-done
}

// 这里假设有一个通过公网IP获取带宽ID的函数
func getBandwidthIdByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthId != nil {
                return *publicip.BandwidthId, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}
func getBandwidthNameByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthName != nil {
                return *publicip.BandwidthName, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}

// Function to update the bandwidth of an EIP.
func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

jenkins运行以上代码实例

最终我需要在jenkins中运行这个更改流量的任务,我将引用ak,sk引用jenkins凭据的方式:
参照之前刷新cdn时候引用凭据的方式:
jenkins Job华为云EIP变更带宽
最终代码如下:

package main

import (
    "errors"
    "fmt"
    "os"
    "strconv"
    "time"

    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // Replace with your own AK/SK, Region, and Project ID.
    // 尝试从环境变量中获取ak和sk
    creds := os.Getenv("huaweiyun-hn-cdn")
    if creds == "" {
        fmt.Fprintln(os.Stderr, "Error: Credentials environment variable is not set.")
        os.Exit(1)
    }

    parts := strings.SplitN(creds, ":", 2)
    if len(parts) != 2 {
        fmt.Fprintln(os.Stderr, "Error: Invalid credential format. Expected 'AK:SK'.")
        os.Exit(1)
    }

    ak, sk := parts[0], parts[1]
    projectID := "xxxxxx"
    if len(os.Args) != 5 {
        fmt.Println("Usage: go run main.go <publicIP> <sizeBandwidth> <sizeTrafficBandwidth> <timerMinutes>")
        return
    }

    publicIP := os.Args[1]
    sizeBandwidth, err := strconv.Atoi(os.Args[2])
    if err != nil {
        fmt.Println("Invalid sizeBandwidth:", err)
        return
    }
    sizeTrafficBandwidth, err := strconv.Atoi(os.Args[3])
    if err != nil {
        fmt.Println("Invalid sizeTrafficBandwidth:", err)
        return
    }
    timerMinutes, err := strconv.Atoi(os.Args[4])
    if err != nil {
        fmt.Println("Invalid timerMinutes:", err)
        return
    }
    // Authenticate using your Access Key and Secret Key.
    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        WithProjectId(projectID).
        Build()

    // Create EIP client configuration.
    eipClient := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            WithHttpConfig(config.DefaultHttpConfig()).
            Build(),
    )

    bandwidthId, err := getBandwidthIdByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    bandwidthName, err := getBandwidthNameByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    go func() {
        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
            fmt.Printf("更新带宽时出错: %s\n", err)
            return
        }

        // 设置计时器在10分钟后切换回流量计费模式。
        timer := time.NewTimer(time.Duration(timerMinutes) * time.Minute)
        <-timer.C

        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeTrafficBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("恢复带宽时出错: %s\n", err)
            return
        }
    }()

    // 使用通道阻塞主 goroutine 无限期。避免在空的 select 语句中旋转,这是不必要的。
    done := make(chan struct{})
    <-done
}

// 这里假设有一个通过公网IP获取带宽ID的函数
func getBandwidthIdByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthId != nil {
                return *publicip.BandwidthId, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}
func getBandwidthNameByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthName != nil {
                return *publicip.BandwidthName, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}

// Function to update the bandwidth of an EIP.
func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

go build main.go,编译为可执行程序!将可执行程序放在工作节点上,jenkins 做了一个demo
参数化构建,字符参数
jenkins Job华为云EIP变更带宽
绑定密钥文件:
jenkins Job华为云EIP变更带宽
Build Steps 执行shell

cd /home/eip-hw&&./main $publicIP $sizeBandwidth $sizeTrafficBandwidth $timerMinutes

jenkins Job华为云EIP变更带宽
输入publicIP运行程序:
jenkins Job华为云EIP变更带宽
程序运行生效,但是进程没有退出:

jenkins Job华为云EIP变更带宽

最终go相关代码:

继续完善一下代码,当程序运行完成后,退出程序。输出Bandwidth update successful, exiting:

package main

import (
    "errors"
    "fmt"
    "os"
    "strconv"
    "time"
        "strings"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/config"
    eip "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/model"
    region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/eip/v2/region"
)

func main() {
    // Replace with your own AK/SK, Region, and Project ID.
    // 尝试从环境变量中获取ak和sk
    creds := os.Getenv("xxxxxx")
    if creds == "" {
        fmt.Fprintln(os.Stderr, "Error: Credentials environment variable is not set.")
        os.Exit(1)
    }

    parts := strings.SplitN(creds, ":", 2)
    if len(parts) != 2 {
        fmt.Fprintln(os.Stderr, "Error: Invalid credential format. Expected 'AK:SK'.")
        os.Exit(1)
    }

    ak, sk := parts[0], parts[1]
    projectID := "xxxxxx"
    if len(os.Args) != 5 {
        fmt.Println("Usage: go run main.go <publicIP> <sizeBandwidth> <sizeTrafficBandwidth> <timerMinutes>")
        return
    }

    publicIP := os.Args[1]
    sizeBandwidth, err := strconv.Atoi(os.Args[2])
    if err != nil {
        fmt.Println("Invalid sizeBandwidth:", err)
        return
    }
    sizeTrafficBandwidth, err := strconv.Atoi(os.Args[3])
    if err != nil {
        fmt.Println("Invalid sizeTrafficBandwidth:", err)
        return
    }
    timerMinutes, err := strconv.Atoi(os.Args[4])
    if err != nil {
        fmt.Println("Invalid timerMinutes:", err)
        return
    }
    // Authenticate using your Access Key and Secret Key.
    auth := basic.NewCredentialsBuilder().
        WithAk(ak).
        WithSk(sk).
        WithProjectId(projectID).
        Build()

    // Create EIP client configuration.
    eipClient := eip.NewEipClient(
        eip.EipClientBuilder().
            WithRegion(region.ValueOf("cn-east-3")).
            WithCredential(auth).
            WithHttpConfig(config.DefaultHttpConfig()).
            Build(),
    )

    bandwidthId, err := getBandwidthIdByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    bandwidthName, err := getBandwidthNameByPublicIP(eipClient, publicIP)
    if err != nil {
        fmt.Println(err)
        return
    }
    go func() {
        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().BANDWIDTH); err != nil {
            fmt.Printf("更新带宽时出错: %s\n", err)
             os.Exit(1)
            return
        }

        // 设置计时器在10分钟后切换回流量计费模式。
        timer := time.NewTimer(time.Duration(timerMinutes) * time.Minute)
        <-timer.C

        if err := updateBandwidth(eipClient, bandwidthId, bandwidthName, int32(sizeTrafficBandwidth), model.GetUpdateBandwidthOptionChargeModeEnum().TRAFFIC); err != nil {
            fmt.Printf("恢复带宽时出错: %s\n", err)
            os.Exit(1)
            return
        }
        fmt.Println("Bandwidth update successful, exiting.") // Log success message
                os.Exit(0) // Exit the process after success
    }()

    // 使用通道阻塞主 goroutine 无限期。避免在空的 select 语句中旋转,这是不必要的。
    done := make(chan struct{})
    <-done
}

// 这里假设有一个通过公网IP获取带宽ID的函数
func getBandwidthIdByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthId != nil {
                return *publicip.BandwidthId, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}
func getBandwidthNameByPublicIP(client *eip.EipClient, publicIP string) (string, error) {
    // 首先构造查询请求
    request := &model.ListPublicipsRequest{
        // 根据需要设置查询参数
    }

    // 调用SDK方法查询EIP详情
    response, err := client.ListPublicips(request)
    if err != nil {
        return "", err
    }

    // 遍历响应中的公共IP地址
    for _, publicip := range *response.Publicips { // 假设返回的是指针指向的切片
        // 检查 PublicIpAddress 是否为nil,并解引用来比较值
        if publicip.PublicIpAddress != nil && *publicip.PublicIpAddress == publicIP {
            // 检查 BandwidthId 是否为nil,并解引用来返回值
            if publicip.BandwidthName != nil {
                return *publicip.BandwidthName, nil
            }
            break // 如果 BandwidthId 为nil,则结束循环
        }
    }

    // 如果没有找到匹配的公共IP地址或带宽ID是nil,则返回错误
    return "", errors.New("public IP not found or bandwidth ID is nil")
}

// Function to update the bandwidth of an EIP.
func updateBandwidth(client *eip.EipClient, bandwidthId, name string, size int32, chargeMode model.UpdateBandwidthOptionChargeMode) error {
    request := &model.UpdateBandwidthRequest{
        BandwidthId: bandwidthId,
        Body: &model.UpdateBandwidthRequestBody{
            Bandwidth: &model.UpdateBandwidthOption{
                Name:       &name,
                Size:       &size,
                ChargeMode: &chargeMode,
            },
        },
    }

    response, err := client.UpdateBandwidth(request)
    if err != nil {
        return err
    }
    fmt.Printf("Update response: %+v\n", response)
    return nil
}

jenkins Job华为云EIP变更带宽
ok这样就简单实现了!
注:以上代码chatgpt生成,个人进行了更改整理.

其他想到的

  1. 修改一下程序,检测流量计费,当带宽到xxx的时候自动切换带宽?
  2. 支持一下更多类型的切换方式?比如固定带宽的直接10变更 5 20,不恢复流量计费?
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
【案例分享】如何利用京东云建设高可用业务架构
本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业务SLA,提升运维自动化水平,降低资源成本及运维成本。有业务迁移上云需求,希望构建云上高可用业务架构的客户或对云上高可用架构规划有兴趣的读者可以一看。
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
1. 容器化部署一套云服务 第一讲 Jenkins(Docker + Jenkins + Yii2 + 云服务器))
容器化部署一套云服务系列1\.容器化部署一套云服务之Jenkins(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fjackson0714%2Fp%2Fdeploy1.html)一、购买服务器服务器!caeef00
百度云,华为云,腾讯云,阿里云测评
在挑选VPS之前,首先你要明确自己的用途,根据自己的用途挑选合适的VPS。2022年国内最好最优质的VPS推荐​阿里云、华为云、腾讯云、百度云哪家便宜一般来说腾讯云价格较华为云、阿里云都便宜。阿里云价格华为云价格腾讯云价格>百度云价格NO1:阿里云简介:阿里云成立于2009年,为中国目前最大的虚拟专用服务器(VPS)提供商。服务范围覆盖全球200多个国家和
IaaS预留实例在线交易策略详解
近年来,公有云占据的云计算市场份额逐年上升。Canalys显示,2023年第一季度,全球IaaS云服务支出达到了664亿美元,同比增长19%。由于IaaS云服务支出的不断增加,成本优化对于用户而言变得尤为重要。通过对IaaS云服务资源交易策略的研究,可以帮助用户根据其工作负载的实际运行情况,来适当调整手中的服务资源,从而降低用户使用IaaS云服务的成本。
浅谈云主机在VPC中进行迁移的使用场景和操作方法
客户在天翼云购买一台云主机并且部署完成想要的应用后,发现云主机的IP地址规划存在问题(比如,要修改VPC、子网的网段)。这时候如果需要重新购买云主机并重新部署应用则将会大大增加客户的使用成本。天翼云网络的基本特点是按需、弹性,可以支持云主机在不同网络场景下的迁移,从而大大减少客户重新购买计算实例的成本。
京东云开发者 京东云开发者
10个月前
【案例分享】如何利用京东云建设高可用业务架构
本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业务SLA,提升运维自动化水平,降低资源成本及运维成本。有
算网资源调度关键技术
在云网融合阶段,网络为云计算提供连接服务,云计算为网络云化提供支撑。随着云网一体化持续演进,云网融合步入算力网络新阶段。算力网络通过对业务、算力资源和网络资源的协同感知,将业务按需调度到合适的算力节点,实现算网资源的统一编排、统一运维、统一运营和统一优化。
选择天翼云混合云管理平台的五大理由
数字时代下,企业上云成为数字化转型的突破口。云上应用越来越多,云业务容灾备份、分级安全、应用弹性扩展等需求也日益增长。出于对综合成本、多云的差异化优势互补等因素的考量,企业多采用混合云部署策略。混合云,成为云服务市场的重要发展趋势。天翼云混合云管理平台面向企业客户提供资源统一开通、统一管理、统一运营、统一运维,支持对硬件资源和虚拟资源进行纳管,并通过整合天翼
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(