add ucloud packer

This commit is contained in:
mingsheng.su 2019-06-13 15:16:49 +08:00
parent 838f2ad0ad
commit c8bab9b382
184 changed files with 11123 additions and 690 deletions

View File

@ -58,7 +58,7 @@ dev: deps ## Build and install a development build
fi
@mkdir -p pkg/$(GOOS)_$(GOARCH)
@mkdir -p bin
@go install -ldflags '$(GOLDFLAGS)'
@go install -mod=vendor -ldflags '$(GOLDFLAGS)'
@cp $(GOPATH)/bin/packer bin/packer
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)

View File

@ -0,0 +1,165 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/template/interpolate"
"github.com/hashicorp/packer/version"
"github.com/ucloud/ucloud-sdk-go/services/uaccount"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"github.com/ucloud/ucloud-sdk-go/services/unet"
"github.com/ucloud/ucloud-sdk-go/services/vpc"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"os"
)
type AccessConfig struct {
PublicKey string `mapstructure:"public_key"`
PrivateKey string `mapstructure:"private_key"`
Region string `mapstructure:"region"`
ProjectId string `mapstructure:"project_id"`
client *UCloudClient
}
func (c *AccessConfig) Client() (*UCloudClient, error) {
if c.client != nil {
return c.client, nil
}
cfg := ucloud.NewConfig()
cfg.Region = c.Region
cfg.ProjectId = c.ProjectId
cfg.UserAgent = fmt.Sprintf("Packer-UCloud/%s", version.FormattedVersion())
cred := auth.NewCredential()
cred.PublicKey = c.PublicKey
cred.PrivateKey = c.PrivateKey
c.client = &UCloudClient{}
c.client.uhostconn = uhost.NewClient(&cfg, &cred)
c.client.unetconn = unet.NewClient(&cfg, &cred)
c.client.vpcconn = vpc.NewClient(&cfg, &cred)
c.client.uaccountconn = uaccount.NewClient(&cfg, &cred)
return c.client, nil
}
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if err := c.Config(); err != nil {
errs = append(errs, err)
}
if c.Region == "" {
c.Region = os.Getenv("UCLOUD_REGION")
}
if c.Region == "" {
errs = append(errs, fmt.Errorf("%q must be set", "region"))
}
if len(errs) > 0 {
return errs
}
return nil
}
func (c *AccessConfig) Config() error {
if c.PublicKey == "" {
c.PublicKey = os.Getenv("UCLOUD_PUBLIC_KEY")
}
if c.PrivateKey == "" {
c.PrivateKey = os.Getenv("UCLOUD_PRIVATE_KEY")
}
if c.ProjectId == "" {
c.ProjectId = os.Getenv("UCLOUD_PROJECT_ID")
}
if c.PublicKey == "" || c.PrivateKey == "" || c.ProjectId == "" {
return fmt.Errorf("%q, %q, and %q must be set", "public_key", "private_key", "project_id")
}
return nil
}
func (c *AccessConfig) ValidateRegion(region string) error {
supportedRegions, err := c.getSupportedRegions()
if err != nil {
return err
}
for _, supportedRegion := range supportedRegions {
if region == supportedRegion {
return nil
}
}
return fmt.Errorf("%q is valid, should be an valid ucloud region, got %q", "region", region)
}
func (c *AccessConfig) ValidateZone(region, zone string) error {
supportedZones, err := c.getSupportedZones(region)
if err != nil {
return err
}
for _, supportedZone := range supportedZones {
if zone == supportedZone {
return nil
}
}
return fmt.Errorf("%q is valid, should be an valid ucloud zone, got %q", "availability_zone", zone)
}
func (c *AccessConfig) getSupportedRegions() ([]string, error) {
client, err := c.Client()
conn := client.uaccountconn
if err != nil {
return nil, err
}
req := conn.NewGetRegionRequest()
resp, err := conn.GetRegion(req)
if err != nil {
return nil, err
}
validRegions := make([]string, len(resp.Regions))
for _, val := range resp.Regions {
if !isStringIn(val.Region, validRegions) {
validRegions = append(validRegions, val.Region)
}
}
return validRegions, nil
}
func (c *AccessConfig) getSupportedZones(region string) ([]string, error) {
client, err := c.Client()
conn := client.uaccountconn
if err != nil {
return nil, err
}
req := conn.NewGetRegionRequest()
resp, err := conn.GetRegion(req)
if err != nil {
return nil, err
}
validZones := make([]string, len(resp.Regions))
for _, val := range resp.Regions {
if val.Region == region && !isStringIn(val.Zone, validZones) {
validZones = append(validZones, val.Zone)
}
}
return validZones, nil
}

View File

@ -0,0 +1,35 @@
package uhost
import (
"os"
"testing"
)
func testAccessConfig() *AccessConfig {
return &AccessConfig{
PublicKey: "test_pub",
PrivateKey: "test_pri",
ProjectId: "test_pro",
}
}
func TestAccessConfigPrepareRegion(t *testing.T) {
c := testAccessConfig()
c.Region = ""
if err := c.Prepare(nil); err == nil {
t.Fatalf("should have err")
}
c.Region = "cn-sh2"
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
os.Setenv("UCLOUD_REGION", "cn-bj2")
c.Region = ""
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
}

View File

@ -0,0 +1,93 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"log"
"sort"
"strings"
)
type Artifact struct {
UCloudImages *imageInfoSet
BuilderIdValue string
Client *UCloudClient
}
func (a *Artifact) BuilderId() string {
return a.BuilderIdValue
}
func (*Artifact) Files() []string {
return nil
}
func (a *Artifact) Id() string {
m := make([]string, 0, len(a.UCloudImages.GetAll()))
for _, v := range a.UCloudImages.GetAll() {
m = append(m, fmt.Sprintf("%s:%s:%s", v.ProjectId, v.Region, v.ImageId))
}
sort.Strings(m)
return strings.Join(m, ",")
}
func (a *Artifact) String() string {
m := make([]string, 0, len(a.UCloudImages.GetAll()))
for _, v := range a.UCloudImages.GetAll() {
m = append(m, fmt.Sprintf("%s: %s: %s", v.ProjectId, v.Region, v.ImageId))
}
sort.Strings(m)
return fmt.Sprintf("UCloud images were created:\n\n%s", strings.Join(m, "\n"))
}
func (a *Artifact) State(name string) interface{} {
switch name {
case "atlas.artifact.metadata":
return a.stateAtlasMetadata()
default:
return nil
}
}
func (a *Artifact) Destroy() error {
conn := a.Client.uhostconn
errors := make([]error, 0)
for _, v := range a.UCloudImages.GetAll() {
log.Printf("Delete ucloud image %s from %s:%s", v.ImageId, v.ProjectId, v.Region)
req := conn.NewTerminateCustomImageRequest()
req.ProjectId = ucloud.String(v.ProjectId)
req.Region = ucloud.String(v.Region)
req.ImageId = ucloud.String(v.ImageId)
if _, err := conn.TerminateCustomImage(req); err != nil {
errors = append(errors, err)
}
}
if len(errors) > 0 {
if len(errors) == 1 {
return errors[0]
} else {
return &packer.MultiError{Errors: errors}
}
}
return nil
}
func (a *Artifact) stateAtlasMetadata() interface{} {
metadata := make(map[string]string)
for _, v := range a.UCloudImages.GetAll() {
k := fmt.Sprintf("%s:%s", v.ProjectId, v.Region)
metadata[k] = v.ImageId
}
return metadata
}

View File

@ -0,0 +1,65 @@
package uhost
import (
"github.com/hashicorp/packer/packer"
"reflect"
"testing"
)
func TestArtifact_Impl(t *testing.T) {
var _ packer.Artifact = new(Artifact)
}
func TestArtifactId(t *testing.T) {
expected := `project1:region1:foo,project2:region2:bar`
images := newImageInfoSet(nil)
images.Set(imageInfo{
Region: "region1",
ProjectId: "project1",
ImageId: "foo",
})
images.Set(imageInfo{
Region: "region2",
ProjectId: "project2",
ImageId: "bar",
})
a := &Artifact{
UCloudImages: images,
}
result := a.Id()
if result != expected {
t.Fatalf("bad: %s", result)
}
}
func TestArtifactState_atlasMetadata(t *testing.T) {
images := newImageInfoSet(nil)
images.Set(imageInfo{
Region: "region1",
ProjectId: "project1",
ImageId: "foo",
})
images.Set(imageInfo{
Region: "region2",
ProjectId: "project2",
ImageId: "bar",
})
a := &Artifact{
UCloudImages: images,
}
actual := a.State("atlas.artifact.metadata")
expected := map[string]string{
"project1:region1": "foo",
"project2:region2": "bar",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}

View File

@ -0,0 +1,146 @@
// The ucloud-uhost contains a packer.Builder implementation that
// builds uhost images for UCloud UHost instance.
package uhost
import (
"context"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
)
// The unique ID for this builder
const BuilderId = "ucloud.uhost"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
AccessConfig `mapstructure:",squash"`
ImageConfig `mapstructure:",squash"`
RunConfig `mapstructure:",squash"`
ctx interpolate.Context
}
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
b.config.ctx.EnableEnv = true
if err != nil {
return nil, err
}
// Accumulate any errors
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.ImageConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packer.LogSecretFilter.Set(b.config.PublicKey, b.config.PrivateKey)
return nil, nil
}
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
client, err := b.config.Client()
if err != nil {
return nil, err
}
// Setup the state bag and initial state for the steps
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("client", client)
state.Put("hook", hook)
state.Put("ui", ui)
var steps []multistep.Step
// Build the steps
steps = []multistep.Step{
&stepPreValidate{
Region: b.config.Region,
Zone: b.config.Zone,
ImageDestinations: b.config.ImageDestinations,
},
&stepCheckSourceImageId{
SourceUHostImageId: b.config.SourceImageId,
},
&stepConfigVPC{
VPCId: b.config.VPCId,
},
&stepConfigSubnet{
SubnetId: b.config.SubnetId,
},
&stepConfigSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
},
&stepCreateInstance{
InstanceType: b.config.InstanceType,
Region: b.config.Region,
Zone: b.config.Zone,
SourceImageId: b.config.SourceImageId,
InstanceName: b.config.InstanceName,
BootDiskType: b.config.BootDiskType,
UsePrivateIp: b.config.UseSSHPrivateIp,
},
&communicator.StepConnect{
Config: &b.config.RunConfig.Comm,
Host: SSHHost(
b.config.UseSSHPrivateIp),
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
},
&common.StepProvision{},
&stepStopInstance{},
&stepCreateImage{},
&stepCopyUCloudImage{
ImageDestinations: b.config.ImageDestinations,
RegionId: b.config.Region,
ProjectId: b.config.ProjectId,
},
}
// Run!
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no ucloud images, then just return
if _, ok := state.GetOk("ucloud_images"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &Artifact{
UCloudImages: state.Get("ucloud_images").(*imageInfoSet),
BuilderIdValue: BuilderId,
Client: client,
}
return artifact, nil
}

View File

@ -0,0 +1,206 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/packer"
"os"
"testing"
builderT "github.com/hashicorp/packer/helper/builder/testing"
)
func TestBuilderAcc_validateRegion(t *testing.T) {
t.Parallel()
if os.Getenv(builderT.TestEnvVar) == "" {
t.Skip(fmt.Sprintf("Acceptance tests skipped unless env '%s' set", builderT.TestEnvVar))
return
}
testAccPreCheck(t)
access := &AccessConfig{Region: "cn-bj2"}
err := access.Config()
if err != nil {
t.Fatalf("Error on initing UCloud AccessConfig, %s", err)
}
err = access.ValidateRegion("cn-sh2")
if err != nil {
t.Fatalf("Expected pass with valid region but failed: %s", err)
}
err = access.ValidateRegion("invalidRegion")
if err == nil {
t.Fatal("Expected failure due to invalid region but passed")
}
}
func TestBuilderAcc_basic(t *testing.T) {
t.Parallel()
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccBasic,
})
}
const testBuilderAccBasic = `
{ "builders": [{
"type": "test",
"region": "cn-bj2",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"source_image_id":"uimage-f1chxn",
"ssh_username":"root",
"image_name": "packer-test-basic_{{timestamp}}"
}]
}`
func TestBuilderAcc_ubuntu(t *testing.T) {
t.Parallel()
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccUbuntu,
})
}
const testBuilderAccUbuntu = `
{ "builders": [{
"type": "test",
"region": "cn-bj2",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"source_image_id":"uimage-irofn4",
"ssh_username":"ubuntu",
"image_name": "packer-test-basic_{{timestamp}}"
}]
}`
func TestBuilderAcc_regionCopy(t *testing.T) {
t.Parallel()
projectId := os.Getenv("UCLOUD_PROJECT_ID")
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccRegionCopy,
Check: checkRegionCopy(
projectId,
[]ImageDestination{
{"org-fthbzm", "cn-sh2", "packer-test-regionCopy-sh", "test"},
}),
})
}
const testBuilderAccRegionCopy = `
{
"builders": [{
"type": "test",
"region": "cn-bj2",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"source_image_id":"uimage-f1chxn",
"ssh_username":"root",
"image_name": "packer-test-regionCopy_{{timestamp}}",
"image_copy_mappings": [{
"image_copy_project_id": "org-fthbzm",
"image_copy_region": "cn-sh2",
"image_copy_name": "packer-test-regionCopy-sh",
"image_copy_description": "test"
}]
}]
}`
func checkRegionCopy(projectId string, imageDst []ImageDestination) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
if len(artifacts) > 1 {
return fmt.Errorf("more than 1 artifact")
}
artifactSet := artifacts[0]
artifact, ok := artifactSet.(*Artifact)
if !ok {
return fmt.Errorf("unknown artifact: %#v", artifactSet)
}
destSet := newImageInfoSet(nil)
for _, dest := range imageDst {
destSet.Set(imageInfo{
Region: dest.Region,
ProjectId: dest.ProjectId,
})
}
for _, r := range artifact.UCloudImages.GetAll() {
if r.ProjectId == projectId && r.Region == "cn-bj2" {
destSet.Remove(r.Id())
continue
}
if destSet.Get(r.ProjectId, r.Region) == nil {
return fmt.Errorf("project%s : region%s is not the target but found in artifacts", r.ProjectId, r.Region)
}
destSet.Remove(r.Id())
}
if len(destSet.GetAll()) > 0 {
return fmt.Errorf("the following copying targets not found in corresponding artifacts : %#v", destSet.GetAll())
}
client, _ := testUCloudClient()
for _, r := range artifact.UCloudImages.GetAll() {
if r.ProjectId == projectId && r.Region == "cn-bj2" {
continue
}
imageSet, err := client.describeImageByInfo(r.ProjectId, r.Region, r.ImageId)
if err != nil {
if isNotFoundError(err) {
return fmt.Errorf("image %s in artifacts can not be found", r.ImageId)
}
return err
}
if r.Region == "cn-sh2" && imageSet.ImageName != "packer-test-regionCopy-sh" {
return fmt.Errorf("the name of image %q in artifacts should be %s, got %s", r.ImageId, "packer-test-regionCopy-sh", imageSet.ImageName)
}
}
return nil
}
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("UCLOUD_PUBLIC_KEY"); v == "" {
t.Fatal("UCLOUD_PUBLIC_KEY must be set for acceptance tests")
}
if v := os.Getenv("UCLOUD_PRIVATE_KEY"); v == "" {
t.Fatal("UCLOUD_PRIVATE_KEY must be set for acceptance tests")
}
if v := os.Getenv("UCLOUD_PROJECT_ID"); v == "" {
t.Fatal("UCLOUD_PROJECT_ID must be set for acceptance tests")
}
}
func testUCloudClient() (*UCloudClient, error) {
access := &AccessConfig{Region: "cn-bj2"}
err := access.Config()
if err != nil {
return nil, err
}
client, err := access.Client()
if err != nil {
return nil, err
}
return client, nil
}

View File

