jenkins Job华为云EIP变更带宽

引言:

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

业务背景:

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

技术方案:

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

代码实现:

参照以下代码实例:UpdateBandwidth 更新带宽方法:
image.png

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
xreUAjVvck.png
可以在控制台查看代码操作,控制台对应实例生效!

继续完善代码:

上面的代码是用户传入bandwidthId,这关键一般用户不知道bandwidthId 阿?控制台用户来说一般能知道的是公网IP
image.png
我这里想到的是使用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时候引用凭据的方式:
image.png
最终代码如下:

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
参数化构建,字符参数
image.png
绑定密钥文件:
image.png
Build Steps 执行shell

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

image.png
输入publicIP运行程序:
image.png
程序运行生效,但是进程没有退出:

image.png

最终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
}

image.png
ok这样就简单实现了!
注:以上代码chatgpt生成,个人进行了更改整理.

其他想到的

  1. 修改一下程序,检测流量计费,当带宽到xxx的时候自动切换带宽?
  2. 支持一下更多类型的切换方式?比如固定带宽的直接10变更 5 20,不恢复流量计费?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/271014.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARM串口通信编程实验

完成&#xff1a;从终端输入选项&#xff0c;完成点灯关灯&#xff0c;打开风扇关闭风扇等操作 #include "gpio.h" int main() {char a;//char buf[128];uart4_config();gpio_config();while(1){//接收一个字符数据a getchar();//发送接收的字符putchar(a);switch(…

网络安全保障领域