@ -0,0 +1,139 @@
package uhost
import (
"github.com/hashicorp/packer/packer"
"reflect"
"testing"
)
func testBuilderConfig() map[string]interface{} {
return map[string]interface{}{
"public_key": "foo",
"private_key": "bar",
"project_id": "foo",
"source_image_id": "bar",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"region": "cn-bj2",
"ssh_username": "root",
"image_name": "foo",
}
}
func TestBuilder_ImplementsBuilder(t *testing.T) {
var raw interface{}
raw = &Builder{}
if _, ok := raw.(packer.Builder); !ok {
t.Fatalf("Builder should be a builder")
}
}
func TestBuilder_Prepare_BadType(t *testing.T) {
b := &Builder{}
c := map[string]interface{}{
"public_key": []string{},
}
warnings, err := b.Prepare(c)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatalf("prepare should fail")
}
}
func TestBuilderPrepare_ImageName(t *testing.T) {
var b Builder
config := testBuilderConfig()
// Test good
config["image_name"] = "foo"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["image_name"] = "foo {{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
// Test bad
delete(config, "image_name")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testBuilderConfig()
// Add a random key
config["i_should_not_be_valid"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_ImageDestinations(t *testing.T) {
var b Builder
config := testBuilderConfig()
config["image_copy_mappings"] = []map[string]interface{}{
{
"image_copy_project_id": "project1",
"image_copy_region": "region1",
"image_copy_name": "bar",
"image_copy_description": "foo",
},
{
"image_copy_project_id": "project2",
"image_copy_region": "region2",
"image_copy_name": "foo",
"image_copy_description": "bar",
},
}
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if !reflect.DeepEqual(b.config.ImageDestinations, []ImageDestination{
{
ProjectId: "project1",
Region: "region1",
Name: "bar",
Description: "foo",
},
{
ProjectId: "project2",
Region: "region2",
Name: "foo",
Description: "bar",
},
}) {
t.Fatalf("image_copy_mappings are not set properly, got: %#v", b.config.ImageDestinations)
}
}

View File

@ -0,0 +1,147 @@
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/services/uaccount"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"github.com/ucloud/ucloud-sdk-go/services/unet"
"github.com/ucloud/ucloud-sdk-go/services/vpc"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/error"
)
type UCloudClient struct {
uhostconn *uhost.UHostClient
unetconn *unet.UNetClient
vpcconn *vpc.VPCClient
uaccountconn *uaccount.UAccountClient
}
func (c *UCloudClient) describeFirewallById(sgId string) (*unet.FirewallDataSet, error) {
if sgId == "" {
return nil, newNotFoundError("security group", sgId)
}
conn := c.unetconn
req := conn.NewDescribeFirewallRequest()
req.FWId = ucloud.String(sgId)
resp, err := conn.DescribeFirewall(req)
// [API-STYLE] Fire wall api has not found err code, but others don't have
if err != nil {
if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 54002 {
return nil, newNotFoundError("security group", sgId)
}
return nil, err
}
if len(resp.DataSet) < 1 {
return nil, newNotFoundError("security group", sgId)
}
return &resp.DataSet[0], nil
}
func (c *UCloudClient) describeSubnetById(subnetId string) (*vpc.VPCSubnetInfoSet, error) {
if subnetId == "" {
return nil, newNotFoundError("Subnet", subnetId)
}
conn := c.vpcconn
req := conn.NewDescribeSubnetRequest()
req.SubnetIds = []string{subnetId}
resp, err := conn.DescribeSubnet(req)
if err != nil {
return nil, err
}
if resp == nil || len(resp.DataSet) < 1 {
return nil, newNotFoundError("Subnet", subnetId)
}
return &resp.DataSet[0], nil
}
func (c *UCloudClient) describeVPCById(vpcId string) (*vpc.VPCInfo, error) {
if vpcId == "" {
return nil, newNotFoundError("VPC", vpcId)
}
conn := c.vpcconn
req := conn.NewDescribeVPCRequest()
req.VPCIds = []string{vpcId}
resp, err := conn.DescribeVPC(req)
if err != nil {
return nil, err
}
if resp == nil || len(resp.DataSet) < 1 {
return nil, newNotFoundError("VPC", vpcId)
}
return &resp.DataSet[0], nil
}
func (c *UCloudClient) DescribeImageById(imageId string) (*uhost.UHostImageSet, error) {
if imageId == "" {
return nil, newNotFoundError("image", imageId)
}
req := c.uhostconn.NewDescribeImageRequest()
req.ImageId = ucloud.String(imageId)
resp, err := c.uhostconn.DescribeImage(req)
if err != nil {
//if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 8889 {
// return nil, newNotFoundError("image", imageId)
//}
return nil, err
}
if len(resp.ImageSet) < 1 {
return nil, newNotFoundError("image", imageId)
}
return &resp.ImageSet[0], nil
}
func (c *UCloudClient) describeUHostById(uhostId string) (*uhost.UHostInstanceSet, error) {
if uhostId == "" {
return nil, newNotFoundError("instance", uhostId)
}
req := c.uhostconn.NewDescribeUHostInstanceRequest()
req.UHostIds = []string{uhostId}
resp, err := c.uhostconn.DescribeUHostInstance(req)
if err != nil {
return nil, err
}
if len(resp.UHostSet) < 1 {
return nil, nil
}
return &resp.UHostSet[0], nil
}
func (c *UCloudClient) describeImageByInfo(projectId, regionId, imageId string) (*uhost.UHostImageSet, error) {
req := c.uhostconn.NewDescribeImageRequest()
req.ProjectId = ucloud.String(projectId)
req.ImageId = ucloud.String(imageId)
req.Region = ucloud.String(regionId)
resp, err := c.uhostconn.DescribeImage(req)
if err != nil {
//if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 8889 {
// return nil, newNotFoundError("image", imageId)
//}
return nil, err
}
if len(resp.ImageSet) < 1 {
return nil, newNotFoundError("image", imageId)
}
return &resp.ImageSet[0], nil
}

View File

@ -0,0 +1,14 @@
package uhost
const (
// defaultPasswordStr, defaultPasswordNum and defaultPasswordSpe are used to general default value of root password of UHost instance
defaultPasswordNum = "012346789"
defaultPasswordStr = "abcdefghijklmnopqrstuvwxyz"
defaultPasswordSpe = "-_"
)
var bootDiskTypeMap = map[string]string{
"cloud_ssd": "CLOUD_SSD",
"local_normal": "LOCAL_NORMAL",
"local_ssd": "LOCAL_SSD",
}

View File

@ -0,0 +1,62 @@
package uhost
import (
"fmt"
)
type NotFoundError struct {
message string
}
type ExpectedStateError struct {
message string
}
type NotCompleteError struct {
message string
}
func (e *ExpectedStateError) Error() string {
return e.message
}
func (e *NotFoundError) Error() string {
return e.message
}
func (e *NotCompleteError) Error() string {
return e.message
}
func newNotFoundError(product, id string) error {
return &NotFoundError{fmt.Sprintf("the %s %s is not found", product, id)}
}
func newExpectedStateError(product, id string) error {
return &ExpectedStateError{fmt.Sprintf("the %s %s not be expected state", product, id)}
}
func newNotCompleteError(product string) error {
return &NotCompleteError{fmt.Sprintf("%s is not completed", product)}
}
func isNotFoundError(err error) bool {
if _, ok := err.(*NotFoundError); ok {
return true
}
return false
}
func isExpectedStateError(err error) bool {
if _, ok := err.(*ExpectedStateError); ok {
return true
}
return false
}
func isNotCompleteError(err error) bool {
if _, ok := err.(*NotCompleteError); ok {
return true
}
return false
}

View File

@ -0,0 +1,65 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/template/interpolate"
"regexp"
)
type ImageDestination struct {
ProjectId string `mapstructure:"project_id"`
Region string `mapstructure:"region"`
Name string `mapstructure:"name"`
Description string `mapstructure:"description"`
}
type ImageConfig struct {
ImageName string `mapstructure:"image_name"`
ImageDescription string `mapstructure:"image_description"`
ImageDestinations []ImageDestination `mapstructure:"image_copy_to_mappings"`
}
var imageNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_\[\]:,.]{1,63}$`)
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
imageName := c.ImageName
if imageName == "" {
errs = append(errs, fmt.Errorf("%q must be set", "image_name"))
} else if !imageNamePattern.MatchString(imageName) {
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_name", imageName))
}
if len(c.ImageDestinations) > 0 {
for _, imageDestination := range c.ImageDestinations {
if imageDestination.Name == "" {
imageDestination.Name = imageName
}
errs = append(errs, imageDestination.validate()...)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
func (imageDestination *ImageDestination) validate() []error {
var errs []error
if imageDestination.Region == "" {
errs = append(errs, fmt.Errorf("%q must be set", "image_copy_region"))
}
if imageDestination.ProjectId == "" {
errs = append(errs, fmt.Errorf("%q must be set", "image_copy_project"))
}
if imageDestination.Name != "" && !imageNamePattern.MatchString(imageDestination.Name) {
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_copy_name", imageDestination.Name))
}
return errs
}

View File

@ -0,0 +1,63 @@
package uhost
import (
"testing"
)
func testImageConfig() *ImageConfig {
return &ImageConfig{
ImageName: "foo",
}
}
func TestImageConfigPrepare_name(t *testing.T) {
c := testImageConfig()
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.ImageName = ""
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
}
}
func TestImageConfigPrepare_destinations(t *testing.T) {
c := testImageConfig()
c.ImageDestinations = nil
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.ImageDestinations = []ImageDestination{
{
ProjectId: "foo",
Region: "cn-bj2",
Name: "bar",
},
{
ProjectId: "bar",
Region: "cn-sh2",
Name: "foo",
},
}
if err := c.Prepare(nil); err != nil {
t.Fatalf("bad: %s", err)
}
c.ImageDestinations = []ImageDestination{
{
ProjectId: "foo",
Name: "bar",
},
{
ProjectId: "bar",
Region: "cn-sh2",
},
}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
}
}

View File

@ -0,0 +1,71 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/template/interpolate"
"regexp"
)
type RunConfig struct {
Zone string `mapstructure:"availability_zone"`
SourceImageId string `mapstructure:"source_image_id"`
InstanceType string `mapstructure:"instance_type"`
InstanceName string `mapstructure:"instance_name"`
BootDiskType string `mapstructure:"boot_disk_type"`
VPCId string `mapstructure:"vpc_id"`
SubnetId string `mapstructure:"subnet_id"`
SecurityGroupId string `mapstructure:"security_group_id"`
// Communicator settings
Comm communicator.Config `mapstructure:",squash"`
UseSSHPrivateIp bool `mapstructure:"use_ssh_private_ip"`
}
var instanceNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_.]{1,63}$`)
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
errs := c.Comm.Prepare(ctx)
if c.Zone == "" {
errs = append(errs, fmt.Errorf("%q must be set", "availability_zone"))
}
if c.SourceImageId == "" {
errs = append(errs, fmt.Errorf("%q must be set", "source_image_id"))
}
if c.InstanceType == "" {
errs = append(errs, fmt.Errorf("%q must be set", "instance_type"))
} else if _, err := parseInstanceType(c.InstanceType); err != nil {
errs = append(errs, err)
}
if (c.VPCId != "" && c.SubnetId == "") || (c.VPCId == "" && c.SubnetId != "") {
errs = append(errs, fmt.Errorf("expected both %q and %q to set or not set", "vpc_id", "subnet_id"))
}
if c.BootDiskType == "" {
c.BootDiskType = "cloud_ssd"
} else if err := checkStringIn(c.BootDiskType,
[]string{"local_normal", "local_ssd", "cloud_normal", "cloud_ssd"}); err != nil {
errs = append(errs, err)
}
if c.InstanceName == "" {
c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()[:8])
} else if !instanceNamePattern.MatchString(c.InstanceName) {
errs = append(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_.', got %q", "instance_name", c.InstanceName))
}
if c.UseSSHPrivateIp == true && c.VPCId == "" {
errs = append(errs, fmt.Errorf("%q must be set when use_ssh_private_ip is true", "vpc_id"))
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@ -0,0 +1,62 @@
package uhost
import (
"github.com/hashicorp/packer/helper/communicator"
"testing"
)
func testConfig() *RunConfig {
return &RunConfig{
SourceImageId: "ucloud_image",
InstanceType: "n-basic-2",
Zone: "cn-bj2-02",
Comm: communicator.Config{
SSHUsername: "ucloud",
},
}
}
func TestRunConfigPrepare(t *testing.T) {
c := testConfig()
err := c.Prepare(nil)
if len(err) > 0 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_InstanceType(t *testing.T) {
c := testConfig()
c.InstanceType = ""
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_SourceImage(t *testing.T) {
c := testConfig()
c.SourceImageId = ""
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_SSHPort(t *testing.T) {
c := testConfig()
c.Comm.SSHPort = 0
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.Comm.SSHPort != 22 {
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
}
c.Comm.SSHPort = 44
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.Comm.SSHPort != 44 {
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
}
}

View File

@ -0,0 +1,35 @@
package uhost
import (
"context"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepCheckSourceImageId struct {
SourceUHostImageId string
}
func (s *stepCheckSourceImageId) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*UCloudClient)
ui.Say("Querying source image id...")
imageSet, err := client.DescribeImageById(s.SourceUHostImageId)
if err != nil {
if isNotFoundError(err) {
return halt(state, err, "")
}
return halt(state, err, "Error on querying source_image_id")
}
if imageSet.OsType == "Windows" {
return halt(state, err, "The builder of ucloud-uhost not support make Windows image yet")
}
state.Put("source_image", imageSet)
return multistep.ActionContinue
}
func (s *stepCheckSourceImageId) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,76 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/ucloud"
)
type stepConfigSecurityGroup struct {
SecurityGroupId string
}
func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
conn := client.unetconn
ui := state.Get("ui").(packer.Ui)
if len(s.SecurityGroupId) != 0 {
ui.Say(fmt.Sprintf("Trying to use specified security group %q...", s.SecurityGroupId))
securityGroupSet, err := client.describeFirewallById(s.SecurityGroupId)
if err != nil {
if isNotFoundError(err) {
err = fmt.Errorf("the specified security group %q not exist", s.SecurityGroupId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying security group")
}
state.Put("security_group_id", securityGroupSet.FWId)
return multistep.ActionContinue
}
ui.Say("Trying to use default security group...")
var securityGroupId string
var limit = 100
var offset int
for {
req := conn.NewDescribeFirewallRequest()
req.Limit = ucloud.Int(limit)
req.Offset = ucloud.Int(offset)
resp, err := conn.DescribeFirewall(req)
if err != nil {
return halt(state, err, "Error on querying security group")
}
if resp == nil || len(resp.DataSet) < 1 {
break
}
for _, item := range resp.DataSet {
if item.Type == "recommend non web" {
securityGroupId = item.FWId
break
}
}
if len(resp.DataSet) < limit {
break
}
offset = offset + limit
}
if securityGroupId != "" {
state.Put("security_group_id", securityGroupId)
return multistep.ActionContinue
}
return multistep.ActionContinue
}
func (s *stepConfigSecurityGroup) Cleanup(state multistep.StateBag) {
}

View File

@ -0,0 +1,39 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepConfigSubnet struct {
SubnetId string
}
func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
ui := state.Get("ui").(packer.Ui)
if len(s.SubnetId) != 0 {
ui.Say(fmt.Sprintf("Trying to use specified subnet %q...", s.SubnetId))
subnetSet, err := client.describeSubnetById(s.SubnetId)
if err != nil {
if isNotFoundError(err) {
err = fmt.Errorf("the specified subnet %q not exist", s.SubnetId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying subnet")
}
state.Put("subnet_id", subnetSet.SubnetId)
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Trying to use default subnet..."))
return multistep.ActionContinue
}
func (s *stepConfigSubnet) Cleanup(state multistep.StateBag) {
}

View File

@ -0,0 +1,42 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepConfigVPC struct {
VPCId string
}
func (s *stepConfigVPC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
ui := state.Get("ui").(packer.Ui)
if len(s.VPCId) != 0 {
ui.Say(fmt.Sprintf("Trying to use specified vpc %q...", s.VPCId))
vpcSet, err := client.describeVPCById(s.VPCId)
if err != nil {
if isNotFoundError(err) {
err = fmt.Errorf("the specified vpc %q not exist", s.VPCId)
return halt(state, err, "")
}
return halt(state, err, "Error on querying vpc")
}
state.Put("vpc_id", vpcSet.VPCId)
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Trying to use default vpc..."))
return multistep.ActionContinue
}
func (s *stepConfigVPC) Cleanup(state multistep.StateBag) {
}

View File

@ -0,0 +1,132 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/common/retry"
"time"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/ucloud"
)
type stepCopyUCloudImage struct {
ImageDestinations []ImageDestination
RegionId string
ProjectId string
}
func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if len(s.ImageDestinations) == 0 {
return multistep.ActionContinue
}
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
ui := state.Get("ui").(packer.Ui)
srcImageId := state.Get("image_id").(string)
artifactImages := state.Get("ucloud_images").(*imageInfoSet)
expectedImages := newImageInfoSet(nil)
ui.Say(fmt.Sprintf("Copying image with %q...", srcImageId))
for _, imageDestination := range s.ImageDestinations {
if imageDestination.ProjectId == s.ProjectId && imageDestination.Region == s.RegionId {
continue
}
req := conn.NewCopyCustomImageRequest()
req.TargetProjectId = ucloud.String(imageDestination.ProjectId)
req.TargetRegion = ucloud.String(imageDestination.Region)
req.SourceImageId = ucloud.String(srcImageId)
req.TargetImageName = ucloud.String(imageDestination.Name)
resp, err := conn.CopyCustomImage(req)
if err != nil {
return halt(state, err, "Error on copying images")
}
image := imageInfo{
Region: imageDestination.Region,
ProjectId: imageDestination.ProjectId,
ImageId: resp.TargetImageId,
}
expectedImages.Set(image)
artifactImages.Set(image)
ui.Message(fmt.Sprintf("Copying image from %s:%s:%s to %s:%s:%s)",
s.ProjectId, s.RegionId, srcImageId, imageDestination.ProjectId, imageDestination.Region, resp.TargetImageId))
}
err := retry.Config{
Tries: 200,
ShouldRetry: func(err error) bool { return isNotCompleteError(err) },
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
for _, v := range expectedImages.GetAll() {
imageSet, err := client.describeImageByInfo(v.ProjectId, v.Region, v.ImageId)
if err != nil {
return err
}
if imageSet.State == "Available" {
expectedImages.Remove(v.Id())
continue
}
}
if len(expectedImages.GetAll()) != 0 {
return newNotCompleteError("copying image")
}
return nil
})
if err != nil {
return halt(state, err, fmt.Sprintf("Error on waiting for copying image finished"))
}
ui.Message(fmt.Sprintf("Copy image complete"))
return multistep.ActionContinue
}
func (s *stepCopyUCloudImage) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
srcImageId := state.Get("image_id").(string)
ucloudImages := state.Get("ucloud_images").(*imageInfoSet)
imageInfos := ucloudImages.GetAll()
if len(imageInfos) == 0 {
return
} else if len(imageInfos) == 1 && imageInfos[0].ImageId == srcImageId {
return
}
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
ui.Say(fmt.Sprintf("Deleting copied image because cancellation or error..."))
for _, v := range imageInfos {
if v.ImageId == srcImageId {
continue
}
req := conn.NewTerminateCustomImageRequest()
req.ProjectId = ucloud.String(v.ProjectId)
req.Region = ucloud.String(v.Region)
req.ImageId = ucloud.String(v.ImageId)
_, err := conn.TerminateCustomImage(req)
if err != nil {
ui.Error(fmt.Sprintf("Error on deleting copied image %q", v.ImageId))
}
}
ui.Message("Delete copied image complete")
}

View File

@ -0,0 +1,103 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/common/retry"
"time"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"github.com/ucloud/ucloud-sdk-go/ucloud"
)
type stepCreateImage struct {
image *uhost.UHostImageSet
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
instance := state.Get("instance").(*uhost.UHostInstanceSet)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config)
ui.Say(fmt.Sprintf("Creating image %s", config.ImageName))
req := conn.NewCreateCustomImageRequest()
req.ImageName = ucloud.String(config.ImageName)
req.ImageDescription = ucloud.String(config.ImageDescription)
req.UHostId = ucloud.String(instance.UHostId)
resp, err := conn.CreateCustomImage(req)
if err != nil {
return halt(state, err, "")
}
err = retry.Config{
Tries: 200,
ShouldRetry: func(err error) bool {
return isExpectedStateError(err)
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
inst, err := client.DescribeImageById(resp.ImageId)
if err != nil {
return err
}
if inst == nil || inst.State != "Available" {
return newExpectedStateError("image", resp.ImageId)
}
return nil
})
if err != nil {
return halt(state, err, "Error on waiting for image to available")
}
imageSet, err := client.DescribeImageById(resp.ImageId)
if err != nil {
return halt(state, err, "Error on reading image")
}
s.image = imageSet
state.Put("image_id", imageSet.ImageId)
images := []imageInfo{
{
ImageId: imageSet.ImageId,
ProjectId: config.ProjectId,
Region: config.Region,
},
}
state.Put("ucloud_images", newImageInfoSet(images))
ui.Message(fmt.Sprintf("Create image %q complete", imageSet.ImageId))
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
if s.image == nil {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting image because of cancellation or error...")
req := conn.NewTerminateCustomImageRequest()
req.ImageId = ucloud.String(s.image.ImageId)
_, err := conn.TerminateCustomImage(req)
if err != nil {
ui.Error(fmt.Sprintf("Error on deleting image %q", s.image.ImageId))
}
ui.Message(fmt.Sprintf("Delete image %q complete", s.image.ImageId))
}

View File

@ -0,0 +1,305 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"math/rand"
"strings"
"time"
)
type stepCreateInstance struct {
Region string
Zone string
InstanceType string
InstanceName string
BootDiskType string
SourceImageId string
UsePrivateIp bool
instanceId string
}
func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating Instance...")
req, err := s.buildCreateInstanceRequest(state)
if err != nil {
return halt(state, err, "")
}
resp, err := conn.CreateUHostInstance(req)
if err != nil {
return halt(state, err, "Error on creating instance")
}
instanceId := resp.UHostIds[0]
err = retry.Config{
Tries: 20,
ShouldRetry: func(err error) bool {
return isExpectedStateError(err)
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
inst, err := client.describeUHostById(instanceId)
if err != nil {
return err
}
if inst == nil || inst.State != "Running" {
return newExpectedStateError("instance", instanceId)
}
return nil
})
if err != nil {
return halt(state, err, "Error on waiting for instance to available")
}
ui.Message(fmt.Sprintf("Create instance %q complete", instanceId))
instance, err := client.describeUHostById(instanceId)
if err != nil {
return halt(state, err, "")
}
s.instanceId = instanceId
state.Put("instance", instance)
if instance.BootDiskState == "Initializing" {
ui.Say(fmt.Sprintf("Waiting for boot disk of instance initialized when boot_disk_type is %q", s.BootDiskType))
err = retry.Config{
Tries: 200,
ShouldRetry: func(err error) bool {
return isExpectedStateError(err)
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
inst, err := client.describeUHostById(instanceId)
if err != nil {
return err
}
if inst.BootDiskState != "Normal" {
return newExpectedStateError("boot_disk of instance", instanceId)
}
return nil
})
if err != nil {
return halt(state, err, "Error on waiting for boot disk of instance initialized")
}
ui.Message(fmt.Sprintf("Waite for boot disk of instance %q initialized complete", instanceId))
}
return multistep.ActionContinue
}
func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
if s.instanceId == "" {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
ui := state.Get("ui").(packer.Ui)
ctx := context.TODO()
if cancelled || halted {
ui.Say("Deleting instance because of cancellation or error...")
} else {
ui.Say("Deleting instance...")
}
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
stopReq := conn.NewPoweroffUHostInstanceRequest()
stopReq.UHostId = ucloud.String(s.instanceId)
instance, err := client.describeUHostById(s.instanceId)
if err != nil {
if isNotFoundError(err) {
return
}
ui.Error(fmt.Sprintf("Error on reading instance when delete %q, %s",
s.instanceId, err.Error()))
return
}
if instance.State != "Stopped" {
err = retry.Config{
Tries: 5,
ShouldRetry: func(err error) bool {
return err != nil
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
if _, err = conn.PoweroffUHostInstance(stopReq); err != nil {
return err
}
return nil
})
if err != nil {
ui.Error(fmt.Sprintf("Error on stopping instance when delete %q, %s",
s.instanceId, err.Error()))
return
}
err = retry.Config{
Tries: 30,
ShouldRetry: func(err error) bool {
return isExpectedStateError(err)
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
instance, err := client.describeUHostById(s.instanceId)
if err != nil {
return err
}
if instance.State != "Stopped" {
return newExpectedStateError("instance", s.instanceId)
}
return nil
})
if err != nil {
ui.Error(fmt.Sprintf("Error on waiting for instance %q to stopped, %s",
s.instanceId, err.Error()))
return
}
}
deleteReq := conn.NewTerminateUHostInstanceRequest()
deleteReq.UHostId = ucloud.String(s.instanceId)
deleteReq.ReleaseUDisk = ucloud.Bool(true)
deleteReq.ReleaseEIP = ucloud.Bool(true)
err = retry.Config{
Tries: 5,
ShouldRetry: func(err error) bool {
return err != nil
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
if _, err = conn.TerminateUHostInstance(deleteReq); err != nil {
return err
}
return nil
})
if err != nil {
ui.Error(fmt.Sprintf("Error on deleting instance %q, %s",
s.instanceId, err.Error()))
return
}
err = retry.Config{
Tries: 30,
ShouldRetry: func(err error) bool { return !isNotFoundError(err) },
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
_, err := client.describeUHostById(s.instanceId)
return err
})
if err != nil {
ui.Error(fmt.Sprintf("Error on waiting for deleting instance %q completed, %s",
s.instanceId, err.Error()))
return
}
ui.Message(fmt.Sprintf("Delete instance %q complete", s.instanceId))
}
func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag) (*uhost.CreateUHostInstanceRequest, error) {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
srcImage := state.Get("source_image").(*uhost.UHostImageSet)
config := state.Get("config").(*Config)
connectConfig := &config.RunConfig.Comm
var password string
if srcImage.OsType == "Linux" {
password = config.Comm.SSHPassword
}
if password == "" {
password = fmt.Sprintf("%s%s%s",
s.randStringFromCharSet(5, defaultPasswordStr),
s.randStringFromCharSet(1, defaultPasswordSpe),
s.randStringFromCharSet(5, defaultPasswordNum))
if srcImage.OsType == "Linux" {
connectConfig.SSHPassword = password
}
}
req := conn.NewCreateUHostInstanceRequest()
t, _ := parseInstanceType(s.InstanceType)
req.CPU = ucloud.Int(t.CPU)
req.Memory = ucloud.Int(t.Memory)
req.Name = ucloud.String(s.InstanceName)
req.LoginMode = ucloud.String("Password")
req.Zone = ucloud.String(s.Zone)
req.ImageId = ucloud.String(s.SourceImageId)
req.ChargeType = ucloud.String("Dynamic")
req.Password = ucloud.String(password)
if v, ok := state.GetOk("security_group_id"); ok {
req.SecurityGroupId = ucloud.String(v.(string))
}
if v, ok := state.GetOk("vpc_id"); ok {
req.VPCId = ucloud.String(v.(string))
}
if v, ok := state.GetOk("subnet_id"); ok {
req.SubnetId = ucloud.String(v.(string))
}
bootDisk := uhost.UHostDisk{}
bootDisk.IsBoot = ucloud.String("true")
bootDisk.Size = ucloud.Int(srcImage.ImageSize)
bootDisk.Type = ucloud.String(bootDiskTypeMap[s.BootDiskType])
req.Disks = append(req.Disks, bootDisk)
if !s.UsePrivateIp {
operatorName := ucloud.String("International")
if strings.HasPrefix(s.Region, "cn-") {
operatorName = ucloud.String("Bgp")
}
networkInterface := uhost.CreateUHostInstanceParamNetworkInterface{
EIP: &uhost.CreateUHostInstanceParamNetworkInterfaceEIP{
Bandwidth: ucloud.Int(30),
PayMode: ucloud.String("Traffic"),
OperatorName: operatorName,
},
}
req.NetworkInterface = append(req.NetworkInterface, networkInterface)
}
return req, nil
}
func (s *stepCreateInstance) randStringFromCharSet(strlen int, charSet string) string {
rand.Seed(time.Now().UTC().UnixNano())
result := make([]byte, strlen)
for i := 0; i < strlen; i++ {
result[i] = charSet[rand.Intn(len(charSet))]
}
return string(result)
}

View File

@ -0,0 +1,68 @@
package uhost
import (
"context"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
type stepPreValidate struct {
Region string
Zone string
ImageDestinations []ImageDestination
}
func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if err := s.validateRegions(state); err != nil {
return halt(state, err, "")
}
if err := s.validateZones(state); err != nil {
return halt(state, err, "")
}
return multistep.ActionContinue
}
func (s *stepPreValidate) validateRegions(state multistep.StateBag) error {
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config)
ui.Say("Validating region and copied regions...")
var errs *packer.MultiError
if err := config.ValidateRegion(s.Region); err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
for _, imageDestination := range s.ImageDestinations {
if err := config.ValidateRegion(imageDestination.Region); err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (s *stepPreValidate) validateZones(state multistep.StateBag) error {
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*Config)
ui.Say("Validating availability_zone...")
var errs *packer.MultiError
if err := config.ValidateZone(s.Region, s.Zone); err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (s *stepPreValidate) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,80 @@
package uhost
import (
"context"
"fmt"
"github.com/hashicorp/packer/common/retry"
"time"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"github.com/ucloud/ucloud-sdk-go/ucloud"
)
type stepStopInstance struct {
}
func (s *stepStopInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*UCloudClient)
conn := client.uhostconn
instance := state.Get("instance").(*uhost.UHostInstanceSet)
ui := state.Get("ui").(packer.Ui)
instance, err := client.describeUHostById(instance.UHostId)
if err != nil {
return halt(state, err, fmt.Sprintf("Error on reading instance when stop %q", instance.UHostId))
}
if instance.State != "Stopped" {
stopReq := conn.NewPoweroffUHostInstanceRequest()
stopReq.UHostId = ucloud.String(instance.UHostId)
ui.Say(fmt.Sprintf("Stopping instance %q", instance.UHostId))
err = retry.Config{
Tries: 5,
ShouldRetry: func(err error) bool {
return err != nil
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
if _, err = conn.PoweroffUHostInstance(stopReq); err != nil {
return err
}
return nil
})
if err != nil {
return halt(state, err, fmt.Sprintf("Error on stopping instance %q", instance.UHostId))
}
err = retry.Config{
Tries: 20,
ShouldRetry: func(err error) bool {
return isExpectedStateError(err)
},
RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 6 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
instance, err := client.describeUHostById(instance.UHostId)
if err != nil {
return err
}
if instance.State != "Stopped" {
return newExpectedStateError("instance", instance.UHostId)
}
return nil
})
if err != nil {
return halt(state, err, fmt.Sprintf("Error on waiting for instance %q to stopped", instance.UHostId))
}
ui.Message(fmt.Sprintf("Stop instance %q complete", instance.UHostId))
}
return multistep.ActionContinue
}
func (s *stepStopInstance) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,215 @@
package uhost
import (
"fmt"
"strconv"
"strings"
"sync"
)
type instanceType struct {
CPU int
Memory int
HostType string
HostScaleType string
}
func parseInstanceType(s string) (*instanceType, error) {
split := strings.Split(s, "-")
if len(split) < 3 {
return nil, fmt.Errorf("instance type is invalid, got %q", s)
}
if split[1] == "customized" {
return parseInstanceTypeByCustomize(split...)
}
return parseInstanceTypeByNormal(split...)
}
func (i *instanceType) String() string {
if i.Iscustomized() {
return fmt.Sprintf("%s-%s-%v-%v", i.HostType, i.HostScaleType, i.CPU, i.Memory)
} else {
return fmt.Sprintf("%s-%s-%v", i.HostType, i.HostScaleType, i.CPU)
}
}
func (i *instanceType) Iscustomized() bool {
return i.HostScaleType == "customized"
}
var instanceTypeScaleMap = map[string]int{
"highcpu": 1 * 1024,
"basic": 2 * 1024,
"standard": 4 * 1024,
"highmem": 8 * 1024,
}
var availableHostTypes = []string{"n"}
func parseInstanceTypeByCustomize(splited ...string) (*instanceType, error) {
if len(splited) != 4 {
return nil, fmt.Errorf("instance type is invalid, expected like n-customize-1-2")
}
hostType := splited[0]
err := checkStringIn(hostType, availableHostTypes)
if err != nil {
return nil, err
}
hostScaleType := splited[1]
cpu, err := strconv.Atoi(splited[2])
if err != nil {
return nil, fmt.Errorf("cpu count is invalid, please use a number")
}
memory, err := strconv.Atoi(splited[3])
if err != nil {
return nil, fmt.Errorf("memory count is invalid, please use a number")
}
if cpu/memory > 2 || memory/cpu > 12 {
return nil, fmt.Errorf("the ratio of cpu to memory should be range of 2:1 ~ 1:12, got %d:%d", cpu, memory)
}
if memory/cpu == 1 || memory/cpu == 2 || memory/cpu == 4 || memory/cpu == 8 {
return nil, fmt.Errorf("instance type is invalid, expected %q like %q,"+
"the Type can be highcpu, basic, standard, highmem when the ratio of cpu to memory is 1:1, 1:2, 1:4, 1:8", "n-Type-CPU", "n-standard-1")
}
if cpu < 1 || 32 < cpu {
return nil, fmt.Errorf("expected cpu to be in the range (1 - 32), got %d", cpu)
}
if memory < 1 || 128 < memory {
return nil, fmt.Errorf("expected memory to be in the range (1 - 128),got %d", memory)
}
if cpu != 1 && (cpu%2) != 0 {
return nil, fmt.Errorf("expected the number of cores of cpu must be divisible by 2 without a remainder (except single core), got %d", cpu)
}
if memory != 1 && (memory%2) != 0 {
return nil, fmt.Errorf("expected the number of memory must be divisible by 2 without a remainder (except single memory), got %d", memory)
}
t := &instanceType{}
t.HostType = hostType
t.HostScaleType = hostScaleType
t.CPU = cpu
t.Memory = memory * 1024
return t, nil
}
var availableOutstandingCpu = []int{4, 8, 16, 32, 64}
func parseInstanceTypeByNormal(split ...string) (*instanceType, error) {
if len(split) != 3 {
return nil, fmt.Errorf("instance type is invalid, expected like n-standard-1")
}
hostType := split[0]
err := checkStringIn(hostType, []string{"n", "o"})
if err != nil {
return nil, err
}
hostScaleType := split[1]
if scale, ok := instanceTypeScaleMap[hostScaleType]; !ok {
return nil, fmt.Errorf("instance type is invalid, expected like n-standard-1")
} else {
cpu, err := strconv.Atoi(split[2])
if err != nil {
return nil, fmt.Errorf("cpu count is invalid, please use a number")
}
if cpu != 1 && (cpu%2) != 0 {
return nil, fmt.Errorf("expected the number of cores of cpu must be divisible by 2 without a remainder (except single core), got %d", cpu)
}
if hostType == "o" {
if err := checkIntIn(cpu, availableOutstandingCpu); err != nil {
return nil, fmt.Errorf("expected cpu of outstanding instancetype %q", err)
}
if hostScaleType == "highmem" && cpu == 64 {
return nil, fmt.Errorf("this instance type %q is not supported, please refer to instance type document", "o-highmem-64")
}
} else {
if hostScaleType == "highmem" && cpu > 16 {
return nil, fmt.Errorf("expected cpu to be in the range (1 - 16) for normal highmem instance type, got %d", cpu)
}
if cpu < 1 || 32 < cpu {
return nil, fmt.Errorf("expected cpu to be in the range (1 - 32) for normal instance type, got %d", cpu)
}
}
memory := cpu * scale
t := &instanceType{}
t.HostType = hostType
t.HostScaleType = hostScaleType
t.CPU = cpu
t.Memory = memory
return t, nil
}
}
type imageInfo struct {
ImageId string
ProjectId string
Region string
}
func (i *imageInfo) Id() string {
return fmt.Sprintf("%s:%s", i.ProjectId, i.Region)
}
type imageInfoSet struct {
m map[string]imageInfo
once sync.Once
}
func newImageInfoSet(vL []imageInfo) *imageInfoSet {
s := imageInfoSet{}
for _, v := range vL {
s.Set(v)
}
return &s
}
func (i *imageInfoSet) init() {
i.m = make(map[string]imageInfo)
}
func (i *imageInfoSet) Set(img imageInfo) {
i.once.Do(i.init)
i.m[img.Id()] = img
}
func (i *imageInfoSet) Remove(id string) {
i.once.Do(i.init)
delete(i.m, id)
}
func (i *imageInfoSet) Get(projectId, region string) *imageInfo {
k := fmt.Sprintf("%s:%s", projectId, region)
if v, ok := i.m[k]; ok {
return &v
}
return nil
}
func (i *imageInfoSet) GetAll() []imageInfo {
var vL []imageInfo
for _, img := range i.m {
vL = append(vL, img)
}
return vL
}

View File