计算机与信息系统安全---最主要领域 云计算安全 IaaS、PasS、SaaS(裸机&#xff0c;装好软件的电脑&#xff0c;装好应用的电脑) 存在风险&#xff1a;开源工具、优先访问权、管理权限、数据处、数据隔离、数据恢复、调查支持、长期发展风险 云计算安全关键技术&#xff1a;可信…

华清远见嵌入式学习——ARM——作业3

作业要求&#xff1a; 代码效果图&#xff1a; 代码&#xff1a; led.h #ifndef __LED_H__ #define __LED_H__#define RCC_GPIO (*(unsigned int *)0x50000a28) #define GPIOE_MODER (*(unsigned int *)0x50006000) #define GPIOF_MODER (*(unsigned int *)0x50007000) #defi…

音视频技术开发周刊 | 325

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 AI读心术震撼登顶会&#xff01;模型翻译脑电波&#xff0c;人类思想被投屏&#xff5c;NeurIPS 2023 在最近举办的NeurIPS大会上&#xff0c;研究人员展示了当代AI更震撼…

FPGA-ZYNQ-7000 SoC在嵌入式系统中的优势

FPGA-ZYNQ-7000 SoC在嵌入式系统中的优势 本章节主要参考书籍《Xilinx Zynq-7000 嵌入式系统设计与实现 基于ARM Cortex-A9双核处理器和Vivado的设计方法 (何宾&#xff0c;张艳辉编著&#xff09;》 本章节主要讲述FPGA-ZYNQ-7000 SoC在嵌入式系统中的优势&#xff0c;学习笔…

Rancher小白学习之路

官网&#xff1a;http://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexhttp://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexRancher2.5集群搭建&K3S生产环境搭建手册 - 知乎 【rancher教程】十年运维大佬两小时带你搞定ran…

关于MybatisPlus自动转化驼峰命名规则配置mapUnderscoreToCamelCase的个人测试和总结

关于MybatisPlus自动转化驼峰命名规则配置mapUnderscoreToCamelCase的个人测试和总结 测试一&#xff1a;没有添加 自动转化的配置&#xff0c;且domain中的属性名称和数据库的字段名称一致测试二&#xff1a;没有添加自动转化配置i&#xff0c;domain属性名userPassword和数据…

【csapp】cachelab

文章目录 Part APart B32 * 3264 * 6461 * 67 实验全程参考大佬的博客CS:APP3e 深入理解计算机系统_3e CacheLab实验 &#xff0c;感觉大佬在矩阵转置那块介绍的还是有些简略&#xff0c;我自己又做了点动图加以补充理解。膜拜大佬&#xff01; Part A 先解决解析命令行参数的…

年龄相关的微环境变化突显了PDGF-C在ER+乳腺癌转移复发中的作用

今天给同学们分享一篇实验文章“Age-associated microenvironmental changes highlight the role of PDGF-C in ER breast cancer metastatic relapse”&#xff0c;这篇文章发表在Nat Cancer期刊上&#xff0c;影响因子为22.7。 结果解读&#xff1a; ER乳腺癌转移复发的同种…

LH7904D 太阳能警示灯 0.4W×2

应用范围: 可安装在电线杆&#xff0c;路灯&#xff0c;围挡&#xff0c;交 通护栏及各种杆式固体等场所起警示作用。 产品特点&#xff1a; 采用进口PS材质; 光控无开关&#xff0c;白天不闪&#xff0c;昏暗环境自动闪烁&#xff0c;无需手动操作&#xff0c;省时省事; …

cfa一级考生复习经验分享系列(十)

我的背景&#xff1a;我是在职备考CFA的&#xff0c;工作也是跟金融不相关的&#xff0c;工作日基本只有晚上能看最多2-3小时&#xff0c;主要靠周末大块时间复习。由于平时工作忙碌直到9月中才开始看各种资料。但是可能我复习策略比较有效率&#xff0c;所以最后4次官方mock平…

彻底搞懂MySQL的执行计划

欢迎关注微信公众号&#xff1a;互联网全栈架构 MySQL执行计划&#xff08;EXPLAIN&#xff09;可以提供SQL运行的一些信息&#xff0c;相当于模拟SQL的执行&#xff0c;从而让我们可以对SQL语句做更深入的分析和了解。在实际开发过程中&#xff0c;我们经常会使用执行计划来分…

编写fastapi接口服务

FastAPI是一个基于 Python 的后端框架&#xff0c;该框架鼓励使用 Pydantic 和 OpenAPI (以前称为 Swagger) 进行文档编制&#xff0c;使用 Docker 进行快速开发和部署以及基于 Starlette 框架进行的简单测试。 step1&#xff1a;安装必要库 pip install fastapi uvicorn st…

FLStudio21中文版水果编曲软件好用吗?如何下载最新版本

FL Studio21版是一款在国内非常受欢迎的多功能音频处理软件&#xff0c;我们可以通过这款软件来对多种不同格式的音频文件来进行编辑处理。而且FL Studio 21版还为用户们准备了超多的音乐乐器伴奏&#xff0c;我们可以直接一键调取自己需要的音调。 FL Studio21版不仅拥有非常…

基于TM的遥感数据的叶面积指数估算解决方案

1.背景与技术路线 叶面积指数是重要的植被结构参数&#xff0c;反演叶面积指数是植被遥感的重要研究内容之一&#xff0c;其影响生态系统的物质和能量循环&#xff0c;成为作物生长、路面过程、水文和生态等模型的输入参数或状态变量。今年来&#xff0c;对也铭记指数的反演已…

vue-awesome-swiper轮播组件

安装版本&#xff1a;"swiper": "^6.0.0", 安装版本&#xff1a;"vue-awesome-swiper": "^4.1.1", <div class"swiper_conter"><swiper class"swiper" :options"swiperOption" ref"mySw…

Windows系统配置pytorch环境,Jupyter notebook编辑器安装使用(深度学习本地篇)

如今现在好一点的笔记本都自带英伟达独立显卡&#xff0c;对于一些简单的深度学习项目&#xff0c;是不需要连接服务器的&#xff0c;甚至数据量不大的话&#xff0c;cpu也足够进行训练学习。我把电脑上一些以前的笔记整理一下&#xff0c;记录起来&#xff0c;方便自己35岁事业…

快速构建空间场景轻应用的一次体验分享

零&#xff1a;前言 最近虚竹哥发现一款基于Web端打造的轻量化工具创作平台&#xff0c;用于快速构建空间场景轻应用&#xff0c;它就是Mapmost Alpha。内置丰富且多样化风格的三维场景、三维模型、数据服务、POI数据、图片、视频、网页等资源&#xff0c;通过拖拉拽的方式&am…

YOLOv8改进 | 主干篇 | RevColV1可逆列网络(特征解耦助力小目标检测)

一、本文介绍 本文给大家带来的是主干网络RevColV1&#xff0c;翻译过来就是可逆列网络去发表于ICLR2022&#xff0c;其是一种新型的神经网络设计(和以前的网络结构的传播方式不太一样)&#xff0c;由多个子网络&#xff08;列&#xff09;通过多级可逆连接组成。这种设计允许…

JavaScript进阶(事件+获取元素+操作元素)

目录 事件基础 事件组成 执行事件的步骤 获取元素 根据ID获取元素 根据标签名获取元素 获取ol中的小li 类选择器&#xff08;html5新增的I9以上支持&#xff09; 获取body和html 操作元素 innerText和innerHtml 表单标签 样式属性操作 操作元素总结 事件基础 事…