@ -0,0 +1,54 @@
package uhost
import (
"testing"
)
func Test_parseInstanceType(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want *instanceType
wantErr bool
}{
{"ok_highcpu", args{"n-highcpu-1"}, &instanceType{1, 1024, "n", "highcpu"}, false},
{"ok_basic", args{"n-basic-1"}, &instanceType{1, 2048, "n", "basic"}, false},
{"ok_standard", args{"n-standard-1"}, &instanceType{1, 4096, "n", "standard"}, false},
{"ok_highmem", args{"n-highmem-1"}, &instanceType{1, 8192, "n", "highmem"}, false},
{"ok_customized", args{"n-customized-1-12"}, &instanceType{1, 12288, "n", "customized"}, false},
{"err_customized", args{"n-customized-1-5"}, nil, true},
{"err_type", args{"nx-highcpu-1"}, nil, true},
{"err_scale_type", args{"n-invalid-1"}, nil, true},
{"err_cpu_too_much", args{"n-highcpu-33"}, nil, true},
{"err_cpu_too_less", args{"n-highcpu-0"}, nil, true},
{"err_cpu_is_invalid", args{"n-highcpu-x"}, nil, true},
{"err_customized_format_len", args{"n-customized-1"}, nil, true},
{"err_customized_format_number", args{"n-customized-x"}, nil, true},
{"err_customized_should_be_standard", args{"n-customized-1-2"}, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseInstanceType(tt.args.s)
if (err != nil) != tt.wantErr {
t.Errorf("parseInstanceType() arg %s got %#v error = %v, wantErr %v", tt.args.s, got, err, tt.wantErr)
return
}
if got == nil {
return
}
if !(tt.want.CPU == got.CPU) ||
!(tt.want.Memory == got.Memory) ||
!(tt.want.HostType == got.HostType) ||
!(tt.want.HostScaleType == got.HostScaleType) {
t.Errorf("parseInstanceType() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -0,0 +1,73 @@
package uhost
import (
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/ucloud/ucloud-sdk-go/services/uhost"
"strings"
)
func checkStringIn(val string, availables []string) error {
for _, choice := range availables {
if val == choice {
return nil
}
}
return fmt.Errorf("should be one of %q, got %q", strings.Join(availables, ","), val)
}
func checkIntIn(val int, availables []int) error {
for _, choice := range availables {
if val == choice {
return nil
}
}
return fmt.Errorf("should be one of %v, got %d", availables, val)
}
func isStringIn(val string, availables []string) bool {
for _, choice := range availables {
if val == choice {
return true
}
}
return false
}
// SSHHost returns a function that can be given to the SSH communicator
func SSHHost(usePrivateIp bool) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
instance := state.Get("instance").(*uhost.UHostInstanceSet)
var privateIp, publicIp string
for _, v := range instance.IPSet {
if v.Type == "Private" {
privateIp = v.IP
} else {
publicIp = v.IP
}
}
if usePrivateIp {
return privateIp, nil
} else {
return publicIp, nil
}
}
}
func halt(state multistep.StateBag, err error, prefix string) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if prefix != "" {
err = fmt.Errorf("%s: %s", prefix, err)
}
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}

View File

@ -46,6 +46,7 @@ import (
scalewaybuilder "github.com/hashicorp/packer/builder/scaleway"
tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
tritonbuilder "github.com/hashicorp/packer/builder/triton"
uclouduhostbuilder "github.com/hashicorp/packer/builder/ucloud/uhost"
vagrantbuilder "github.com/hashicorp/packer/builder/vagrant"
virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso"
virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf"
@ -127,6 +128,7 @@ var Builders = map[string]packer.Builder{
"scaleway": new(scalewaybuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"triton": new(tritonbuilder.Builder),
"ucloud-uhost": new(uclouduhostbuilder.Builder),
"vagrant": new(vagrantbuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),

View File

@ -1,5 +1,3 @@
// Code generated by pigeon; DO NOT EDIT.
package bootcommand
import (
@ -8,9 +6,7 @@ import (
"fmt"
"io"
"io/ioutil"
"math"
"os"
"sort"
"strconv"
"strings"
"time"
@ -791,85 +787,18 @@ var (
// errNoRule is returned when the grammar to parse has no rule.
errNoRule = errors.New("grammar has no rule")
// errInvalidEntrypoint is returned when the specified entrypoint rule
// does not exit.
errInvalidEntrypoint = errors.New("invalid entrypoint")
// errInvalidEncoding is returned when the source is not properly
// utf8-encoded.
errInvalidEncoding = errors.New("invalid encoding")
// errMaxExprCnt is used to signal that the maximum number of
// expressions have been parsed.
errMaxExprCnt = errors.New("max number of expresssions parsed")
// errNoMatch is returned if no match could be found.
errNoMatch = errors.New("no match found")
)
// Option is a function that can set an option on the parser. It returns
// the previous setting as an Option.
type Option func(*parser) Option
// MaxExpressions creates an Option to stop parsing after the provided
// number of expressions have been parsed, if the value is 0 then the parser will
// parse for as many steps as needed (possibly an infinite number).
//
// The default for maxExprCnt is 0.
func MaxExpressions(maxExprCnt uint64) Option {
return func(p *parser) Option {
oldMaxExprCnt := p.maxExprCnt
p.maxExprCnt = maxExprCnt
return MaxExpressions(oldMaxExprCnt)
}
}
// Entrypoint creates an Option to set the rule name to use as entrypoint.
// The rule name must have been specified in the -alternate-entrypoints
// if generating the parser with the -optimize-grammar flag, otherwise
// it may have been optimized out. Passing an empty string sets the
// entrypoint to the first rule in the grammar.
//
// The default is to start parsing at the first rule in the grammar.
func Entrypoint(ruleName string) Option {
return func(p *parser) Option {
oldEntrypoint := p.entrypoint
p.entrypoint = ruleName
if ruleName == "" {
p.entrypoint = g.rules[0].name
}
return Entrypoint(oldEntrypoint)
}
}
// Statistics adds a user provided Stats struct to the parser to allow
// the user to process the results after the parsing has finished.
// Also the key for the "no match" counter is set.
//
// Example usage:
//
// input := "input"
// stats := Stats{}
// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match"))
// if err != nil {
// log.Panicln(err)
// }
// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ")
// if err != nil {
// log.Panicln(err)
// }
// fmt.Println(string(b))
//
func Statistics(stats *Stats, choiceNoMatch string) Option {
return func(p *parser) Option {
oldStats := p.Stats
p.Stats = stats
oldChoiceNoMatch := p.choiceNoMatch
p.choiceNoMatch = choiceNoMatch
if p.Stats.ChoiceAltCnt == nil {
p.Stats.ChoiceAltCnt = make(map[string]map[string]int)
}
return Statistics(oldStats, oldChoiceNoMatch)
}
}
// Debug creates an Option to set the debug flag to b. When set to true,
// debugging information is printed to stdout while parsing.
//
@ -896,20 +825,6 @@ func Memoize(b bool) Option {
}
}
// AllowInvalidUTF8 creates an Option to allow invalid UTF-8 bytes.
// Every invalid UTF-8 byte is treated as a utf8.RuneError (U+FFFD)
// by character class matchers and is matched by the any matcher.
// The returned matched value, c.text and c.offset are NOT affected.
//
// The default is false.
func AllowInvalidUTF8(b bool) Option {
return func(p *parser) Option {
old := p.allowInvalidUTF8
p.allowInvalidUTF8 = b
return AllowInvalidUTF8(old)
}
}
// Recover creates an Option to set the recover flag to b. When set to
// true, this causes the parser to recover from panics and convert it
// to an error. Setting it to false can be useful while debugging to
@ -924,37 +839,13 @@ func Recover(b bool) Option {
}
}
// GlobalStore creates an Option to set a key to a certain value in
// the globalStore.
func GlobalStore(key string, value interface{}) Option {
return func(p *parser) Option {
old := p.cur.globalStore[key]
p.cur.globalStore[key] = value
return GlobalStore(key, old)
}
}
// InitState creates an Option to set a key to a certain value in
// the global "state" store.
func InitState(key string, value interface{}) Option {
return func(p *parser) Option {
old := p.cur.state[key]
p.cur.state[key] = value
return InitState(key, old)
}
}
// ParseFile parses the file identified by filename.
func ParseFile(filename string, opts ...Option) (i interface{}, err error) {
func ParseFile(filename string, opts ...Option) (interface{}, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer func() {
if closeErr := f.Close(); closeErr != nil {
err = closeErr
}
}()
defer f.Close()
return ParseReader(filename, f, opts...)
}
@ -995,22 +886,8 @@ type savepoint struct {
type current struct {
pos position // start position of the match
text []byte // raw text of the match
// state is a store for arbitrary key,value pairs that the user wants to be
// tied to the backtracking of the parser.
// This is always rolled back if a parsing rule fails.
state storeDict
// globalStore is a general store for the user to store arbitrary key-value
// pairs that they need to manage and that they do not want tied to the
// backtracking of the parser. This is only modified by the user and never
// rolled back by the parser. It is always up to the user to keep this in a
// consistent state.
globalStore storeDict
}
type storeDict map[string]interface{}
// the AST types...
type grammar struct {
@ -1036,23 +913,11 @@ type actionExpr struct {
run func(*parser) (interface{}, error)
}
type recoveryExpr struct {
pos position
expr interface{}
recoverExpr interface{}
failureLabel []string
}
type seqExpr struct {
pos position
exprs []interface{}
}
type throwExpr struct {
pos position
label string
}
type labeledExpr struct {
pos position
label string
@ -1075,11 +940,6 @@ type ruleRefExpr struct {
name string
}
type stateCodeExpr struct {
pos position
run func(*parser) error
}
type andCodeExpr struct {
pos position
run func(*parser) (bool, error)
@ -1097,14 +957,13 @@ type litMatcher struct {
}
type charClassMatcher struct {
pos position
val string
basicLatinChars [128]bool
chars []rune
ranges []rune
classes []*unicode.RangeTable
ignoreCase bool
inverted bool
pos position
val string
chars []rune
ranges []rune
classes []*unicode.RangeTable
ignoreCase bool
inverted bool
}
type anyMatcher position
@ -1158,10 +1017,9 @@ func (e errList) Error() string {
// parserError wraps an error with a prefix indicating the rule in which
// the error occurred. The original error is stored in the Inner field.
type parserError struct {
Inner error
pos position
prefix string
expected []string
Inner error
pos position
prefix string
}
// Error returns the error message.
@ -1171,32 +1029,14 @@ func (p *parserError) Error() string {
// newParser creates a parser with the specified input source and options.
func newParser(filename string, b []byte, opts ...Option) *parser {
stats := Stats{
ChoiceAltCnt: make(map[string]map[string]int),
}
p := &parser{
filename: filename,
errs: new(errList),
data: b,
pt: savepoint{position: position{line: 1}},
recover: true,
cur: current{
state: make(storeDict),
globalStore: make(storeDict),
},
maxFailPos: position{col: 1, line: 1},
maxFailExpected: make([]string, 0, 20),
Stats: &stats,
// start rule is rule [0] unless an alternate entrypoint is specified
entrypoint: g.rules[0].name,
}
p.setOptions(opts)
if p.maxExprCnt == 0 {
p.maxExprCnt = math.MaxUint64
}
return p
}
@ -1213,30 +1053,6 @@ type resultTuple struct {
end savepoint
}
const choiceNoMatch = -1
// Stats stores some statistics, gathered during parsing
type Stats struct {
// ExprCnt counts the number of expressions processed during parsing
// This value is compared to the maximum number of expressions allowed
// (set by the MaxExpressions option).
ExprCnt uint64
// ChoiceAltCnt is used to count for each ordered choice expression,
// which alternative is used how may times.
// These numbers allow to optimize the order of the ordered choice expression
// to increase the performance of the parser
//
// The outer key of ChoiceAltCnt is composed of the name of the rule as well
// as the line and the column of the ordered choice.
// The inner key of ChoiceAltCnt is the number (one-based) of the matching alternative.
// For each alternative the number of matches are counted. If an ordered choice does not
// match, a special counter is incremented. The name of this counter is set with
// the parser option Statistics.
// For an alternative to be included in ChoiceAltCnt, it has to match at least once.
ChoiceAltCnt map[string]map[string]int
}
type parser struct {
filename string
pt savepoint
@ -1245,9 +1061,9 @@ type parser struct {
data []byte
errs *errList
depth int
recover bool
debug bool
depth int
memoize bool
// memoization table for the packrat algorithm:
@ -1261,23 +1077,8 @@ type parser struct {
// rule stack, allows identification of the current rule in errors
rstack []*rule
// parse fail
maxFailPos position
maxFailExpected []string
maxFailInvertExpected bool
// max number of expressions to be parsed
maxExprCnt uint64
// entrypoint for the parser
entrypoint string
allowInvalidUTF8 bool
*Stats
choiceNoMatch string
// recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse
recoveryStack []map[string]interface{}
// stats
exprCnt int
}
// push a variable set on the vstack.
@ -1312,31 +1113,6 @@ func (p *parser) popV() {
p.vstack = p.vstack[:len(p.vstack)-1]
}
// push a recovery expression with its labels to the recoveryStack
func (p *parser) pushRecovery(labels []string, expr interface{}) {
if cap(p.recoveryStack) == len(p.recoveryStack) {
// create new empty slot in the stack
p.recoveryStack = append(p.recoveryStack, nil)
} else {
// slice to 1 more
p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1]
}
m := make(map[string]interface{}, len(labels))
for _, fl := range labels {
m[fl] = expr
}
p.recoveryStack[len(p.recoveryStack)-1] = m
}
// pop a recovery expression from the recoveryStack
func (p *parser) popRecovery() {
// GC that map
p.recoveryStack[len(p.recoveryStack)-1] = nil
p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1]
}
func (p *parser) print(prefix, s string) string {
if !p.debug {
return s
@ -1358,10 +1134,10 @@ func (p *parser) out(s string) string {
}
func (p *parser) addErr(err error) {
p.addErrAt(err, p.pt.position, []string{})
p.addErrAt(err, p.pt.position)
}
func (p *parser) addErrAt(err error, pos position, expected []string) {
func (p *parser) addErrAt(err error, pos position) {
var buf bytes.Buffer
if p.filename != "" {
buf.WriteString(p.filename)
@ -1381,29 +1157,10 @@ func (p *parser) addErrAt(err error, pos position, expected []string) {
buf.WriteString("rule " + rule.name)
}
}
pe := &parserError{Inner: err, pos: pos, prefix: buf.String(), expected: expected}
pe := &parserError{Inner: err, pos: pos, prefix: buf.String()}
p.errs.add(pe)
}
func (p *parser) failAt(fail bool, pos position, want string) {
// process fail if parsing fails and not inverted or parsing succeeds and invert is set
if fail == p.maxFailInvertExpected {
if pos.offset < p.maxFailPos.offset {
return
}
if pos.offset > p.maxFailPos.offset {
p.maxFailPos = pos
p.maxFailExpected = p.maxFailExpected[:0]
}
if p.maxFailInvertExpected {
want = "!" + want
}
p.maxFailExpected = append(p.maxFailExpected, want)
}
}
// read advances the parser to the next rune.
func (p *parser) read() {
p.pt.offset += p.pt.w
@ -1416,8 +1173,8 @@ func (p *parser) read() {
p.pt.col = 0
}
if rn == utf8.RuneError && n == 1 { // see utf8.DecodeRune
if !p.allowInvalidUTF8 {
if rn == utf8.RuneError {
if n == 1 {
p.addErr(errInvalidEncoding)
}
}
@ -1434,43 +1191,6 @@ func (p *parser) restore(pt savepoint) {
p.pt = pt
}
// Cloner is implemented by any value that has a Clone method, which returns a
// copy of the value. This is mainly used for types which are not passed by
// value (e.g map, slice, chan) or structs that contain such types.
//
// This is used in conjunction with the global state feature to create proper
// copies of the state to allow the parser to properly restore the state in
// the case of backtracking.
type Cloner interface {
Clone() interface{}
}
// clone and return parser current state.
func (p *parser) cloneState() storeDict {
if p.debug {
defer p.out(p.in("cloneState"))
}
state := make(storeDict, len(p.cur.state))
for k, v := range p.cur.state {
if c, ok := v.(Cloner); ok {
state[k] = c.Clone()
} else {
state[k] = v
}
}
return state
}
// restore parser current state to the state storeDict.
// every restoreState should applied only one time for every cloned state
func (p *parser) restoreState(state storeDict) {
if p.debug {
defer p.out(p.in("restoreState"))
}
p.cur.state = state
}
// get the slice of bytes from the savepoint start to the current position.
func (p *parser) sliceFrom(start savepoint) []byte {
return p.data[start.position.offset:p.pt.position.offset]
@ -1536,54 +1256,19 @@ func (p *parser) parse(g *grammar) (val interface{}, err error) {
}()
}
startRule, ok := p.rules[p.entrypoint]
if !ok {
p.addErr(errInvalidEntrypoint)
return nil, p.errs.err()
}
// start rule is rule [0]
p.read() // advance to first rune
val, ok = p.parseRule(startRule)
val, ok := p.parseRule(g.rules[0])
if !ok {
if len(*p.errs) == 0 {
// If parsing fails, but no errors have been recorded, the expected values
// for the farthest parser position are returned as error.
maxFailExpectedMap := make(map[string]struct{}, len(p.maxFailExpected))
for _, v := range p.maxFailExpected {
maxFailExpectedMap[v] = struct{}{}
}
expected := make([]string, 0, len(maxFailExpectedMap))
eof := false
if _, ok := maxFailExpectedMap["!."]; ok {
delete(maxFailExpectedMap, "!.")
eof = true
}
for k := range maxFailExpectedMap {
expected = append(expected, k)
}
sort.Strings(expected)
if eof {
expected = append(expected, "EOF")
}
p.addErrAt(errors.New("no match found, expected: "+listJoin(expected, ", ", "or")), p.maxFailPos, expected)
// make sure this doesn't go out silently
p.addErr(errNoMatch)
}
return nil, p.errs.err()
}
return val, p.errs.err()
}
func listJoin(list []string, sep string, lastSep string) string {
switch len(list) {
case 0:
return ""
case 1:
return list[0]
default:
return fmt.Sprintf("%s %s %s", strings.Join(list[:len(list)-1], sep), lastSep, list[len(list)-1])
}
}
func (p *parser) parseRule(rule *rule) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseRule " + rule.name))
@ -1615,6 +1300,7 @@ func (p *parser) parseRule(rule *rule) (interface{}, bool) {
func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
var pt savepoint
var ok bool
if p.memoize {
res, ok := p.getMemoized(expr)
@ -1625,13 +1311,8 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
pt = p.pt
}
p.ExprCnt++
if p.ExprCnt > p.maxExprCnt {
panic(errMaxExprCnt)
}
p.exprCnt++
var val interface{}
var ok bool
switch expr := expr.(type) {
case *actionExpr:
val, ok = p.parseActionExpr(expr)
@ -1655,16 +1336,10 @@ func (p *parser) parseExpr(expr interface{}) (interface{}, bool) {
val, ok = p.parseNotExpr(expr)
case *oneOrMoreExpr:
val, ok = p.parseOneOrMoreExpr(expr)
case *recoveryExpr:
val, ok = p.parseRecoveryExpr(expr)
case *ruleRefExpr:
val, ok = p.parseRuleRefExpr(expr)
case *seqExpr:
val, ok = p.parseSeqExpr(expr)
case *stateCodeExpr:
val, ok = p.parseStateCodeExpr(expr)
case *throwExpr:
val, ok = p.parseThrowExpr(expr)
case *zeroOrMoreExpr:
val, ok = p.parseZeroOrMoreExpr(expr)
case *zeroOrOneExpr:
@ -1688,13 +1363,10 @@ func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) {
if ok {
p.cur.pos = start.position
p.cur.text = p.sliceFrom(start)
state := p.cloneState()
actVal, err := act.run(p)
if err != nil {
p.addErrAt(err, start.position, []string{})
p.addErrAt(err, start.position)
}
p.restoreState(state)
val = actVal
}
if ok && p.debug {
@ -1708,14 +1380,10 @@ func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) {
defer p.out(p.in("parseAndCodeExpr"))
}
state := p.cloneState()
ok, err := and.run(p)
if err != nil {
p.addErr(err)
}
p.restoreState(state)
return nil, ok
}
@ -1725,13 +1393,10 @@ func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) {
}
pt := p.pt
state := p.cloneState()
p.pushV()
_, ok := p.parseExpr(and.expr)
p.popV()
p.restoreState(state)
p.restore(pt)
return nil, ok
}
@ -1740,15 +1405,12 @@ func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) {
defer p.out(p.in("parseAnyMatcher"))
}
if p.pt.rn == utf8.RuneError && p.pt.w == 0 {
// EOF - see utf8.DecodeRune
p.failAt(false, p.pt.position, ".")
return nil, false
if p.pt.rn != utf8.RuneError {
start := p.pt
p.read()
return p.sliceFrom(start), true
}
start := p.pt
p.read()
p.failAt(true, start.position, ".")
return p.sliceFrom(start), true
return nil, false
}
func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) {
@ -1757,14 +1419,11 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool
}
cur := p.pt.rn
start := p.pt
// can't match EOF
if cur == utf8.RuneError && p.pt.w == 0 { // see utf8.DecodeRune
p.failAt(false, start.position, chr.val)
if cur == utf8.RuneError {
return nil, false
}
start := p.pt
if chr.ignoreCase {
cur = unicode.ToLower(cur)
}
@ -1773,11 +1432,9 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool
for _, rn := range chr.chars {
if rn == cur {
if chr.inverted {
p.failAt(false, start.position, chr.val)
return nil, false
}
p.read()
p.failAt(true, start.position, chr.val)
return p.sliceFrom(start), true
}
}
@ -1786,11 +1443,9 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool
for i := 0; i < len(chr.ranges); i += 2 {
if cur >= chr.ranges[i] && cur <= chr.ranges[i+1] {
if chr.inverted {
p.failAt(false, start.position, chr.val)
return nil, false
}
p.read()
p.failAt(true, start.position, chr.val)
return p.sliceFrom(start), true
}
}
@ -1799,60 +1454,33 @@ func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool
for _, cl := range chr.classes {
if unicode.Is(cl, cur) {
if chr.inverted {
p.failAt(false, start.position, chr.val)
return nil, false
}
p.read()
p.failAt(true, start.position, chr.val)
return p.sliceFrom(start), true
}
}
if chr.inverted {
p.read()
p.failAt(true, start.position, chr.val)
return p.sliceFrom(start), true
}
p.failAt(false, start.position, chr.val)
return nil, false
}
func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) {
choiceIdent := fmt.Sprintf("%s %d:%d", p.rstack[len(p.rstack)-1].name, ch.pos.line, ch.pos.col)
m := p.ChoiceAltCnt[choiceIdent]
if m == nil {
m = make(map[string]int)
p.ChoiceAltCnt[choiceIdent] = m
}
// We increment altI by 1, so the keys do not start at 0
alt := strconv.Itoa(altI + 1)
if altI == choiceNoMatch {
alt = p.choiceNoMatch
}
m[alt]++
}
func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseChoiceExpr"))
}
for altI, alt := range ch.alternatives {
// dummy assignment to prevent compile error if optimized
_ = altI
state := p.cloneState()
for _, alt := range ch.alternatives {
p.pushV()
val, ok := p.parseExpr(alt)
p.popV()
if ok {
p.incChoiceAltCnt(ch, altI)
return val, ok
}
p.restoreState(state)
}
p.incChoiceAltCnt(ch, choiceNoMatch)
return nil, false
}
@ -1876,11 +1504,6 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
defer p.out(p.in("parseLitMatcher"))
}
ignoreCase := ""
if lit.ignoreCase {
ignoreCase = "i"
}
val := fmt.Sprintf("%q%s", lit.val, ignoreCase)
start := p.pt
for _, want := range lit.val {
cur := p.pt.rn
@ -1888,13 +1511,11 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
cur = unicode.ToLower(cur)
}
if cur != want {
p.failAt(false, start.position, val)
p.restore(start)
return nil, false
}
p.read()
}
p.failAt(true, start.position, val)
return p.sliceFrom(start), true
}
@ -1903,14 +1524,10 @@ func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) {
defer p.out(p.in("parseNotCodeExpr"))
}
state := p.cloneState()
ok, err := not.run(p)
if err != nil {
p.addErr(err)
}
p.restoreState(state)
return nil, !ok
}
@ -1920,15 +1537,10 @@ func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) {
}
pt := p.pt
state := p.cloneState()
p.pushV()
p.maxFailInvertExpected = !p.maxFailInvertExpected
_, ok := p.parseExpr(not.expr)
p.maxFailInvertExpected = !p.maxFailInvertExpected
p.popV()
p.restoreState(state)
p.restore(pt)
return nil, !ok
}
@ -1954,18 +1566,6 @@ func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) {
}
}
func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")"))
}
p.pushRecovery(recover.failureLabel, recover.recoverExpr)
val, ok := p.parseExpr(recover.expr)
p.popRecovery()
return val, ok
}
func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseRuleRefExpr " + ref.name))
@ -1988,14 +1588,12 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
defer p.out(p.in("parseSeqExpr"))
}
vals := make([]interface{}, 0, len(seq.exprs))
var vals []interface{}
pt := p.pt
state := p.cloneState()
for _, expr := range seq.exprs {
val, ok := p.parseExpr(expr)
if !ok {
p.restoreState(state)
p.restore(pt)
return nil, false
}
@ -2004,34 +1602,6 @@ func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) {
return vals, true
}
func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseStateCodeExpr"))
}
err := state.run(p)
if err != nil {
p.addErr(err)
}
return nil, true
}
func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseThrowExpr"))
}
for i := len(p.recoveryStack) - 1; i >= 0; i-- {
if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok {
if val, ok := p.parseExpr(recoverExpr); ok {
return val, ok
}
}
}
return nil, false
}
func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) {
if p.debug {
defer p.out(p.in("parseZeroOrMoreExpr"))
@ -2061,3 +1631,18 @@ func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) {
// whether it matched or not, consider it a match
return val, true
}
func rangeTable(class string) *unicode.RangeTable {
if rt, ok := unicode.Categories[class]; ok {
return rt
}
if rt, ok := unicode.Properties[class]; ok {
return rt
}
if rt, ok := unicode.Scripts[class]; ok {
return rt
}
// cannot happen
panic(fmt.Sprintf("invalid Unicode class: %s", class))
}

View File

@ -0,0 +1,32 @@
{
"variables": {
"ucloud_public_key": "{{env `UCLOUD_PUBLIC_KEY`}}",
"ucloud_private_key": "{{env `UCLOUD_PRIVATE_KEY`}}",
"ucloud_project_id": "{{env `UCLOUD_PROJECT_ID`}}"
},
"builders": [{
"type": "ucloud-uhost",
"public_key":"{{user `ucloud_public_key`}}",
"private_key":"{{user `ucloud_private_key`}}",
"project_id": "{{user `ucloud_project_id`}}",
"region": "cn-bj2",
"availability_zone": "cn-bj2-02",
"instance_type": "n-basic-2",
"source_image_id":"uimage-f1chxn",
"ssh_username":"root",
"image_name": "packer-test-regionCopy-bj",
"image_copy_to_mappings": [{
"project_id": "org-fthbzm",
"region": "cn-sh2",
"description": "test"
}]
}],
"provisioners": [{
"type": "shell",
"inline": [
"yum install mysql -y"
]
}]
}

3
go.mod
View File

@ -80,6 +80,7 @@ require (
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/mitchellh/reflectwalk v1.0.0
github.com/mna/pigeon v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/moul/anonuuid v0.0.0-20160222162117-609b752a95ef // indirect
@ -103,6 +104,7 @@ require (
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
github.com/stretchr/testify v1.3.0
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346
github.com/ucloud/ucloud-sdk-go v0.8.6
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
github.com/ulikunitz/xz v0.5.5
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311
@ -115,6 +117,7 @@ require (
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190425145619-16072639606e
golang.org/x/text v0.3.1 // indirect
golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5 // indirect
google.golang.org/api v0.3.1
google.golang.org/grpc v1.19.1
gopkg.in/h2non/gock.v1 v1.0.12 // indirect

12
go.sum
View File

@ -291,6 +291,8 @@ github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mna/pigeon v1.0.0 h1:n46IoStjdzjaXuyBH53j9HZ8CVqGWpC7P5/v8dP4qEY=
github.com/mna/pigeon v1.0.0/go.mod h1:Iym28+kJVnC1hfQvv5MUtI6AiFFzvQjHcvI4RFTG/04=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
@ -384,6 +386,8 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
@ -400,6 +404,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346 h1:a014AaXz7AISMePv8xKRffUZZkr5z2XmSDf41gRV3+A=
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181220135002-f1744d40d346/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/ucloud/ucloud-sdk-go v0.8.6 h1:TBdjokuRMjrFXWEXXXOFXd5B6XStunQJmXxPNvMBaCQ=
github.com/ucloud/ucloud-sdk-go v0.8.6/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
@ -499,6 +505,12 @@ golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190610231749-f8d1dee965f7 h1:WxK+jPGx5s7BushiqEK+KKKSELWPhIEaIDJKU052UKU=
golang.org/x/tools v0.0.0-20190610231749-f8d1dee965f7/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b h1:/mJ+GKieZA6hFDQGdWZrjj4AXPl5ylY+5HusG80roy0=
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5 h1:WfRBLVK37R+k1gUOKuZX8JtangyEXmuopHz5tazlZRo=
golang.org/x/tools v0.0.0-20190612232758-d4e310b4a8a5/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI=

View File

@ -1,4 +1,5 @@
language: go
go_import_path: github.com/sirupsen/logrus
env:
- GOMAXPROCS=4 GORACE=halt_on_error=1
matrix:

View File

@ -361,6 +361,7 @@ The built-in logging formatters are:
Third party logging formatters:
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.

View File

@ -108,23 +108,34 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
for k, v := range entry.Data {
data[k] = v
}
var field_err string
fieldErr := entry.err
for k, v := range fields {
if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func {
field_err = fmt.Sprintf("can not add field %q", k)
if entry.err != "" {
field_err = entry.err + ", " + field_err
isErrField := false
if t := reflect.TypeOf(v); t != nil {
switch t.Kind() {
case reflect.Func:
isErrField = true
case reflect.Ptr:
isErrField = t.Elem().Kind() == reflect.Func
}
}
if isErrField {
tmp := fmt.Sprintf("can not add field %q", k)
if fieldErr != "" {
fieldErr = entry.err + ", " + tmp
} else {
fieldErr = tmp
}
} else {
data[k] = v
}
}
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err}
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr}
}
// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t}
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err}
}
// getPackageName reduces a fully qualified function name to the package name
@ -240,16 +251,18 @@ func (entry *Entry) write() {
}
}
func (entry *Entry) Trace(args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) {
entry.log(TraceLevel, fmt.Sprint(args...))
func (entry *Entry) Log(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) {
entry.log(level, fmt.Sprint(args...))
}
}
func (entry *Entry) Trace(args ...interface{}) {
entry.Log(TraceLevel, args...)
}
func (entry *Entry) Debug(args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) {
entry.log(DebugLevel, fmt.Sprint(args...))
}
entry.Log(DebugLevel, args...)
}
func (entry *Entry) Print(args ...interface{}) {
@ -257,15 +270,11 @@ func (entry *Entry) Print(args ...interface{}) {
}
func (entry *Entry) Info(args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) {
entry.log(InfoLevel, fmt.Sprint(args...))
}
entry.Log(InfoLevel, args...)
}
func (entry *Entry) Warn(args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) {
entry.log(WarnLevel, fmt.Sprint(args...))
}
entry.Log(WarnLevel, args...)
}
func (entry *Entry) Warning(args ...interface{}) {
@ -273,43 +282,35 @@ func (entry *Entry) Warning(args ...interface{}) {
}
func (entry *Entry) Error(args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) {
entry.log(ErrorLevel, fmt.Sprint(args...))
}
entry.Log(ErrorLevel, args...)
}
func (entry *Entry) Fatal(args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) {
entry.log(FatalLevel, fmt.Sprint(args...))
}
entry.Log(FatalLevel, args...)
entry.Logger.Exit(1)
}
func (entry *Entry) Panic(args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) {
entry.log(PanicLevel, fmt.Sprint(args...))
}
entry.Log(PanicLevel, args...)
panic(fmt.Sprint(args...))
}
// Entry Printf family functions
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
entry.Log(level, fmt.Sprintf(format, args...))
}
func (entry *Entry) Tracef(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) {
entry.Trace(fmt.Sprintf(format, args...))
}
entry.Logf(TraceLevel, format, args...)
}
func (entry *Entry) Debugf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) {
entry.Debug(fmt.Sprintf(format, args...))
}
entry.Logf(DebugLevel, format, args...)
}
func (entry *Entry) Infof(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) {
entry.Info(fmt.Sprintf(format, args...))
}
entry.Logf(InfoLevel, format, args...)
}
func (entry *Entry) Printf(format string, args ...interface{}) {
@ -317,9 +318,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
}
func (entry *Entry) Warnf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) {
entry.Warn(fmt.Sprintf(format, args...))
}
entry.Logf(WarnLevel, format, args...)
}
func (entry *Entry) Warningf(format string, args ...interface{}) {
@ -327,42 +326,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
}
func (entry *Entry) Errorf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) {
entry.Error(fmt.Sprintf(format, args...))
}
entry.Logf(ErrorLevel, format, args...)
}
func (entry *Entry) Fatalf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) {
entry.Fatal(fmt.Sprintf(format, args...))
}
entry.Logf(FatalLevel, format, args...)
entry.Logger.Exit(1)
}
func (entry *Entry) Panicf(format string, args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) {
entry.Panic(fmt.Sprintf(format, args...))
}
entry.Logf(PanicLevel, format, args...)
}
// Entry Println family functions
func (entry *Entry) Traceln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(TraceLevel) {
entry.Trace(entry.sprintlnn(args...))
func (entry *Entry) Logln(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) {
entry.Log(level, entry.sprintlnn(args...))
}
}
func (entry *Entry) Traceln(args ...interface{}) {
entry.Logln(TraceLevel, args...)
}
func (entry *Entry) Debugln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(DebugLevel) {
entry.Debug(entry.sprintlnn(args...))
}
entry.Logln(DebugLevel, args...)
}
func (entry *Entry) Infoln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(InfoLevel) {
entry.Info(entry.sprintlnn(args...))
}
entry.Logln(InfoLevel, args...)
}
func (entry *Entry) Println(args ...interface{}) {
@ -370,9 +363,7 @@ func (entry *Entry) Println(args ...interface{}) {
}
func (entry *Entry) Warnln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(WarnLevel) {
entry.Warn(entry.sprintlnn(args...))
}
entry.Logln(WarnLevel, args...)
}
func (entry *Entry) Warningln(args ...interface{}) {
@ -380,22 +371,16 @@ func (entry *Entry) Warningln(args ...interface{}) {
}
func (entry *Entry) Errorln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(ErrorLevel) {
entry.Error(entry.sprintlnn(args...))
}
entry.Logln(ErrorLevel, args...)
}
func (entry *Entry) Fatalln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(FatalLevel) {
entry.Fatal(entry.sprintlnn(args...))
}
entry.Logln(FatalLevel, args...)
entry.Logger.Exit(1)
}
func (entry *Entry) Panicln(args ...interface{}) {
if entry.Logger.IsLevelEnabled(PanicLevel) {
entry.Panic(entry.sprintlnn(args...))
}
entry.Logln(PanicLevel, args...)
}
// Sprintlnn => Sprint no newline. This is to get the behavior of how

View File

@ -131,28 +131,24 @@ func (logger *Logger) WithTime(t time.Time) *Entry {
return entry.WithTime(t)
}
func (logger *Logger) Tracef(format string, args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) {
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
entry.Tracef(format, args...)
entry.Logf(level, format, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Tracef(format string, args ...interface{}) {
logger.Logf(TraceLevel, format, args...)
}
func (logger *Logger) Debugf(format string, args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) {
entry := logger.newEntry()
entry.Debugf(format, args...)
logger.releaseEntry(entry)
}
logger.Logf(DebugLevel, format, args...)
}
func (logger *Logger) Infof(format string, args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) {
entry := logger.newEntry()
entry.Infof(format, args...)
logger.releaseEntry(entry)
}
logger.Logf(InfoLevel, format, args...)
}
func (logger *Logger) Printf(format string, args ...interface{}) {
@ -162,68 +158,44 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
}
func (logger *Logger) Warnf(format string, args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
logger.Logf(WarnLevel, format, args...)
}
func (logger *Logger) Warningf(format string, args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warnf(format, args...)
logger.releaseEntry(entry)
}
logger.Warnf(format, args...)
}
func (logger *Logger) Errorf(format string, args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) {
entry := logger.newEntry()
entry.Errorf(format, args...)
logger.releaseEntry(entry)
}
logger.Logf(ErrorLevel, format, args...)
}
func (logger *Logger) Fatalf(format string, args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) {
entry := logger.newEntry()
entry.Fatalf(format, args...)
logger.releaseEntry(entry)
}
logger.Logf(FatalLevel, format, args...)
logger.Exit(1)
}
func (logger *Logger) Panicf(format string, args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) {
logger.Logf(PanicLevel, format, args...)
}
func (logger *Logger) Log(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
entry.Panicf(format, args...)
entry.Log(level, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Trace(args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) {
entry := logger.newEntry()
entry.Trace(args...)
logger.releaseEntry(entry)
}
logger.Log(TraceLevel, args...)
}
func (logger *Logger) Debug(args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) {
entry := logger.newEntry()
entry.Debug(args...)
logger.releaseEntry(entry)
}
logger.Log(DebugLevel, args...)
}
func (logger *Logger) Info(args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) {
entry := logger.newEntry()
entry.Info(args...)
logger.releaseEntry(entry)
}
logger.Log(InfoLevel, args...)
}
func (logger *Logger) Print(args ...interface{}) {
@ -233,68 +205,44 @@ func (logger *Logger) Print(args ...interface{}) {
}
func (logger *Logger) Warn(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
logger.Log(WarnLevel, args...)
}
func (logger *Logger) Warning(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warn(args...)
logger.releaseEntry(entry)
}
logger.Warn(args...)
}
func (logger *Logger) Error(args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) {
entry := logger.newEntry()
entry.Error(args...)
logger.releaseEntry(entry)
}
logger.Log(ErrorLevel, args...)
}
func (logger *Logger) Fatal(args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) {
entry := logger.newEntry()
entry.Fatal(args...)
logger.releaseEntry(entry)
}
logger.Log(FatalLevel, args...)
logger.Exit(1)
}
func (logger *Logger) Panic(args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) {
logger.Log(PanicLevel, args...)
}
func (logger *Logger) Logln(level Level, args ...interface{}) {
if logger.IsLevelEnabled(level) {
entry := logger.newEntry()
entry.Panic(args...)
entry.Logln(level, args...)
logger.releaseEntry(entry)
}
}
func (logger *Logger) Traceln(args ...interface{}) {
if logger.IsLevelEnabled(TraceLevel) {
entry := logger.newEntry()
entry.Traceln(args...)
logger.releaseEntry(entry)
}
logger.Logln(TraceLevel, args...)
}
func (logger *Logger) Debugln(args ...interface{}) {
if logger.IsLevelEnabled(DebugLevel) {
entry := logger.newEntry()
entry.Debugln(args...)
logger.releaseEntry(entry)
}
logger.Logln(DebugLevel, args...)
}
func (logger *Logger) Infoln(args ...interface{}) {
if logger.IsLevelEnabled(InfoLevel) {
entry := logger.newEntry()
entry.Infoln(args...)
logger.releaseEntry(entry)
}
logger.Logln(InfoLevel, args...)
}
func (logger *Logger) Println(args ...interface{}) {
@ -304,44 +252,24 @@ func (logger *Logger) Println(args ...interface{}) {
}
func (logger *Logger) Warnln(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
logger.Logln(WarnLevel, args...)
}
func (logger *Logger) Warningln(args ...interface{}) {
if logger.IsLevelEnabled(WarnLevel) {
entry := logger.newEntry()
entry.Warnln(args...)
logger.releaseEntry(entry)
}
logger.Warn(args...)
}
func (logger *Logger) Errorln(args ...interface{}) {
if logger.IsLevelEnabled(ErrorLevel) {
entry := logger.newEntry()
entry.Errorln(args...)
logger.releaseEntry(entry)
}
logger.Logln(ErrorLevel, args...)
}
func (logger *Logger) Fatalln(args ...interface{}) {
if logger.IsLevelEnabled(FatalLevel) {
entry := logger.newEntry()
entry.Fatalln(args...)
logger.releaseEntry(entry)
}
logger.Logln(FatalLevel, args...)
logger.Exit(1)
}
func (logger *Logger) Panicln(args ...interface{}) {
if logger.IsLevelEnabled(PanicLevel) {
entry := logger.newEntry()
entry.Panicln(args...)
logger.releaseEntry(entry)
}
logger.Logln(PanicLevel, args...)
}
func (logger *Logger) Exit(code int) {

View File

@ -14,24 +14,11 @@ type Level uint32
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
func (level Level) String() string {
switch level {
case TraceLevel:
return "trace"
case DebugLevel:
return "debug"
case InfoLevel:
return "info"
case WarnLevel:
return "warning"
case ErrorLevel:
return "error"
case FatalLevel:
return "fatal"
case PanicLevel:
return "panic"
if b, err := level.MarshalText(); err == nil {
return string(b)
} else {
return "unknown"
}
return "unknown"
}
// ParseLevel takes a string level and returns the Logrus log level constant.
@ -69,6 +56,27 @@ func (level *Level) UnmarshalText(text []byte) error {
return nil
}
func (level Level) MarshalText() ([]byte, error) {
switch level {
case TraceLevel:
return []byte("trace"), nil
case DebugLevel:
return []byte("debug"), nil
case InfoLevel:
return []byte("info"), nil
case WarnLevel:
return []byte("warning"), nil
case ErrorLevel:
return []byte("error"), nil
case FatalLevel:
return []byte("fatal"), nil
case PanicLevel:
return []byte("panic"), nil
}
return nil, fmt.Errorf("not a valid lorus level %q", level)
}
// A constant exposing all logging levels
var AllLevels = []Level{
PanicLevel,

View File

@ -0,0 +1,9 @@
// +build !appengine,!js,!windows,aix
package logrus
import "io"
func checkIfTerminal(w io.Writer) bool {
return false
}

View File

@ -1,4 +1,4 @@
// +build !appengine,!js,!windows
// +build !appengine,!js,!windows,!aix
package logrus

View File

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"os"
"runtime"
"sort"
"strings"
"sync"
@ -90,7 +91,7 @@ func (f *TextFormatter) init(entry *Entry) {
}
func (f *TextFormatter) isColored() bool {
isColored := f.ForceColors || f.isTerminal
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
if f.EnvironmentOverrideColors {
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
@ -107,14 +108,17 @@ func (f *TextFormatter) isColored() bool {
// Format renders a single log entry
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller())
keys := make([]string, 0, len(entry.Data))
for k := range entry.Data {
data := make(Fields)
for k, v := range entry.Data {
data[k] = v
}
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
keys := make([]string, 0, len(data))
for k := range data {
keys = append(keys, k)
}
fixedKeys := make([]string, 0, 4+len(entry.Data))
fixedKeys := make([]string, 0, 4+len(data))
if !f.DisableTimestamp {
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
}
@ -160,7 +164,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
timestampFormat = defaultTimestampFormat
}
if f.isColored() {
f.printColored(b, entry, keys, timestampFormat)
f.printColored(b, entry, keys, data, timestampFormat)
} else {
for _, key := range fixedKeys {
var value interface{}
@ -178,7 +182,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
default:
value = entry.Data[key]
value = data[key]
}
f.appendKeyValue(b, key, value)
}
@ -188,7 +192,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
return b.Bytes(), nil
}
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
var levelColor int
switch entry.Level {
case DebugLevel, TraceLevel:
@ -225,7 +229,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
}
for _, k := range keys {
v := entry.Data[k]
v := data[k]
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
f.appendValue(b, v)
}

202
vendor/github.com/ucloud/ucloud-sdk-go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,80 @@
/*
Package http is an implementation of http protocol
*/
package http
import (
"io/ioutil"
"net/http"
"time"
)
// Client is the interface of http client
type Client interface {
Send(*HttpRequest) (*HttpResponse, error)
}
// HttpClient used to send a real request via http to server
type HttpClient struct {
}
// NewHttpClient will create a new HttpClient instance
func NewHttpClient() HttpClient {
return HttpClient{}
}
// Send will send a real http request to remote server
func (c *HttpClient) Send(req *HttpRequest) (*HttpResponse, error) {
// build http.Client with timeout settings
httpClient, err := c.buildHTTPClient(req.GetTimeout())
if err != nil {
return nil, err
}
// convert sdk http request to origin http.Request
httpReq, err := req.buildHTTPRequest()
if err != nil {
return nil, err
}
// TODO: enable tracer via `httptrace` package
resp, err := c.doHTTPRequest(httpClient, httpReq)
if err != nil {
return nil, err
}
return resp, nil
}
func (c *HttpClient) buildHTTPClient(timeout time.Duration) (*http.Client, error) {
httpClient := http.Client{}
if timeout != 0 {
httpClient = http.Client{Timeout: timeout}
}
return &httpClient, nil
}
func (c *HttpClient) doHTTPRequest(client *http.Client, req *http.Request) (*HttpResponse, error) {
// send request
httpResp, err := client.Do(req)
if err != nil {
return nil, err
}
defer httpResp.Body.Close()
// raise status error
if httpResp.StatusCode >= 400 {
return nil, NewStatusError(httpResp.StatusCode, httpResp.Status)
}
// read content
body, err := ioutil.ReadAll(httpResp.Body)
if err != nil {
return nil, err
}
// build response wrapper
resp := NewHttpResponse()
resp.setHttpResponse(httpResp)
resp.SetBody(body)
return resp, nil
}

View File

@ -0,0 +1,21 @@
package http
import (
"time"
)
type mimeType string
const (
mimeFormURLEncoded mimeType = "application/x-www-form-urlencoded"
mimeJSON mimeType = "application/json"
)
// DefaultHeaders defined default http headers
var DefaultHeaders = map[string]string{
"Content-Type": string(mimeFormURLEncoded),
// "X-SDK-VERSION": VERSION,
}
// DefaultTimeout is the default timeout of each request
var DefaultTimeout = 30 * time.Second

View File

@ -0,0 +1,23 @@
package http
import (
"fmt"
)
// StatusError is the error for http status code >= 400
type StatusError struct {
StatusCode int
Message string
}
func (e StatusError) Error() string {
return fmt.Sprintf("http status %v error", e.StatusCode)
}
// NewStatusError will create a new status error
func NewStatusError(code int, message string) StatusError {
return StatusError{
StatusCode: code,
Message: message,
}
}

View File

@ -0,0 +1,223 @@
package http
import (
"bytes"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/pkg/errors"
"github.com/ucloud/ucloud-sdk-go/private/utils"
)
var availableHTTPMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTION", "HEAD", "PATCH"}
// HttpRequest is the internal http request of sdk, don't use it at your code
type HttpRequest struct {
url string
method string
queryMap map[string]string
queryString string
headers map[string]string
requestBody []byte
timeout time.Duration
}
// NewHttpRequest will create a http request
func NewHttpRequest() *HttpRequest {
r := &HttpRequest{
queryMap: make(map[string]string),
headers: make(map[string]string),
timeout: DefaultTimeout,
}
for k, v := range DefaultHeaders {
r.headers[k] = v
}
return r
}
// SetURL will set url into request
func (h *HttpRequest) SetURL(val string) error {
// check url is valid
uri, err := url.ParseRequestURI(val)
if err != nil {
return errors.Errorf("url is invalid, got %s", val)
}
err = h.SetQueryString(uri.RawQuery)
if err != nil {
return err
}
h.url = fmt.Sprintf("%s://%s%s", uri.Scheme, uri.Host, uri.Path)
return nil
}
// GetURL will get request url value
func (h *HttpRequest) GetURL() string {
return h.url
}
// SetMethod will set method of current request
func (h *HttpRequest) SetMethod(val string) error {
err := utils.CheckStringIn(val, availableHTTPMethods)
if err != nil {
return errors.Errorf("method is invalid, %s", err)
}
h.method = strings.ToUpper(val)
return nil
}
// GetMethod will get request url value
func (h *HttpRequest) GetMethod() string {
return h.method
}
// SetQueryString will set query map by query string,
// it also save as query string attribute to keep query ordered.
func (h *HttpRequest) SetQueryString(val string) error {
// check url query is valid
values, err := url.ParseQuery(val)
if err != nil {
return errors.Errorf("url query is invalid, got %s", val)
}
// copy url query into request query map, it will overwrite current query
for k, v := range values {
if len(v) > 0 {
h.SetQuery(k, v[0])
}
}
h.queryString = val
return nil
}
// BuildQueryString will return the query string of this request,
// it will also append key-value of query map after existed query string
func (h *HttpRequest) BuildQueryString() (string, error) {
values := url.Values{}
for k, v := range h.queryMap {
values.Add(k, v)
}
// if query string is not set by user,
// otherwise needn't keep them ordered, encode immediately.
if h.queryString == "" {
return values.Encode(), nil
}
// exclude query that existed in query string pass by user,
// to keep ordered from user definition
existsValues, _ := url.ParseQuery(h.queryString)
for k := range existsValues {
values.Del(k)
}
// append query map after existed query string
qs := h.queryString
if len(values) > 0 {
qs += "&" + values.Encode()
}
return qs, nil
}
// SetQuery will store key-value data into query map
func (h *HttpRequest) SetQuery(k, v string) error {
if h.queryMap == nil {
h.queryMap = make(map[string]string)
}
h.queryMap[k] = v
return nil
}
// GetQuery will get value by key from map
func (h *HttpRequest) GetQuery(k string) string {
if v, ok := h.queryMap[k]; ok {
return v
}
return ""
}
// GetQueryMap will get all of query as a map
func (h *HttpRequest) GetQueryMap() map[string]string {
return h.queryMap
}
// SetTimeout will set timeout of current request
func (h *HttpRequest) SetTimeout(val time.Duration) error {
h.timeout = val
return nil
}
// GetTimeout will get timeout of current request
func (h *HttpRequest) GetTimeout() time.Duration {
return h.timeout
}
// SetHeader will set http header of current request
func (h *HttpRequest) SetHeader(k, v string) error {
if h.headers == nil {
h.headers = make(map[string]string)
}
h.headers[k] = v
return nil
}
// GetHeaderMap wiil get all of header as a map
func (h *HttpRequest) GetHeaderMap() map[string]string {
return h.headers
}
// SetRequestBody will set http body of current request
func (h *HttpRequest) SetRequestBody(val []byte) error {
h.requestBody = val
return nil
}
// GetRequestBody will get origin http request ("net/http")
func (h *HttpRequest) GetRequestBody() []byte {
return h.requestBody
}
func (h *HttpRequest) String() string {
if qs, err := h.BuildQueryString(); err == nil {
return fmt.Sprintf("%s?%s", h.GetURL(), qs)
}
return h.GetURL()
}
func (h *HttpRequest) getContentType() string {
if v, ok := h.headers["Content-Type"]; ok {
return v
}
return string(mimeFormURLEncoded)
}
func (h *HttpRequest) buildHTTPRequest() (*http.Request, error) {
qs, err := h.BuildQueryString()
if err != nil {
return nil, errors.Errorf("cannot build query string, %s", err)
}
var httpReq *http.Request
if h.getContentType() == string(mimeFormURLEncoded) && len(h.GetRequestBody()) == 0 {
httpReq, err = http.NewRequest(h.GetMethod(), h.GetURL(), strings.NewReader(qs))
} else {
httpReq, err = http.NewRequest(h.GetMethod(), h.String(), bytes.NewReader(h.GetRequestBody()))
}
if err != nil {
return nil, errors.Errorf("cannot build request, %s", err)
}
for k, v := range utils.MergeMap(DefaultHeaders, h.GetHeaderMap()) {
httpReq.Header.Set(k, v)
}
return httpReq, nil
}

View File

@ -0,0 +1,52 @@
package http
import (
"net/http"
)
// HttpResponse is a simple wrapper of "net/http" response
type HttpResponse struct {
body []byte
statusCode int
originHttpResponse *http.Response // origin "net/http" response
}
// NewHttpResponse will create a new response of http request
func NewHttpResponse() *HttpResponse {
return &HttpResponse{}
}
// GetBody will get body from from sdk http request
func (h *HttpResponse) GetBody() []byte {
return h.body
}
func (h *HttpResponse) GetHeaders() http.Header {
if h.originHttpResponse == nil {
return http.Header{}
}
return h.originHttpResponse.Header
}
// SetBody will set body into http response
// it usually used for restore the body already read from an stream
// it will also cause extra memory usage
func (h *HttpResponse) SetBody(body []byte) error {
h.body = body
return nil
}
// GetStatusCode will return status code of origin http response
func (h *HttpResponse) GetStatusCode() int {
return h.statusCode
}
// SetStatusCode will return status code of origin http response
func (h *HttpResponse) SetStatusCode(code int) {
h.statusCode = code
}
func (h *HttpResponse) setHttpResponse(resp *http.Response) {
h.statusCode = resp.StatusCode
h.originHttpResponse = resp
}

View File

@ -0,0 +1,73 @@
package utils
import (
"reflect"
"strconv"
"strings"
"github.com/pkg/errors"
)
// ValueAtPath will get struct attribute value by recursive
func ValueAtPath(v interface{}, path string) (interface{}, error) {
components := strings.Split(path, ".")
rv := reflect.ValueOf(v)
for rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return nil, errors.Errorf("object %#v is nil", v)
}
rv = rv.Elem()
}
if rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {
i, err := strconv.Atoi(components[0])
if err != nil {
return nil, errors.Errorf("path %s is invalid at index of array", path)
}
length := rv.Len()
if i >= length {
return nil, errors.Errorf("path %s is invalid, array has length %v, but got %v", path, length, i)
}
itemV := rv.Index(i)
if !itemV.IsValid() {
return nil, errors.Errorf("path %s is invalid for map", path)
}
if len(components) > 1 {
return ValueAtPath(itemV.Interface(), strings.Join(components[1:], "."))
}
return itemV.Interface(), nil
}
if rv.Kind() == reflect.Map && !rv.IsNil() {
itemV := rv.MapIndex(reflect.ValueOf(components[0]))
if !itemV.IsValid() {
return nil, errors.Errorf("path %s is invalid for map", path)
}
if len(components) > 1 {
return ValueAtPath(itemV.Interface(), strings.Join(components[1:], "."))
}
return itemV.Interface(), nil
}
if rv.Kind() == reflect.Struct {
itemV := rv.FieldByName(components[0])
if !itemV.IsValid() {
return nil, errors.Errorf("path %s is invalid for struct", path)
}
if len(components) > 1 {
return ValueAtPath(itemV.Interface(), strings.Join(components[1:], "."))
}
return itemV.Interface(), nil
}
return nil, errors.Errorf("object %#v is invalid, need map or struct", v)
}

View File

@ -0,0 +1,43 @@
package utils
import (
"fmt"
"strings"
)
// MergeMap will merge two map and return a new map
func MergeMap(args ...map[string]string) map[string]string {
m := map[string]string{}
for _, kv := range args {
for k, v := range kv {
m[k] = v
}
}
return m
}
// SetMapIfNotExists will set a key-value of the map if the key is not exists
func SetMapIfNotExists(m map[string]string, k string, v string) {
if _, ok := m[k]; !ok && v != "" {
m[k] = v
}
}
// IsStringIn will return if the value is contains by an array
func IsStringIn(val string, availables []string) bool {
for _, choice := range availables {
if val == choice {
return true
}
}
return false
}
// CheckStringIn will check if the value is contains by an array
func CheckStringIn(val string, availables []string) error {
if IsStringIn(val, availables) {
return nil
}
return fmt.Errorf("got %s, should be one of %s", val, strings.Join(availables, ","))
}

View File

@ -0,0 +1,4 @@
/*
Package utils is the utilities to process internal data of sdk
*/
package utils

View File

@ -0,0 +1,44 @@
package utils
import (
"regexp"
)
// Patch is the patch object to provider a converter function
type Patch interface {
Patch([]byte) []byte
}
// RegexpPatcher a patch object to provider a converter function from regular expression
type RegexpPatcher struct {
pattern *regexp.Regexp
replacement string
}
// NewRegexpPatcher will return a patch object to provider a converter function from regular expression
func NewRegexpPatcher(regex string, repl string) *RegexpPatcher {
return &RegexpPatcher{
pattern: regexp.MustCompile(regex),
replacement: repl,
}
}
// Patch will convert a bytes to another bytes with patch rules
func (p *RegexpPatcher) Patch(body []byte) []byte {
// TODO: ensure why the pattern will be disabled when there are multiple goroutines for bytes replacement
return []byte(p.PatchString(string(body)))
}
// PatchString will convert a string to another string with patch rules
func (p *RegexpPatcher) PatchString(body string) string {
return p.pattern.ReplaceAllString(body, p.replacement)
}
// RetCodePatcher will convert `RetCode` as integer
var RetCodePatcher = NewRegexpPatcher(`"RetCode":\s?"(\d+)"`, `"RetCode": $1`)
// PortPatcher will convert `Port` as integer
var PortPatcher = NewRegexpPatcher(`"Port":\s?"(\d+)"`, `"Port": $1`)
// FrequencePatcher will convert `Frequence` as float64
var FrequencePatcher = NewRegexpPatcher(`"Frequence":\s?"([\d.]+)"`, `"Frequence": $1`)

View File

@ -0,0 +1,53 @@
package utils
import (
"errors"
"time"
"github.com/ucloud/ucloud-sdk-go/ucloud/log"
)
// Waiter to wait sth until it completed.
type Waiter interface {
WaitForCompletion() error
Cancel() error
}
// FuncWaiter used for waiting any condition function.
type FuncWaiter struct {
Interval time.Duration
MaxAttempts int
Checker func() (bool, error)
IgnoreError bool
cancel chan struct{}
}
// WaitForCompletion will wait until the state of consdition is available.
// It will call the condition function to ensure state with interval.
func (w *FuncWaiter) WaitForCompletion() error {
for i := 0; ; i++ {
log.Infof("Waiting for completion ... attempted %v times, %v total", i, w.MaxAttempts)
if i >= w.MaxAttempts {
return errors.New("maximum attempts are reached")
}
if ok, err := w.Checker(); ok || (!w.IgnoreError && err != nil) {
return err
}
select {
case <-time.After(w.Interval):
continue
case <-w.cancel:
break
}
}
}
// Cancel will stop all of WaitForCompletion function call.
func (w *FuncWaiter) Cancel() error {
w.cancel <- struct{}{}
return nil
}

View File

@ -0,0 +1,19 @@
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
)
// UAccountClient is the client of UAccount
type UAccountClient struct {
*ucloud.Client
}
// NewClient will return a instance of UAccountClient
func NewClient(config *ucloud.Config, credential *auth.Credential) *UAccountClient {
client := ucloud.NewClient(config, credential)
return &UAccountClient{
client,
}
}

View File

@ -0,0 +1,53 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount CreateProject
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// CreateProjectRequest is request schema for CreateProject action
type CreateProjectRequest struct {
request.CommonBase
// 项目名称
ProjectName *string `required:"true"`
// 项目父节点Id, 不填写创建顶层项目
ParentId *string `required:"false"`
}
// CreateProjectResponse is response schema for CreateProject action
type CreateProjectResponse struct {
response.CommonBase
// 所创建项目的Id
ProjectId string
}
// NewCreateProjectRequest will create request of CreateProject action.
func (c *UAccountClient) NewCreateProjectRequest() *CreateProjectRequest {
req := &CreateProjectRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// CreateProject - 创建项目
func (c *UAccountClient) CreateProject(req *CreateProjectRequest) (*CreateProjectResponse, error) {
var err error
var res CreateProjectResponse
err = c.Client.InvokeAction("CreateProject", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,11 @@
/*
Package uaccount include resources of ucloud uaccount product
See also
- API: https://docs.ucloud.cn/api/uaccount-api/index
- Product: https://www.ucloud.cn/site/product/uaccount.html
for detail.
*/
package uaccount

View File

@ -0,0 +1,53 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount GetProjectList
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetProjectListRequest is request schema for GetProjectList action
type GetProjectListRequest struct {
request.CommonBase
// 是否是财务账号(Yes: 是, No: 否)
IsFinance *string `required:"false"`
}
// GetProjectListResponse is response schema for GetProjectList action
type GetProjectListResponse struct {
response.CommonBase
// 项目总数
ProjectCount int
// JSON格式的项目列表实例
ProjectSet []ProjectListInfo
}
// NewGetProjectListRequest will create request of GetProjectList action.
func (c *UAccountClient) NewGetProjectListRequest() *GetProjectListRequest {
req := &GetProjectListRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetProjectList - 获取项目列表
func (c *UAccountClient) GetProjectList(req *GetProjectListRequest) (*GetProjectListResponse, error) {
var err error
var res GetProjectListResponse
err = c.Client.InvokeAction("GetProjectList", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,47 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount GetRegion
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetRegionRequest is request schema for GetRegion action
type GetRegionRequest struct {
request.CommonBase
}
// GetRegionResponse is response schema for GetRegion action
type GetRegionResponse struct {
response.CommonBase
// 各数据中心信息
Regions []RegionInfo
}
// NewGetRegionRequest will create request of GetRegion action.
func (c *UAccountClient) NewGetRegionRequest() *GetRegionRequest {
req := &GetRegionRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetRegion - 获取用户在各数据中心的权限等信息
func (c *UAccountClient) GetRegion(req *GetRegionRequest) (*GetRegionResponse, error) {
var err error
var res GetRegionResponse
err = c.Client.InvokeAction("GetRegion", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,47 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount GetUserInfo
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetUserInfoRequest is request schema for GetUserInfo action
type GetUserInfoRequest struct {
request.CommonBase
}
// GetUserInfoResponse is response schema for GetUserInfo action
type GetUserInfoResponse struct {
response.CommonBase
// 用户信息返回数组
DataSet []UserInfo
}
// NewGetUserInfoRequest will create request of GetUserInfo action.
func (c *UAccountClient) NewGetUserInfoRequest() *GetUserInfoRequest {
req := &GetUserInfoRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetUserInfo - 获取用户信息
func (c *UAccountClient) GetUserInfo(req *GetUserInfoRequest) (*GetUserInfoResponse, error) {
var err error
var res GetUserInfoResponse
err = c.Client.InvokeAction("GetUserInfo", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,50 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount ModifyProject
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ModifyProjectRequest is request schema for ModifyProject action
type ModifyProjectRequest struct {
request.CommonBase
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"true"`
// 新的项目名称
ProjectName *string `required:"true"`
}
// ModifyProjectResponse is response schema for ModifyProject action
type ModifyProjectResponse struct {
response.CommonBase
}
// NewModifyProjectRequest will create request of ModifyProject action.
func (c *UAccountClient) NewModifyProjectRequest() *ModifyProjectRequest {
req := &ModifyProjectRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ModifyProject - 修改项目
func (c *UAccountClient) ModifyProject(req *ModifyProjectRequest) (*ModifyProjectResponse, error) {
var err error
var res ModifyProjectResponse
err = c.Client.InvokeAction("ModifyProject", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,48 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UAccount TerminateProject
package uaccount
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// TerminateProjectRequest is request schema for TerminateProject action
type TerminateProjectRequest struct {
request.CommonBase
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
}
// TerminateProjectResponse is response schema for TerminateProject action
type TerminateProjectResponse struct {
response.CommonBase
}
// NewTerminateProjectRequest will create request of TerminateProject action.
func (c *UAccountClient) NewTerminateProjectRequest() *TerminateProjectRequest {
req := &TerminateProjectRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// TerminateProject - 删除项目
func (c *UAccountClient) TerminateProject(req *TerminateProjectRequest) (*TerminateProjectResponse, error) {
var err error
var res TerminateProjectResponse
err = c.Client.InvokeAction("TerminateProject", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,34 @@
package uaccount
/*
ProjectListInfo - 项目信息
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type ProjectListInfo struct {
// 项目ID
ProjectId string
// 项目名称
ProjectName string
// 父项目ID
ParentId string
// 父项目名称
ParentName string
// 创建时间(Unix时间戳)
CreateTime int
// 是否为默认项目
IsDefault bool
// 项目下资源数量
ResourceCount int
// 项目下成员数量
MemberCount int
}

View File

@ -0,0 +1,28 @@
package uaccount
/*
RegionInfo - 数据中心信息
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type RegionInfo struct {
// 数据中心ID
RegionId int
// 数据中心名称
RegionName string
// 是否用户当前默认数据中心
IsDefault bool
// 用户在此数据中心的权限位
BitMaps string
// 地域名字如cn-bj
Region string
// 可用区名字如cn-bj-01
Zone string
}

View File

@ -0,0 +1,58 @@
package uaccount
/*
UserInfo - 用户信息
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UserInfo struct {
// 用户Id
UserId int
// 用户邮箱
UserEmail string
// 用户手机
UserPhone string
// 国际号码前缀
PhonePrefix string
// 会员类型
UserType int
// 称呼
UserName string
// 公司名称
CompanyName string
// 所属行业
IndustryType int
// 省份
Province string
// 城市
City string
// 公司地址
UserAddress string
// 是否超级管理员 0:否 1:是
Admin int
// 是否子帐户(大于100为子帐户)
UserVersion int
// 是否有财务权限 0:否 1:是
Finance int
// 管理员
Administrator string
// 实名认证状态
AuthState string
}

View File

@ -0,0 +1,19 @@
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
)
// UHostClient is the client of UHost
type UHostClient struct {
*ucloud.Client
}
// NewClient will return a instance of UHostClient
func NewClient(config *ucloud.Config, credential *auth.Credential) *UHostClient {
client := ucloud.NewClient(config, credential)
return &UHostClient{
client,
}
}

View File

@ -0,0 +1,71 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost CopyCustomImage
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// CopyCustomImageRequest is request schema for CopyCustomImage action
type CopyCustomImageRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 源镜像Id, 参见 DescribeImage
SourceImageId *string `required:"true"`
// 目标项目Id, 参见 GetProjectList
TargetProjectId *string `required:"true"`
// 目标地域,不跨地域不用填
TargetRegion *string `required:"false"`
// 目标镜像名称
TargetImageName *string `required:"false"`
// 目标镜像描述
TargetImageDescription *string `required:"false"`
}
// CopyCustomImageResponse is response schema for CopyCustomImage action
type CopyCustomImageResponse struct {
response.CommonBase
// 目标镜像Id
TargetImageId string
}
// NewCopyCustomImageRequest will create request of CopyCustomImage action.
func (c *UHostClient) NewCopyCustomImageRequest() *CopyCustomImageRequest {
req := &CopyCustomImageRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// CopyCustomImage - 复制自制镜像
func (c *UHostClient) CopyCustomImage(req *CopyCustomImageRequest) (*CopyCustomImageResponse, error) {
var err error
var res CopyCustomImageResponse
err = c.Client.InvokeAction("CopyCustomImage", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,65 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost CreateCustomImage
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// CreateCustomImageRequest is request schema for CreateCustomImage action
type CreateCustomImageRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 镜像名称
ImageName *string `required:"true"`
// 镜像描述
ImageDescription *string `required:"false"`
}
// CreateCustomImageResponse is response schema for CreateCustomImage action
type CreateCustomImageResponse struct {
response.CommonBase
// 镜像Id
ImageId string
}
// NewCreateCustomImageRequest will create request of CreateCustomImage action.
func (c *UHostClient) NewCreateCustomImageRequest() *CreateCustomImageRequest {
req := &CreateCustomImageRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// CreateCustomImage - 从指定UHost实例生成自定义镜像。
func (c *UHostClient) CreateCustomImage(req *CreateCustomImageRequest) (*CreateCustomImageResponse, error) {
var err error
var res CreateCustomImageResponse
err = c.Client.InvokeAction("CreateCustomImage", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,202 @@
package uhost
import (
"encoding/base64"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// CreateUHostInstanceRequest is request schema for CreateUHostInstance action
type CreateUHostInstanceRequest struct {
request.CommonBase
// 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"true"`
// 镜像ID。 请通过 [DescribeImage](describe_image.html)获取
ImageId *string `required:"true"`
// UHost密码LoginMode为Password时此项必须密码需使用base64进行编码
Password *string `required:"true"`
// 磁盘列表
Disks []UHostDisk `required:"true"`
// UHost实例名称。默认UHost。请遵照[[api:uhost-api:specification|字段规范]]设定实例名称。
Name *string `required:"false"`
// 业务组。默认DefaultDefault即为未分组
Tag *string `required:"false"`
// 计费模式。枚举值为: Year按年付费 Month按月付费 Dynamic按小时付费需开启权限。默认为月付
ChargeType *string `required:"false"`
// 购买时长。默认: 1。按小时购买(Dynamic)时无需此参数。 月付时此参数传0代表了购买至月末。
Quantity *int `required:"false"`
// 云主机机型。枚举值N1系列1标准型N2系列2标准型I1: 系列1高IO型I2系列2高IO型 D1: 系列1大数据机型G1: 系列1GPU型型号为K80G2系列2GPU型型号为P40G3系列2GPU型型号为V100北京A、北京C、上海二A、香港A可用区默认N1其他机房默认N2。参考[[api:uhost-api:uhost_type|云主机机型说明]]。
UHostType *string `required:"false"`
// 虚拟CPU核数。可选参数1-32可选范围与UHostType相关。默认值: 4
CPU *int `required:"false"`
// 内存大小。单位MB。范围 [1024, 262144]取值为1024的倍数可选范围与UHostType相关。默认值8192
Memory *int `required:"false"`
// GPU卡核心数。仅GPU机型支持此字段系列1可选1,2系列2可选1,2,3,4。GPU可选数量与CPU有关联详情请参考控制台。
GPU *int `required:"false"`
// 主机登陆模式。密码(默认选项): Passwordkey: KeyPair此项暂不支持
LoginMode *string `required:"false"`
// 【暂不支持】Keypair公钥LoginMode为KeyPair时此项必须
KeyPair *string `required:"false"`
// 【待废弃不建议调用】磁盘类型同时设定系统盘和数据盘的磁盘类型。枚举值为LocalDisk本地磁盘; UDisk云硬盘默认为LocalDisk。仅部分可用区支持云硬盘方式的主机存储方式具体请查询控制台。
StorageType *string `required:"false"`
// 【待废弃,不建议调用】系统盘大小。 单位GB 范围[20,100] 步长10
BootDiskSpace *int `required:"false"`
// 【待废弃,不建议调用】数据盘大小。 单位GB 范围[0,8000] 步长10 默认值20云盘支持0-8000本地普通盘支持0-2000本地SSD盘包括所有GPU机型支持100-1000
DiskSpace *int `required:"false"`
// 网络增强。目前仅Normal不开启 和Super开启可用。默认Normal。 不同机房的网络增强支持情况不同。详情请参考控制台。
NetCapability *string `required:"false"`
// 是否开启方舟特性。Yes为开启方舟No为关闭方舟。目前仅选择普通本地盘+普通本地盘 或 SSD云盘+普通云盘的组合支持开启方舟。
TimemachineFeature *string `required:"false"`
// 是否开启热升级特性。True为开启False为未开启默认False。仅系列1云主机需要使用此字段系列2云主机根据镜像是否支持云主机。
HotplugFeature *bool `required:"false"`
// 网络IDVPC2.0情况下无需填写。VPC1.0情况下,若不填写,代表选择基础网络; 若填写代表选择子网。参见DescribeSubnet。
NetworkId *string `required:"false"`
// VPC ID。VPC2.0下需要填写此字段。
VPCId *string `required:"false"`
// 子网ID。VPC2.0下需要填写此字段。
SubnetId *string `required:"false"`
// 【数组】创建云主机时指定内网IP。当前只支持一个内网IP。调用方式举例PrivateIp.0=x.x.x.x。
PrivateIp []string `required:"false"`
// 创建云主机时指定Mac。调用方式举例PrivateMac="xx:xx:xx:xx:xx:xx"。
PrivateMac *string `required:"false"`
// 防火墙Id默认Web推荐防火墙。如何查询SecurityGroupId请参见 [DescribeSecurityGroup](../unet-api/describe_security_group.html)
SecurityGroupId *string `required:"false"`
// 【暂不支持】cloudinit方式下用户初始化脚本
UserDataScript *string `required:"false"`
// 【已废弃】宿主机类型N2N1
HostType *string `required:"false"`
// 【暂不支持】是否安装UGA。'yes': 安装;其他或者不填:不安装。
InstallAgent *string `required:"false"`
// 【内部参数】资源类型
ResourceType *int `required:"false"`
// 代金券ID。请通过DescribeCoupon接口查询或登录用户中心查看
CouponId *string `required:"false"`
// 云主机类型,枚举值["N", "C", "G", "O"]
MachineType *string `required:"false"`
// 最低cpu平台枚举值["Intel/Auto", "Intel/LvyBridge", "Intel/Haswell", "Intel/Broadwell", "Intel/Skylake", "Intel/Cascadelake"(只有O型云主机可选)]
MinimalCpuPlatform *string `required:"false"`
// NetworkInterface
NetworkInterface []CreateUHostInstanceParamNetworkInterface
}
/*
CreateUHostInstanceParamNetworkInterface is request schema for complex param
*/
type CreateUHostInstanceParamNetworkInterface struct {
// EIP
EIP *CreateUHostInstanceParamNetworkInterfaceEIP
}
/*
CreateUHostInstanceParamNetworkInterfaceEIP is request schema for complex param
*/
type CreateUHostInstanceParamNetworkInterfaceEIP struct {
// 弹性IP的计费模式. 枚举值: "Traffic", 流量计费; "Bandwidth", 带宽计费; "ShareBandwidth",共享带宽模式. "Free":免费带宽模式.默认为 "Bandwidth".
PayMode *string
// 当前EIP代金券id。请通过DescribeCoupon接口查询或登录用户中心查看
CouponId *string
// 【如果绑定EIP这个参数必填】弹性IP的外网带宽, 单位为Mbps. 共享带宽模式必须指定0M带宽, 非共享带宽模式必须指定非0Mbps带宽. 各地域非共享带宽的带宽范围如下: 流量计费[1-300],带宽计费[1-800]
Bandwidth *int
// 绑定的共享带宽Id仅当PayMode为ShareBandwidth时有效
ShareBandwidthId *string
// GlobalSSH
GlobalSSH *CreateUHostInstanceParamNetworkInterfaceEIPGlobalSSH
// 【如果绑定EIP这个参数必填】弹性IP的线路如下: 国际: International BGP: Bgp 各地域允许的线路参数如下: cn-sh1: Bgp cn-sh2: Bgp cn-gd: Bgp cn-bj1: Bgp cn-bj2: Bgp hk: International us-ca: International th-bkk: International kr-seoul:International us-ws:International ge-fra:International sg:International tw-kh:International.其他海外线路均为 International
OperatorName *string
}
/*
CreateUHostInstanceParamNetworkInterfaceEIPGlobalSSH is request schema for complex param
*/
type CreateUHostInstanceParamNetworkInterfaceEIPGlobalSSH struct {
// 填写支持SSH访问IP的地区名称如“洛杉矶”“新加坡”“香港”“东京”“华盛顿”“法兰克福”。Area和AreaCode两者必填一个
Area *string
// AreaCode, 区域航空港国际通用代码。Area和AreaCode两者必填一个
AreaCode *string
// SSH端口1-65535且不能使用80443端口
Port *int
}
// CreateUHostInstanceResponse is response schema for CreateUHostInstance action
type CreateUHostInstanceResponse struct {
response.CommonBase
// UHost实例Id集合
UHostIds []string
// IP信息
IPs []string
}
// NewCreateUHostInstanceRequest will create request of CreateUHostInstance action.
func (c *UHostClient) NewCreateUHostInstanceRequest() *CreateUHostInstanceRequest {
req := &CreateUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// CreateUHostInstance - 指定数据中心根据资源使用量创建指定数量的UHost实例。
func (c *UHostClient) CreateUHostInstance(req *CreateUHostInstanceRequest) (*CreateUHostInstanceResponse, error) {
var err error
var res CreateUHostInstanceResponse
var reqImmutable = *req
reqImmutable.Password = ucloud.String(base64.StdEncoding.EncodeToString([]byte(ucloud.StringValue(req.Password))))
err = c.Client.InvokeAction("CreateUHostInstance", &reqImmutable, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,77 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost DescribeImage
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// DescribeImageRequest is request schema for DescribeImage action
type DescribeImageRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 镜像类型。标准镜像Base镜像市场Business 自定义镜像Custom默认返回所有类型
ImageType *string `required:"false"`
// 操作系统类型Linux Windows 默认返回所有类型
OsType *string `required:"false"`
// 镜像Id
ImageId *string `required:"false"`
// 列表起始位置偏移量默认为0
Offset *int `required:"false"`
// 返回数据长度默认为20
Limit *int `required:"false"`
// 是否返回价格1返回0不返回默认不返回
PriceSet *int `required:"false"`
}
// DescribeImageResponse is response schema for DescribeImage action
type DescribeImageResponse struct {
response.CommonBase
// 满足条件的镜像总数
TotalCount int
// 镜像列表详见 UHostImageSet
ImageSet []UHostImageSet
}
// NewDescribeImageRequest will create request of DescribeImage action.
func (c *UHostClient) NewDescribeImageRequest() *DescribeImageRequest {
req := &DescribeImageRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// DescribeImage - 获取指定数据中心镜像列表用户可通过指定操作系统类型镜像Id进行过滤。
func (c *UHostClient) DescribeImage(req *DescribeImageRequest) (*DescribeImageResponse, error) {
var err error
var res DescribeImageResponse
err = c.Client.InvokeAction("DescribeImage", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,83 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost DescribeUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// DescribeUHostInstanceRequest is request schema for DescribeUHostInstance action
type DescribeUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 【数组】UHost主机的资源ID例如UHostIds.0代表希望获取信息 的主机1UHostIds.1代表主机2。 如果不传入则返回当前Region 所有符合条件的UHost实例。
UHostIds []string `required:"false"`
// 要查询的业务组名称
Tag *string `required:"false"`
// 1普通云主机2抢占型云主机如不传此参数默认全部获取
LifeCycle *int `required:"false"`
// 列表起始位置偏移量默认为0
Offset *int `required:"false"`
// 返回数据长度默认为20最大100
Limit *int `required:"false"`
// 硬件隔离组id
IsolationGroup *string `required:"false"`
// 云主机ip对应的vpc id
VPCId *string `required:"false"`
// 云主机ip对应的子网id
SubnetId *string `required:"false"`
}
// DescribeUHostInstanceResponse is response schema for DescribeUHostInstance action
type DescribeUHostInstanceResponse struct {
response.CommonBase
// UHostInstance总数
TotalCount int
// 云主机实例列表,每项参数可见下面 UHostInstanceSet
UHostSet []UHostInstanceSet
}
// NewDescribeUHostInstanceRequest will create request of DescribeUHostInstance action.
func (c *UHostClient) NewDescribeUHostInstanceRequest() *DescribeUHostInstanceRequest {
req := &DescribeUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// DescribeUHostInstance - 获取主机或主机列表信息并可根据数据中心主机ID等参数进行过滤。
func (c *UHostClient) DescribeUHostInstance(req *DescribeUHostInstanceRequest) (*DescribeUHostInstanceResponse, error) {
var err error
var res DescribeUHostInstanceResponse
err = c.Client.InvokeAction("DescribeUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,60 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost DescribeUHostTags
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// DescribeUHostTagsRequest is request schema for DescribeUHostTags action
type DescribeUHostTagsRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
}
// DescribeUHostTagsResponse is response schema for DescribeUHostTags action
type DescribeUHostTagsResponse struct {
response.CommonBase
// 已有主机的业务组总个数
TotalCount int
// 业务组集合见 UHostTagSet
TagSet []UHostTagSet
}
// NewDescribeUHostTagsRequest will create request of DescribeUHostTags action.
func (c *UHostClient) NewDescribeUHostTagsRequest() *DescribeUHostTagsRequest {
req := &DescribeUHostTagsRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// DescribeUHostTags - 获取指定数据中心的业务组列表。
func (c *UHostClient) DescribeUHostTags(req *DescribeUHostTagsRequest) (*DescribeUHostTagsResponse, error) {
var err error
var res DescribeUHostTagsResponse
err = c.Client.InvokeAction("DescribeUHostTags", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,11 @@
/*
Package uhost include resources of ucloud host product
See also
- API: https://docs.ucloud.cn/api/uhost-api/index
- Product: https://www.ucloud.cn/site/product/uhost.html
for detail.
*/
package uhost

View File

@ -0,0 +1,20 @@
package uhost
// State is the state of UHost instance
type State string
// Enum values for State
const (
StateInitializing State = "Initializing"
StateStarting State = "Starting"
StateRunning State = "Running"
StateStopping State = "Stopping"
StateStopped State = "Stopped"
StateInstallFail State = "InstallFail"
StateRebooting State = "Rebooting"
)
// MarshalValue will marshal state value to string
func (enum State) MarshalValue() (string, error) {
return string(enum), nil
}

View File

@ -0,0 +1,94 @@
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetUHostInstancePriceRequest is request schema for GetUHostInstancePrice action
type GetUHostInstancePriceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 镜像Id可通过 [DescribeImage](describe_image.html) 获取镜像ID
ImageId *string `required:"true"`
// 虚拟CPU核数。可选参数1-32可选范围与UHostType相关。默认值: 4
CPU *int `required:"true"`
// 内存大小。单位MB。范围 [1024, 262144]取值为1024的倍数可选范围与UHostType相关。默认值8192
Memory *int `required:"true"`
// 【未启用】购买台数,范围[1,5]
Count *int `required:"false"`
// 磁盘列表
Disks []UHostDisk
// GPU卡核心数。仅GPU机型支持此字段可选范围与UHostType相关
GPU *int `required:"false"`
// 计费模式。枚举值为: \\ > Year按年付费 \\ > Month按月付费\\ > Dynamic按小时付费 \\ 默认为月付。
ChargeType *string `required:"false"`
// 【待废弃】磁盘类型,同时设定系统盘和数据盘, 枚举值为LocalDisk本地磁盘; UDisk云硬盘; 默认为LocalDisk 仅部分可用区支持云硬盘方式的主机存储方式,具体请查询控制台。
StorageType *string `required:"false"`
// 【待废弃】数据盘大小,单位: GB范围[0,1000],步长: 10默认值: 0
DiskSpace *int `required:"false"`
// 网络增强。枚举值:\\ > Normal不开启 \\ > Super开启 \\ 默认值未为Normal。
NetCapability *string `required:"false"`
// 【待废弃】方舟机型。NoYes。默认是No。
TimemachineFeature *string `required:"false"`
// 主机类型 Normal: 标准机型 SSDSSD机型 BigData:大数据 GPU:GPU型G1(原GPU型) GPU_G2:GPU型G2 GPU_G3:GPU型G3 不同机房的主机类型支持情况不同。详情请参考控制台。
UHostType *string `required:"false"`
// 【未支持】1普通云主机2抢占性云主机默认普通
LifeCycle *int `required:"false"`
// 购买时长。默认: 1。按小时购买(Dynamic)时无需此参数。 月付时此参数传0代表了购买至月末。
Quantity *int `required:"false"`
}
// GetUHostInstancePriceResponse is response schema for GetUHostInstancePrice action
type GetUHostInstancePriceResponse struct {
response.CommonBase
// 价格列表 UHostPriceSet
PriceSet []UHostPriceSet
}
// NewGetUHostInstancePriceRequest will create request of GetUHostInstancePrice action.
func (c *UHostClient) NewGetUHostInstancePriceRequest() *GetUHostInstancePriceRequest {
req := &GetUHostInstancePriceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetUHostInstancePrice - 根据UHost实例配置获取UHost实例的价格。
func (c *UHostClient) GetUHostInstancePrice(req *GetUHostInstancePriceRequest) (*GetUHostInstancePriceResponse, error) {
var err error
var res GetUHostInstancePriceResponse
err = c.Client.InvokeAction("GetUHostInstancePrice", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,68 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost GetUHostInstanceVncInfo
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetUHostInstanceVncInfoRequest is request schema for GetUHostInstanceVncInfo action
type GetUHostInstanceVncInfoRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](./describe_uhost_instance.html)
UHostId *string `required:"true"`
}
// GetUHostInstanceVncInfoResponse is response schema for GetUHostInstanceVncInfo action
type GetUHostInstanceVncInfoResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
// Vnc登录IP
VncIP string
// Vnc登录端口
VncPort int
// Vnc 登录密码
VncPassword string
}
// NewGetUHostInstanceVncInfoRequest will create request of GetUHostInstanceVncInfo action.
func (c *UHostClient) NewGetUHostInstanceVncInfoRequest() *GetUHostInstanceVncInfoRequest {
req := &GetUHostInstanceVncInfoRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetUHostInstanceVncInfo - 获取指定UHost实例的管理VNC配置详细信息。
func (c *UHostClient) GetUHostInstanceVncInfo(req *GetUHostInstanceVncInfoRequest) (*GetUHostInstanceVncInfoResponse, error) {
var err error
var res GetUHostInstanceVncInfoResponse
err = c.Client.InvokeAction("GetUHostInstanceVncInfo", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,80 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost GetUHostUpgradePrice
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// GetUHostUpgradePriceRequest is request schema for GetUHostUpgradePrice action
type GetUHostUpgradePriceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID。 参见 [DescribeUHostInstance](describe_uhost_instance.html)。
UHostId *string `required:"true"`
// 虚拟CPU核数。可选参数1-32可选范围与UHostType相关。默认值为当前实例的CPU核数。
CPU *int `required:"false"`
// 内存大小。单位MB。范围 [1024, 262144]取值为1024的倍数可选范围与UHostType相关。默认值为当前实例的内存大小。
Memory *int `required:"false"`
// 【待废弃】数据盘大小,单位: GB范围[0,1000],步长: 10 默认值是该主机当前数据盘大小。
DiskSpace *int `required:"false"`
// 【待废弃】系统大小,单位: GB范围[20,100],步长: 10。
BootDiskSpace *int `required:"false"`
// 方舟机型。NoYes。默认是No。
TimemachineFeature *string `required:"false"`
// 网卡升降级1表示升级2表示降级0表示不变
NetCapValue *int `required:"false"`
// 【待废弃】主机系列目前支持N1,N2
HostType *string `required:"false"`
}
// GetUHostUpgradePriceResponse is response schema for GetUHostUpgradePrice action
type GetUHostUpgradePriceResponse struct {
response.CommonBase
// 规格调整差价。精确到小数点后2位。
Price float64
}
// NewGetUHostUpgradePriceRequest will create request of GetUHostUpgradePrice action.
func (c *UHostClient) NewGetUHostUpgradePriceRequest() *GetUHostUpgradePriceRequest {
req := &GetUHostUpgradePriceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// GetUHostUpgradePrice - 获取UHost实例升级配置的价格。可选配置范围请参考[[api:uhost-api:uhost_type|云主机机型说明]]。
func (c *UHostClient) GetUHostUpgradePrice(req *GetUHostUpgradePriceRequest) (*GetUHostUpgradePriceResponse, error) {
var err error
var res GetUHostUpgradePriceResponse
err = c.Client.InvokeAction("GetUHostUpgradePrice", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,74 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ImportCustomImage
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ImportCustomImageRequest is request schema for ImportCustomImage action
type ImportCustomImageRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 镜像名称
ImageName *string `required:"true"`
// UFile私有空间地址
UFileUrl *string `required:"true"`
// 操作系统平台比如CentOS、Ubuntu、Windows、RedHat等请参考控制台的镜像版本若导入控制台上没有的操作系统参数为Other
OsType *string `required:"true"`
// 操作系统详细版本请参考控制台的镜像版本OsType为Other时输入参数为Other
OsName *string `required:"true"`
// 镜像格式可选RAW、VHD、VMDK、qcow2
Format *string `required:"true"`
// 是否授权。必须填true
Auth *bool `required:"true"`
// 镜像描述
ImageDescription *string `required:"false"`
}
// ImportCustomImageResponse is response schema for ImportCustomImage action
type ImportCustomImageResponse struct {
response.CommonBase
// 镜像Id
ImageId string
}
// NewImportCustomImageRequest will create request of ImportCustomImage action.
func (c *UHostClient) NewImportCustomImageRequest() *ImportCustomImageRequest {
req := &ImportCustomImageRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// ImportCustomImage - 把UFile的镜像文件导入到UHost生成自定义镜像
func (c *UHostClient) ImportCustomImage(req *ImportCustomImageRequest) (*ImportCustomImageResponse, error) {
var err error
var res ImportCustomImageResponse
err = c.Client.InvokeAction("ImportCustomImage", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,62 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ModifyUHostInstanceName
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ModifyUHostInstanceNameRequest is request schema for ModifyUHostInstanceName action
type ModifyUHostInstanceNameRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// UHost实例名称
Name *string `required:"false"`
}
// ModifyUHostInstanceNameResponse is response schema for ModifyUHostInstanceName action
type ModifyUHostInstanceNameResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewModifyUHostInstanceNameRequest will create request of ModifyUHostInstanceName action.
func (c *UHostClient) NewModifyUHostInstanceNameRequest() *ModifyUHostInstanceNameRequest {
req := &ModifyUHostInstanceNameRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ModifyUHostInstanceName - 修改指定UHost实例名称需要给出数据中心UHostId及新的实例名称。
func (c *UHostClient) ModifyUHostInstanceName(req *ModifyUHostInstanceNameRequest) (*ModifyUHostInstanceNameResponse, error) {
var err error
var res ModifyUHostInstanceNameResponse
err = c.Client.InvokeAction("ModifyUHostInstanceName", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,62 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ModifyUHostInstanceRemark
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ModifyUHostInstanceRemarkRequest is request schema for ModifyUHostInstanceRemark action
type ModifyUHostInstanceRemarkRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 备注
Remark *string `required:"false"`
}
// ModifyUHostInstanceRemarkResponse is response schema for ModifyUHostInstanceRemark action
type ModifyUHostInstanceRemarkResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewModifyUHostInstanceRemarkRequest will create request of ModifyUHostInstanceRemark action.
func (c *UHostClient) NewModifyUHostInstanceRemarkRequest() *ModifyUHostInstanceRemarkRequest {
req := &ModifyUHostInstanceRemarkRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ModifyUHostInstanceRemark - 修改指定UHost实例备注信息。
func (c *UHostClient) ModifyUHostInstanceRemark(req *ModifyUHostInstanceRemarkRequest) (*ModifyUHostInstanceRemarkResponse, error) {
var err error
var res ModifyUHostInstanceRemarkResponse
err = c.Client.InvokeAction("ModifyUHostInstanceRemark", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,62 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ModifyUHostInstanceTag
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ModifyUHostInstanceTagRequest is request schema for ModifyUHostInstanceTag action
type ModifyUHostInstanceTagRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 业务组名称
Tag *string `required:"false"`
}
// ModifyUHostInstanceTagResponse is response schema for ModifyUHostInstanceTag action
type ModifyUHostInstanceTagResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewModifyUHostInstanceTagRequest will create request of ModifyUHostInstanceTag action.
func (c *UHostClient) NewModifyUHostInstanceTagRequest() *ModifyUHostInstanceTagRequest {
req := &ModifyUHostInstanceTagRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ModifyUHostInstanceTag - 修改指定UHost实例业务组标识。
func (c *UHostClient) ModifyUHostInstanceTag(req *ModifyUHostInstanceTagRequest) (*ModifyUHostInstanceTagResponse, error) {
var err error
var res ModifyUHostInstanceTagResponse
err = c.Client.InvokeAction("ModifyUHostInstanceTag", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,59 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost PoweroffUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// PoweroffUHostInstanceRequest is request schema for PoweroffUHostInstance action
type PoweroffUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](./describe_uhost_instance.html)
UHostId *string `required:"true"`
}
// PoweroffUHostInstanceResponse is response schema for PoweroffUHostInstance action
type PoweroffUHostInstanceResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewPoweroffUHostInstanceRequest will create request of PoweroffUHostInstance action.
func (c *UHostClient) NewPoweroffUHostInstanceRequest() *PoweroffUHostInstanceRequest {
req := &PoweroffUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// PoweroffUHostInstance - 直接关闭UHost实例电源无需等待实例正常关闭。
func (c *UHostClient) PoweroffUHostInstance(req *PoweroffUHostInstanceRequest) (*PoweroffUHostInstanceResponse, error) {
var err error
var res PoweroffUHostInstanceResponse
err = c.Client.InvokeAction("PoweroffUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,62 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost RebootUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// RebootUHostInstanceRequest is request schema for RebootUHostInstance action
type RebootUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 加密盘密码
DiskPassword *string `required:"false"`
}
// RebootUHostInstanceResponse is response schema for RebootUHostInstance action
type RebootUHostInstanceResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewRebootUHostInstanceRequest will create request of RebootUHostInstance action.
func (c *UHostClient) NewRebootUHostInstanceRequest() *RebootUHostInstanceRequest {
req := &RebootUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// RebootUHostInstance - 重新启动UHost实例需要指定数据中心及UHostID两个参数的值。
func (c *UHostClient) RebootUHostInstance(req *RebootUHostInstanceRequest) (*RebootUHostInstanceResponse, error) {
var err error
var res RebootUHostInstanceResponse
err = c.Client.InvokeAction("RebootUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,76 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ReinstallUHostInstance
package uhost
import (
"encoding/base64"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ReinstallUHostInstanceRequest is request schema for ReinstallUHostInstance action
type ReinstallUHostInstanceRequest struct {
request.CommonBase
// 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// UHost实例资源ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 如果创建UHost实例时LoginMode为Password则必须填写如果LoginMode为KeyPair不需要填写 密码格式使用BASE64编码LoginMode不可变更
Password *string `required:"false"`
// 镜像Id默认使用原镜像 参见 [DescribeImage](describe_image.html)
ImageId *string `required:"false"`
// 系统盘大小。 单位GB 范围[20,100] 步长10
BootDiskSpace *int `required:"false"`
// 是否保留数据盘保留Yes不报留No 默认Yes如果是从Windows重装为Linux或反之则无法保留数据盘
ReserveDisk *string `required:"false"`
// 云灾备指明191
ResourceType *int `required:"false"`
// 针对非私有子网主机可自定义DNS。n可为0-2
DNSServers []string `required:"false"`
}
// ReinstallUHostInstanceResponse is response schema for ReinstallUHostInstance action
type ReinstallUHostInstanceResponse struct {
response.CommonBase
// UHost实例资源ID
UhostId string
}
// NewReinstallUHostInstanceRequest will create request of ReinstallUHostInstance action.
func (c *UHostClient) NewReinstallUHostInstanceRequest() *ReinstallUHostInstanceRequest {
req := &ReinstallUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ReinstallUHostInstance - 重新安装指定UHost实例的操作系统
func (c *UHostClient) ReinstallUHostInstance(req *ReinstallUHostInstanceRequest) (*ReinstallUHostInstanceResponse, error) {
var err error
var res ReinstallUHostInstanceResponse
var reqImmutable = *req
reqImmutable.Password = ucloud.String(base64.StdEncoding.EncodeToString([]byte(ucloud.StringValue(req.Password))))
err = c.Client.InvokeAction("ReinstallUHostInstance", &reqImmutable, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,67 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ResetUHostInstancePassword
package uhost
import (
"encoding/base64"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ResetUHostInstancePasswordRequest is request schema for ResetUHostInstancePassword action
type ResetUHostInstancePasswordRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID
UHostId *string `required:"true"`
// UHost新密码密码格式使用BASE64编码
Password *string `required:"true"`
}
// ResetUHostInstancePasswordResponse is response schema for ResetUHostInstancePassword action
type ResetUHostInstancePasswordResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewResetUHostInstancePasswordRequest will create request of ResetUHostInstancePassword action.
func (c *UHostClient) NewResetUHostInstancePasswordRequest() *ResetUHostInstancePasswordRequest {
req := &ResetUHostInstancePasswordRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ResetUHostInstancePassword - 重置UHost实例的管理员密码。
func (c *UHostClient) ResetUHostInstancePassword(req *ResetUHostInstancePasswordRequest) (*ResetUHostInstancePasswordResponse, error) {
var err error
var res ResetUHostInstancePasswordResponse
var reqImmutable = *req
reqImmutable.Password = ucloud.String(base64.StdEncoding.EncodeToString([]byte(ucloud.StringValue(req.Password))))
err = c.Client.InvokeAction("ResetUHostInstancePassword", &reqImmutable, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,65 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ResizeAttachedDisk
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ResizeAttachedDiskRequest is request schema for ResizeAttachedDisk action
type ResizeAttachedDiskRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID。 参见 [DescribeUHostInstance](describe_uhost_instance.html)。
UHostId *string `required:"true"`
// 磁盘大小单位GB步长为10。取值范围需大于当前磁盘大小最大值请参考[[api:uhost-api:disk_type|磁盘类型]]。
DiskSpace *int `required:"true"`
// 磁盘ID。参见 [DescribeUHostInstance](describe_uhost_instance.html)返回值中的DiskSet。
DiskId *string `required:"true"`
}
// ResizeAttachedDiskResponse is response schema for ResizeAttachedDisk action
type ResizeAttachedDiskResponse struct {
response.CommonBase
// 改配成功的磁盘id
DiskId string
}
// NewResizeAttachedDiskRequest will create request of ResizeAttachedDisk action.
func (c *UHostClient) NewResizeAttachedDiskRequest() *ResizeAttachedDiskRequest {
req := &ResizeAttachedDiskRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ResizeAttachedDisk - 修改挂载的磁盘大小,包含系统盘和数据盘
func (c *UHostClient) ResizeAttachedDisk(req *ResizeAttachedDiskRequest) (*ResizeAttachedDiskResponse, error) {
var err error
var res ResizeAttachedDiskResponse
err = c.Client.InvokeAction("ResizeAttachedDisk", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,74 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost ResizeUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// ResizeUHostInstanceRequest is request schema for ResizeUHostInstance action
type ResizeUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 虚拟CPU核数。可选参数1-32可选范围与UHostType相关。默认值为当前实例的CPU核数
CPU *int `required:"false"`
// 内存大小。单位MB。范围 [1024, 262144]取值为1024的倍数可选范围与UHostType相关。默认值为当前实例的内存大小。
Memory *int `required:"false"`
// 【待废弃】数据盘大小单位GB范围[10,1000] SSD机型单位GB范围[100,500]步长10默认值为当前实例的数据盘大小数据盘不支持缩容因此不允许输入比当前实例数据盘大小的值
DiskSpace *int `required:"false"`
// 【待废弃】系统盘大小单位GB范围[20,100]步长10系统盘不支持缩容因此不允许输入比当前实例系统盘小的值
BootDiskSpace *int `required:"false"`
// 网卡升降级1表示升级2表示降级0表示不变
NetCapValue *int `required:"false"`
}
// ResizeUHostInstanceResponse is response schema for ResizeUHostInstance action
type ResizeUHostInstanceResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewResizeUHostInstanceRequest will create request of ResizeUHostInstance action.
func (c *UHostClient) NewResizeUHostInstanceRequest() *ResizeUHostInstanceRequest {
req := &ResizeUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// ResizeUHostInstance - 修改指定UHost实例的资源配置如CPU核心数内存容量大小网络增强等。可选配置范围请参考[[api:uhost-api:uhost_type|云主机机型说明]]。
func (c *UHostClient) ResizeUHostInstance(req *ResizeUHostInstanceRequest) (*ResizeUHostInstanceResponse, error) {
var err error
var res ResizeUHostInstanceResponse
err = c.Client.InvokeAction("ResizeUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,62 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost StartUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// StartUHostInstanceRequest is request schema for StartUHostInstance action
type StartUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 加密盘密码
DiskPassword *string `required:"false"`
}
// StartUHostInstanceResponse is response schema for StartUHostInstance action
type StartUHostInstanceResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewStartUHostInstanceRequest will create request of StartUHostInstance action.
func (c *UHostClient) NewStartUHostInstanceRequest() *StartUHostInstanceRequest {
req := &StartUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// StartUHostInstance - 启动处于关闭状态的UHost实例需要指定数据中心及UHostID两个参数的值。
func (c *UHostClient) StartUHostInstance(req *StartUHostInstanceRequest) (*StartUHostInstanceResponse, error) {
var err error
var res StartUHostInstanceResponse
err = c.Client.InvokeAction("StartUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,59 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost StopUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// StopUHostInstanceRequest is request schema for StopUHostInstance action
type StopUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost实例ID 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
}
// StopUHostInstanceResponse is response schema for StopUHostInstance action
type StopUHostInstanceResponse struct {
response.CommonBase
// UHost实例ID
UhostId string
}
// NewStopUHostInstanceRequest will create request of StopUHostInstance action.
func (c *UHostClient) NewStopUHostInstanceRequest() *StopUHostInstanceRequest {
req := &StopUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// StopUHostInstance - 指停止处于运行状态的UHost实例需指定数据中心及UhostID。
func (c *UHostClient) StopUHostInstance(req *StopUHostInstanceRequest) (*StopUHostInstanceResponse, error) {
var err error
var res StopUHostInstanceResponse
err = c.Client.InvokeAction("StopUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,56 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost TerminateCustomImage
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// TerminateCustomImageRequest is request schema for TerminateCustomImage action
type TerminateCustomImageRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 自制镜像ID 参见 [DescribeImage](describe_image.html)
ImageId *string `required:"true"`
}
// TerminateCustomImageResponse is response schema for TerminateCustomImage action
type TerminateCustomImageResponse struct {
response.CommonBase
// 自制镜像Id
ImageId string
}
// NewTerminateCustomImageRequest will create request of TerminateCustomImage action.
func (c *UHostClient) NewTerminateCustomImageRequest() *TerminateCustomImageRequest {
req := &TerminateCustomImageRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// TerminateCustomImage - 删除用户自定义镜像
func (c *UHostClient) TerminateCustomImage(req *TerminateCustomImageRequest) (*TerminateCustomImageResponse, error) {
var err error
var res TerminateCustomImageResponse
err = c.Client.InvokeAction("TerminateCustomImage", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,71 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost TerminateUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// TerminateUHostInstanceRequest is request schema for TerminateUHostInstance action
type TerminateUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// UHost资源Id 参见 [DescribeUHostInstance](describe_uhost_instance.html)
UHostId *string `required:"true"`
// 是否直接删除0表示按照原来的逻辑有回收站权限则进入回收站1表示直接删除
Destroy *int `required:"false"`
// 是否释放绑定的EIP。true: 解绑EIP后并释放其他值或不填解绑EIP。
ReleaseEIP *bool `required:"false"`
// 是否删除挂载的数据盘。true删除其他不删除。
ReleaseUDisk *bool `required:"false"`
}
// TerminateUHostInstanceResponse is response schema for TerminateUHostInstance action
type TerminateUHostInstanceResponse struct {
response.CommonBase
// 放入回收站:"Yes", 彻底删除“No”
InRecycle string
// UHost 实例 Id
UHostId string
}
// NewTerminateUHostInstanceRequest will create request of TerminateUHostInstance action.
func (c *UHostClient) NewTerminateUHostInstanceRequest() *TerminateUHostInstanceRequest {
req := &TerminateUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// TerminateUHostInstance - 删除指定数据中心的UHost实例。
func (c *UHostClient) TerminateUHostInstance(req *TerminateUHostInstanceRequest) (*TerminateUHostInstanceResponse, error) {
var err error
var res TerminateUHostInstanceResponse
err = c.Client.InvokeAction("TerminateUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,18 @@
package uhost
/*
UHostDisk - the request query for disk of uhost
*/
type UHostDisk struct {
// 磁盘大小单位GB。请参考[[api:uhost-api:disk_type|磁盘类型]]。
Size *int `required:"true"`
// 磁盘类型。枚举值LOCAL_NORMAL 普通本地盘 | CLOUD_NORMAL 普通云盘 |LOCAL_SSD SSD本地盘 | CLOUD_SSD SSD云盘默认为LOCAL_NORMAL。请参考[[api:uhost-api:disk_type|磁盘类型]]。
Type *string `required:"true"`
// 是否是系统盘。枚举值:\\ > True是系统盘 \\ > False是数据盘默认。Disks数组中有且只能有一块盘是系统盘。
IsBoot *string `required:"true"`
// 磁盘备份方案。枚举值:\\ > NONE无备份 \\ > DATAARK数据方舟 \\ 当前磁盘支持的备份模式参考 [[api:uhost-api:disk_type|磁盘类型]]
BackupType *string `required:"false"`
}

View File

@ -0,0 +1,34 @@
package uhost
/*
UHostDiskSet - DescribeUHostInstance
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostDiskSet struct {
// 磁盘类型。请参考[[api:uhost-api:disk_type|磁盘类型]]。
DiskType string
// 是否是系统盘。枚举值:\\ > True是系统盘 \\ > False是数据盘默认。Disks数组中有且只能有一块盘是系统盘。
IsBoot string
// 【建议不再使用】磁盘类型。系统盘: Boot数据盘: Data,网络盘Udisk
Type string
// 磁盘ID
DiskId string
// UDisk名字仅当磁盘是UDisk时返回
Name string
// 磁盘盘符
Drive string
// 磁盘大小,单位: GB
Size int
// 备份方案。若开通了数据方舟则为DataArk
BackupType string
}

View File

@ -0,0 +1,58 @@
package uhost
/*
UHostImageSet - DescribeImage
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostImageSet struct {
// 可用区,参见 [可用区列表](../summary/regionlist.html) |
Zone string
// 镜像ID
ImageId string
// 镜像名称
ImageName string
// 操作系统类型LiunxWindows
OsType string
// 操作系统名称
OsName string
// 镜像类型 标准镜像Base 行业镜像Business自定义镜像Custom
ImageType string
// 特殊状态标识, 目前包含NetEnhnced网络增强1.0, NetEnhanced_Ultra]网络增强2.0
Features []string
// 行业镜像类型(仅行业镜像将返回这个值)
FuncType string
// 集成软件名称(仅行业镜像将返回这个值)
IntegratedSoftware string
// 供应商(仅行业镜像将返回这个值)
Vendor string
// 介绍链接(仅行业镜像将返回这个值)
Links string
// 镜像状态, 可用Available制作中Making 不可用Unavailable
State string
// 镜像描述
ImageDescription string
// 创建时间格式为Unix时间戳
CreateTime int
// 镜像大小
ImageSize int
// 默认值为空'''。当CentOS 7.3/7.4/7.5等镜像会标记为“Broadwell”
MinimalCPU string
}

View File

@ -0,0 +1,112 @@
package uhost
/*
UHostInstanceSet - DescribeUHostInstance
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostInstanceSet struct {
// 可用区。参见 [可用区列表](../summary/regionlist.html)
Zone string
// UHost实例ID
UHostId string
// 云主机机型。参考[[api:uhost-api:uhost_type|云主机机型说明]]。
UHostType string
// 【建议不再使用】主机磁盘类型。 枚举值为:\\ > LocalDisk本地磁盘; \\ > UDisk 云盘。\\只要有一块磁盘为本地盘即返回LocalDisk。
StorageType string
// 【建议不再使用】主机的系统盘ID。
ImageId string
// 基础镜像ID指当前自定义镜像的来源镜像
BasicImageId string
// 基础镜像名称(指当前自定义镜像的来源镜像)
BasicImageName string
// 业务组名称
Tag string
// 备注
Remark string
// UHost实例名称
Name string
// 实例状态,枚举值:\\ >初始化: Initializing; \\ >启动中: Starting; \\> 运行中: Running; \\> 关机中: Stopping; \\ >关机: Stopped \\ >安装失败: Install Fail; \\ >重启中: Rebooting
State string
// 创建时间格式为Unix时间戳
CreateTime int
// 计费模式,枚举值为: Year按年付费 Month按月付费 Dynamic按需付费需开启权限
ChargeType string
// 到期时间格式为Unix时间戳
ExpireTime int
// 虚拟CPU核数单位: 个
CPU int
// 内存大小,单位: MB
Memory int
// 是否自动续费自动续费“Yes”不自动续费“No”
AutoRenew string
// 磁盘信息见 UHostDiskSet
DiskSet []UHostDiskSet
// 详细信息见 UHostIPSet
IPSet []UHostIPSet
// 网络增强。Normal: 无Super 网络增强1.0 Ultra: 网络增强2.0
NetCapability string
// 【建议不再使用】网络状态。 连接Connected 断开NotConnected
NetworkState string
// 【建议不再使用】数据方舟模式。枚举值:\\ > Yes: 开启方舟; \\ > no未开启方舟
TimemachineFeature string
// true: 开启热升级; false未开启热升级
HotplugFeature bool
// 【建议不再使用】仅北京A的云主机会返回此字段。基础网络模式Default子网模式Private
SubnetType string
// 内网的IP地址
IPs []string
// 创建主机的最初来源镜像的操作系统名称若直接通过基础镜像创建此处返回和BasicImageName一致
OsName string
// 操作系统类别。返回"Linux"或者"Windows"
OsType string
// 删除时间格式为Unix时间戳
DeleteTime int
// 主机系列N2表示系列2N1表示系列1
HostType string
// 主机的生命周期类型。目前仅支持Normal普通
LifeCycle string
// GPU个数
GPU int
// 系统盘状态 Normal表示初始化完成Initializing表示在初始化。仍在初始化的系统盘无法制作镜像。
BootDiskState string
// 总的数据盘存储空间。
TotalDiskSpace int
// 隔离组id不在隔离组则返回""
IsolationGroup string
}

View File

@ -0,0 +1,37 @@
package uhost
/*
UHostIPSet - DescribeUHostInstance
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostIPSet struct {
// 【暂未支持】是否为默认网卡。True: 是默认网卡;其他值:不是。
Default string
// 当前网卡的Mac。
Mac string
// 当前EIP的权重。权重最大的为当前的出口IP。
Weight int
// 国际: InternationBGP: Bgp内网: Private
Type string
// 外网IP资源ID 。(内网IP无对应的资源ID)
IPId string
// IP地址
IP string
// IP对应的带宽, 单位: Mb (内网IP不显示带宽信息)
Bandwidth int
// IP地址对应的VPC ID。北京一不支持字段返回为空
VPCId string
// IP地址对应的子网 ID。北京一不支持字段返回为空
SubnetId string
}

View File

@ -0,0 +1,16 @@
package uhost
/*
UHostPriceSet - 主机价格
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostPriceSet struct {
// 计费类型。YearMonthDynamic
ChargeType string
// 价格,单位: 元,保留小数点后两位有效数字
Price float64
}

View File

@ -0,0 +1,19 @@
package uhost
/*
UHostTagSet - DescribeUHostTags
this model is auto created by ucloud code generater for open api,
you can also see https://docs.ucloud.cn for detail.
*/
type UHostTagSet struct {
// 业务组名称
Tag string
// 该业务组中包含的主机个数
TotalCount int
// 可用区
Zone string
}

View File

@ -0,0 +1,59 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UHost UpgradeToArkUHostInstance
package uhost
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// UpgradeToArkUHostInstanceRequest is request schema for UpgradeToArkUHostInstance action
type UpgradeToArkUHostInstanceRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 可用区。参见 [可用区列表](../summary/regionlist.html)
// Zone *string `required:"true"`
// UHost主机的资源ID例如UHostIds.0代表希望升级的主机1UHostIds.1代表主机2。
UHostIds []string `required:"true"`
// 代金券ID 请参考DescribeCoupon接口
CouponId *string `required:"false"`
}
// UpgradeToArkUHostInstanceResponse is response schema for UpgradeToArkUHostInstance action
type UpgradeToArkUHostInstanceResponse struct {
response.CommonBase
// UHost主机的资源ID数组
UHostSet []string
}
// NewUpgradeToArkUHostInstanceRequest will create request of UpgradeToArkUHostInstance action.
func (c *UHostClient) NewUpgradeToArkUHostInstanceRequest() *UpgradeToArkUHostInstanceRequest {
req := &UpgradeToArkUHostInstanceRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(true)
return req
}
// UpgradeToArkUHostInstance - 普通升级为方舟机型
func (c *UHostClient) UpgradeToArkUHostInstance(req *UpgradeToArkUHostInstanceRequest) (*UpgradeToArkUHostInstanceResponse, error) {
var err error
var res UpgradeToArkUHostInstanceResponse
err = c.Client.InvokeAction("UpgradeToArkUHostInstance", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,71 @@
package uhost
import (
"time"
"github.com/ucloud/ucloud-sdk-go/private/utils"
"github.com/ucloud/ucloud-sdk-go/ucloud"
uerr "github.com/ucloud/ucloud-sdk-go/ucloud/error"
"github.com/ucloud/ucloud-sdk-go/ucloud/log"
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
)
// WaitUntilUHostInstanceStateRequest is the request of uhost instance state waiter
type WaitUntilUHostInstanceStateRequest struct {
request.CommonBase
Interval *time.Duration
MaxAttempts *int
DescribeRequest *DescribeUHostInstanceRequest
State State
IgnoreError *bool
}
// NewWaitUntilUHostInstanceStateRequest will create request of WaitUntilUHostInstanceState action.
func (c *UHostClient) NewWaitUntilUHostInstanceStateRequest() *WaitUntilUHostInstanceStateRequest {
cfg := c.Client.GetConfig()
return &WaitUntilUHostInstanceStateRequest{
CommonBase: request.CommonBase{
Region: ucloud.String(cfg.Region),
ProjectId: ucloud.String(cfg.ProjectId),
},
}
}
// WaitUntilUHostInstanceState will pending current goroutine until the state has changed to expected state.
func (c *UHostClient) WaitUntilUHostInstanceState(req *WaitUntilUHostInstanceStateRequest) error {
waiter := utils.FuncWaiter{
Interval: ucloud.TimeDurationValue(req.Interval),
MaxAttempts: ucloud.IntValue(req.MaxAttempts),
IgnoreError: ucloud.BoolValue(req.IgnoreError),
Checker: func() (bool, error) {
resp, err := c.DescribeUHostInstance(req.DescribeRequest)
if err != nil {
skipErrors := []string{uerr.ErrNetwork, uerr.ErrHTTPStatus, uerr.ErrRetCode}
if uErr, ok := err.(uerr.Error); ok && utils.IsStringIn(uErr.Name(), skipErrors) {
log.Infof("skip error for wait resource state, %s", uErr)
return false, nil
}
log.Infof("wait for resource state is ready, %s", err)
return false, err
}
// TODO: Ensure if it is any data consistency problem?
// Such as creating a new uhost, but cannot describe it's correct state immediately ...
for _, uhost := range resp.UHostSet {
if val, _ := req.State.MarshalValue(); uhost.State != val {
return false, nil
}
}
if len(resp.UHostSet) > 0 {
return true, nil
}
return false, nil
},
}
return waiter.WaitForCompletion()
}

View File

@ -0,0 +1,83 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UNet AllocateEIP
package unet
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// AllocateEIPRequest is request schema for AllocateEIP action
type AllocateEIPRequest struct {
request.CommonBase
// [公共参数] 地域。
// Region *string `required:"true"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。
// ProjectId *string `required:"false"`
// 弹性IP的线路如下: 国际: International BGP: Bgp 各地域允许的线路参数如下: cn-sh1: Bgp cn-sh2: Bgp cn-gd: Bgp cn-bj1: Bgp cn-bj2: Bgp hk: International us-ca: International th-bkk: International kr-seoul:International us-ws:International ge-fra:International sg:International tw-kh:International.其他海外线路均为 International
OperatorName *string `required:"true"`
// 弹性IP的外网带宽, 单位为Mbps. 共享带宽模式必须指定0M带宽, 非共享带宽模式必须指定非0Mbps带宽. 各地域非共享带宽的带宽范围如下: 流量计费[1-200],带宽计费[1-800]
Bandwidth *int `required:"true"`
// 业务组名称, 默认为 "Default"
Tag *string `required:"false"`
// 付费方式, 枚举值为: Year, 按年付费; Month, 按月付费; Dynamic, 按需付费(需开启权限); Trial, 试用(需开启权限) 默认为按月付费
ChargeType *string `required:"false"`
// 购买时长, 默认: 1
Quantity *int `required:"false"`
// 弹性IP的计费模式. 枚举值: "Traffic", 流量计费; "Bandwidth", 带宽计费; "ShareBandwidth",共享带宽模式. 默认为 "Bandwidth".
PayMode *string `required:"false"`
// 绑定的共享带宽Id仅当PayMode为ShareBandwidth时有效
ShareBandwidthId *string `required:"false"`
// 弹性IP的名称, 默认为 "EIP"
Name *string `required:"false"`
// 弹性IP的备注, 默认为空
Remark *string `required:"false"`
// 代金券ID, 默认不使用
CouponId *string `required:"false"`
}
// AllocateEIPResponse is response schema for AllocateEIP action
type AllocateEIPResponse struct {
response.CommonBase
// 申请到的EIP资源详情 参见 UnetAllocateEIPSet
EIPSet []UnetAllocateEIPSet
}
// NewAllocateEIPRequest will create request of AllocateEIP action.
func (c *UNetClient) NewAllocateEIPRequest() *AllocateEIPRequest {
req := &AllocateEIPRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// AllocateEIP - 根据提供信息, 申请弹性IP
func (c *UNetClient) AllocateEIP(req *AllocateEIPRequest) (*AllocateEIPResponse, error) {
var err error
var res AllocateEIPResponse
err = c.Client.InvokeAction("AllocateEIP", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,68 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UNet AllocateShareBandwidth
package unet
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// AllocateShareBandwidthRequest is request schema for AllocateShareBandwidth action
type AllocateShareBandwidthRequest struct {
request.CommonBase
// [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html)
// Region *string `required:"true"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。
// ProjectId *string `required:"false"`
// 共享带宽名字
Name *string `required:"true"`
// 付费方式:Year 按年,Month 按月,Dynamic 按时;
ChargeType *string `required:"true"`
// 共享带宽值
ShareBandwidth *int `required:"true"`
// 购买时长
Quantity *int `required:"false"`
// 共享带宽保底值(后付费)
ShareBandwidthGuarantee *int `required:"false"`
}
// AllocateShareBandwidthResponse is response schema for AllocateShareBandwidth action
type AllocateShareBandwidthResponse struct {
response.CommonBase
// 共享带宽资源Id
ShareBandwidthId string
}
// NewAllocateShareBandwidthRequest will create request of AllocateShareBandwidth action.
func (c *UNetClient) NewAllocateShareBandwidthRequest() *AllocateShareBandwidthRequest {
req := &AllocateShareBandwidthRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// AllocateShareBandwidth - 开通共享带宽
func (c *UNetClient) AllocateShareBandwidth(req *AllocateShareBandwidthRequest) (*AllocateShareBandwidthResponse, error) {
var err error
var res AllocateShareBandwidthResponse
err = c.Client.InvokeAction("AllocateShareBandwidth", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

View File

@ -0,0 +1,80 @@
//Code is generated by ucloud code generator, don't modify it by hand, it will cause undefined behaviors.
//go:generate ucloud-gen-go-api UNet AllocateVIP
package unet
import (
"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/response"
)
// AllocateVIPRequest is request schema for AllocateVIP action
type AllocateVIPRequest struct {
request.CommonBase
// [公共参数] 地域
// Region *string `required:"true"`
// [公共参数] 可用区
// Zone *string `required:"false"`
// [公共参数] 项目ID。不填写为默认项目子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html)
// ProjectId *string `required:"false"`
// 指定vip所属的VPC
VPCId *string `required:"true"`
// 子网id
SubnetId *string `required:"true"`
// 申请数量,默认: 1
Count *int `required:"false"`
// vip名默认为VIP
Name *string `required:"false"`
// 业务组名称默认为Default
Tag *string `required:"false"`
// 备注
Remark *string `required:"false"`
// 业务组
BusinessId *string `required:"false"`
}
// AllocateVIPResponse is response schema for AllocateVIP action
type AllocateVIPResponse struct {
response.CommonBase
// 申请到的VIP资源相关信息
VIPSet []VIPSet
// 申请到的VIP地址
DataSet []string
}
// NewAllocateVIPRequest will create request of AllocateVIP action.
func (c *UNetClient) NewAllocateVIPRequest() *AllocateVIPRequest {
req := &AllocateVIPRequest{}
// setup request with client config
c.Client.SetupRequest(req)
// setup retryable with default retry policy (retry for non-create action and common error)
req.SetRetryable(false)
return req
}
// AllocateVIP - 根据提供信息申请内网VIP(Virtual IP多用于高可用程序作为漂移IP。
func (c *UNetClient) AllocateVIP(req *AllocateVIPRequest) (*AllocateVIPResponse, error) {
var err error
var res AllocateVIPResponse
err = c.Client.InvokeAction("AllocateVIP", req, &res)
if err != nil {
return &res, err
}
return &res, nil
}

Some files were not shown because too many files have changed in this diff Show More