diff --git a/builder/ucloud/uhost/access_config.go b/builder/ucloud/common/access_config.go similarity index 83% rename from builder/ucloud/uhost/access_config.go rename to builder/ucloud/common/access_config.go index 389d74578..4c95d8695 100644 --- a/builder/ucloud/uhost/access_config.go +++ b/builder/ucloud/common/access_config.go @@ -1,12 +1,14 @@ -package uhost +package common import ( "fmt" + "net/url" "os" "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/ufile" "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/services/unet" "github.com/ucloud/ucloud-sdk-go/services/vpc" @@ -36,16 +38,19 @@ func (c *AccessConfig) Client() (*UCloudClient, error) { cfg.BaseUrl = c.BaseUrl } cfg.UserAgent = fmt.Sprintf("Packer-UCloud/%s", version.FormattedVersion()) + // set default max retry count + cfg.MaxRetries = 3 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) + 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) + c.client.UFileConn = ufile.NewClient(&cfg, &cred) return c.client, nil } @@ -64,6 +69,12 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { errs = append(errs, fmt.Errorf("%q must be set", "region")) } + if c.BaseUrl != "" { + if _, err := url.Parse(c.BaseUrl); err != nil { + errs = append(errs, fmt.Errorf("%q is invalid, should be an valid ucloud base_url, got %q, parse error: %s", "base_url", c.BaseUrl, err)) + } + } + if len(errs) > 0 { return errs } @@ -137,7 +148,7 @@ func (c *AccessConfig) ValidateZone(region, zone string) error { func (c *AccessConfig) getSupportedProjectIds() ([]string, error) { client, err := c.Client() - conn := client.uaccountconn + conn := client.UAccountConn if err != nil { return nil, err } @@ -150,7 +161,7 @@ func (c *AccessConfig) getSupportedProjectIds() ([]string, error) { validProjectIds := make([]string, len(resp.ProjectSet)) for _, val := range resp.ProjectSet { - if !isStringIn(val.ProjectId, validProjectIds) { + if !IsStringIn(val.ProjectId, validProjectIds) { validProjectIds = append(validProjectIds, val.ProjectId) } } @@ -160,7 +171,7 @@ func (c *AccessConfig) getSupportedProjectIds() ([]string, error) { func (c *AccessConfig) getSupportedRegions() ([]string, error) { client, err := c.Client() - conn := client.uaccountconn + conn := client.UAccountConn if err != nil { return nil, err } @@ -173,7 +184,7 @@ func (c *AccessConfig) getSupportedRegions() ([]string, error) { validRegions := make([]string, len(resp.Regions)) for _, val := range resp.Regions { - if !isStringIn(val.Region, validRegions) { + if !IsStringIn(val.Region, validRegions) { validRegions = append(validRegions, val.Region) } } @@ -183,7 +194,7 @@ func (c *AccessConfig) getSupportedRegions() ([]string, error) { func (c *AccessConfig) getSupportedZones(region string) ([]string, error) { client, err := c.Client() - conn := client.uaccountconn + conn := client.UAccountConn if err != nil { return nil, err } @@ -196,7 +207,7 @@ func (c *AccessConfig) getSupportedZones(region string) ([]string, error) { validZones := make([]string, len(resp.Regions)) for _, val := range resp.Regions { - if val.Region == region && !isStringIn(val.Zone, validZones) { + if val.Region == region && !IsStringIn(val.Zone, validZones) { validZones = append(validZones, val.Zone) } diff --git a/builder/ucloud/uhost/access_config_test.go b/builder/ucloud/common/access_config_test.go similarity index 97% rename from builder/ucloud/uhost/access_config_test.go rename to builder/ucloud/common/access_config_test.go index 3c949bb8b..179776e43 100644 --- a/builder/ucloud/uhost/access_config_test.go +++ b/builder/ucloud/common/access_config_test.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "os" diff --git a/builder/ucloud/uhost/artifact.go b/builder/ucloud/common/artifact.go similarity index 96% rename from builder/ucloud/uhost/artifact.go rename to builder/ucloud/common/artifact.go index fc8a43ff6..961bd9399 100644 --- a/builder/ucloud/uhost/artifact.go +++ b/builder/ucloud/common/artifact.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "fmt" @@ -10,7 +10,7 @@ import ( ) type Artifact struct { - UCloudImages *imageInfoSet + UCloudImages *ImageInfoSet BuilderIdValue string @@ -56,7 +56,7 @@ func (a *Artifact) State(name string) interface{} { } func (a *Artifact) Destroy() error { - conn := a.Client.uhostconn + conn := a.Client.UHostConn errors := make([]error, 0) for _, v := range a.UCloudImages.GetAll() { diff --git a/builder/ucloud/uhost/artifact_test.go b/builder/ucloud/common/artifact_test.go similarity index 85% rename from builder/ucloud/uhost/artifact_test.go rename to builder/ucloud/common/artifact_test.go index ec4278565..d3f0f12d7 100644 --- a/builder/ucloud/uhost/artifact_test.go +++ b/builder/ucloud/common/artifact_test.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "github.com/hashicorp/packer/packer" @@ -13,14 +13,14 @@ func TestArtifact_Impl(t *testing.T) { func TestArtifactId(t *testing.T) { expected := `project1:region1:foo,project2:region2:bar` - images := newImageInfoSet(nil) - images.Set(imageInfo{ + images := NewImageInfoSet(nil) + images.Set(ImageInfo{ Region: "region1", ProjectId: "project1", ImageId: "foo", }) - images.Set(imageInfo{ + images.Set(ImageInfo{ Region: "region2", ProjectId: "project2", ImageId: "bar", @@ -37,14 +37,14 @@ func TestArtifactId(t *testing.T) { } func TestArtifactState_atlasMetadata(t *testing.T) { - images := newImageInfoSet(nil) - images.Set(imageInfo{ + images := NewImageInfoSet(nil) + images.Set(ImageInfo{ Region: "region1", ProjectId: "project1", ImageId: "foo", }) - images.Set(imageInfo{ + images.Set(ImageInfo{ Region: "region2", ProjectId: "project2", ImageId: "bar", diff --git a/builder/ucloud/uhost/client.go b/builder/ucloud/common/client.go similarity index 57% rename from builder/ucloud/uhost/client.go rename to builder/ucloud/common/client.go index 120f2d33c..0e1cf0434 100644 --- a/builder/ucloud/uhost/client.go +++ b/builder/ucloud/common/client.go @@ -1,7 +1,8 @@ -package uhost +package common import ( "github.com/ucloud/ucloud-sdk-go/services/uaccount" + "github.com/ucloud/ucloud-sdk-go/services/ufile" "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/services/unet" "github.com/ucloud/ucloud-sdk-go/services/vpc" @@ -10,17 +11,18 @@ import ( ) type UCloudClient struct { - uhostconn *uhost.UHostClient - unetconn *unet.UNetClient - vpcconn *vpc.VPCClient - uaccountconn *uaccount.UAccountClient + UHostConn *uhost.UHostClient + UNetConn *unet.UNetClient + VPCConn *vpc.VPCClient + UAccountConn *uaccount.UAccountClient + UFileConn *ufile.UFileClient } -func (c *UCloudClient) describeFirewallById(sgId string) (*unet.FirewallDataSet, error) { +func (c *UCloudClient) DescribeFirewallById(sgId string) (*unet.FirewallDataSet, error) { if sgId == "" { - return nil, newNotFoundError("security group", sgId) + return nil, NewNotFoundError("security group", sgId) } - conn := c.unetconn + conn := c.UNetConn req := conn.NewDescribeFirewallRequest() req.FWId = ucloud.String(sgId) @@ -29,23 +31,23 @@ func (c *UCloudClient) describeFirewallById(sgId string) (*unet.FirewallDataSet, if err != nil { if uErr, ok := err.(uerr.Error); ok && uErr.Code() == 54002 { - return nil, newNotFoundError("security group", sgId) + return nil, NewNotFoundError("security group", sgId) } return nil, err } if len(resp.DataSet) < 1 { - return nil, newNotFoundError("security group", sgId) + return nil, NewNotFoundError("security group", sgId) } return &resp.DataSet[0], nil } -func (c *UCloudClient) describeSubnetById(subnetId string) (*vpc.VPCSubnetInfoSet, error) { +func (c *UCloudClient) DescribeSubnetById(subnetId string) (*vpc.VPCSubnetInfoSet, error) { if subnetId == "" { - return nil, newNotFoundError("Subnet", subnetId) + return nil, NewNotFoundError("Subnet", subnetId) } - conn := c.vpcconn + conn := c.VPCConn req := conn.NewDescribeSubnetRequest() req.SubnetIds = []string{subnetId} @@ -56,17 +58,17 @@ func (c *UCloudClient) describeSubnetById(subnetId string) (*vpc.VPCSubnetInfoSe } if resp == nil || len(resp.DataSet) < 1 { - return nil, newNotFoundError("Subnet", subnetId) + return nil, NewNotFoundError("Subnet", subnetId) } return &resp.DataSet[0], nil } -func (c *UCloudClient) describeVPCById(vpcId string) (*vpc.VPCInfo, error) { +func (c *UCloudClient) DescribeVPCById(vpcId string) (*vpc.VPCInfo, error) { if vpcId == "" { - return nil, newNotFoundError("VPC", vpcId) + return nil, NewNotFoundError("VPC", vpcId) } - conn := c.vpcconn + conn := c.VPCConn req := conn.NewDescribeVPCRequest() req.VPCIds = []string{vpcId} @@ -77,7 +79,7 @@ func (c *UCloudClient) describeVPCById(vpcId string) (*vpc.VPCInfo, error) { } if resp == nil || len(resp.DataSet) < 1 { - return nil, newNotFoundError("VPC", vpcId) + return nil, NewNotFoundError("VPC", vpcId) } return &resp.DataSet[0], nil @@ -85,31 +87,31 @@ func (c *UCloudClient) describeVPCById(vpcId string) (*vpc.VPCInfo, error) { func (c *UCloudClient) DescribeImageById(imageId string) (*uhost.UHostImageSet, error) { if imageId == "" { - return nil, newNotFoundError("image", imageId) + return nil, NewNotFoundError("image", imageId) } - req := c.uhostconn.NewDescribeImageRequest() + req := c.UHostConn.NewDescribeImageRequest() req.ImageId = ucloud.String(imageId) - resp, err := c.uhostconn.DescribeImage(req) + resp, err := c.UHostConn.DescribeImage(req) if err != nil { return nil, err } if len(resp.ImageSet) < 1 { - return nil, newNotFoundError("image", imageId) + return nil, NewNotFoundError("image", imageId) } return &resp.ImageSet[0], nil } -func (c *UCloudClient) describeUHostById(uhostId string) (*uhost.UHostInstanceSet, error) { +func (c *UCloudClient) DescribeUHostById(uhostId string) (*uhost.UHostInstanceSet, error) { if uhostId == "" { - return nil, newNotFoundError("instance", uhostId) + return nil, NewNotFoundError("instance", uhostId) } - req := c.uhostconn.NewDescribeUHostInstanceRequest() + req := c.UHostConn.NewDescribeUHostInstanceRequest() req.UHostIds = []string{uhostId} - resp, err := c.uhostconn.DescribeUHostInstance(req) + resp, err := c.UHostConn.DescribeUHostInstance(req) if err != nil { return nil, err } @@ -120,19 +122,19 @@ func (c *UCloudClient) describeUHostById(uhostId string) (*uhost.UHostInstanceSe return &resp.UHostSet[0], nil } -func (c *UCloudClient) describeImageByInfo(projectId, regionId, imageId string) (*uhost.UHostImageSet, error) { - req := c.uhostconn.NewDescribeImageRequest() +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) + resp, err := c.UHostConn.DescribeImage(req) if err != nil { return nil, err } if len(resp.ImageSet) < 1 { - return nil, newNotFoundError("image", imageId) + return nil, NewNotFoundError("image", imageId) } return &resp.ImageSet[0], nil diff --git a/builder/ucloud/common/consts.go b/builder/ucloud/common/consts.go new file mode 100644 index 000000000..dd5049085 --- /dev/null +++ b/builder/ucloud/common/consts.go @@ -0,0 +1,31 @@ +package common + +const ( + // DefaultPasswordStr, DefaultPasswordNum and DefaultPasswordSpe are used to general default value of root password of UHost instance + DefaultPasswordNum = "012346789" + DefaultPasswordStr = "abcdefghijklmnopqrstuvwxyz" + DefaultPasswordSpe = "-_" +) + +const ( + InstanceStateRunning = "Running" + InstanceStateStopped = "Stopped" + + ImageStateAvailable = "Available" + ImageStateUnavailable = "Unavailable" + + BootDiskStateNormal = "Normal" + OsTypeWindows = "Windows" + SecurityGroupNonWeb = "recommend non web" + IpTypePrivate = "Private" +) + +const ( + DefaultCreateImageTimeout = 3600 +) + +var BootDiskTypeMap = NewStringConverter(map[string]string{ + "cloud_ssd": "CLOUD_SSD", + "local_normal": "LOCAL_NORMAL", + "local_ssd": "LOCAL_SSD", +}) diff --git a/builder/ucloud/uhost/errors.go b/builder/ucloud/common/errors.go similarity index 57% rename from builder/ucloud/uhost/errors.go rename to builder/ucloud/common/errors.go index 2f5d85192..f1ad49eae 100644 --- a/builder/ucloud/uhost/errors.go +++ b/builder/ucloud/common/errors.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "fmt" @@ -12,7 +12,7 @@ type ExpectedStateError struct { message string } -type NotCompleteError struct { +type NotCompletedError struct { message string } @@ -24,38 +24,38 @@ func (e *NotFoundError) Error() string { return e.message } -func (e *NotCompleteError) Error() string { +func (e *NotCompletedError) Error() string { return e.message } -func newNotFoundError(product, id string) error { +func NewNotFoundError(product, id string) error { return &NotFoundError{fmt.Sprintf("the %s %s is not found", product, id)} } -func newExpectedStateError(product, id string) error { +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 NewNotCompletedError(product string) error { + return &NotCompletedError{fmt.Sprintf("%s is not completed", product)} } -func isNotFoundError(err error) bool { +func IsNotFoundError(err error) bool { if _, ok := err.(*NotFoundError); ok { return true } return false } -func isExpectedStateError(err error) bool { +func IsExpectedStateError(err error) bool { if _, ok := err.(*ExpectedStateError); ok { return true } return false } -func isNotCompleteError(err error) bool { - if _, ok := err.(*NotCompleteError); ok { +func IsNotCompleteError(err error) bool { + if _, ok := err.(*NotCompletedError); ok { return true } return false diff --git a/builder/ucloud/uhost/image_config.go b/builder/ucloud/common/image_config.go similarity index 72% rename from builder/ucloud/uhost/image_config.go rename to builder/ucloud/common/image_config.go index 7a9ada8ce..e4b8a90c0 100644 --- a/builder/ucloud/uhost/image_config.go +++ b/builder/ucloud/common/image_config.go @@ -1,6 +1,6 @@ //go:generate mapstructure-to-hcl2 -type ImageDestination -package uhost +package common import ( "fmt" @@ -17,19 +17,20 @@ type ImageDestination struct { } type ImageConfig struct { - ImageName string `mapstructure:"image_name"` - ImageDescription string `mapstructure:"image_description"` - ImageDestinations []ImageDestination `mapstructure:"image_copy_to_mappings"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + ImageDestinations []ImageDestination `mapstructure:"image_copy_to_mappings"` + WaitImageReadyTimeout int `mapstructure:"wait_image_ready_timeout"` } -var imageNamePattern = regexp.MustCompile(`^[A-Za-z0-9\p{Han}-_\[\]:,.]{1,63}$`) +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) { + } 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)) } @@ -43,6 +44,10 @@ func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error { } } + if c.WaitImageReadyTimeout <= 0 { + c.WaitImageReadyTimeout = DefaultCreateImageTimeout + } + if len(errs) > 0 { return errs } @@ -61,7 +66,7 @@ func (imageDestination *ImageDestination) validate() []error { errs = append(errs, fmt.Errorf("%q must be set", "image_copy_project")) } - if imageDestination.Name != "" && !imageNamePattern.MatchString(imageDestination.Name) { + 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)) } diff --git a/builder/ucloud/uhost/image_config.hcl2spec.go b/builder/ucloud/common/image_config.hcl2spec.go similarity index 99% rename from builder/ucloud/uhost/image_config.hcl2spec.go rename to builder/ucloud/common/image_config.hcl2spec.go index ae0b07ed8..9839cad4c 100644 --- a/builder/ucloud/uhost/image_config.hcl2spec.go +++ b/builder/ucloud/common/image_config.hcl2spec.go @@ -1,5 +1,5 @@ // Code generated by "mapstructure-to-hcl2 -type ImageDestination"; DO NOT EDIT. -package uhost +package common import ( "github.com/hashicorp/hcl/v2/hcldec" diff --git a/builder/ucloud/uhost/image_config_test.go b/builder/ucloud/common/image_config_test.go similarity index 98% rename from builder/ucloud/uhost/image_config_test.go rename to builder/ucloud/common/image_config_test.go index 9388bb80d..4fcf9cccb 100644 --- a/builder/ucloud/uhost/image_config_test.go +++ b/builder/ucloud/common/image_config_test.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "testing" diff --git a/builder/ucloud/uhost/run_config.go b/builder/ucloud/common/run_config.go similarity index 96% rename from builder/ucloud/uhost/run_config.go rename to builder/ucloud/common/run_config.go index 6464e2ea8..89959147b 100644 --- a/builder/ucloud/uhost/run_config.go +++ b/builder/ucloud/common/run_config.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "fmt" @@ -39,7 +39,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { if c.InstanceType == "" { errs = append(errs, fmt.Errorf("%q must be set", "instance_type")) - } else if _, err := parseInstanceType(c.InstanceType); err != nil { + } else if _, err := ParseInstanceType(c.InstanceType); err != nil { errs = append(errs, err) } @@ -49,7 +49,7 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { if c.BootDiskType == "" { c.BootDiskType = "cloud_ssd" - } else if err := checkStringIn(c.BootDiskType, + } else if err := CheckStringIn(c.BootDiskType, []string{"local_normal", "local_ssd", "cloud_ssd"}); err != nil { errs = append(errs, err) } diff --git a/builder/ucloud/uhost/run_config_test.go b/builder/ucloud/common/run_config_test.go similarity index 98% rename from builder/ucloud/uhost/run_config_test.go rename to builder/ucloud/common/run_config_test.go index ff156905d..4aaebef14 100644 --- a/builder/ucloud/uhost/run_config_test.go +++ b/builder/ucloud/common/run_config_test.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "testing" diff --git a/builder/ucloud/common/type_converter.go b/builder/ucloud/common/type_converter.go new file mode 100644 index 000000000..2a84940e9 --- /dev/null +++ b/builder/ucloud/common/type_converter.go @@ -0,0 +1,31 @@ +package common + +type stringConverter struct { + c map[string]string + r map[string]string +} + +func NewStringConverter(input map[string]string) stringConverter { + reversed := make(map[string]string) + for k, v := range input { + reversed[v] = k + } + return stringConverter{ + c: input, + r: reversed, + } +} + +func (c stringConverter) Convert(src string) string { + if dst, ok := c.c[src]; ok { + return dst + } + return src +} + +func (c stringConverter) UnConvert(dst string) string { + if src, ok := c.r[dst]; ok { + return src + } + return dst +} diff --git a/builder/ucloud/uhost/types.go b/builder/ucloud/common/types.go similarity index 81% rename from builder/ucloud/uhost/types.go rename to builder/ucloud/common/types.go index 9695450fb..14e883620 100644 --- a/builder/ucloud/uhost/types.go +++ b/builder/ucloud/common/types.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "fmt" @@ -7,14 +7,14 @@ import ( "sync" ) -type instanceType struct { +type InstanceType struct { CPU int Memory int HostType string HostScaleType string } -func parseInstanceType(s string) (*instanceType, error) { +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) @@ -26,7 +26,7 @@ func parseInstanceType(s string) (*instanceType, error) { return parseInstanceTypeByNormal(split...) } -func (i *instanceType) String() string { +func (i *InstanceType) String() string { if i.Iscustomized() { return fmt.Sprintf("%s-%s-%v-%v", i.HostType, i.HostScaleType, i.CPU, i.Memory) } else { @@ -34,7 +34,7 @@ func (i *instanceType) String() string { } } -func (i *instanceType) Iscustomized() bool { +func (i *InstanceType) Iscustomized() bool { return i.HostScaleType == "customized" } @@ -47,13 +47,13 @@ var instanceTypeScaleMap = map[string]int{ var availableHostTypes = []string{"n"} -func parseInstanceTypeByCustomize(splited ...string) (*instanceType, error) { +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) + err := CheckStringIn(hostType, availableHostTypes) if err != nil { return nil, err } @@ -95,7 +95,7 @@ func parseInstanceTypeByCustomize(splited ...string) (*instanceType, error) { 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 := &InstanceType{} t.HostType = hostType t.HostScaleType = hostScaleType t.CPU = cpu @@ -105,13 +105,13 @@ func parseInstanceTypeByCustomize(splited ...string) (*instanceType, error) { var availableOutstandingCpu = []int{4, 8, 16, 32, 64} -func parseInstanceTypeByNormal(split ...string) (*instanceType, error) { +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"}) + err := CheckStringIn(hostType, []string{"n", "o"}) if err != nil { return nil, err } @@ -131,7 +131,7 @@ func parseInstanceTypeByNormal(split ...string) (*instanceType, error) { } if hostType == "o" { - if err := checkIntIn(cpu, availableOutstandingCpu); err != nil { + if err := CheckIntIn(cpu, availableOutstandingCpu); err != nil { return nil, fmt.Errorf("expected cpu of outstanding instancetype %q", err) } @@ -150,7 +150,7 @@ func parseInstanceTypeByNormal(split ...string) (*instanceType, error) { memory := cpu * scale - t := &instanceType{} + t := &InstanceType{} t.HostType = hostType t.HostScaleType = hostScaleType t.CPU = cpu @@ -159,46 +159,46 @@ func parseInstanceTypeByNormal(split ...string) (*instanceType, error) { } } -type imageInfo struct { +type ImageInfo struct { ImageId string ProjectId string Region string } -func (i *imageInfo) Id() string { +func (i *ImageInfo) Id() string { return fmt.Sprintf("%s:%s", i.ProjectId, i.Region) } -type imageInfoSet struct { - m map[string]imageInfo +type ImageInfoSet struct { + m map[string]ImageInfo once sync.Once } -func newImageInfoSet(vL []imageInfo) *imageInfoSet { - s := imageInfoSet{} +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) init() { + i.m = make(map[string]ImageInfo) } -func (i *imageInfoSet) Set(img imageInfo) { +func (i *ImageInfoSet) Set(img ImageInfo) { i.once.Do(i.init) i.m[img.Id()] = img } -func (i *imageInfoSet) Remove(id string) { +func (i *ImageInfoSet) Remove(id string) { i.once.Do(i.init) delete(i.m, id) } -func (i *imageInfoSet) Get(projectId, region string) *imageInfo { +func (i *ImageInfoSet) Get(projectId, region string) *ImageInfo { k := fmt.Sprintf("%s:%s", projectId, region) if v, ok := i.m[k]; ok { return &v @@ -206,8 +206,8 @@ func (i *imageInfoSet) Get(projectId, region string) *imageInfo { return nil } -func (i *imageInfoSet) GetAll() []imageInfo { - var vL []imageInfo +func (i *ImageInfoSet) GetAll() []ImageInfo { + var vL []ImageInfo for _, img := range i.m { vL = append(vL, img) } diff --git a/builder/ucloud/uhost/types_test.go b/builder/ucloud/common/types_test.go similarity index 69% rename from builder/ucloud/uhost/types_test.go rename to builder/ucloud/common/types_test.go index 1509855ca..2766149eb 100644 --- a/builder/ucloud/uhost/types_test.go +++ b/builder/ucloud/common/types_test.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "testing" @@ -12,14 +12,14 @@ func Test_parseInstanceType(t *testing.T) { tests := []struct { name string args args - want *instanceType + 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}, + {"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}, @@ -33,9 +33,9 @@ func Test_parseInstanceType(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := parseInstanceType(tt.args.s) + 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) + t.Errorf("ParseInstanceType() arg %s got %#v error = %v, wantErr %v", tt.args.s, got, err, tt.wantErr) return } @@ -47,7 +47,7 @@ func Test_parseInstanceType(t *testing.T) { !(tt.want.Memory == got.Memory) || !(tt.want.HostType == got.HostType) || !(tt.want.HostScaleType == got.HostScaleType) { - t.Errorf("parseInstanceType() = %v, want %v", got, tt.want) + t.Errorf("ParseInstanceType() = %v, want %v", got, tt.want) } }) } diff --git a/builder/ucloud/uhost/utils.go b/builder/ucloud/common/utils.go similarity index 82% rename from builder/ucloud/uhost/utils.go rename to builder/ucloud/common/utils.go index b05216bc3..1f58444d5 100644 --- a/builder/ucloud/uhost/utils.go +++ b/builder/ucloud/common/utils.go @@ -1,4 +1,4 @@ -package uhost +package common import ( "fmt" @@ -8,7 +8,7 @@ import ( "strings" ) -func checkStringIn(val string, available []string) error { +func CheckStringIn(val string, available []string) error { for _, choice := range available { if val == choice { return nil @@ -18,7 +18,7 @@ func checkStringIn(val string, available []string) error { return fmt.Errorf("should be one of %q, got %q", strings.Join(available, ","), val) } -func checkIntIn(val int, available []int) error { +func CheckIntIn(val int, available []int) error { for _, choice := range available { if val == choice { return nil @@ -28,7 +28,7 @@ func checkIntIn(val int, available []int) error { return fmt.Errorf("should be one of %v, got %d", available, val) } -func isStringIn(val string, available []string) bool { +func IsStringIn(val string, available []string) bool { for _, choice := range available { if val == choice { return true @@ -46,7 +46,7 @@ func SSHHost(usePrivateIp bool) func(multistep.StateBag) (string, error) { var privateIp, publicIp string for _, v := range instance.IPSet { - if v.Type == ipTypePrivate { + if v.Type == IpTypePrivate { privateIp = v.IP } else { publicIp = v.IP @@ -61,7 +61,7 @@ func SSHHost(usePrivateIp bool) func(multistep.StateBag) (string, error) { } } -func halt(state multistep.StateBag, err error, prefix string) multistep.StepAction { +func Halt(state multistep.StateBag, err error, prefix string) multistep.StepAction { ui := state.Get("ui").(packer.Ui) if prefix != "" { diff --git a/builder/ucloud/uhost/builder.go b/builder/ucloud/uhost/builder.go index 1c606494f..a9903bf4a 100644 --- a/builder/ucloud/uhost/builder.go +++ b/builder/ucloud/uhost/builder.go @@ -7,6 +7,7 @@ package uhost import ( "context" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -19,10 +20,10 @@ import ( const BuilderId = "ucloud.uhost" type Config struct { - common.PackerConfig `mapstructure:",squash"` - AccessConfig `mapstructure:",squash"` - ImageConfig `mapstructure:",squash"` - RunConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + ucloudcommon.AccessConfig `mapstructure:",squash"` + ucloudcommon.ImageConfig `mapstructure:",squash"` + ucloudcommon.RunConfig `mapstructure:",squash"` ctx interpolate.Context } @@ -111,7 +112,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &communicator.StepConnect{ Config: &b.config.RunConfig.Comm, - Host: SSHHost( + Host: ucloudcommon.SSHHost( b.config.UseSSHPrivateIp), SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(), }, @@ -119,9 +120,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &stepStopInstance{}, &stepCreateImage{}, &stepCopyUCloudImage{ - ImageDestinations: b.config.ImageDestinations, - RegionId: b.config.Region, - ProjectId: b.config.ProjectId, + ImageDestinations: b.config.ImageDestinations, + RegionId: b.config.Region, + ProjectId: b.config.ProjectId, + WaitImageReadyTimeout: b.config.WaitImageReadyTimeout, }, } @@ -140,8 +142,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } // Build the artifact and return it - artifact := &Artifact{ - UCloudImages: state.Get("ucloud_images").(*imageInfoSet), + artifact := &ucloudcommon.Artifact{ + UCloudImages: state.Get("ucloud_images").(*ucloudcommon.ImageInfoSet), BuilderIdValue: BuilderId, Client: client, } diff --git a/builder/ucloud/uhost/builder.hcl2spec.go b/builder/ucloud/uhost/builder.hcl2spec.go index fc405781d..e35dee79e 100644 --- a/builder/ucloud/uhost/builder.hcl2spec.go +++ b/builder/ucloud/uhost/builder.hcl2spec.go @@ -3,76 +3,78 @@ package uhost import ( "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/ucloud/common" "github.com/zclconf/go-cty/cty" ) // FlatConfig is an auto-generated flat version of Config. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` - PublicKey *string `mapstructure:"public_key" cty:"public_key"` - PrivateKey *string `mapstructure:"private_key" cty:"private_key"` - Region *string `mapstructure:"region" cty:"region"` - ProjectId *string `mapstructure:"project_id" cty:"project_id"` - BaseUrl *string `mapstructure:"base_url" cty:"base_url"` - ImageName *string `mapstructure:"image_name" cty:"image_name"` - ImageDescription *string `mapstructure:"image_description" cty:"image_description"` - ImageDestinations []FlatImageDestination `mapstructure:"image_copy_to_mappings" cty:"image_copy_to_mappings"` - Zone *string `mapstructure:"availability_zone" cty:"availability_zone"` - SourceImageId *string `mapstructure:"source_image_id" cty:"source_image_id"` - InstanceType *string `mapstructure:"instance_type" cty:"instance_type"` - InstanceName *string `mapstructure:"instance_name" cty:"instance_name"` - BootDiskType *string `mapstructure:"boot_disk_type" cty:"boot_disk_type"` - VPCId *string `mapstructure:"vpc_id" cty:"vpc_id"` - SubnetId *string `mapstructure:"subnet_id" cty:"subnet_id"` - SecurityGroupId *string `mapstructure:"security_group_id" cty:"security_group_id"` - Type *string `mapstructure:"communicator" cty:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` - UseSSHPrivateIp *bool `mapstructure:"use_ssh_private_ip" cty:"use_ssh_private_ip"` + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + PublicKey *string `mapstructure:"public_key" cty:"public_key"` + PrivateKey *string `mapstructure:"private_key" cty:"private_key"` + Region *string `mapstructure:"region" cty:"region"` + ProjectId *string `mapstructure:"project_id" cty:"project_id"` + BaseUrl *string `mapstructure:"base_url" cty:"base_url"` + ImageName *string `mapstructure:"image_name" cty:"image_name"` + ImageDescription *string `mapstructure:"image_description" cty:"image_description"` + ImageDestinations []common.FlatImageDestination `mapstructure:"image_copy_to_mappings" cty:"image_copy_to_mappings"` + WaitImageReadyTimeout *int `mapstructure:"wait_image_ready_timeout" cty:"wait_image_ready_timeout"` + Zone *string `mapstructure:"availability_zone" cty:"availability_zone"` + SourceImageId *string `mapstructure:"source_image_id" cty:"source_image_id"` + InstanceType *string `mapstructure:"instance_type" cty:"instance_type"` + InstanceName *string `mapstructure:"instance_name" cty:"instance_name"` + BootDiskType *string `mapstructure:"boot_disk_type" cty:"boot_disk_type"` + VPCId *string `mapstructure:"vpc_id" cty:"vpc_id"` + SubnetId *string `mapstructure:"subnet_id" cty:"subnet_id"` + SecurityGroupId *string `mapstructure:"security_group_id" cty:"security_group_id"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `cty:"ssh_public_key"` + SSHPrivateKey []byte `cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + UseSSHPrivateIp *bool `mapstructure:"use_ssh_private_ip" cty:"use_ssh_private_ip"` } // FlatMapstructure returns a new FlatConfig. @@ -98,7 +100,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "base_url": &hcldec.AttrSpec{Name: "base_url", Type: cty.String, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, - "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*FlatImageDestination)(nil).HCL2Spec())}}, + "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*common.FlatImageDestination)(nil).HCL2Spec())}}, + "wait_image_ready_timeout": &hcldec.AttrSpec{Name: "wait_image_ready_timeout", Type: cty.Number, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, diff --git a/builder/ucloud/uhost/builder_acc_test.go b/builder/ucloud/uhost/builder_acc_test.go index e7f4ad3ce..0d5a82d6d 100644 --- a/builder/ucloud/uhost/builder_acc_test.go +++ b/builder/ucloud/uhost/builder_acc_test.go @@ -2,6 +2,7 @@ package uhost import ( "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/packer" "github.com/stretchr/testify/assert" "os" @@ -20,7 +21,7 @@ func TestBuilderAcc_validateRegion(t *testing.T) { testAccPreCheck(t) - access := &AccessConfig{Region: "cn-bj2"} + access := &ucloudcommon.AccessConfig{Region: "cn-bj2"} err := access.Config() if err != nil { t.Fatalf("Error on initing UCloud AccessConfig, %s", err) @@ -94,8 +95,8 @@ func TestBuilderAcc_regionCopy(t *testing.T) { Template: testBuilderAccRegionCopy(projectId), Check: checkRegionCopy( projectId, - []ImageDestination{ - {projectId, "cn-sh2", "packer-test-regionCopy-sh", "test"}, + []ucloudcommon.ImageDestination{ + {ProjectId: projectId, Region: "cn-sh2", Name: "packer-test-regionCopy-sh", Description: "test"}, }), }) } @@ -121,21 +122,21 @@ func testBuilderAccRegionCopy(projectId string) string { }`, projectId) } -func checkRegionCopy(projectId string, imageDst []ImageDestination) builderT.TestCheckFunc { +func checkRegionCopy(projectId string, imageDst []ucloudcommon.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) + artifact, ok := artifactSet.(*ucloudcommon.Artifact) if !ok { return fmt.Errorf("unknown artifact: %#v", artifactSet) } - destSet := newImageInfoSet(nil) + destSet := ucloudcommon.NewImageInfoSet(nil) for _, dest := range imageDst { - destSet.Set(imageInfo{ + destSet.Set(ucloudcommon.ImageInfo{ Region: dest.Region, ProjectId: dest.ProjectId, }) @@ -163,9 +164,9 @@ func checkRegionCopy(projectId string, imageDst []ImageDestination) builderT.Tes if r.ProjectId == projectId && r.Region == "cn-bj2" { continue } - imageSet, err := client.describeImageByInfo(r.ProjectId, r.Region, r.ImageId) + imageSet, err := client.DescribeImageByInfo(r.ProjectId, r.Region, r.ImageId) if err != nil { - if isNotFoundError(err) { + if ucloudcommon.IsNotFoundError(err) { return fmt.Errorf("image %s in artifacts can not be found", r.ImageId) } return err @@ -196,17 +197,17 @@ func testAccPreCheck(t *testing.T) { func TestUCloudClientBaseUrlConfigurable(t *testing.T) { const url = "baseUrl" - access := &AccessConfig{BaseUrl: url} + access := &ucloudcommon.AccessConfig{BaseUrl: url} client, err := access.Client() assert.Nil(t, err) - assert.Equal(t, url, client.uaccountconn.Client.GetConfig().BaseUrl, "account conn's base url not configurable") - assert.Equal(t, url, client.uhostconn.Client.GetConfig().BaseUrl, "host conn's base url not configurable") - assert.Equal(t, url, client.unetconn.Client.GetConfig().BaseUrl, "net conn's base url not configurable") - assert.Equal(t, url, client.vpcconn.Client.GetConfig().BaseUrl, "vpc conn's base url not configurable") + assert.Equal(t, url, client.UAccountConn.Client.GetConfig().BaseUrl, "account conn's base url not configurable") + assert.Equal(t, url, client.UHostConn.Client.GetConfig().BaseUrl, "host conn's base url not configurable") + assert.Equal(t, url, client.UNetConn.Client.GetConfig().BaseUrl, "net conn's base url not configurable") + assert.Equal(t, url, client.VPCConn.Client.GetConfig().BaseUrl, "vpc conn's base url not configurable") } -func testUCloudClient() (*UCloudClient, error) { - access := &AccessConfig{Region: "cn-bj2"} +func testUCloudClient() (*ucloudcommon.UCloudClient, error) { + access := &ucloudcommon.AccessConfig{Region: "cn-bj2"} err := access.Config() if err != nil { return nil, err diff --git a/builder/ucloud/uhost/builder_test.go b/builder/ucloud/uhost/builder_test.go index 250444fb5..dca944bc5 100644 --- a/builder/ucloud/uhost/builder_test.go +++ b/builder/ucloud/uhost/builder_test.go @@ -1,6 +1,7 @@ package uhost import ( + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/packer" "reflect" "testing" @@ -120,7 +121,7 @@ func TestBuilderPrepare_ImageDestinations(t *testing.T) { t.Fatalf("should not have error: %s", err) } - if !reflect.DeepEqual(b.config.ImageDestinations, []ImageDestination{ + if !reflect.DeepEqual(b.config.ImageDestinations, []ucloudcommon.ImageDestination{ { ProjectId: "project1", Region: "region1", diff --git a/builder/ucloud/uhost/consts.go b/builder/ucloud/uhost/consts.go deleted file mode 100644 index a275c4230..000000000 --- a/builder/ucloud/uhost/consts.go +++ /dev/null @@ -1,24 +0,0 @@ -package uhost - -const ( - // defaultPasswordStr, defaultPasswordNum and defaultPasswordSpe are used to general default value of root password of UHost instance - defaultPasswordNum = "012346789" - defaultPasswordStr = "abcdefghijklmnopqrstuvwxyz" - defaultPasswordSpe = "-_" -) - -const ( - osTypeWindows = "Windows" - securityGroupNonWeb = "recommend non web" - instanceStateRunning = "Running" - instanceStateStopped = "Stopped" - bootDiskStateNormal = "Normal" - imageStateAvailable = "Available" - ipTypePrivate = "Private" -) - -var bootDiskTypeMap = map[string]string{ - "cloud_ssd": "CLOUD_SSD", - "local_normal": "LOCAL_NORMAL", - "local_ssd": "LOCAL_SSD", -} diff --git a/builder/ucloud/uhost/step_check_source_image.go b/builder/ucloud/uhost/step_check_source_image.go index b8a570610..fd77d7eee 100644 --- a/builder/ucloud/uhost/step_check_source_image.go +++ b/builder/ucloud/uhost/step_check_source_image.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -13,20 +14,20 @@ type stepCheckSourceImageId struct { func (s *stepCheckSourceImageId) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - client := state.Get("client").(*UCloudClient) + client := state.Get("client").(*ucloudcommon.UCloudClient) ui.Say("Querying source image id...") imageSet, err := client.DescribeImageById(s.SourceUHostImageId) if err != nil { - if isNotFoundError(err) { - return halt(state, err, "") + if ucloudcommon.IsNotFoundError(err) { + return ucloudcommon.Halt(state, err, "") } - return halt(state, err, fmt.Sprintf("Error on querying specified source_image_id %q", s.SourceUHostImageId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on querying specified source_image_id %q", s.SourceUHostImageId)) } - if imageSet.OsType == osTypeWindows { - return halt(state, err, "The ucloud-uhost builder does not support Windows images yet") + if imageSet.OsType == ucloudcommon.OsTypeWindows { + return ucloudcommon.Halt(state, err, "The ucloud-uhost builder does not support Windows images yet") } state.Put("source_image", imageSet) diff --git a/builder/ucloud/uhost/step_config_security_group.go b/builder/ucloud/uhost/step_config_security_group.go index eee0d0977..66c4cd27c 100644 --- a/builder/ucloud/uhost/step_config_security_group.go +++ b/builder/ucloud/uhost/step_config_security_group.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/ucloud" @@ -13,19 +14,19 @@ type stepConfigSecurityGroup struct { } func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) - conn := client.unetconn + client := state.Get("client").(*ucloudcommon.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) + securityGroupSet, err := client.DescribeFirewallById(s.SecurityGroupId) if err != nil { - if isNotFoundError(err) { + if ucloudcommon.IsNotFoundError(err) { err = fmt.Errorf("the specified security group %q does not exist", s.SecurityGroupId) - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } - return halt(state, err, fmt.Sprintf("Error on querying specified security group %q", s.SecurityGroupId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on querying specified security group %q", s.SecurityGroupId)) } state.Put("security_group_id", securityGroupSet.FWId) @@ -44,7 +45,7 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State resp, err := conn.DescribeFirewall(req) if err != nil { - return halt(state, err, "Error on querying default security group") + return ucloudcommon.Halt(state, err, "Error on querying default security group") } if resp == nil || len(resp.DataSet) < 1 { @@ -52,7 +53,7 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State } for _, item := range resp.DataSet { - if item.Type == securityGroupNonWeb { + if item.Type == ucloudcommon.SecurityGroupNonWeb { securityGroupId = item.FWId break } @@ -66,7 +67,7 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State } if securityGroupId == "" { - return halt(state, fmt.Errorf("the default security group does not exist"), "") + return ucloudcommon.Halt(state, fmt.Errorf("the default security group does not exist"), "") } state.Put("security_group_id", securityGroupId) diff --git a/builder/ucloud/uhost/step_config_subnet.go b/builder/ucloud/uhost/step_config_subnet.go index 44e22c6db..b6bb4bd53 100644 --- a/builder/ucloud/uhost/step_config_subnet.go +++ b/builder/ucloud/uhost/step_config_subnet.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -12,18 +13,18 @@ type stepConfigSubnet struct { } func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) + client := state.Get("client").(*ucloudcommon.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) + subnetSet, err := client.DescribeSubnetById(s.SubnetId) if err != nil { - if isNotFoundError(err) { + if ucloudcommon.IsNotFoundError(err) { err = fmt.Errorf("the specified subnet %q does not exist", s.SubnetId) - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } - return halt(state, err, fmt.Sprintf("Error on querying specified subnet %q", s.SubnetId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on querying specified subnet %q", s.SubnetId)) } state.Put("subnet_id", subnetSet.SubnetId) diff --git a/builder/ucloud/uhost/step_config_vpc.go b/builder/ucloud/uhost/step_config_vpc.go index 3378071f4..e8e2bcec4 100644 --- a/builder/ucloud/uhost/step_config_vpc.go +++ b/builder/ucloud/uhost/step_config_vpc.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -13,19 +14,19 @@ type stepConfigVPC struct { } func (s *stepConfigVPC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) + client := state.Get("client").(*ucloudcommon.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) + vpcSet, err := client.DescribeVPCById(s.VPCId) if err != nil { - if isNotFoundError(err) { + if ucloudcommon.IsNotFoundError(err) { err = fmt.Errorf("the specified vpc %q does not exist", s.VPCId) - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } - return halt(state, err, fmt.Sprintf("Error on querying specified vpc %q", s.VPCId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on querying specified vpc %q", s.VPCId)) } state.Put("vpc_id", vpcSet.VPCId) diff --git a/builder/ucloud/uhost/step_copy_image.go b/builder/ucloud/uhost/step_copy_image.go index dab78bbd7..690b9deca 100644 --- a/builder/ucloud/uhost/step_copy_image.go +++ b/builder/ucloud/uhost/step_copy_image.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "strings" "time" @@ -13,9 +14,10 @@ import ( ) type stepCopyUCloudImage struct { - ImageDestinations []ImageDestination - RegionId string - ProjectId string + ImageDestinations []ucloudcommon.ImageDestination + RegionId string + ProjectId string + WaitImageReadyTimeout int } func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -23,13 +25,13 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionContinue } - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.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) + artifactImages := state.Get("ucloud_images").(*ucloudcommon.ImageInfoSet) + expectedImages := ucloudcommon.NewImageInfoSet(nil) ui.Say(fmt.Sprintf("Copying images from %q...", srcImageId)) for _, v := range s.ImageDestinations { if v.ProjectId == s.ProjectId && v.Region == s.RegionId { @@ -45,10 +47,10 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) resp, err := conn.CopyCustomImage(req) if err != nil { - return halt(state, err, fmt.Sprintf("Error on copying image %q to %s:%s", srcImageId, v.ProjectId, v.Region)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on copying image %q to %s:%s", srcImageId, v.ProjectId, v.Region)) } - image := imageInfo{ + image := ucloudcommon.ImageInfo{ Region: v.Region, ProjectId: v.ProjectId, ImageId: resp.TargetImageId, @@ -62,24 +64,26 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) ui.Message("Waiting for the copied images to become available...") 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, + StartTimeout: time.Duration(s.WaitImageReadyTimeout) * time.Second, + ShouldRetry: func(err error) bool { + return ucloudcommon.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) + imageSet, err := client.DescribeImageByInfo(v.ProjectId, v.Region, v.ImageId) if err != nil { return fmt.Errorf("reading %s:%s:%s failed, %s", v.ProjectId, v.Region, v.ImageId, err) } - if imageSet.State == imageStateAvailable { + if imageSet.State == ucloudcommon.ImageStateAvailable { expectedImages.Remove(v.Id()) continue } } if len(expectedImages.GetAll()) != 0 { - return newNotCompleteError("copying image") + return ucloudcommon.NewNotCompletedError("copying image") } return nil @@ -91,7 +95,7 @@ func (s *stepCopyUCloudImage) Run(ctx context.Context, state multistep.StateBag) s = append(s, fmt.Sprintf("%s:%s:%s", v.ProjectId, v.Region, v.ImageId)) } - return halt(state, err, fmt.Sprintf("Error on waiting for copying images %q to become available", strings.Join(s, ","))) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on waiting for copying images %q to become available", strings.Join(s, ","))) } ui.Message(fmt.Sprintf("Copying image complete")) @@ -107,7 +111,7 @@ func (s *stepCopyUCloudImage) Cleanup(state multistep.StateBag) { } srcImageId := state.Get("image_id").(string) - ucloudImages := state.Get("ucloud_images").(*imageInfoSet) + ucloudImages := state.Get("ucloud_images").(*ucloudcommon.ImageInfoSet) imageInfos := ucloudImages.GetAll() if len(imageInfos) == 0 { return @@ -116,8 +120,8 @@ func (s *stepCopyUCloudImage) Cleanup(state multistep.StateBag) { } ui := state.Get("ui").(packer.Ui) - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn ui.Say(fmt.Sprintf("Deleting copied image because of cancellation or error...")) for _, v := range imageInfos { diff --git a/builder/ucloud/uhost/step_create_image.go b/builder/ucloud/uhost/step_create_image.go index 6389b4b22..235cdb7ff 100644 --- a/builder/ucloud/uhost/step_create_image.go +++ b/builder/ucloud/uhost/step_create_image.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "time" @@ -17,8 +18,8 @@ type stepCreateImage struct { } func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn instance := state.Get("instance").(*uhost.UHostInstanceSet) ui := state.Get("ui").(packer.Ui) config := state.Get("config").(*Config) @@ -32,14 +33,14 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul resp, err := conn.CreateCustomImage(req) if err != nil { - return halt(state, err, "Error on creating image") + return ucloudcommon.Halt(state, err, "Error on creating image") } ui.Message(fmt.Sprintf("Waiting for the created image %q to become available...", resp.ImageId)) err = retry.Config{ - Tries: 200, + StartTimeout: time.Duration(config.WaitImageReadyTimeout) * time.Second, ShouldRetry: func(err error) bool { - return isExpectedStateError(err) + return ucloudcommon.IsExpectedStateError(err) }, RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear, }.Run(ctx, func(ctx context.Context) error { @@ -47,26 +48,26 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul if err != nil { return err } - if inst == nil || inst.State != imageStateAvailable { - return newExpectedStateError("image", resp.ImageId) + if inst == nil || inst.State != ucloudcommon.ImageStateAvailable { + return ucloudcommon.NewExpectedStateError("image", resp.ImageId) } return nil }) if err != nil { - return halt(state, err, fmt.Sprintf("Error on waiting for image %q to become available", resp.ImageId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on waiting for image %q to become available", resp.ImageId)) } imageSet, err := client.DescribeImageById(resp.ImageId) if err != nil { - return halt(state, err, fmt.Sprintf("Error on reading image when creating %q", resp.ImageId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on reading image when creating %q", resp.ImageId)) } s.image = imageSet state.Put("image_id", imageSet.ImageId) - images := []imageInfo{ + images := []ucloudcommon.ImageInfo{ { ImageId: imageSet.ImageId, ProjectId: config.ProjectId, @@ -74,7 +75,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul }, } - state.Put("ucloud_images", newImageInfoSet(images)) + state.Put("ucloud_images", ucloudcommon.NewImageInfoSet(images)) ui.Message(fmt.Sprintf("Creating image %q complete", imageSet.ImageId)) return multistep.ActionContinue } @@ -89,8 +90,8 @@ func (s *stepCreateImage) Cleanup(state multistep.StateBag) { return } - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn ui := state.Get("ui").(packer.Ui) ui.Say("Deleting image because of cancellation or error...") diff --git a/builder/ucloud/uhost/step_create_instance.go b/builder/ucloud/uhost/step_create_instance.go index f5554ee70..3c60daef8 100644 --- a/builder/ucloud/uhost/step_create_instance.go +++ b/builder/ucloud/uhost/step_create_instance.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -26,25 +27,25 @@ type stepCreateInstance struct { } func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn ui := state.Get("ui").(packer.Ui) ui.Say("Creating Instance...") resp, err := conn.CreateUHostInstance(s.buildCreateInstanceRequest(state)) if err != nil { - return halt(state, err, "Error on creating instance") + return ucloudcommon.Halt(state, err, "Error on creating instance") } instanceId := resp.UHostIds[0] err = retry.Config{ Tries: 20, ShouldRetry: func(err error) bool { - return isExpectedStateError(err) + return ucloudcommon.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) + inst, err := client.DescribeUHostById(instanceId) if err != nil { return err } @@ -57,27 +58,27 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) return fmt.Errorf("install failed") } - if inst == nil || inst.State != instanceStateRunning { - return newExpectedStateError("instance", instanceId) + if inst == nil || inst.State != ucloudcommon.InstanceStateRunning { + return ucloudcommon.NewExpectedStateError("instance", instanceId) } return nil }) if err != nil { - return halt(state, err, fmt.Sprintf("Error on waiting for instance %q to become available", instanceId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on waiting for instance %q to become available", instanceId)) } ui.Message(fmt.Sprintf("Creating instance %q complete", instanceId)) - instance, err := client.describeUHostById(instanceId) + instance, err := client.DescribeUHostById(instanceId) if err != nil { - return halt(state, err, fmt.Sprintf("Error on reading instance when creating %q", instanceId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on reading instance when creating %q", instanceId)) } s.instanceId = instanceId state.Put("instance", instance) - if instance.BootDiskState != bootDiskStateNormal { + if instance.BootDiskState != ucloudcommon.BootDiskStateNormal { ui.Say("Waiting for boot disk of instance initialized") if s.BootDiskType == "local_normal" || s.BootDiskType == "local_ssd" { ui.Message(fmt.Sprintf("Warning: It takes around 10 mins for boot disk initialization when `boot_disk_type` is %q", s.BootDiskType)) @@ -86,23 +87,23 @@ func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) err = retry.Config{ Tries: 200, ShouldRetry: func(err error) bool { - return isExpectedStateError(err) + return ucloudcommon.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) + inst, err := client.DescribeUHostById(instanceId) if err != nil { return err } - if inst.BootDiskState != bootDiskStateNormal { - return newExpectedStateError("boot_disk of instance", instanceId) + if inst.BootDiskState != ucloudcommon.BootDiskStateNormal { + return ucloudcommon.NewExpectedStateError("boot_disk of instance", instanceId) } return nil }) if err != nil { - return halt(state, err, fmt.Sprintf("Error on waiting for boot disk of instance %q initialized", instanceId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on waiting for boot disk of instance %q initialized", instanceId)) } ui.Message(fmt.Sprintf("Waiting for boot disk of instance %q initialized complete", instanceId)) @@ -127,12 +128,12 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { ui.Say("Deleting instance...") } - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn - instance, err := client.describeUHostById(s.instanceId) + instance, err := client.DescribeUHostById(s.instanceId) if err != nil { - if isNotFoundError(err) { + if ucloudcommon.IsNotFoundError(err) { return } ui.Error(fmt.Sprintf("Error on reading instance when deleting %q, %s", @@ -140,7 +141,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { return } - if instance.State != instanceStateStopped { + if instance.State != ucloudcommon.InstanceStateStopped { stopReq := conn.NewStopUHostInstanceRequest() stopReq.UHostId = ucloud.String(s.instanceId) if _, err = conn.StopUHostInstance(stopReq); err != nil { @@ -152,17 +153,17 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { err = retry.Config{ Tries: 30, ShouldRetry: func(err error) bool { - return isExpectedStateError(err) + return ucloudcommon.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) + instance, err := client.DescribeUHostById(s.instanceId) if err != nil { return err } - if instance.State != instanceStateStopped { - return newExpectedStateError("instance", s.instanceId) + if instance.State != ucloudcommon.InstanceStateStopped { + return ucloudcommon.NewExpectedStateError("instance", s.instanceId) } return nil @@ -188,10 +189,10 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { err = retry.Config{ Tries: 30, - ShouldRetry: func(err error) bool { return !isNotFoundError(err) }, + ShouldRetry: func(err error) bool { return !ucloudcommon.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) + _, err := client.DescribeUHostById(s.instanceId) return err }) @@ -205,8 +206,8 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { } func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag) *uhost.CreateUHostInstanceRequest { - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn srcImage := state.Get("source_image").(*uhost.UHostImageSet) config := state.Get("config").(*Config) connectConfig := &config.RunConfig.Comm @@ -218,16 +219,16 @@ func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag if password == "" { password = fmt.Sprintf("%s%s%s", - s.randStringFromCharSet(5, defaultPasswordStr), - s.randStringFromCharSet(1, defaultPasswordSpe), - s.randStringFromCharSet(5, defaultPasswordNum)) + s.randStringFromCharSet(5, ucloudcommon.DefaultPasswordStr), + s.randStringFromCharSet(1, ucloudcommon.DefaultPasswordSpe), + s.randStringFromCharSet(5, ucloudcommon.DefaultPasswordNum)) if srcImage.OsType == "Linux" { connectConfig.SSHPassword = password } } req := conn.NewCreateUHostInstanceRequest() - t, _ := parseInstanceType(s.InstanceType) + t, _ := ucloudcommon.ParseInstanceType(s.InstanceType) req.CPU = ucloud.Int(t.CPU) req.Memory = ucloud.Int(t.Memory) @@ -238,6 +239,12 @@ func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag req.ChargeType = ucloud.String("Dynamic") req.Password = ucloud.String(password) + req.MachineType = ucloud.String("N") + req.MinimalCpuPlatform = ucloud.String("Intel/Auto") + if t.HostType == "o" { + req.MachineType = ucloud.String("O") + } + if v, ok := state.GetOk("security_group_id"); ok { req.SecurityGroupId = ucloud.String(v.(string)) } @@ -253,7 +260,7 @@ func (s *stepCreateInstance) buildCreateInstanceRequest(state multistep.StateBag bootDisk := uhost.UHostDisk{} bootDisk.IsBoot = ucloud.String("true") bootDisk.Size = ucloud.Int(srcImage.ImageSize) - bootDisk.Type = ucloud.String(bootDiskTypeMap[s.BootDiskType]) + bootDisk.Type = ucloud.String(ucloudcommon.BootDiskTypeMap.Convert(s.BootDiskType)) req.Disks = append(req.Disks, bootDisk) diff --git a/builder/ucloud/uhost/step_pre_validate.go b/builder/ucloud/uhost/step_pre_validate.go index 5c578d08a..843616c10 100644 --- a/builder/ucloud/uhost/step_pre_validate.go +++ b/builder/ucloud/uhost/step_pre_validate.go @@ -2,6 +2,7 @@ package uhost import ( "context" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -10,20 +11,20 @@ type stepPreValidate struct { ProjectId string Region string Zone string - ImageDestinations []ImageDestination + ImageDestinations []ucloudcommon.ImageDestination } func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { if err := s.validateProjectIds(state); err != nil { - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } if err := s.validateRegions(state); err != nil { - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } if err := s.validateZones(state); err != nil { - return halt(state, err, "") + return ucloudcommon.Halt(state, err, "") } return multistep.ActionContinue diff --git a/builder/ucloud/uhost/step_stop_instance.go b/builder/ucloud/uhost/step_stop_instance.go index 548a8d20f..463428d1a 100644 --- a/builder/ucloud/uhost/step_stop_instance.go +++ b/builder/ucloud/uhost/step_stop_instance.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "time" @@ -16,17 +17,17 @@ type stepStopInstance struct { } func (s *stepStopInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*UCloudClient) - conn := client.uhostconn + client := state.Get("client").(*ucloudcommon.UCloudClient) + conn := client.UHostConn instance := state.Get("instance").(*uhost.UHostInstanceSet) ui := state.Get("ui").(packer.Ui) - instance, err := client.describeUHostById(instance.UHostId) + instance, err := client.DescribeUHostById(instance.UHostId) if err != nil { - return halt(state, err, fmt.Sprintf("Error on reading instance when stopping %q", instance.UHostId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on reading instance when stopping %q", instance.UHostId)) } - if instance.State != instanceStateStopped { + if instance.State != ucloudcommon.InstanceStateStopped { stopReq := conn.NewStopUHostInstanceRequest() stopReq.UHostId = ucloud.String(instance.UHostId) ui.Say(fmt.Sprintf("Stopping instance %q", instance.UHostId)) @@ -44,30 +45,30 @@ func (s *stepStopInstance) Run(ctx context.Context, state multistep.StateBag) mu }) if err != nil { - return halt(state, err, fmt.Sprintf("Error on stopping instance %q", instance.UHostId)) + return ucloudcommon.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) + return ucloudcommon.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) + instance, err := client.DescribeUHostById(instance.UHostId) if err != nil { return err } - if instance.State != instanceStateStopped { - return newExpectedStateError("instance", instance.UHostId) + if instance.State != ucloudcommon.InstanceStateStopped { + return ucloudcommon.NewExpectedStateError("instance", instance.UHostId) } return nil }) if err != nil { - return halt(state, err, fmt.Sprintf("Error on waiting for stopping instance when stopping %q", instance.UHostId)) + return ucloudcommon.Halt(state, err, fmt.Sprintf("Error on waiting for stopping instance when stopping %q", instance.UHostId)) } ui.Message(fmt.Sprintf("Stopping instance %q complete", instance.UHostId)) diff --git a/command/plugin.go b/command/plugin.go index 277685a9b..619a162bf 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -75,6 +75,7 @@ import ( googlecomputeimportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-import" manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" + ucloudimportpostprocessor "github.com/hashicorp/packer/post-processor/ucloud-import" vagrantpostprocessor "github.com/hashicorp/packer/post-processor/vagrant" vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud" vspherepostprocessor "github.com/hashicorp/packer/post-processor/vsphere" @@ -188,6 +189,7 @@ var PostProcessors = map[string]packer.PostProcessor{ "googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor), "manifest": new(manifestpostprocessor.PostProcessor), "shell-local": new(shelllocalpostprocessor.PostProcessor), + "ucloud-import": new(ucloudimportpostprocessor.PostProcessor), "vagrant": new(vagrantpostprocessor.PostProcessor), "vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor), "vsphere": new(vspherepostprocessor.PostProcessor), diff --git a/examples/ucloud/basic.json b/examples/ucloud/basic.json index df1dc32af..e0ef9161b 100644 --- a/examples/ucloud/basic.json +++ b/examples/ucloud/basic.json @@ -4,30 +4,35 @@ "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-basic-bj", - "image_copy_to_mappings": [{ + "builders": [ + { + "type": "ucloud-uhost", + "public_key": "{{user `ucloud_public_key`}}", + "private_key": "{{user `ucloud_private_key`}}", "project_id": "{{user `ucloud_project_id`}}", - "region": "cn-sh2", - "description": "test", - "name": "packer-test-basic-sh" - }] - }], - "provisioners": [{ - "type": "shell", - "inline": [ - "yum install -y nginx" - ] - }] + "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-bj", + "image_copy_to_mappings": [ + { + "project_id": "{{user `ucloud_project_id`}}", + "region": "cn-sh2", + "description": "test", + "name": "packer-test-basic-sh" + } + ] + } + ], + "provisioners": [ + { + "type": "shell", + "inline": [ + "yum install -y nginx" + ] + } + ] } diff --git a/examples/ucloud/local/http/centos-6.10/ks.cfg b/examples/ucloud/local/http/centos-6.10/ks.cfg new file mode 100644 index 000000000..b6cf3436e --- /dev/null +++ b/examples/ucloud/local/http/centos-6.10/ks.cfg @@ -0,0 +1,25 @@ +install +cdrom +lang en_US.UTF-8 +keyboard us +network --bootproto=dhcp +rootpw ucloud_packer +firewall --disabled +selinux --permissive +timezone UTC +unsupported_hardware +bootloader --location=mbr +text +skipx +zerombr +clearpart --all +autopart +auth --enableshadow --passalgo=sha512 +firstboot --disabled +reboot + +%packages --nobase --ignoremissing +sudo +gcc +make +%end \ No newline at end of file diff --git a/examples/ucloud/local/local.json b/examples/ucloud/local/local.json new file mode 100644 index 000000000..ae760194b --- /dev/null +++ b/examples/ucloud/local/local.json @@ -0,0 +1,56 @@ +{"variables": { + "ucloud_public_key": "{{env `UCLOUD_PUBLIC_KEY`}}", + "ucloud_private_key": "{{env `UCLOUD_PRIVATE_KEY`}}", + "ucloud_project_id": "{{env `UCLOUD_PROJECT_ID`}}", + "disk_size": "4096", + "iso_checksum": "0da4a1206e7642906e33c0f155d2f835", + "iso_checksum_type": "md5", + "iso_name": "CentOS-6.10-x86_64-minimal.iso", + "ks_path": "centos-6.10/ks.cfg", + "mirror": "http://mirrors.ustc.edu.cn/centos", + "mirror_directory": "6.10/isos/x86_64", + "template": "centos-6.10-x86_64" +}, + "builders":[ + { + "type": "qemu", + "boot_command": [ + " text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/{{user `ks_path`}}" + ], + "boot_wait": "10s", + "disk_size": "{{user `disk_size`}}", + "http_directory": "http", + "iso_checksum": "{{user `iso_checksum`}}", + "iso_checksum_type": "{{user `iso_checksum_type`}}", + "iso_url": "{{user `mirror`}}/{{user `mirror_directory`}}/{{user `iso_name`}}", + "output_directory": "packer-{{user `template`}}-qemu", + "shutdown_command": "echo 'packer'|sudo -S shutdown -P now", + "ssh_password": "ucloud_packer", + "ssh_port": 22, + "ssh_username": "root", + "ssh_timeout": "10000s", + "vm_name": "{{ user `template` }}.raw", + "net_device": "virtio-net", + "disk_interface": "virtio", + "format": "raw", + "use_default_display": "false", + "qemuargs": [ + ["-display", "cocoa"] + ] + } + ], + "post-processors":[ + { + "type":"ucloud-import", + "public_key": "{{user `ucloud_public_key`}}", + "private_key": "{{user `ucloud_private_key`}}", + "project_id": "{{user `ucloud_project_id`}}", + "region":"cn-bj2", + "ufile_bucket_name": "packer-test", + "image_name": "packer_import_test", + "image_os_type": "CentOS", + "image_os_name": "CentOS 6.10 64位", + "format": "raw" + } + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod index a3aec0f38..dc733c418 100644 --- a/go.mod +++ b/go.mod @@ -133,7 +133,8 @@ require ( github.com/stretchr/testify v1.4.0 github.com/temoto/robotstxt v1.1.1 // indirect github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible - github.com/ucloud/ucloud-sdk-go v0.8.7 + github.com/ucloud/ucloud-sdk-go v0.12.0 + github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 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 diff --git a/go.sum b/go.sum index 009210c9a..767315ae8 100644 --- a/go.sum +++ b/go.sum @@ -414,6 +414,10 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible h1:G8i7dPMK1RCp github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/ucloud/ucloud-sdk-go v0.8.7 h1:BmXOb5RivI0Uu4oZRpjI6SQ9/y7n/H9wxTGR1txIE8o= github.com/ucloud/ucloud-sdk-go v0.8.7/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c= +github.com/ucloud/ucloud-sdk-go v0.12.0 h1:VCFN3jWg/G4wvwjG6qG5AhFuAT1JdmGvY6+4WHbuJcw= +github.com/ucloud/ucloud-sdk-go v0.12.0/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c= +github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiqocJ04wC4Znj7Ax4PGWstZijayO6ifuHHvb+vI= +github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6/go.mod h1:R5FMQxkQ+QK/9Vz+jfnJP4rZIktYrRcWmuAnbOSkROI= 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= diff --git a/post-processor/ucloud-import/post-processor.go b/post-processor/ucloud-import/post-processor.go new file mode 100644 index 000000000..69dded174 --- /dev/null +++ b/post-processor/ucloud-import/post-processor.go @@ -0,0 +1,331 @@ +package ucloudimport + +import ( + "context" + "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/retry" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" + "github.com/ucloud/ucloud-sdk-go/services/ufile" + "github.com/ucloud/ucloud-sdk-go/services/uhost" + "github.com/ucloud/ucloud-sdk-go/ucloud" + ufsdk "github.com/ufilesdk-dev/ufile-gosdk" + "log" + "net/url" + "strings" + "time" +) + +const ( + BuilderId = "packer.post-processor.ucloud-import" + + ImageFileFormatRAW = "raw" + ImageFileFormatVHD = "vhd" + ImageFileFormatVMDK = "vmdk" + ImageFileFormatQCOW2 = "qcow2" +) + +var imageFormatMap = ucloudcommon.NewStringConverter(map[string]string{ + "raw": "RAW", + "vhd": "VHD", + "vmdk": "VMDK", +}) + +// Configuration of this post processor +type Config struct { + common.PackerConfig `mapstructure:",squash"` + ucloudcommon.AccessConfig `mapstructure:",squash"` + + // Variables specific to this post processor + UFileBucket string `mapstructure:"ufile_bucket_name"` + UFileKey string `mapstructure:"ufile_key_name"` + SkipClean bool `mapstructure:"skip_clean"` + ImageName string `mapstructure:"image_name"` + ImageDescription string `mapstructure:"image_description"` + OSType string `mapstructure:"image_os_type"` + OSName string `mapstructure:"image_os_name"` + Format string `mapstructure:"format"` + WaitImageReadyTimeout int `mapstructure:"wait_image_ready_timeout"` + + ctx interpolate.Context +} + +type PostProcessor struct { + config Config +} + +// Entry point for configuration parsing when we've defined +func (p *PostProcessor) Configure(raws ...interface{}) error { + err := config.Decode(&p.config, &config.DecodeOpts{ + Interpolate: true, + InterpolateContext: &p.config.ctx, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{ + "ufile_key_name", + }, + }, + }, raws...) + if err != nil { + return err + } + + // Set defaults + if p.config.UFileKey == "" { + p.config.UFileKey = "packer-import-{{timestamp}}." + p.config.Format + } + + if p.config.WaitImageReadyTimeout <= 0 { + p.config.WaitImageReadyTimeout = ucloudcommon.DefaultCreateImageTimeout + } + + errs := new(packer.MultiError) + + // Check and render ufile_key_name + if err = interpolate.Validate(p.config.UFileKey, &p.config.ctx); err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Error parsing ufile_key_name template: %s", err)) + } + + // Check we have ucloud access variables defined somewhere + errs = packer.MultiErrorAppend(errs, p.config.AccessConfig.Prepare(&p.config.ctx)...) + + // define all our required parameters + templates := map[string]*string{ + "ufile_bucket_name": &p.config.UFileBucket, + "image_name": &p.config.ImageName, + "image_os_type": &p.config.OSType, + "image_os_name": &p.config.OSName, + "format": &p.config.Format, + } + // Check out required params are defined + for key, ptr := range templates { + if *ptr == "" { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("%s must be set", key)) + } + } + + imageName := p.config.ImageName + if !ucloudcommon.ImageNamePattern.MatchString(imageName) { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("expected %q to be 1-63 characters and only support chinese, english, numbers, '-_,.:[]', got %q", "image_name", imageName)) + } + + switch p.config.Format { + case ImageFileFormatVHD, ImageFileFormatRAW, ImageFileFormatVMDK, ImageFileFormatQCOW2: + default: + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("expected %q only be one of 'raw', 'vhd', 'vmdk', or 'qcow2', got %q", "format", p.config.Format)) + } + + // Anything which flagged return back up the stack + if len(errs.Errors) > 0 { + return errs + } + + packer.LogSecretFilter.Set(p.config.PublicKey, p.config.PrivateKey) + log.Println(p.config) + return nil +} + +func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) { + var err error + + client, err := p.config.Client() + if err != nil { + return nil, false, false, fmt.Errorf("Failed to connect ucloud client %s", err) + } + uhostconn := client.UHostConn + ufileconn := client.UFileConn + + // Render this key since we didn't in the configure phase + p.config.UFileKey, err = interpolate.Render(p.config.UFileKey, &p.config.ctx) + if err != nil { + return nil, false, false, fmt.Errorf("Error rendering ufile_key_name template: %s", err) + } + + ui.Message(fmt.Sprintf("Rendered ufile_key_name as %s", p.config.UFileKey)) + + ui.Message("Looking for image in artifact") + // Locate the files output from the builder + var source string + for _, path := range artifact.Files() { + if strings.HasSuffix(path, "."+p.config.Format) { + source = path + break + } + } + + // Hope we found something useful + if source == "" { + return nil, false, false, fmt.Errorf("No %s image file found in artifact from builder", p.config.Format) + } + + keyName := p.config.UFileKey + bucketName := p.config.UFileBucket + + // query bucket + domain, err := queryBucket(ufileconn, bucketName) + if err != nil { + return nil, false, false, fmt.Errorf("Failed to query bucket, %s", err) + } + + var bucketHost string + if p.config.BaseUrl != "" { + // skip error because it has been validated by prepare + urlObj, _ := url.Parse(p.config.BaseUrl) + bucketHost = urlObj.Host + } else { + bucketHost = "api.ucloud.cn" + } + + fileHost := strings.SplitN(domain, ".", 2)[1] + + config := &ufsdk.Config{ + PublicKey: p.config.PublicKey, + PrivateKey: p.config.PrivateKey, + BucketName: bucketName, + FileHost: fileHost, + BucketHost: bucketHost, + } + + ui.Say(fmt.Sprintf("Waiting for uploading image file %s to UFile: %s/%s...", source, bucketName, keyName)) + + // upload file to bucket + ufileUrl, err := uploadFile(ufileconn, config, keyName, source) + if err != nil { + return nil, false, false, fmt.Errorf("Failed to Upload image file, %s", err) + } + + ui.Say(fmt.Sprintf("Image file %s has been uploaded to UFile: %s/%s", source, bucketName, keyName)) + + importImageRequest := p.buildImportImageRequest(uhostconn, ufileUrl) + importImageResponse, err := uhostconn.ImportCustomImage(importImageRequest) + if err != nil { + return nil, false, false, fmt.Errorf("Failed to import image from UFile: %s/%s, %s", bucketName, keyName, err) + } + + ui.Say(fmt.Sprintf("Waiting for importing image from UFile: %s/%s ...", bucketName, keyName)) + + imageId := importImageResponse.ImageId + err = retry.Config{ + StartTimeout: time.Duration(p.config.WaitImageReadyTimeout) * time.Second, + ShouldRetry: func(err error) bool { + return ucloudcommon.IsExpectedStateError(err) + }, + RetryDelay: (&retry.Backoff{InitialBackoff: 2 * time.Second, MaxBackoff: 12 * time.Second, Multiplier: 2}).Linear, + }.Run(ctx, func(ctx context.Context) error { + image, err := client.DescribeImageById(imageId) + if err != nil { + return err + } + + if image.State == ucloudcommon.ImageStateUnavailable { + return fmt.Errorf("Unavailable importing image %q", imageId) + } + + if image.State != ucloudcommon.ImageStateAvailable { + return ucloudcommon.NewExpectedStateError("image", imageId) + } + + return nil + }) + + if err != nil { + return nil, false, false, fmt.Errorf("Error on waiting for importing image %q from UFile: %s/%s, %s", + imageId, bucketName, keyName, err) + } + + // Add the reported UCloud image ID to the artifact list + ui.Say(fmt.Sprintf("Importing created ucloud image %q in region %q Complete.", imageId, p.config.Region)) + images := []ucloudcommon.ImageInfo{ + { + ImageId: imageId, + ProjectId: p.config.ProjectId, + Region: p.config.Region, + }, + } + + artifact = &ucloudcommon.Artifact{ + UCloudImages: ucloudcommon.NewImageInfoSet(images), + BuilderIdValue: BuilderId, + Client: client, + } + + if !p.config.SkipClean { + ui.Message(fmt.Sprintf("Deleting import source UFile: %s/%s", p.config.UFileBucket, p.config.UFileKey)) + if err = deleteFile(config, p.config.UFileKey); err != nil { + return nil, false, false, fmt.Errorf("Failed to delete UFile: %s/%s, %s", p.config.UFileBucket, p.config.UFileKey, err) + } + } + + return artifact, false, false, nil +} + +func (p *PostProcessor) buildImportImageRequest(conn *uhost.UHostClient, privateUrl string) *uhost.ImportCustomImageRequest { + req := conn.NewImportCustomImageRequest() + req.ImageName = ucloud.String(p.config.ImageName) + req.ImageDescription = ucloud.String(p.config.ImageDescription) + req.UFileUrl = ucloud.String(privateUrl) + req.OsType = ucloud.String(p.config.OSType) + req.OsName = ucloud.String(p.config.OSName) + req.Format = ucloud.String(imageFormatMap.Convert(p.config.Format)) + req.Auth = ucloud.Bool(true) + return req +} + +func queryBucket(conn *ufile.UFileClient, bucketName string) (string, error) { + req := conn.NewDescribeBucketRequest() + req.BucketName = ucloud.String(bucketName) + resp, err := conn.DescribeBucket(req) + if err != nil { + return "", fmt.Errorf("error on reading bucket %q when create bucket, %s", bucketName, err) + } + + if len(resp.DataSet) < 1 { + return "", fmt.Errorf("the bucket %s is not exit", bucketName) + } + + return resp.DataSet[0].Domain.Src[0], nil +} + +func uploadFile(conn *ufile.UFileClient, config *ufsdk.Config, keyName, source string) (string, error) { + reqFile, err := ufsdk.NewFileRequest(config, nil) + if err != nil { + return "", fmt.Errorf("error on building upload file request, %s", err) + } + + // upload file in segments + err = reqFile.AsyncMPut(source, keyName, "") + if err != nil { + return "", fmt.Errorf("error on upload file, %s, details: %s", err, reqFile.DumpResponse(true)) + } + + reqBucket := conn.NewDescribeBucketRequest() + reqBucket.BucketName = ucloud.String(config.BucketName) + resp, err := conn.DescribeBucket(reqBucket) + if err != nil { + return "", fmt.Errorf("error on reading bucket list when upload file, %s", err) + } + + if resp.DataSet[0].Type == "private" { + return reqFile.GetPrivateURL(keyName, time.Duration(24*60*60)*time.Second), nil + } + + return reqFile.GetPublicURL(keyName), nil +} + +func deleteFile(config *ufsdk.Config, keyName string) error { + req, err := ufsdk.NewFileRequest(config, nil) + if err != nil { + return fmt.Errorf("error on new deleting file, %s", err) + } + req.DeleteFile(keyName) + if err != nil { + return fmt.Errorf("error on deleting file, %s", err) + } + + return nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/accessor.go b/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/accessor.go index efe834c6b..5dc2ac8e2 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/accessor.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/private/utils/accessor.go @@ -57,7 +57,10 @@ func ValueAtPath(v interface{}, path string) (interface{}, error) { } if rv.Kind() == reflect.Struct { - itemV := rv.FieldByName(components[0]) + itemV := rv.FieldByNameFunc(func(s string) bool { + return strings.ToLower(s) == strings.ToLower(components[0]) + }) + if !itemV.IsValid() { return nil, errors.Errorf("path %s is invalid for struct", path) } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uaccount/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uaccount/client.go index 26c8f390c..2dd7e401b 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uaccount/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uaccount/client.go @@ -1,3 +1,5 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + package uaccount import ( @@ -12,7 +14,8 @@ type UAccountClient struct { // NewClient will return a instance of UAccountClient func NewClient(config *ucloud.Config, credential *auth.Credential) *UAccountClient { - client := ucloud.NewClient(config, credential) + meta := ucloud.ClientMeta{Product: "UAccount"} + client := ucloud.NewClientWithMeta(config, credential, meta) return &UAccountClient{ client, } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/client.go new file mode 100644 index 000000000..479a8df2f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/client.go @@ -0,0 +1,21 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/ucloud-sdk-go/ucloud/auth" +) + +// UFileClient is the client of UFile +type UFileClient struct { + *ucloud.Client +} + +// NewClient will return a instance of UFileClient +func NewClient(config *ucloud.Config, credential *auth.Credential) *UFileClient { + client := ucloud.NewClient(config, credential) + return &UFileClient{ + client, + } +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_bucket.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_bucket.go new file mode 100644 index 000000000..081ac606d --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_bucket.go @@ -0,0 +1,63 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateBucketRequest is request schema for CreateBucket action +type CreateBucketRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 待创建Bucket的名称,具有全局唯一性 + BucketName *string `required:"true"` + + // Bucket访问类型,public或private; 默认为private + Type *string `required:"false"` +} + +// CreateBucketResponse is response schema for CreateBucket action +type CreateBucketResponse struct { + response.CommonBase + + // 已创建Bucket的ID + BucketId string + + // 已创建Bucket的名称 + BucketName string +} + +// NewCreateBucketRequest will create request of CreateBucket action. +func (c *UFileClient) NewCreateBucketRequest() *CreateBucketRequest { + req := &CreateBucketRequest{} + + // 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 +} + +// CreateBucket - 创建Bucket +func (c *UFileClient) CreateBucket(req *CreateBucketRequest) (*CreateBucketResponse, error) { + var err error + var res CreateBucketResponse + + reqCopier := *req + + err = c.Client.InvokeAction("CreateBucket", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_ufile_token.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_ufile_token.go new file mode 100644 index 000000000..47887bf79 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/create_ufile_token.go @@ -0,0 +1,69 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateUFileTokenRequest is request schema for CreateUFileToken action +type CreateUFileTokenRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"false"` + + // 令牌允许操作的bucket,默认*表示全部 + AllowedBuckets []string `required:"false"` + + // 令牌允许执行的操作,[ TOKEN_ALLOW_NONE , TOKEN_ALLOW_READ , TOKEN_ALLOW_WRITE , TOKEN_ALLOW_DELETE , TOKEN_ALLOW_LIST, TOKEN_ALLOW_IOP , TOKEN_ALLOW_DP ]。默认TOKEN_ALLOW_NONE + AllowedOps []string `required:"false"` + + // 令牌允许操作的key前缀,默认*表示全部 + AllowedPrefixes []string `required:"false"` + + // 令牌的超时时间点(时间戳),默认一天;注意:过期时间不能超过 4102416000 + ExpireTime *int `required:"false"` + + // 令牌名称 + TokenName *string `required:"true"` +} + +// CreateUFileTokenResponse is response schema for CreateUFileToken action +type CreateUFileTokenResponse struct { + response.CommonBase + + // 创建令牌的token_id + TokenId string +} + +// NewCreateUFileTokenRequest will create request of CreateUFileToken action. +func (c *UFileClient) NewCreateUFileTokenRequest() *CreateUFileTokenRequest { + req := &CreateUFileTokenRequest{} + + // 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 +} + +// CreateUFileToken - 创建ufile令牌 +func (c *UFileClient) CreateUFileToken(req *CreateUFileTokenRequest) (*CreateUFileTokenResponse, error) { + var err error + var res CreateUFileTokenResponse + + reqCopier := *req + + err = c.Client.InvokeAction("CreateUFileToken", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_bucket.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_bucket.go new file mode 100644 index 000000000..8d62e1fd3 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_bucket.go @@ -0,0 +1,60 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteBucketRequest is request schema for DeleteBucket action +type DeleteBucketRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // 待删除Bucket的名称 + BucketName *string `required:"true"` +} + +// DeleteBucketResponse is response schema for DeleteBucket action +type DeleteBucketResponse struct { + response.CommonBase + + // Bucket的ID + BucketId string + + // Bucket的名称 + BucketName string +} + +// NewDeleteBucketRequest will create request of DeleteBucket action. +func (c *UFileClient) NewDeleteBucketRequest() *DeleteBucketRequest { + req := &DeleteBucketRequest{} + + // 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 +} + +// DeleteBucket - 删除Bucket +func (c *UFileClient) DeleteBucket(req *DeleteBucketRequest) (*DeleteBucketResponse, error) { + var err error + var res DeleteBucketResponse + + reqCopier := *req + // In order to ignore the parameters about Region + reqCopier.Region = ucloud.String("") + + err = c.Client.InvokeAction("DeleteBucket", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_ufile_token.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_ufile_token.go new file mode 100644 index 000000000..c8a460089 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/delete_ufile_token.go @@ -0,0 +1,54 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteUFileTokenRequest is request schema for DeleteUFileToken action +type DeleteUFileTokenRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 令牌ID + TokenId *string `required:"true"` +} + +// DeleteUFileTokenResponse is response schema for DeleteUFileToken action +type DeleteUFileTokenResponse struct { + response.CommonBase +} + +// NewDeleteUFileTokenRequest will create request of DeleteUFileToken action. +func (c *UFileClient) NewDeleteUFileTokenRequest() *DeleteUFileTokenRequest { + req := &DeleteUFileTokenRequest{} + + // 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 +} + +// DeleteUFileToken - 删除令牌 +func (c *UFileClient) DeleteUFileToken(req *DeleteUFileTokenRequest) (*DeleteUFileTokenResponse, error) { + var err error + var res DeleteUFileTokenResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DeleteUFileToken", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_bucket.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_bucket.go new file mode 100644 index 000000000..6cc873192 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_bucket.go @@ -0,0 +1,64 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeBucketRequest is request schema for DescribeBucket action +type DescribeBucketRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // 待获取Bucket的名称,若不提供,则获取所有Bucket + BucketName *string `required:"false"` + + // 获取所有Bucket列表的限制数目,默认为20 + Limit *int `required:"false"` + + // 获取所有Bucket列表的偏移数目,默认为0 + Offset *int `required:"false"` +} + +// DescribeBucketResponse is response schema for DescribeBucket action +type DescribeBucketResponse struct { + response.CommonBase + + // Bucket的描述信息 参数见 UFileBucketSet + DataSet []UFileBucketSet +} + +// NewDescribeBucketRequest will create request of DescribeBucket action. +func (c *UFileClient) NewDescribeBucketRequest() *DescribeBucketRequest { + req := &DescribeBucketRequest{} + + // 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 +} + +// DescribeBucket - 获取Bucket的描述信息 +func (c *UFileClient) DescribeBucket(req *DescribeBucketRequest) (*DescribeBucketResponse, error) { + var err error + var res DescribeBucketResponse + + reqCopier := *req + + // In order to ignore the parameters about Region + reqCopier.Region = ucloud.String("") + + err = c.Client.InvokeAction("DescribeBucket", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_ufile_token.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_ufile_token.go new file mode 100644 index 000000000..c935f4309 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/describe_ufile_token.go @@ -0,0 +1,66 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeUFileTokenRequest is request schema for DescribeUFileToken action +type DescribeUFileTokenRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"false"` + + // 0表示显示部分token信息;不传递和其他情况表示显示全部token信息 + Display *int `required:"false"` + + // 令牌ID,只返回指定ID信息,否则拉取所有令牌 + TokenId *string `required:"false"` +} + +// DescribeUFileTokenResponse is response schema for DescribeUFileToken action +type DescribeUFileTokenResponse struct { + response.CommonBase + + // 操作名称 + Action string + + // 令牌描述信息 + DataSet []UFileTokenSet + + // 返回码 + RetCode int +} + +// NewDescribeUFileTokenRequest will create request of DescribeUFileToken action. +func (c *UFileClient) NewDescribeUFileTokenRequest() *DescribeUFileTokenRequest { + req := &DescribeUFileTokenRequest{} + + // 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 +} + +// DescribeUFileToken - 获取令牌信息 +func (c *UFileClient) DescribeUFileToken(req *DescribeUFileTokenRequest) (*DescribeUFileTokenResponse, error) { + var err error + var res DescribeUFileTokenResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DescribeUFileToken", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/doc.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/doc.go new file mode 100644 index 000000000..1be7726ee --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/doc.go @@ -0,0 +1,13 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +/* +Package ufile include resources of ucloud ufile product + +See also + + - API: https://docs.ucloud.cn/api/ufile-api/index + - Product: https://www.ucloud.cn/site/product/ufile.html + +for detail. +*/ +package ufile diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_bucket_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_bucket_set.go new file mode 100644 index 000000000..5747ae002 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_bucket_set.go @@ -0,0 +1,45 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +/* +UFileBucketSet - DescribeBucket + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type UFileBucketSet struct { + + // Bucket所属业务, general或vod或udb general: 普通业务; vod: 视频云业务; udb: 云数据库业务 + Biz string + + // Bucket的ID + BucketId string + + // Bucket名称 + BucketName string + + // 与Bucket关联的CND加速域名的ID列表 + CdnDomainId []string + + // Bucket的创建时间 + CreateTime int + + // Bucket的域名集合 参数见 UFileDomainSet + Domain UFileDomainSet + + // 是否存在自定义域名。0不存在,1存在,2错误 + HasUserDomain int + + // Bucket的修改时间 + ModifyTime int + + // Bucket所属地域 + Region string + + // 所属业务组 + Tag string + + // Bucket访问类型 + Type string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_domain_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_domain_set.go new file mode 100644 index 000000000..6960186d0 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_domain_set.go @@ -0,0 +1,24 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +/* +UFileDomainSet - DescribeBucket + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type UFileDomainSet struct { + + // UCDN加速域名 + Cdn []string + + // 用户自定义CDN加速域名 + CustomCdn []string + + // 用户自定义源站域名 + CustomSrc []string + + // 源站域名 + Src []string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_token_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_token_set.go new file mode 100644 index 000000000..596773ffe --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/types_ufile_token_set.go @@ -0,0 +1,45 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +/* +UFileTokenSet - ufile令牌集合 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type UFileTokenSet struct { + + // 令牌允许操作的bucket + AllowedBuckets []string + + // 令牌允许执行的操作,[ TOKEN_ALLOW_NONE , TOKEN_ALLOW_READ , TOKEN_ALLOW_WRITE , TOKEN_ALLOW_DELETE , TOKEN_ALLOW_LIST, TOKEN_ALLOW_IOP , TOKEN_ALLOW_DP ] + AllowedOps []string + + // 令牌允许操作的key前缀 + AllowedPrefixes []string + + // 创建时间 + CreateTime int + + // 令牌的超时时间点 + ExpireTime int + + // 修改时间 + ModifyTime int + + // 令牌私钥 + PrivateKey string + + // 令牌公钥 + PublicKey string + + // 所属地区 + Region string + + // 令牌ID + TokenId string + + // 令牌名称 + TokenName string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_bucket.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_bucket.go new file mode 100644 index 000000000..ff5c79885 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_bucket.go @@ -0,0 +1,62 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// UpdateBucketRequest is request schema for UpdateBucket action +type UpdateBucketRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // 待修改Bucket的名称 + BucketName *string `required:"true"` + + // Bucket访问类型;public或private + Type *string `required:"true"` +} + +// UpdateBucketResponse is response schema for UpdateBucket action +type UpdateBucketResponse struct { + response.CommonBase + + // Bucket的ID + BucketId string + + // Bucket的名称 + BucketName string +} + +// NewUpdateBucketRequest will create request of UpdateBucket action. +func (c *UFileClient) NewUpdateBucketRequest() *UpdateBucketRequest { + req := &UpdateBucketRequest{} + + // 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 +} + +// UpdateBucket - 更改Bucket的属性 +func (c *UFileClient) UpdateBucket(req *UpdateBucketRequest) (*UpdateBucketResponse, error) { + var err error + var res UpdateBucketResponse + + reqCopier := *req + reqCopier.Region = ucloud.String("") + + err = c.Client.InvokeAction("UpdateBucket", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_ufile_token.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_ufile_token.go new file mode 100644 index 000000000..e801cb873 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/ufile/update_ufile_token.go @@ -0,0 +1,69 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package ufile + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// UpdateUFileTokenRequest is request schema for UpdateUFileToken action +type UpdateUFileTokenRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"false"` + + // 令牌允许操作的bucket + AllowedBuckets []string `required:"false"` + + // 令牌允许执行的操作,[ TOKEN_ALLOW_NONE , TOKEN_ALLOW_READ , TOKEN_ALLOW_WRITE , TOKEN_ALLOW_DELETE , TOKEN_ALLOW_LIST, TOKEN_ALLOW_IOP , TOKEN_ALLOW_DP ] + AllowedOps []string `required:"false"` + + // 令牌允许操作的key前缀 + AllowedPrefixes []string `required:"false"` + + // 令牌的超时时间点(时间戳);注意:过期时间不能超过 4102416000 + ExpireTime *int `required:"false"` + + // 令牌ID + TokenId *string `required:"true"` + + // 令牌名称 + TokenName *string `required:"false"` +} + +// UpdateUFileTokenResponse is response schema for UpdateUFileToken action +type UpdateUFileTokenResponse struct { + response.CommonBase +} + +// NewUpdateUFileTokenRequest will create request of UpdateUFileToken action. +func (c *UFileClient) NewUpdateUFileTokenRequest() *UpdateUFileTokenRequest { + req := &UpdateUFileTokenRequest{} + + // 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 +} + +// UpdateUFileToken - 更新令牌的操作权限,可操作key的前缀,可操作bucket和令牌超时时间点 +func (c *UFileClient) UpdateUFileToken(req *UpdateUFileTokenRequest) (*UpdateUFileTokenResponse, error) { + var err error + var res UpdateUFileTokenResponse + + reqCopier := *req + + err = c.Client.InvokeAction("UpdateUFileToken", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/client.go index 85b57089a..bf38bf07b 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/client.go @@ -1,3 +1,5 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + package uhost import ( @@ -12,7 +14,8 @@ type UHostClient struct { // NewClient will return a instance of UHostClient func NewClient(config *ucloud.Config, credential *auth.Credential) *UHostClient { - client := ucloud.NewClient(config, credential) + meta := ucloud.ClientMeta{Product: "UHost"} + client := ucloud.NewClientWithMeta(config, credential, meta) return &UHostClient{ client, } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_isolation_group.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_isolation_group.go new file mode 100644 index 000000000..1c64bf7b2 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_isolation_group.go @@ -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 CreateIsolationGroup + +package uhost + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateIsolationGroupRequest is request schema for CreateIsolationGroup action +type CreateIsolationGroupRequest struct { + request.CommonBase + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // [公共参数] 项目id + // ProjectId *string `required:"false"` + + // 硬件隔离组名称。请遵照[[api:uhost-api:specification|字段规范]]设定隔离组名称。 + GroupName *string `required:"true"` + + // 备注。请遵照[[api:uhost-api:specification|字段规范]]设定隔离组备注。 + Remark *string `required:"false"` +} + +// CreateIsolationGroupResponse is response schema for CreateIsolationGroup action +type CreateIsolationGroupResponse struct { + response.CommonBase + + // 硬件隔离组id + GroupId string +} + +// NewCreateIsolationGroupRequest will create request of CreateIsolationGroup action. +func (c *UHostClient) NewCreateIsolationGroupRequest() *CreateIsolationGroupRequest { + req := &CreateIsolationGroupRequest{} + + // 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 +} + +// CreateIsolationGroup - 创建硬件隔离组,组内机器严格隔离在不同宿主机上。 +func (c *UHostClient) CreateIsolationGroup(req *CreateIsolationGroupRequest) (*CreateIsolationGroupResponse, error) { + var err error + var res CreateIsolationGroupResponse + + err = c.Client.InvokeAction("CreateIsolationGroup", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_uhost_instance.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_uhost_instance.go index 805211729..0a87ab99e 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_uhost_instance.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/create_uhost_instance.go @@ -90,6 +90,9 @@ type CreateUHostInstanceRequest struct { // 防火墙Id,默认:Web推荐防火墙。如何查询SecurityGroupId请参见 [DescribeSecurityGroup](../unet-api/describe_security_group.html) SecurityGroupId *string `required:"false"` + // 硬件隔离组id。可通过DescribeIsolationGroup获取。 + IsolationGroup *string `required:"false"` + // 【暂不支持】cloudinit方式下,用户初始化脚本 UserDataScript *string `required:"false"` @@ -111,6 +114,12 @@ type CreateUHostInstanceRequest struct { // 最低cpu平台,枚举值["Intel/Auto", "Intel/LvyBridge", "Intel/Haswell", "Intel/Broadwell", "Intel/Skylake", "Intel/Cascadelake"(只有O型云主机可选)] MinimalCpuPlatform *string `required:"false"` + // 【批量创建主机时必填】最大创建主机数量,取值范围是[1,100]; + MaxCount *int `required:"false"` + + // GPU类型,枚举值["K80", "P40", "V100"],MachineType为G时必填 + GpuType *string `required:"false"` + // NetworkInterface NetworkInterface []CreateUHostInstanceParamNetworkInterface } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/delete_isolation_group.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/delete_isolation_group.go new file mode 100644 index 000000000..64f7510ce --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/delete_isolation_group.go @@ -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 DeleteIsolationGroup + +package uhost + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteIsolationGroupRequest is request schema for DeleteIsolationGroup action +type DeleteIsolationGroupRequest struct { + request.CommonBase + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // [公共参数] 项目id + // ProjectId *string `required:"false"` + + // 硬件隔离组id + GroupId *string `required:"true"` +} + +// DeleteIsolationGroupResponse is response schema for DeleteIsolationGroup action +type DeleteIsolationGroupResponse struct { + response.CommonBase + + // 硬件隔离组id + GroupId string +} + +// NewDeleteIsolationGroupRequest will create request of DeleteIsolationGroup action. +func (c *UHostClient) NewDeleteIsolationGroupRequest() *DeleteIsolationGroupRequest { + req := &DeleteIsolationGroupRequest{} + + // 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 +} + +// DeleteIsolationGroup - 删除硬件隔离组。 +func (c *UHostClient) DeleteIsolationGroup(req *DeleteIsolationGroupRequest) (*DeleteIsolationGroupResponse, error) { + var err error + var res DeleteIsolationGroupResponse + + err = c.Client.InvokeAction("DeleteIsolationGroup", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/describe_isolation_group.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/describe_isolation_group.go new file mode 100644 index 000000000..7be4f9043 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/describe_isolation_group.go @@ -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 DescribeIsolationGroup + +package uhost + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeIsolationGroupRequest is request schema for DescribeIsolationGroup action +type DescribeIsolationGroupRequest struct { + request.CommonBase + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // [公共参数] 项目id + // ProjectId *string `required:"false"` + + // 待查的硬件隔离组id + GroupId *string `required:"false"` + + // 列表起始位置偏移量,默认为0 + Offset *int `required:"false"` + + // 返回数据长度,默认为20,最大100 + Limit *int `required:"false"` +} + +// DescribeIsolationGroupResponse is response schema for DescribeIsolationGroup action +type DescribeIsolationGroupResponse struct { + response.CommonBase + + // 硬件隔离组集合。参见数据结构IsolationGroup。 + IsolationGroupSet []IsolationGroup +} + +// NewDescribeIsolationGroupRequest will create request of DescribeIsolationGroup action. +func (c *UHostClient) NewDescribeIsolationGroupRequest() *DescribeIsolationGroupRequest { + req := &DescribeIsolationGroupRequest{} + + // 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 +} + +// DescribeIsolationGroup - 查询硬件隔离组列表。 +func (c *UHostClient) DescribeIsolationGroup(req *DescribeIsolationGroupRequest) (*DescribeIsolationGroupResponse, error) { + var err error + var res DescribeIsolationGroupResponse + + err = c.Client.InvokeAction("DescribeIsolationGroup", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/leave_isolation_group.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/leave_isolation_group.go new file mode 100644 index 000000000..4b0b683c7 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/leave_isolation_group.go @@ -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 LeaveIsolationGroup + +package uhost + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// LeaveIsolationGroupRequest is request schema for LeaveIsolationGroup action +type LeaveIsolationGroupRequest struct { + request.CommonBase + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // [公共参数] 可用区信息 + // Zone *string `required:"false"` + + // [公共参数] 项目id + // ProjectId *string `required:"false"` + + // 硬件隔离组id + GroupId *string `required:"true"` + + // 主机id + UHostId *string `required:"true"` +} + +// LeaveIsolationGroupResponse is response schema for LeaveIsolationGroup action +type LeaveIsolationGroupResponse struct { + response.CommonBase + + // 主机id + UHostId string +} + +// NewLeaveIsolationGroupRequest will create request of LeaveIsolationGroup action. +func (c *UHostClient) NewLeaveIsolationGroupRequest() *LeaveIsolationGroupRequest { + req := &LeaveIsolationGroupRequest{} + + // 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 +} + +// LeaveIsolationGroup - 移除硬件隔离组中的主机 +func (c *UHostClient) LeaveIsolationGroup(req *LeaveIsolationGroupRequest) (*LeaveIsolationGroupResponse, error) { + var err error + var res LeaveIsolationGroupResponse + + err = c.Client.InvokeAction("LeaveIsolationGroup", req, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_isolation_group.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_isolation_group.go new file mode 100644 index 000000000..a82ea65f3 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_isolation_group.go @@ -0,0 +1,22 @@ +package uhost + +/* +IsolationGroup - 硬件隔离组信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn for detail. +*/ +type IsolationGroup struct { + + // 硬件隔离组名称 + GroupName string + + // 硬件隔离组id + GroupId string + + // 每个可用区中的机器数量。参见数据结构SpreadInfo。 + SpreadInfoSet []SpreadInfo + + // 备注 + Remark string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_spread_info.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_spread_info.go new file mode 100644 index 000000000..b8d2ef8c3 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/uhost/types_spread_info.go @@ -0,0 +1,16 @@ +package uhost + +/* +SpreadInfo - 每个可用区中硬件隔离组信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn for detail. +*/ +type SpreadInfo struct { + + // 可用区信息 + Zone string + + // 可用区中硬件隔离组中云主机的数量,不超过7。 + UHostCount int +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/client.go index 745ad23ee..e714444f2 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/client.go @@ -1,3 +1,5 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + package unet import ( @@ -12,7 +14,8 @@ type UNetClient struct { // NewClient will return a instance of UNetClient func NewClient(config *ucloud.Config, credential *auth.Credential) *UNetClient { - client := ucloud.NewClient(config, credential) + meta := ucloud.ClientMeta{Product: "UNet"} + client := ucloud.NewClientWithMeta(config, credential, meta) return &UNetClient{ client, } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/describe_vip.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/describe_vip.go index fd8c972f0..df70ab839 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/describe_vip.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/describe_vip.go @@ -1,5 +1,4 @@ -//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 DescribeVIP +// Code is generated by ucloud-model, DO NOT EDIT IT. package unet @@ -12,17 +11,17 @@ import ( type DescribeVIPRequest struct { request.CommonBase + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + // [公共参数] 地域。 参见 [地域和可用区列表](../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"` - - // vpc的id,指定SubnetId时必填 - VPCId *string `required:"false"` + // 业务组 + BusinessId *string `required:"false"` // 子网id,不指定则获取VPCId下的所有vip SubnetId *string `required:"false"` @@ -30,22 +29,25 @@ type DescribeVIPRequest struct { // 业务组名称, 默认为 Default Tag *string `required:"false"` - // 业务组 - BusinessId *string `required:"false"` + // VIP ID + VIPId *string `required:"false"` + + // vpc的id,指定SubnetId时必填 + VPCId *string `required:"false"` } // DescribeVIPResponse is response schema for DescribeVIP action type DescribeVIPResponse struct { response.CommonBase - // 内网VIP详情,请见VIPDetailSet - VIPSet []VIPDetailSet - // 内网VIP地址列表 DataSet []string // vip数量 TotalCount int + + // 内网VIP详情,请见VIPDetailSet + VIPSet []VIPDetailSet } // NewDescribeVIPRequest will create request of DescribeVIP action. @@ -65,7 +67,9 @@ func (c *UNetClient) DescribeVIP(req *DescribeVIPRequest) (*DescribeVIPResponse, var err error var res DescribeVIPResponse - err = c.Client.InvokeAction("DescribeVIP", req, &res) + reqCopier := *req + + err = c.Client.InvokeAction("DescribeVIP", &reqCopier, &res) if err != nil { return &res, err } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vip_detail_set.go similarity index 65% rename from vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go rename to vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vip_detail_set.go index 44bcbf41a..918daa3eb 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vipdetail_set.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/types_vip_detail_set.go @@ -1,34 +1,42 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + package unet /* VIPDetailSet - VIPDetailSet this model is auto created by ucloud code generater for open api, -you can also see https://docs.ucloud.cn for detail. +you can also see https://docs.ucloud.cn/api for detail. */ type VIPDetailSet struct { - // 地域 - Zone string - - // 虚拟ip id - VIPId string - // 创建时间 CreateTime int + // VIP名称 + Name string + // 真实主机ip RealIp string - // 虚拟ip - VIP string + // VIP备注 + Remark string // 子网id SubnetId string + // VIP所属业务组 + Tag string + + // 虚拟ip + VIP string + + // 虚拟ip id + VIPId string + // VPC id VPCId string - // Virtual IP 名称 - Name string + // 地域 + Zone string } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/update_vip_attribute.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/update_vip_attribute.go new file mode 100644 index 000000000..cc36dee25 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/unet/update_vip_attribute.go @@ -0,0 +1,63 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package unet + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// UpdateVIPAttributeRequest is request schema for UpdateVIPAttribute action +type UpdateVIPAttributeRequest struct { + request.CommonBase + + // [公共参数] 项目ID。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 内网VIP的名称 + Name *string `required:"false"` + + // 内网VIP的备注 + Remark *string `required:"false"` + + // 内网VIP所属的业务组 + Tag *string `required:"false"` + + // 内网VIP的资源Id + VIPId *string `required:"true"` +} + +// UpdateVIPAttributeResponse is response schema for UpdateVIPAttribute action +type UpdateVIPAttributeResponse struct { + response.CommonBase +} + +// NewUpdateVIPAttributeRequest will create request of UpdateVIPAttribute action. +func (c *UNetClient) NewUpdateVIPAttributeRequest() *UpdateVIPAttributeRequest { + req := &UpdateVIPAttributeRequest{} + + // 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 +} + +// UpdateVIPAttribute - 更新VIP信息 +func (c *UNetClient) UpdateVIPAttribute(req *UpdateVIPAttributeRequest) (*UpdateVIPAttributeResponse, error) { + var err error + var res UpdateVIPAttributeResponse + + reqCopier := *req + + err = c.Client.InvokeAction("UpdateVIPAttribute", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/add_white_list_resource.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/add_white_list_resource.go new file mode 100644 index 000000000..32d54ae04 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/add_white_list_resource.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// AddWhiteListResourceRequest is request schema for AddWhiteListResource action +type AddWhiteListResourceRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 可添加白名单的资源Id + ResourceIds []string `required:"true"` +} + +// AddWhiteListResourceResponse is response schema for AddWhiteListResource action +type AddWhiteListResourceResponse struct { + response.CommonBase +} + +// NewAddWhiteListResourceRequest will create request of AddWhiteListResource action. +func (c *VPCClient) NewAddWhiteListResourceRequest() *AddWhiteListResourceRequest { + req := &AddWhiteListResourceRequest{} + + // 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 +} + +// AddWhiteListResource - 添加NAT网关白名单 +func (c *VPCClient) AddWhiteListResource(req *AddWhiteListResourceRequest) (*AddWhiteListResourceResponse, error) { + var err error + var res AddWhiteListResourceResponse + + reqCopier := *req + + err = c.Client.InvokeAction("AddWhiteListResource", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/client.go index fe52e6c3d..1298deba4 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/client.go @@ -1,3 +1,5 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + package vpc import ( @@ -5,14 +7,15 @@ import ( "github.com/ucloud/ucloud-sdk-go/ucloud/auth" ) -// VPCClient is the client of VPC2.0 +// VPCClient is the client of VPC type VPCClient struct { *ucloud.Client } // NewClient will return a instance of VPCClient func NewClient(config *ucloud.Config, credential *auth.Credential) *VPCClient { - client := ucloud.NewClient(config, credential) + meta := ucloud.ClientMeta{Product: "VPC2.0"} + client := ucloud.NewClientWithMeta(config, credential, meta) return &VPCClient{ client, } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw.go new file mode 100644 index 000000000..f62f28b9f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw.go @@ -0,0 +1,78 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateNATGWRequest is request schema for CreateNATGW action +type CreateNATGWRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关绑定的EIPId + EIPIds []string `required:"true"` + + // NAT网关绑定的防火墙Id + FirewallId *string `required:"true"` + + // 白名单开关标记。0表示关闭,1表示开启。默认为0 + IfOpen *int `required:"false"` + + // NAT网关名称 + NATGWName *string `required:"true"` + + // 备注。默认为空 + Remark *string `required:"false"` + + // NAT网关绑定的子网Id + SubnetworkIds []string `required:"true"` + + // 业务组。默认为空 + Tag *string `required:"false"` + + // NAT网关所属的VPC Id。默认为Default VPC Id + VPCId *string `required:"false"` +} + +// CreateNATGWResponse is response schema for CreateNATGW action +type CreateNATGWResponse struct { + response.CommonBase + + // 申请到的NATGateWay Id + NATGWId string +} + +// NewCreateNATGWRequest will create request of CreateNATGW action. +func (c *VPCClient) NewCreateNATGWRequest() *CreateNATGWRequest { + req := &CreateNATGWRequest{} + + // 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 +} + +// CreateNATGW - 创建NAT网关 +func (c *VPCClient) CreateNATGW(req *CreateNATGWRequest) (*CreateNATGWResponse, error) { + var err error + var res CreateNATGWResponse + + reqCopier := *req + + err = c.Client.InvokeAction("CreateNATGW", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw_policy.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw_policy.go new file mode 100644 index 000000000..008e01b50 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/create_natgw_policy.go @@ -0,0 +1,75 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// CreateNATGWPolicyRequest is request schema for CreateNATGWPolicy action +type CreateNATGWPolicyRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 目标IP。填写对应的目标IP地址 + DstIP *string `required:"true"` + + // 目标端口。可填写固定端口,也可填写端口范围。支持的端口范围为1-65535 + DstPort *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 转发策略名称。默认为空 + PolicyName *string `required:"false"` + + // 协议类型。枚举值为:TCP、UDP + Protocol *string `required:"true"` + + // 源IP。填写对应的EIP Id + SrcEIPId *string `required:"true"` + + // 源端口。可填写固定端口,也可填写端口范围。支持的端口范围为1-65535 + SrcPort *string `required:"true"` +} + +// CreateNATGWPolicyResponse is response schema for CreateNATGWPolicy action +type CreateNATGWPolicyResponse struct { + response.CommonBase + + // 创建时分配的策略Id + PolicyId string +} + +// NewCreateNATGWPolicyRequest will create request of CreateNATGWPolicy action. +func (c *VPCClient) NewCreateNATGWPolicyRequest() *CreateNATGWPolicyRequest { + req := &CreateNATGWPolicyRequest{} + + // 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 +} + +// CreateNATGWPolicy - 添加NAT网关端口转发规则 +func (c *VPCClient) CreateNATGWPolicy(req *CreateNATGWPolicyRequest) (*CreateNATGWPolicyResponse, error) { + var err error + var res CreateNATGWPolicyResponse + + reqCopier := *req + + err = c.Client.InvokeAction("CreateNATGWPolicy", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw.go new file mode 100644 index 000000000..258ad84b6 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteNATGWRequest is request schema for DeleteNATGW action +type DeleteNATGWRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 是否释放绑定的EIP。true:解绑并释放;false:只解绑不释放。默认为false + ReleaseEip *bool `required:"false"` +} + +// DeleteNATGWResponse is response schema for DeleteNATGW action +type DeleteNATGWResponse struct { + response.CommonBase +} + +// NewDeleteNATGWRequest will create request of DeleteNATGW action. +func (c *VPCClient) NewDeleteNATGWRequest() *DeleteNATGWRequest { + req := &DeleteNATGWRequest{} + + // 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 +} + +// DeleteNATGW - 删除NAT网关 +func (c *VPCClient) DeleteNATGW(req *DeleteNATGWRequest) (*DeleteNATGWResponse, error) { + var err error + var res DeleteNATGWResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DeleteNATGW", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw_policy.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw_policy.go new file mode 100644 index 000000000..3779d9d09 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_natgw_policy.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteNATGWPolicyRequest is request schema for DeleteNATGWPolicy action +type DeleteNATGWPolicyRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 端口转发规则Id + PolicyId *string `required:"true"` +} + +// DeleteNATGWPolicyResponse is response schema for DeleteNATGWPolicy action +type DeleteNATGWPolicyResponse struct { + response.CommonBase +} + +// NewDeleteNATGWPolicyRequest will create request of DeleteNATGWPolicy action. +func (c *VPCClient) NewDeleteNATGWPolicyRequest() *DeleteNATGWPolicyRequest { + req := &DeleteNATGWPolicyRequest{} + + // 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 +} + +// DeleteNATGWPolicy - 删除NAT网关端口转发规则 +func (c *VPCClient) DeleteNATGWPolicy(req *DeleteNATGWPolicyRequest) (*DeleteNATGWPolicyResponse, error) { + var err error + var res DeleteNATGWPolicyResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DeleteNATGWPolicy", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_white_list_resource.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_white_list_resource.go new file mode 100644 index 000000000..dd7f08d22 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/delete_white_list_resource.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DeleteWhiteListResourceRequest is request schema for DeleteWhiteListResource action +type DeleteWhiteListResourceRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 删除白名单的资源Id + ResourceIds []string `required:"true"` +} + +// DeleteWhiteListResourceResponse is response schema for DeleteWhiteListResource action +type DeleteWhiteListResourceResponse struct { + response.CommonBase +} + +// NewDeleteWhiteListResourceRequest will create request of DeleteWhiteListResource action. +func (c *VPCClient) NewDeleteWhiteListResourceRequest() *DeleteWhiteListResourceRequest { + req := &DeleteWhiteListResourceRequest{} + + // 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 +} + +// DeleteWhiteListResource - 删除NAT网关白名单列表 +func (c *VPCClient) DeleteWhiteListResource(req *DeleteWhiteListResourceRequest) (*DeleteWhiteListResourceResponse, error) { + var err error + var res DeleteWhiteListResourceResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DeleteWhiteListResource", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw.go new file mode 100644 index 000000000..40d303174 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw.go @@ -0,0 +1,66 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeNATGWRequest is request schema for DescribeNATGW action +type DescribeNATGWRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 数据分页值。默认为20 + Limit *int `required:"false"` + + // NAT网关Id。默认为该项目下所有NAT网关 + NATGWIds []string `required:"false"` + + // 数据偏移量。默认为0 + Offset *int `required:"false"` +} + +// DescribeNATGWResponse is response schema for DescribeNATGW action +type DescribeNATGWResponse struct { + response.CommonBase + + // 查到的NATGW信息列表 + DataSet []NatGatewayDataSet + + // 满足条件的实例的总数 + TotalCount int +} + +// NewDescribeNATGWRequest will create request of DescribeNATGW action. +func (c *VPCClient) NewDescribeNATGWRequest() *DescribeNATGWRequest { + req := &DescribeNATGWRequest{} + + // 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 +} + +// DescribeNATGW - 获取NAT网关信息 +func (c *VPCClient) DescribeNATGW(req *DescribeNATGWRequest) (*DescribeNATGWResponse, error) { + var err error + var res DescribeNATGWResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DescribeNATGW", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw_policy.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw_policy.go new file mode 100644 index 000000000..cd0b2b421 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_natgw_policy.go @@ -0,0 +1,66 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeNATGWPolicyRequest is request schema for DescribeNATGWPolicy action +type DescribeNATGWPolicyRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 返回数据长度,默认为10000 + Limit *int `required:"false"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 列表起始位置偏移量,默认为0 + Offset *int `required:"false"` +} + +// DescribeNATGWPolicyResponse is response schema for DescribeNATGWPolicy action +type DescribeNATGWPolicyResponse struct { + response.CommonBase + + // 查到的NATGW 转发策略的详细信息 + DataSet []NATGWPolicyDataSet + + // 满足条件的转发策略总数 + TotalCount int +} + +// NewDescribeNATGWPolicyRequest will create request of DescribeNATGWPolicy action. +func (c *VPCClient) NewDescribeNATGWPolicyRequest() *DescribeNATGWPolicyRequest { + req := &DescribeNATGWPolicyRequest{} + + // 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 +} + +// DescribeNATGWPolicy - 展示NAT网关端口转发规则 +func (c *VPCClient) DescribeNATGWPolicy(req *DescribeNATGWPolicyRequest) (*DescribeNATGWPolicyResponse, error) { + var err error + var res DescribeNATGWPolicyResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DescribeNATGWPolicy", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_white_list_resource.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_white_list_resource.go new file mode 100644 index 000000000..8d64a3eba --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/describe_white_list_resource.go @@ -0,0 +1,60 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// DescribeWhiteListResourceRequest is request schema for DescribeWhiteListResource action +type DescribeWhiteListResourceRequest struct { + request.CommonBase + + // [公共参数] 项目id + // ProjectId *string `required:"true"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关的Id + NATGWIds []string `required:"true"` +} + +// DescribeWhiteListResourceResponse is response schema for DescribeWhiteListResource action +type DescribeWhiteListResourceResponse struct { + response.CommonBase + + // 白名单资源的详细信息,详见DescribeResourceWhiteListDataSet + DataSet []NatGWWhitelistDataSet + + // 上述DataSet总数量 + TotalCount int +} + +// NewDescribeWhiteListResourceRequest will create request of DescribeWhiteListResource action. +func (c *VPCClient) NewDescribeWhiteListResourceRequest() *DescribeWhiteListResourceRequest { + req := &DescribeWhiteListResourceRequest{} + + // 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 +} + +// DescribeWhiteListResource - 展示NAT网关白名单资源列表 +func (c *VPCClient) DescribeWhiteListResource(req *DescribeWhiteListResourceRequest) (*DescribeWhiteListResourceResponse, error) { + var err error + var res DescribeWhiteListResourceResponse + + reqCopier := *req + + err = c.Client.InvokeAction("DescribeWhiteListResource", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/enable_white_list.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/enable_white_list.go new file mode 100644 index 000000000..90f1fd499 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/enable_white_list.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// EnableWhiteListRequest is request schema for EnableWhiteList action +type EnableWhiteListRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 白名单开关标记。0:关闭;1:开启。默认为0 + IfOpen *int `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` +} + +// EnableWhiteListResponse is response schema for EnableWhiteList action +type EnableWhiteListResponse struct { + response.CommonBase +} + +// NewEnableWhiteListRequest will create request of EnableWhiteList action. +func (c *VPCClient) NewEnableWhiteListRequest() *EnableWhiteListRequest { + req := &EnableWhiteListRequest{} + + // 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 +} + +// EnableWhiteList - 修改NAT网关白名单开关 +func (c *VPCClient) EnableWhiteList(req *EnableWhiteListRequest) (*EnableWhiteListResponse, error) { + var err error + var res EnableWhiteListResponse + + reqCopier := *req + + err = c.Client.InvokeAction("EnableWhiteList", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_policy.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_policy.go new file mode 100644 index 000000000..c3a8dd235 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_policy.go @@ -0,0 +1,63 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// GetAvailableResourceForPolicyRequest is request schema for GetAvailableResourceForPolicy action +type GetAvailableResourceForPolicyRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 返回数据长度,默认为10000 + Limit *int `required:"false"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 列表起始位置偏移量,默认为0 + Offset *int `required:"false"` +} + +// GetAvailableResourceForPolicyResponse is response schema for GetAvailableResourceForPolicy action +type GetAvailableResourceForPolicyResponse struct { + response.CommonBase + + // 支持资源类型的信息 + DataSet []GetAvailableResourceForPolicyDataSet +} + +// NewGetAvailableResourceForPolicyRequest will create request of GetAvailableResourceForPolicy action. +func (c *VPCClient) NewGetAvailableResourceForPolicyRequest() *GetAvailableResourceForPolicyRequest { + req := &GetAvailableResourceForPolicyRequest{} + + // 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 +} + +// GetAvailableResourceForPolicy - 获取NAT网关可配置端口转发规则的资源信息 +func (c *VPCClient) GetAvailableResourceForPolicy(req *GetAvailableResourceForPolicyRequest) (*GetAvailableResourceForPolicyResponse, error) { + var err error + var res GetAvailableResourceForPolicyResponse + + reqCopier := *req + + err = c.Client.InvokeAction("GetAvailableResourceForPolicy", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_white_list.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_white_list.go new file mode 100644 index 000000000..ea29920cb --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/get_available_resource_for_white_list.go @@ -0,0 +1,60 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// GetAvailableResourceForWhiteListRequest is request schema for GetAvailableResourceForWhiteList action +type GetAvailableResourceForWhiteListRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` +} + +// GetAvailableResourceForWhiteListResponse is response schema for GetAvailableResourceForWhiteList action +type GetAvailableResourceForWhiteListResponse struct { + response.CommonBase + + // 返回白名单列表的详细信息 + DataSet []GetAvailableResourceForWhiteListDataSet + + // 白名单资源列表的总的个数 + TotalCount int +} + +// NewGetAvailableResourceForWhiteListRequest will create request of GetAvailableResourceForWhiteList action. +func (c *VPCClient) NewGetAvailableResourceForWhiteListRequest() *GetAvailableResourceForWhiteListRequest { + req := &GetAvailableResourceForWhiteListRequest{} + + // 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 +} + +// GetAvailableResourceForWhiteList - 获取NAT网关可添加白名单的资源 +func (c *VPCClient) GetAvailableResourceForWhiteList(req *GetAvailableResourceForWhiteListRequest) (*GetAvailableResourceForWhiteListResponse, error) { + var err error + var res GetAvailableResourceForWhiteListResponse + + reqCopier := *req + + err = c.Client.InvokeAction("GetAvailableResourceForWhiteList", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/list_subnet_for_natgw.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/list_subnet_for_natgw.go new file mode 100644 index 000000000..f38c6ac75 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/list_subnet_for_natgw.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// ListSubnetForNATGWRequest is request schema for ListSubnetForNATGW action +type ListSubnetForNATGWRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关所属VPC Id。默认值为Default VPC Id + VPCId *string `required:"false"` +} + +// ListSubnetForNATGWResponse is response schema for ListSubnetForNATGW action +type ListSubnetForNATGWResponse struct { + response.CommonBase + + // 具体参数请见NatgwSubnetDataSet + DataSet []NatgwSubnetDataSet +} + +// NewListSubnetForNATGWRequest will create request of ListSubnetForNATGW action. +func (c *VPCClient) NewListSubnetForNATGWRequest() *ListSubnetForNATGWRequest { + req := &ListSubnetForNATGWRequest{} + + // 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 +} + +// ListSubnetForNATGW - 展示NAT网关可绑定的子网列表 +func (c *VPCClient) ListSubnetForNATGW(req *ListSubnetForNATGWRequest) (*ListSubnetForNATGWResponse, error) { + var err error + var res ListSubnetForNATGWResponse + + reqCopier := *req + + err = c.Client.InvokeAction("ListSubnetForNATGW", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/set_gw_default_export.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/set_gw_default_export.go new file mode 100644 index 000000000..e0713d4d3 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/set_gw_default_export.go @@ -0,0 +1,60 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// SetGwDefaultExportRequest is request schema for SetGwDefaultExport action +type SetGwDefaultExportRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关绑定的EIP Id。ExportIp和ExportEipId必填一个 + ExportEipId *string `required:"false"` + + // NAT网关绑定的EIP。ExportIp和ExportEipId必填一个 + ExportIp *string `required:"false"` + + // NAT网关Id + NATGWId *string `required:"true"` +} + +// SetGwDefaultExportResponse is response schema for SetGwDefaultExport action +type SetGwDefaultExportResponse struct { + response.CommonBase +} + +// NewSetGwDefaultExportRequest will create request of SetGwDefaultExport action. +func (c *VPCClient) NewSetGwDefaultExportRequest() *SetGwDefaultExportRequest { + req := &SetGwDefaultExportRequest{} + + // 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 +} + +// SetGwDefaultExport - 设置NAT网关的默认出口 +func (c *VPCClient) SetGwDefaultExport(req *SetGwDefaultExportRequest) (*SetGwDefaultExportResponse, error) { + var err error + var res SetGwDefaultExportResponse + + reqCopier := *req + + err = c.Client.InvokeAction("SetGwDefaultExport", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_describe_white_list_resource_object_ip_info.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_describe_white_list_resource_object_ip_info.go new file mode 100644 index 000000000..2798b852d --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_describe_white_list_resource_object_ip_info.go @@ -0,0 +1,39 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +DescribeWhiteListResourceObjectIPInfo - DescribeWhiteListResource + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type DescribeWhiteListResourceObjectIPInfo struct { + + // natgw字符串 + GwType string + + // 白名单资源的内网IP + PrivateIP string + + // 白名单资源Id信息 + ResourceId string + + // 白名单资源名称 + ResourceName string + + // 白名单资源类型 + ResourceType string + + // 资源绑定的虚拟网卡的实例ID + SubResourceId string + + // 资源绑定的虚拟网卡的实例名称 + SubResourceName string + + // 资源绑定的虚拟网卡的类型 + SubResourceType string + + // 白名单资源所属VPCId + VPCId string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_policy_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_policy_data_set.go new file mode 100644 index 000000000..295e4ab4f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_policy_data_set.go @@ -0,0 +1,21 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +GetAvailableResourceForPolicyDataSet - GetAvailableResourceForPolicy + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type GetAvailableResourceForPolicyDataSet struct { + + // 资源对应的内网Ip + PrivateIP string + + // 资源的Id + ResourceId string + + // 资源类型。"uhost":云主机; "upm",物理云主机; "hadoophost":hadoop节点; "fortresshost":堡垒机: "udockhost",容器 + ResourceType string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_white_list_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_white_list_data_set.go new file mode 100644 index 000000000..7b1af27bc --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_get_available_resource_for_white_list_data_set.go @@ -0,0 +1,39 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +GetAvailableResourceForWhiteListDataSet - GetAvailableResourceForWhiteList + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type GetAvailableResourceForWhiteListDataSet struct { + + // 资源的内网Ip + PrivateIP string + + // 资源类型Id + ResourceId string + + // 资源名称 + ResourceName string + + // 资源类型。"uhost":云主机; "upm",物理云主机; "hadoophost":hadoop节点; "fortresshost":堡垒机: "udockhost",容器 + ResourceType string + + // 资源绑定的虚拟网卡的实例ID + SubResouceId string + + // 资源绑定的虚拟网卡的实例类型 + SubResouceType string + + // 资源绑定的虚拟网卡的实例名称 + SubResourceName string + + // 资源所属子网Id + SubnetworkId string + + // 资源所属VPCId + VPCId string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_data_set.go new file mode 100644 index 000000000..022792727 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_data_set.go @@ -0,0 +1,45 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatGatewayDataSet - natgw的信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatGatewayDataSet struct { + + // natgw创建时间 + CreateTime int + + // 绑定的防火墙Id + FirewallId string + + // 绑定的EIP 信息 + IPSet []NatGatewayIPSet + + // natgw id + NATGWId string + + // natgw名称 + NATGWName string + + // 转发策略Id + PolicyId []string + + // 备注 + Remark string + + // 子网 Id + SubnetSet []NatGatewaySubnetSet + + // 业务组 + Tag string + + // 所属VPC Id + VPCId string + + // 所属VPC 信息 + VPCInfo string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_ip_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_ip_set.go new file mode 100644 index 000000000..85cfac2f5 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_ip_set.go @@ -0,0 +1,27 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatGatewayIPSet - IPSet信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatGatewayIPSet struct { + + // 带宽 + Bandwidth int + + // EIP带宽类型 + BandwidthType string + + // 外网IP的 EIPId + EIPId string + + // 外网IP信息 + IPResInfo []NatGWIPResInfo + + // 权重为100的为出口 + Weight int +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_subnet_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_subnet_set.go new file mode 100644 index 000000000..d8b919543 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gateway_subnet_set.go @@ -0,0 +1,21 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatGatewaySubnetSet - natgw里面的子网信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatGatewaySubnetSet struct { + + // 子网网段 + Subnet string + + // 子网名字 + SubnetName string + + // 子网id + SubnetworkId string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_ip_res_info.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_ip_res_info.go new file mode 100644 index 000000000..dd2694fe6 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_ip_res_info.go @@ -0,0 +1,18 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatGWIPResInfo - IP信息 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatGWIPResInfo struct { + + // 外网IP + EIP string + + // IP的运营商信息 + OperatorName string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_whitelist_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_whitelist_data_set.go new file mode 100644 index 000000000..b4ec7a39a --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_nat_gw_whitelist_data_set.go @@ -0,0 +1,21 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatGWWhitelistDataSet - nat网关白名单数据 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatGWWhitelistDataSet struct { + + // 白名单开关标记 + IfOpen int + + // NATGateWay Id + NATGWId string + + // 白名单详情 + ObjectIPInfo []DescribeWhiteListResourceObjectIPInfo +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_policy_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_policy_data_set.go new file mode 100644 index 000000000..4b476cb2b --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_policy_data_set.go @@ -0,0 +1,39 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NATGWPolicyDataSet - DescribeNATGWPolicy + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NATGWPolicyDataSet struct { + + // 目的地址 + DstIP string + + // 目的端口 + DstPort string + + // NAT网关Id + NATGWId string + + // 转发策略Id + PolicyId string + + // 转发策略名称 + PolicyName string + + // 协议类型 + Protocol string + + // 端口转发前端EIP + SrcEIP string + + // 端口转发前端EIP Id + SrcEIPId string + + // 源端口 + SrcPort string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_subnet_data_set.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_subnet_data_set.go new file mode 100644 index 000000000..5700c1f4f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/types_natgw_subnet_data_set.go @@ -0,0 +1,27 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +/* +NatgwSubnetDataSet - natgw可以绑定的子网 + +this model is auto created by ucloud code generater for open api, +you can also see https://docs.ucloud.cn/api for detail. +*/ +type NatgwSubnetDataSet struct { + + // 是否绑定NATGW + HasNATGW bool + + // 掩码 + Netmask string + + // 子网网段 + Subnet string + + // 子网id + SubnetId string + + // 子网名字 + SubnetName string +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_policy.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_policy.go new file mode 100644 index 000000000..8dee917c0 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_policy.go @@ -0,0 +1,75 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// UpdateNATGWPolicyRequest is request schema for UpdateNATGWPolicy action +type UpdateNATGWPolicyRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // 目标IP。填写对饮的目标IP地址 + DstIP *string `required:"true"` + + // 目标端口。可填写固定端口,也可填写端口范围。支持的端口范围为1-65535 + DstPort *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // 转发策略Id + PolicyId *string `required:"true"` + + // 转发策略名称。默认为空 + PolicyName *string `required:"false"` + + // 协议类型。枚举值为:TCP 、 UDP + Protocol *string `required:"true"` + + // 源IP。填写对应的EIP Id + SrcEIPId *string `required:"true"` + + // 源端口。可填写固定端口,也可填写端口范围。支持的端口范围为1-6553 + SrcPort *string `required:"true"` +} + +// UpdateNATGWPolicyResponse is response schema for UpdateNATGWPolicy action +type UpdateNATGWPolicyResponse struct { + response.CommonBase +} + +// NewUpdateNATGWPolicyRequest will create request of UpdateNATGWPolicy action. +func (c *VPCClient) NewUpdateNATGWPolicyRequest() *UpdateNATGWPolicyRequest { + req := &UpdateNATGWPolicyRequest{} + + // 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 +} + +// UpdateNATGWPolicy - 更新NAT网关端口转发规则 +func (c *VPCClient) UpdateNATGWPolicy(req *UpdateNATGWPolicyRequest) (*UpdateNATGWPolicyResponse, error) { + var err error + var res UpdateNATGWPolicyResponse + + reqCopier := *req + + err = c.Client.InvokeAction("UpdateNATGWPolicy", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_subnet.go b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_subnet.go new file mode 100644 index 000000000..31178e5a2 --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/services/vpc/update_natgw_subnet.go @@ -0,0 +1,57 @@ +// Code is generated by ucloud-model, DO NOT EDIT IT. + +package vpc + +import ( + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" +) + +// UpdateNATGWSubnetRequest is request schema for UpdateNATGWSubnet action +type UpdateNATGWSubnetRequest struct { + request.CommonBase + + // [公共参数] 项目Id。不填写为默认项目,子帐号必须填写。 请参考[GetProjectList接口](../summary/get_project_list.html) + // ProjectId *string `required:"false"` + + // [公共参数] 地域。 参见 [地域和可用区列表](../summary/regionlist.html) + // Region *string `required:"true"` + + // NAT网关Id + NATGWId *string `required:"true"` + + // NAT网关绑定的子网Id + SubnetworkIds []string `required:"true"` +} + +// UpdateNATGWSubnetResponse is response schema for UpdateNATGWSubnet action +type UpdateNATGWSubnetResponse struct { + response.CommonBase +} + +// NewUpdateNATGWSubnetRequest will create request of UpdateNATGWSubnet action. +func (c *VPCClient) NewUpdateNATGWSubnetRequest() *UpdateNATGWSubnetRequest { + req := &UpdateNATGWSubnetRequest{} + + // 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 +} + +// UpdateNATGWSubnet - 更新NAT网关绑定的子网 +func (c *VPCClient) UpdateNATGWSubnet(req *UpdateNATGWSubnetRequest) (*UpdateNATGWSubnetResponse, error) { + var err error + var res UpdateNATGWSubnetResponse + + reqCopier := *req + + err = c.Client.InvokeAction("UpdateNATGWSubnet", &reqCopier, &res) + if err != nil { + return &res, err + } + + return &res, nil +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go index 05ff2409c..7785f2a03 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/auth/credential.go @@ -9,13 +9,23 @@ import ( "io" "net/url" "sort" - "strings" + "time" ) // Credential is the information of credential keys type Credential struct { - PublicKey string + // UCloud Public Key + PublicKey string + + // UCloud Private Key PrivateKey string + + // UCloud STS token for temporary usage + SecurityToken string + + // Time the credentials will expire. + CanExpire bool + Expires time.Time } // NewCredential will return credential config with default values @@ -25,32 +35,51 @@ func NewCredential() Credential { // CreateSign will encode query string to credential signature. func (c *Credential) CreateSign(query string) string { - // replace "=" "&" - str := strings.Replace(query, "&", "", -1) - str = strings.Replace(str, "=", "", -1) - - // crypto by SHA1 - strUnescaped, _ := url.QueryUnescape(str) - h := sha1.New() - s := strUnescaped + c.PrivateKey - io.WriteString(h, s) - bs := h.Sum(nil) - result := hex.EncodeToString(bs) - - return result + urlValues, err := url.ParseQuery(query) + if err != nil { + return "" + } + urlValues.Set("PublicKey", c.PublicKey) + return c.verifyAc(urlValues) } // BuildCredentialedQuery will build query string with signature query param. -func (c *Credential) BuildCredentialedQuery(query map[string]string) string { - var queryList []string - for k, v := range query { - queryList = append(queryList, k+"="+url.QueryEscape(v)) +func (c *Credential) BuildCredentialedQuery(params map[string]string) string { + urlValues := url.Values{} + for k, v := range params { + urlValues.Set(k, v) } - queryList = append(queryList, "PublicKey="+url.QueryEscape(c.PublicKey)) - sort.Strings(queryList) - queryString := strings.Join(queryList, "&") + if len(c.SecurityToken) != 0 { + urlValues.Set("SecurityToken", c.SecurityToken) + } + urlValues.Set("PublicKey", c.PublicKey) + urlValues.Set("Signature", c.verifyAc(urlValues)) + return urlValues.Encode() +} - sign := c.CreateSign(queryString) - queryString = queryString + "&Signature=" + sign - return queryString +func (c *Credential) IsExpired() bool { + return c.CanExpire && time.Now().After(c.Expires) +} + +func (c *Credential) verifyAc(urlValues url.Values) string { + // sort keys + var keys []string + for k := range urlValues { + keys = append(keys, k) + } + sort.Strings(keys) + + signQuery := "" + for _, k := range keys { + signQuery += k + urlValues.Get(k) + } + signQuery += c.PrivateKey + return encodeSha1(signQuery) +} + +func encodeSha1(s string) string { + h := sha1.New() + _, _ = io.WriteString(h, s) + bs := h.Sum(nil) + return hex.EncodeToString(bs) } diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go index a2e2cc989..07f9c9986 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/client.go @@ -4,6 +4,7 @@ Package ucloud is a package of utilities to setup ucloud sdk and improve using e package ucloud import ( + "fmt" "time" "github.com/ucloud/ucloud-sdk-go/private/utils" @@ -16,6 +17,10 @@ import ( "github.com/ucloud/ucloud-sdk-go/ucloud/response" ) +type ClientMeta struct { + Product string +} + // Client 客户端 type Client struct { // configurations @@ -31,6 +36,9 @@ type Client struct { httpRequestHandlers []HttpRequestHandler responseHandlers []ResponseHandler httpResponseHandlers []HttpResponseHandler + + // client information injection + meta ClientMeta } // NewClient will create an client of ucloud sdk @@ -38,6 +46,7 @@ func NewClient(config *Config, credential *auth.Credential) *Client { client := Client{ credential: credential, config: config, + meta: ClientMeta{}, } client.requestHandlers = append(client.requestHandlers, defaultRequestHandlers...) @@ -51,13 +60,19 @@ func NewClient(config *Config, credential *auth.Credential) *Client { return &client } +func NewClientWithMeta(config *Config, credential *auth.Credential, meta ClientMeta) *Client { + client := NewClient(config, credential) + client.meta = meta + return client +} + // SetHttpClient will setup a http client func (c *Client) SetHttpClient(httpClient http.Client) error { c.httpClient = httpClient return nil } -// GetCredential will return the creadential config of client. +// GetCredential will return the credential config of client. func (c *Client) GetCredential() *auth.Credential { return c.credential } @@ -67,6 +82,11 @@ func (c *Client) GetConfig() *Config { return c.config } +// GetMeta will return the meta data of client. +func (c *Client) GetMeta() ClientMeta { + return c.meta +} + // SetLogger will set the logger of client func (c *Client) SetLogger(logger log.Logger) { c.logger = logger @@ -89,6 +109,13 @@ func (c *Client) InvokeActionWithPatcher(action string, req request.Common, resp req.SetRequestTime(time.Now()) resp.SetRequest(req) + if c.credential.CanExpire && c.credential.IsExpired() { + return uerr.NewClientError( + uerr.ErrCredentialExpired, + fmt.Errorf("credential is expired at %s", c.credential.Expires.Format(time.RFC3339)), + ) + } + for _, handler := range c.requestHandlers { req, err = handler(c, req) if err != nil { @@ -123,7 +150,7 @@ func (c *Client) InvokeActionWithPatcher(action string, req request.Common, resp if err == nil { // use patch object to resolve the http response body - // in general, it will be fix common server error before server bugfix is released. + // in general, it will be fix common server error before server bug fix is released. body := httpResp.GetBody() for _, patch := range patches { @@ -131,6 +158,9 @@ func (c *Client) InvokeActionWithPatcher(action string, req request.Common, resp } err = c.unmarshalHTTPResponse(body, resp) + + uuid := httpResp.GetHeaders().Get(headerKeyRequestUUID) + resp.SetRequestUUID(uuid) } // use response middle to build and convert response when response has been created. diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/config.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/config.go index 2b79bc119..a8f3fc507 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/config.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/config.go @@ -4,8 +4,14 @@ import ( "time" "github.com/ucloud/ucloud-sdk-go/ucloud/log" + "github.com/ucloud/ucloud-sdk-go/ucloud/version" ) +// Version is the version of sdk +const Version = version.Version + +const headerKeyRequestUUID = "X-UCLOUD-REQUEST-UUID" + // Config is the config of ucloud sdk, use for setting up client type Config struct { // Region is the region of backend service diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go index 3f9b9db59..57313a6c4 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/error/client_error.go @@ -20,6 +20,8 @@ var ( // ErrUnexpected is the error for any unexpected error ErrUnexpected = "client.UnexpectedError" + + ErrCredentialExpired = "client.CredentialExpiredError" ) // ClientError is the ucloud common errorfor server response diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go index 62bddd859..eb17917f2 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/common.go @@ -59,7 +59,6 @@ type CommonBase struct { retryCount int timeout time.Duration requestTime time.Time - requestUUID string } // SetRetryCount will set retry count of request diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/encoder.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/encoder.go index f6c78f39d..c06816707 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/encoder.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/encoder.go @@ -1,12 +1,18 @@ package request import ( + "encoding/base64" "errors" "fmt" "reflect" "strconv" ) +// ToBase64Query will encode a wrapped string as base64 wrapped string +func ToBase64Query(s *string) *string { + return String(base64.StdEncoding.EncodeToString([]byte(StringValue(s)))) +} + // ToQueryMap will convert a request to string map func ToQueryMap(req Common) (map[string]string, error) { v := reflect.ValueOf(req) diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/schema.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/schema.go new file mode 100644 index 000000000..dfd09163f --- /dev/null +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/request/schema.go @@ -0,0 +1,70 @@ +package request + +import ( + "time" +) + +// String will return a pointer to string +func String(val string) *string { + return &val +} + +// StringValue will return a string from string pointer +func StringValue(ptr *string) string { + if ptr != nil { + return *ptr + } + return "" +} + +// Int will return a pointer to int +func Int(val int) *int { + return &val +} + +// IntValue will return a int from int pointer +func IntValue(ptr *int) int { + if ptr != nil { + return *ptr + } + return 0 +} + +// Bool will return a pointer to bool +func Bool(val bool) *bool { + return &val +} + +// BoolValue will return a bool from bool pointer +func BoolValue(ptr *bool) bool { + if ptr != nil { + return *ptr + } + return false +} + +// Float64 will return a pointer to float64 +func Float64(val float64) *float64 { + return &val +} + +// Float64Value will return a float64 from float64 pointer +func Float64Value(ptr *float64) float64 { + if ptr != nil { + return *ptr + } + return 0.0 +} + +// TimeDuration will return a pointer to time.Duration +func TimeDuration(val time.Duration) *time.Duration { + return &val +} + +// TimeDurationValue will return a time.Duration from a time.Duration pointer +func TimeDurationValue(ptr *time.Duration) time.Duration { + if ptr != nil { + return *ptr + } + return 0 +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go index 1677a785c..6b175df8c 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/response/common.go @@ -16,6 +16,9 @@ type Common interface { GetRequest() request.Common SetRequest(request.Common) + + SetRequestUUID(string) + GetRequestUUID() string } // CommonBase has common attribute and method, @@ -25,6 +28,8 @@ type CommonBase struct { RetCode int Message string + requestUUID string + request request.Common } @@ -53,3 +58,13 @@ func (c *CommonBase) GetRequest() request.Common { func (c *CommonBase) SetRequest(req request.Common) { c.request = req } + +// SetRequestUUID will set uuid of request +func (c *CommonBase) SetRequestUUID(uuid string) { + c.requestUUID = uuid +} + +// GetRequestUUID will get uuid of request +func (c *CommonBase) GetRequestUUID() string { + return c.requestUUID +} diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/schema.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/schema.go index 26ffec2de..cca0cceed 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/schema.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/schema.go @@ -1,75 +1,18 @@ package ucloud import ( - "time" - - "github.com/ucloud/ucloud-sdk-go/ucloud/version" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" ) -// Version is the version of sdk -const Version = version.Version - -// String will return a pointer to string -func String(val string) *string { - return &val -} - -// StringValue will return a string from string pointer -func StringValue(ptr *string) string { - if ptr != nil { - return *ptr - } - return "" -} - -// Int will return a pointer to int -func Int(val int) *int { - return &val -} - -// IntValue will return a int from int pointer -func IntValue(ptr *int) int { - if ptr != nil { - return *ptr - } - return 0 -} - -// Bool will return a pointer to bool -func Bool(val bool) *bool { - return &val -} - -// BoolValue will return a bool from bool pointer -func BoolValue(ptr *bool) bool { - if ptr != nil { - return *ptr - } - return false -} - -// Float64 will return a pointer to float64 -func Float64(val float64) *float64 { - return &val -} - -// Float64Value will return a float64 from float64 pointer -func Float64Value(ptr *float64) float64 { - if ptr != nil { - return *ptr - } - return 0.0 -} - -// TimeDuration will return a pointer to time.Duration -func TimeDuration(val time.Duration) *time.Duration { - return &val -} - -// TimeDurationValue will return a time.Duration from a time.Duration pointer -func TimeDurationValue(ptr *time.Duration) time.Duration { - if ptr != nil { - return *ptr - } - return 0 -} +var ( + String = request.String + StringValue = request.StringValue + Int = request.Int + IntValue = request.IntValue + Bool = request.Bool + BoolValue = request.BoolValue + Float64 = request.Float64 + Float64Value = request.Float64Value + TimeDuration = request.TimeDuration + TimeDurationValue = request.TimeDurationValue +) diff --git a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go index c067977df..22fdead9b 100644 --- a/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go +++ b/vendor/github.com/ucloud/ucloud-sdk-go/ucloud/version/version.go @@ -4,4 +4,4 @@ Package version is the version of sdk package version // Version see also semantic version: https://semver.org/ -const Version = "0.8.7" +const Version = "0.12.0" diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/.gitignore b/vendor/github.com/ufilesdk-dev/ufile-gosdk/.gitignore new file mode 100644 index 000000000..f3d3ced63 --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/.gitignore @@ -0,0 +1,4 @@ +example/config.json +example/*.txt +*.exe + diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/README.md b/vendor/github.com/ufilesdk-dev/ufile-gosdk/README.md new file mode 100644 index 000000000..29bbf75b6 --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/README.md @@ -0,0 +1,73 @@ +# UCloud 对象存储 SDK GoDoc +> Modules are interface and implementation. +> The best modules are where interface is much simpler than implementation. +> **By: John Ousterhout** + +## UFile 对象存储基本概念 +在对象存储系统中,存储空间(Bucket)是文件(File)的组织管理单位,文件(File)是存储空间的逻辑存储单元。对于每个账号,该账号里存放的每个文件都有唯一的一对存储空间(Bucket)与键(Key)作为标识。我们可以把 Bucket 理解成一类文件的集合,Key 理解成文件名。由于每个 Bucket 需要配置和权限不同,每个账户里面会有多个 Bucket。在 UFile 里面,Bucket 主要分为公有和私有两种,公有 Bucket 里面的文件可以对任何人开放,私有 Bucket 需要配置对应访问签名才能访问。 + +### 签名 +本 SDK 接口是基于 HTTP 的,为了连接的安全性,UFile 使用 HMAC SHA1 对每个连接进行签名校验。使用本 SDK 可以忽略签名相关的算法过程,只要把公私钥写入到配置文件里面(注意不要传到版本控制里面),读取并传给 UFileRequest 里面的 New 方法即可。 +签名相关的算法与详细实现请见 [Auth 模块](auth.go) + +## 安装 +`go get github.com/ufilesdk-dev/ufile-gosdk` + +### 执行测试 +`cd example; go run demo_file.go` + +## 功能列表 +### 文件操作相关功能 +[Put 上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.PutFile) +[Post 上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.PostFile) +分片上传 [同步分片上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.MPut),[异步分片上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.AsyncMPut) +手动分片上传,[步骤一](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.InitiateMultipartUpload),[步骤二](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.UploadPart),[步骤三](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.FinishMultipartUpload)。[取消分片上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.AbortMultipartUpload) +[文件秒传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.UploadHit) +[获取文件列表](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.PrefixFileList) +[获取私有空间下载地址](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.GetPrivateURL),[获取公有空间下载地址](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.GetPublicURL)。 +[删除文件](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.DeleteFile) +[查看文件信息](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.HeadFile) +[下载文件](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.DownloadFile) +[比对本地与远程文件的 Etag](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.CompareFileEtag) +[拷贝文件](https://godoc.org/github.com/ufilesdk-dev/ufile-gosdk#UFileRequest.Copy) +[重命名文件](https://godoc.org/github.com/ufilesdk-dev/ufile-gosdk#UFileRequest.Rename) +[Put 带回调上传](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.PutFileWithPolicy) +[同步分片上传-带回调](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.MPutWithPolicy), +[异步分片上传-带回调](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.AsyncMPutWithPolicy) + +### Bucket 操作相关功能 +[创建 bucket](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.CreateBucket) +[删除 bucket](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.DeleteBucket) +[获取 bucket 列表](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.DescribeBucket) +[修改 bucket](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#UFileRequest.UpdateBucket) + +### 签名构造 +[构造文件管理签名](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#Auth.Authorization) +[构造私有空间下载签名](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#Auth.AuthorizationPrivateURL) +[构造 bucket 管理签名](https://godoc.org/pkg/github.com/ufilesdk-dev/ufile-gosdk/#Auth.AuthorizationBucketMgr) + +## 示例代码 +SDK 主要分为两个模块,一个是 bucket 管理,一个是 file 管理。使用对象存储你需要频繁的调用 file 管理相关的接口,bucket 管理用到的地方不会太频繁。以下是用 SDK 上传一个文件的例子: +```go +import ufsdk "github.com/ufilesdk-dev/ufile-gosdk" +config, err := ufsdk.LoadConfig(configFile) +if err != nil { + panic(err.Error()) +} +req := ufsdk.NewFileRequest(config, nil) +err = req.PutFile(filePath, keyName, "") +if err != nil { + fmt.Println("文件上传失败!!,错误信息为:", err.Error()) + //把 HTTP 详细的 HTTP response dump 出来 + fmt.Printf("%s\n",req.DumpResponse(true)) +} +``` +更详细的代码请参考 [example/demo_file.go](/example/demo_file.go) 和 [example/demo_bucket.go](example/demo_bucket.go) + +## 文档说明 +本 SDK 使用 [godoc](https://blog.golang.org/godoc-documenting-go-code) 约定的方法对每个 export 出来的接口进行注释。 +你可以直接访问生成好的[在线文档](https://godoc.org/github.com/ufilesdk-dev/ufile-gosdk)。 + +## 如何排错? +使用 UFileRequest 里面的方法对返回的 error 进行检查。如果不为 nil,调用 Error() 查看错误信息。调用 DumpResponse(true) 并获取返回值查看详细的 HTTP 返回值。 + diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/auth.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/auth.go new file mode 100644 index 000000000..0c5d56243 --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/auth.go @@ -0,0 +1,139 @@ +package ufsdk + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "io" + "net/http" + "net/url" + "sort" + "strings" +) + +//Auth 构造签名的工具,使用本 SDK 你不需要关心 Auth 这一整个模块,以及它暴露出来的签名算法。 +//如果你希望自己封装 API 可以使用这里面的暴露出来的接口来填充 http authorization。 +type Auth struct { + publicKey string + privateKey string +} + +//NewAuth 构造一个新的签名工具,传入你的公钥匙和私钥。 +func NewAuth(publicKey, privateKey string) Auth { + return Auth{ + publicKey: publicKey, + privateKey: privateKey, + } +} + +//Authorization 构造一个主要用于上传文件的签名,返回 HMAC-Sh1 的签名字符串,可以直接填充到 HTTP authorization header 里面。 +//key 是传到 ufile 所使用的文件名,bucekt 是文件上传后存放的 bucket。 +//method 就是你当前这个 HTTP 请求的 Method。 +//header 就是你当前这个 HTTP 的 header。 +func (A Auth) Authorization(method, bucket, key string, header http.Header) string { + var sigData string + method = strings.ToUpper(method) + + md5 := header.Get("Content-MD5") + contentType := header.Get("Content-Type") + date := header.Get("Date") + + sigData = method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n" + sigData += A.CanonicalizedUcloudHeaders(header) + resource := "/" + bucket + "/" + key + sigData += resource + + signature := A.signature(sigData) + + return "UCloud " + A.publicKey + ":" + signature +} + +//AuthorizationPrivateURL 构造私有空间文件下载链接的签名,其中 expires 是当前的时间加上一个过期的时间,再转为字符串。格式是 unix time second. +//其他的参数含义和 Authoriazation 函数一样。 +//有时我们需要把签名后的 URL 直接拿来用,header 参数可以直接构造一个空的 http.Header{} 传入即可。 +func (A Auth) AuthorizationPrivateURL(method, bucket, key, expires string, header http.Header) (string, string) { + var sigData string + method = strings.ToUpper(method) + md5 := header.Get("Content-MD5") + contentType := header.Get("Content-Type") + + sigData = method + "\n" + md5 + "\n" + contentType + "\n" + expires + "\n" + resource := "/" + bucket + "/" + key + sigData += resource + + signature := A.signature(sigData) + + return signature, A.publicKey +} + +//AuthorizationPolicy 构造支持回调策略的签名,policy 是经过 base64 编码后的 json string。 +//本签名函数就是多了一个 policy 字段,其他的参数和 Authoriazation 一样。 +func (A Auth) AuthorizationPolicy(method, bucket, key, policy string, header http.Header) string { + var sigData string + method = strings.ToUpper(method) + + md5 := header.Get("Content-MD5") + contentType := header.Get("Content-Type") + date := header.Get("Date") + + sigData = method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n" + resource := "/" + bucket + "/" + key + sigData += resource + sigData += policy + + signature := A.signature(sigData) + + return "UCloud " + A.publicKey + ":" + signature + ":" + policy +} + +func (A Auth) signature(data string) string { + mac := hmac.New(sha1.New, []byte(A.privateKey)) + mac.Write([]byte(data)) + + return base64.StdEncoding.EncodeToString(mac.Sum(nil)) +} + +//AuthorizationBucketMgr 生成用于管理 bucket 的签名。 +func (A Auth) AuthorizationBucketMgr(query url.Values) string { + query.Add("PublicKey", A.publicKey) + + var signstring string + var keys []string + for k := range query { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + signstring += k + query.Get(k) //Get first value + } + signstring += A.privateKey + + h := sha1.New() + io.WriteString(h, signstring) + + query.Add("Signature", fmt.Sprintf("%x", h.Sum(nil))) + return query.Encode() +} + +//CanonicalizedUcloudHeaders 用于将自定义请求报头规范化为string +//ucloudHeader 支持自定义请求头 +func (A Auth) CanonicalizedUcloudHeaders(ucloudHeader http.Header) string { + keys := make([]string, 0) + headers := make(map[string]string, 0) + for k := range ucloudHeader { + newkey := strings.ToLower(k) + if strings.HasPrefix(newkey, "x-ufile-") || strings.HasPrefix(newkey, "x-ucloud-") { + headers[newkey] = strings.TrimSpace(ucloudHeader[k][0]) + keys = append(keys, newkey) + } + } + sort.Strings(keys) + + s := "" + for _, k := range keys { + v := headers[k] + s += k + ":" + v + "\n" + } + return s +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/bucket.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/bucket.go new file mode 100644 index 000000000..14cae238a --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/bucket.go @@ -0,0 +1,183 @@ +package ufsdk + +import ( + "encoding/json" + "errors" + "net/http" + "net/url" + "strconv" +) + +type response interface { + Error() error +} + +//BucketResponse 用于 Bucket 模块返回的数据。 +type BucketResponse struct { + RetCode int `json:"RetCode,omitempty"` + Action string `json:"Action,omitempty"` + BucketName string `json:"BucketName,omitempty"` + BucketID string `json:"BucketId,omitempty"` + Message string `json:"Message,omitempty"` +} + +func (b BucketResponse) Error() error { + if b.RetCode != 0 { + return errors.New(b.Message) + } + return nil +} + +func (b BucketResponse) String() string { + return structPrettyStr(b) +} + +//DomainSet 用于 BucketDataSet 里面的 Domain 字段 +type DomainSet struct { + Src []string `json:"Src,omitempty"` + Cdn []string `json:"Cdn,omitempty"` + CustomSrc []string `json:"CustomSrc,omitempty"` + CustomCdn []string `json:"CustomCdn,omitempty"` +} + +//BucketDataSet 用于 BucketListResponse 里面的 DataSet 字段 +type BucketDataSet struct { + BucketName string `json:"BucketName,omitempty"` + BucketID string `json:"BucketId,omitempty"` + Domain DomainSet `json:"Domain,omitempty"` + Type string `json:"Type,omitempty"` + CreateTime int `json:"CreateTime,omitempty"` + ModifyTime int `json:"ModifyTime,omitempty"` + CdnDomainID []string `json:"CdnDomainId,omitempty"` + Biz string `json:"Biz,omitempty"` + Region string `json:"Region,omitempty"` + HasUserDomain int `json:"HasUserDomain,omitempty"` +} + +//BucketListResponse 用于 DescribeBucket 返回的数据。 +type BucketListResponse struct { + RetCode int `json:"RetCode,omitempty"` + Action string `json:"Action,omitempty"` + Message string `json:"Message,omitempty"` + DataSet []BucketDataSet `json:"DataSet,omitempty"` +} + +func (b BucketListResponse) Error() error { + if b.RetCode != 0 { + return errors.New(b.Message) + } + return nil +} + +//String 把 BucketListResponse 里面的字段格式化。 +func (b BucketListResponse) String() string { + return structPrettyStr(b) +} + +//CreateBucket 创建一个 bucket, bucketName 必须全部为小写字母,不能带符号和特殊字符。 +// +//region 表示 ufile 所在的可用区,目前支持北京,香港,广州,上海二,雅加达,洛杉矶。一下是可用区值的映射: +// +//北京 cn-bj +// +//广州 cn-gd +// +//可用区以控制台列出来的为准,更多可用区具体的值在 https://docs.ucloud.cn/api/summary/regionlist 查看。 +// +//bucketType 可以填 public(公有空间) 和 private(私有空间) +//projectID bucket 所在的项目 ID,可为空。 +func (u *UFileRequest) CreateBucket(bucketName, region, bucketType, projectID string) (bucket BucketResponse, err error) { + query := url.Values{} + query.Add("Action", "CreateBucket") + query.Add("BucketName", bucketName) + query.Add("Type", bucketType) + query.Add("Region", region) + + if projectID != "" { + query.Add("ProjectId", projectID) + } + + err = u.bucketRequest(query, &bucket) + return +} + +//DeleteBucket 删除一个 bucket,如果成功,status code 会返回 204 no-content +func (u *UFileRequest) DeleteBucket(bucketName, projectID string) (bucket BucketResponse, err error) { + query := url.Values{} + query.Add("Action", "DeleteBucket") + query.Add("BucketName", bucketName) + if projectID != "" { + query.Add("ProjectId", projectID) + } + + err = u.bucketRequest(query, &bucket) + return +} + +//UpdateBucket 更新一个 bucket,你可以改 bucket 的类型(私有或公有)和 项目 ID。 +//bucketType 填公有(public)或私有(private)。 +//projectID 没有可以填空("")。 +func (u *UFileRequest) UpdateBucket(bucketName, bucketType, projectID string) (bucket BucketResponse, err error) { + query := url.Values{} + query.Add("Action", "UpdateBucket") + query.Add("BucketName", bucketName) + query.Add("Type", bucketType) + if projectID != "" { + query.Add("ProjectId", projectID) + } + + err = u.bucketRequest(query, &bucket) + return +} + +//DescribeBucket 获取 bucket 的详细信息,如果 bucketName 为空,返回当前账号下所有的 bucket。 +//limit 是限制返回的 bucket 列表数量。 +//offset 是列表的偏移量,默认为 0。 +//projectID 可为空。 +func (u *UFileRequest) DescribeBucket(bucketName string, offset, limit int, projectID string) (list BucketListResponse, err error) { + query := url.Values{} + query.Add("Action", "DescribeBucket") + if bucketName != "" { + query.Add("BucketName", bucketName) + } + //offset default is 0 + query.Add("Offset", strconv.Itoa(offset)) + + if limit == 0 { + limit = 20 + } + query.Add("Limit", strconv.Itoa(limit)) + + if projectID != "" { + query.Add("ProjectId", projectID) + } + err = u.bucketRequest(query, &list) + return +} + +func (u *UFileRequest) bucketRequest(query url.Values, data response) error { + reqURL := u.genBucketURL(query) + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + return err + } + + resp, err := u.Client.Do(req) + if err != nil { + return err + } + err = u.responseParse(resp) + if err != nil { + return err + } + err = json.Unmarshal(u.LastResponseBody, data) + if err != nil { + return err + } + return data.Error() +} + +func (u *UFileRequest) genBucketURL(query url.Values) string { + u.baseURL.RawQuery = u.Auth.AuthorizationBucketMgr(query) + return u.baseURL.String() +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/file.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file.go new file mode 100644 index 000000000..1cca94d8e --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file.go @@ -0,0 +1,419 @@ +package ufsdk + +import ( + "bytes" + "encoding/base64" + "crypto/md5" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + fourMegabyte = 1 << 22 //4M +) + +//FileDataSet 用于 FileListResponse 里面的 DataSet 字段。 +type FileDataSet struct { + BucketName string `json:"BucketName,omitempty"` + FileName string `json:"FileName,omitempty"` + Hash string `json:"Hash,omitempty"` + MimeType string `json:"MimeType,omitempty"` + FirstObject string `json:"first_object,omitempty"` + Size int `json:"Size,omitempty"` + CreateTime int `json:"CreateTime,omitempty"` + ModifyTime int `json:"ModifyTime,omitempty"` + StorageClass string `json:"StorageClass,omitempty"` + RestoreStatus string `json:"RestoreStatus,omitempty"` +} + +//FileListResponse 用 PrefixFileList 接口返回的 list 数据。 +type FileListResponse struct { + BucketName string `json:"BucketName,omitempty"` + BucketID string `json:"BucketId,omitempty"` + NextMarker string `json:"NextMarker,omitempty"` + DataSet []FileDataSet `json:"DataSet,omitempty"` +} + +func (f FileListResponse) String() string { + return structPrettyStr(f) +} + +//UploadHit 文件秒传,它的原理是计算出文件的 etag 值与远端服务器进行对比,如果文件存在就快速返回。 +func (u *UFileRequest) UploadHit(filePath, keyName string) (err error) { + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + fsize := getFileSize(file) + etag := calculateEtag(file) + + query := &url.Values{} + query.Add("Hash", etag) + query.Add("FileName", keyName) + query.Add("FileSize", strconv.FormatInt(fsize, 10)) + reqURL := u.genFileURL("uploadhit") + "?" + query.Encode() + req, err := http.NewRequest("POST", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("POST", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + + return u.request(req) +} + +//PostFile 使用 HTTP Form 的方式上传一个文件。 +//注意:使用本接口上传文件后,调用 UploadHit 接口会返回 404,因为经过 form 包装的文件,etag 值会不一样,所以会调用失败。 +//mimeType 如果为空的话,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//小于 100M 的文件推荐使用本接口上传。 +func (u *UFileRequest) PostFile(filePath, keyName, mimeType string) (err error) { + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + + h := make(http.Header) + for k, v := range u.RequestHeader { + for i := 0; i < len(v); i++ { + h.Add(k, v[i]) + } + } + if mimeType == "" { + mimeType = getMimeType(file) + } + h.Add("Content-Type", mimeType) + + authorization := u.Auth.Authorization("POST", u.BucketName, keyName, h) + + boundry := makeBoundry() + body := makeFormBody(authorization, boundry, keyName, mimeType, u.verifyUploadMD5, file) + //lastline 一定要写,否则后端解析不到。 + lastline := fmt.Sprintf("\r\n--%s--\r\n", boundry) + body.Write([]byte(lastline)) + + reqURL := u.genFileURL("") + req, err := http.NewRequest("POST", reqURL, body) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundry) + contentLength := body.Len() + req.Header.Add("Content-Length", strconv.Itoa(contentLength)) + for k, v := range u.RequestHeader { + for i := 0; i < len(v); i++ { + req.Header.Add(k, v[i]) + } + } + return u.request(req) +} + +//PutFile 把文件直接放到 HTTP Body 里面上传,相对 PostFile 接口,这个要更简单,速度会更快(因为不用包装 form)。 +//mimeType 如果为空的,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//小于 100M 的文件推荐使用本接口上传。 +func (u *UFileRequest) PutFile(filePath, keyName, mimeType string) error { + reqURL := u.genFileURL(keyName) + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + + b, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + req, err := http.NewRequest("PUT", reqURL, bytes.NewBuffer(b)) + if err != nil { + return err + } + + if mimeType == "" { + mimeType = getMimeType(file) + } + req.Header.Add("Content-Type", mimeType) + for k, v := range u.RequestHeader { + for i := 0; i < len(v); i++ { + req.Header.Add(k, v[i]) + } + } + + if u.verifyUploadMD5 { + md5Str := fmt.Sprintf("%x", md5.Sum(b)) + req.Header.Add("Content-MD5", md5Str) + } + + authorization := u.Auth.Authorization("PUT", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + fileSize := getFileSize(file) + req.Header.Add("Content-Length", strconv.FormatInt(fileSize, 10)) + + return u.request(req) +} + +//PutFile 把文件直接放到 HTTP Body 里面上传,相对 PostFile 接口,这个要更简单,速度会更快(因为不用包装 form)。 +//mimeType 如果为空的,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//小于 100M 的文件推荐使用本接口上传。 +//支持带上传回调的参数, policy_json 为json 格式字符串 +func (u *UFileRequest) PutFileWithPolicy(filePath, keyName, mimeType string, policy_json string) error { + reqURL := u.genFileURL(keyName) + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + + b, err := ioutil.ReadAll(file) + if err != nil { + return err + } + + req, err := http.NewRequest("PUT", reqURL, bytes.NewBuffer(b)) + if err != nil { + return err + } + + if mimeType == "" { + mimeType = getMimeType(file) + } + req.Header.Add("Content-Type", mimeType) + + if u.verifyUploadMD5 { + md5Str := fmt.Sprintf("%x", md5.Sum(b)) + req.Header.Add("Content-MD5", md5Str) + } + + policy := base64.URLEncoding.EncodeToString([]byte(policy_json)) + authorization := u.Auth.AuthorizationPolicy("PUT", u.BucketName, keyName, policy, req.Header) + req.Header.Add("authorization", authorization) + fileSize := getFileSize(file) + req.Header.Add("Content-Length", strconv.FormatInt(fileSize, 10)) + + return u.request(req) +} + + +//DeleteFile 删除一个文件,如果删除成功 statuscode 会返回 204,否则会返回 404 表示文件不存在。 +//keyName 表示传到 ufile 的文件名。 +func (u *UFileRequest) DeleteFile(keyName string) error { + reqURL := u.genFileURL(keyName) + req, err := http.NewRequest("DELETE", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("DELETE", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//HeadFile 获取一个文件的基本信息,返回的信息全在 header 里面。包含 mimeType, content-length(文件大小), etag, Last-Modified:。 +//keyName 表示传到 ufile 的文件名。 +func (u *UFileRequest) HeadFile(keyName string) error { + reqURL := u.genFileURL(keyName) + req, err := http.NewRequest("HEAD", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("HEAD", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//PrefixFileList 获取文件列表。 +//prefix 表示匹配文件前缀。 +//marker 标志字符串 +//limit 列表数量限制,传 0 会默认设置为 20. +func (u *UFileRequest) PrefixFileList(prefix, marker string, limit int) (list FileListResponse, err error) { + query := &url.Values{} + query.Add("prefix", prefix) + query.Add("marker", marker) + if limit == 0 { + limit = 20 + } + query.Add("limit", strconv.Itoa(limit)) + reqURL := u.genFileURL("") + "?list&" + query.Encode() + + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + return + } + + authorization := u.Auth.Authorization("GET", u.BucketName, "", req.Header) + req.Header.Add("authorization", authorization) + + err = u.request(req) + if err != nil { + return + } + err = json.Unmarshal(u.LastResponseBody, &list) + return +} + +//GetPublicURL 获取公有空间的文件下载 URL +//keyName 表示传到 ufile 的文件名。 +func (u *UFileRequest) GetPublicURL(keyName string) string { + return u.genFileURL(keyName) +} + +//GetPrivateURL 获取私有空间的文件下载 URL。 +//keyName 表示传到 ufile 的文件名。 +//expiresDuation 表示下载链接的过期时间,从现在算起,24 * time.Hour 表示过期时间为一天。 +func (u *UFileRequest) GetPrivateURL(keyName string, expiresDuation time.Duration) string { + t := time.Now() + t = t.Add(expiresDuation) + expires := strconv.FormatInt(t.Unix(), 10) + signature, publicKey := u.Auth.AuthorizationPrivateURL("GET", u.BucketName, keyName, expires, http.Header{}) + query := url.Values{} + query.Add("UCloudPublicKey", publicKey) + query.Add("Signature", signature) + query.Add("Expires", expires) + reqURL := u.genFileURL(keyName) + return reqURL + "?" + query.Encode() +} + +//Download 把文件下载到 HTTP Body 里面,这里只能用来下载小文件,建议使用 DownloadFile 来下载大文件。 +func (u *UFileRequest) Download(reqURL string) error { + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + return err + } + return u.request(req) +} + +//Download 文件下载接口,下载前会先获取文件大小,如果小于 4M 直接下载。大于 4M 每次会按 4M 的分片来下载。 +func (u *UFileRequest) DownloadFile(writer io.Writer, keyName string) error { + err := u.HeadFile(keyName) + if err != nil { + return err + } + size := u.LastResponseHeader.Get("Content-Length") + fileSize, err := strconv.ParseInt(size, 10, 0) + if err != nil || fileSize <= 0 { + return fmt.Errorf("Parse content-lengt returned error") + } + + reqURL := u.GetPrivateURL(keyName, 24*time.Hour) + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + return err + } + + if fileSize < fourMegabyte { + err = u.request(req) + if err != nil { + return err + } + writer.Write(u.LastResponseBody) + } else { + var i int64 + for i = 0; i < fileSize; i += fourMegabyte { // 一次下载 4 M + start := i + end := i + fourMegabyte - 1 //数组是从 0 开始的。 &_& ..... + if end > fileSize { + end = fileSize + } + req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end)) + err = u.request(req) + if err != nil { + return err + } + writer.Write(u.LastResponseBody) + } + } + return nil +} + +//CompareFileEtag 检查远程文件的 etag 和本地文件的 etag 是否一致 +func (u *UFileRequest) CompareFileEtag(remoteKeyName, localFilePath string) bool { + err := u.HeadFile(remoteKeyName) + if err != nil { + return false + } + remoteEtag := strings.Trim(u.LastResponseHeader.Get("Etag"), "\"") + localEtag := GetFileEtag(localFilePath) + return remoteEtag == localEtag +} + +func (u *UFileRequest) genFileURL(keyName string) string { + return u.baseURL.String() + keyName +} + +//Restore 用于解冻冷存类型的文件 +func (u *UFileRequest) Restore(keyName string) (err error) { + reqURL := u.genFileURL(keyName) + "?restore" + req, err := http.NewRequest("PUT", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("PUT", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//ClassSwitch 存储类型转换接口 +//keyName 文件名称 +//storageClass 所要转换的新文件存储类型,目前支持的类型分别是标准:"STANDARD"、低频:"IA"、冷存:"ARCHIVE" +func (u *UFileRequest) ClassSwitch(keyName string, storageClass string) (err error) { + query := &url.Values{} + query.Add("storageClass", storageClass) + reqURL := u.genFileURL(keyName) + "?" + query.Encode() + req, err := http.NewRequest("PUT", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("PUT", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//Rename 重命名指定文件 +//keyName 需要被重命名的源文件 +//newKeyName 修改后的新文件名 +//force 如果已存在同名文件,值为"true"则覆盖,否则会操作失败 +func (u *UFileRequest) Rename(keyName, newKeyName, force string) (err error) { + + query := url.Values{} + query.Add("newFileName", newKeyName) + query.Add("force", force) + reqURL := u.genFileURL(keyName) + "?" + query.Encode() + + req, err := http.NewRequest("PUT", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("PUT", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//Copy 从同组织下的源Bucket中拷贝指定文件到目的Bucket中,并以新文件名命名 +//dstkeyName 拷贝到目的Bucket后的新文件名 +//srcBucketName 待拷贝文件所在的源Bucket名称 +//srcKeyName 待拷贝文件名称 +func (u *UFileRequest) Copy(dstkeyName, srcBucketName, srcKeyName string) (err error) { + + reqURL := u.genFileURL(dstkeyName) + + req, err := http.NewRequest("PUT", reqURL, nil) + if err != nil { + return err + } + req.Header.Add("X-Ufile-Copy-Source", "/" + srcBucketName + "/" + srcKeyName) + + authorization := u.Auth.Authorization("PUT", u.BucketName, dstkeyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload.go new file mode 100644 index 000000000..06150970c --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload.go @@ -0,0 +1,291 @@ +package ufsdk + +import ( + "bytes" + "crypto/md5" + "encoding/json" + "fmt" + "io" + "math" + "net/http" + "net/url" + "strconv" + "strings" + "sync" +) + +//MultipartState 用于保存分片上传的中间状态 +type MultipartState struct { + BlkSize int //服务器返回的分片大小 + uploadID string + mimeType string + keyName string + etags map[int]string + mux sync.Mutex +} + +//UnmarshalJSON custom unmarshal json +func (m *MultipartState) UnmarshalJSON(bytes []byte) error { + tmp := struct { + BlkSize int `json:"BlkSize"` + UploadID string `json:"UploadId"` + }{} + err := json.Unmarshal(bytes, &tmp) + if err != nil { + return err + } + m.BlkSize = tmp.BlkSize + m.uploadID = tmp.UploadID + return nil +} + +type uploadChan struct { + etag string + err error +} + +//MPut 分片上传一个文件,filePath 是本地文件所在的路径,内部会自动对文件进行分片上传,上传的方式是同步一片一片的上传。 +//mimeType 如果为空的话,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//大于 100M 的文件推荐使用本接口上传。 +func (u *UFileRequest) MPut(filePath, keyName, mimeType string) error { + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + if mimeType == "" { + mimeType = getMimeType(file) + } + + state, err := u.InitiateMultipartUpload(keyName, mimeType) + if err != nil { + return err + } + + chunk := make([]byte, state.BlkSize) + var pos int + for { + bytesRead, fileErr := file.Read(chunk) + if fileErr == io.EOF || bytesRead == 0 { //后面直接读到了结尾 + break + } + buf := bytes.NewBuffer(chunk[:bytesRead]) + err := u.UploadPart(buf, state, pos) + if err != nil { + u.AbortMultipartUpload(state) + return err + } + pos++ + } + + return u.FinishMultipartUpload(state) +} + +//AsyncMPut 异步分片上传一个文件,filePath 是本地文件所在的路径,内部会自动对文件进行分片上传,上传的方式是使用异步的方式同时传多个分片的块。 +//mimeType 如果为空的话,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//大于 100M 的文件推荐使用本接口上传。 +//同时并发上传的分片数量为10 +func (u *UFileRequest) AsyncMPut(filePath, keyName, mimeType string) error { + return u.AsyncUpload(filePath, keyName, mimeType, 10) +} + +//AsyncUpload AsyncMPut 的升级版, jobs 表示同时并发的数量。 +func (u *UFileRequest) AsyncUpload(filePath, keyName, mimeType string, jobs int) error { + if jobs <= 0 { + jobs = 1 + } + + if jobs >= 30 { + jobs = 10 + } + + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + if mimeType == "" { + mimeType = getMimeType(file) + } + + state, err := u.InitiateMultipartUpload(keyName, mimeType) + if err != nil { + return err + } + fsize := getFileSize(file) + chunkCount := divideCeil(fsize, int64(state.BlkSize)) //向上取整 + concurrentChan := make(chan error, jobs) + for i := 0; i != jobs; i++ { + concurrentChan <- nil + } + + wg := &sync.WaitGroup{} + for i := 0; i != chunkCount; i++ { + uploadErr := <-concurrentChan //最初允许启动 10 个 goroutine,超出10个后,有分片返回才会开新的goroutine. + if uploadErr != nil { + err = uploadErr + break // 中间如果出现错误立即停止继续上传 + } + wg.Add(1) + go func(pos int) { + defer wg.Done() + offset := int64(state.BlkSize * pos) + chunk := make([]byte, state.BlkSize) + bytesRead, _ := file.ReadAt(chunk, offset) + e := u.UploadPart(bytes.NewBuffer(chunk[:bytesRead]), state, pos) + concurrentChan <- e //跑完一个 goroutine 后,发信号表示可以开启新的 goroutine。 + }(i) + } + wg.Wait() //等待所有任务返回 + if err == nil { //再次检查剩余上传完的分片是否有错误 + loopCheck: + for { + select { + case e := <-concurrentChan: + err = e + if err != nil { + break loopCheck + } + default: + break loopCheck + } + } + } + close(concurrentChan) + if err != nil { + u.AbortMultipartUpload(state) + return err + } + + return u.FinishMultipartUpload(state) +} + +//AbortMultipartUpload 取消分片上传,如果掉用 UploadPart 出现错误,可以调用本函数取消分片上传。 +//state 参数是 InitiateMultipartUpload 返回的 +func (u *UFileRequest) AbortMultipartUpload(state *MultipartState) error { + query := &url.Values{} + query.Add("uploadId", state.uploadID) + reqURL := u.genFileURL(state.keyName) + "?" + query.Encode() + + req, err := http.NewRequest("DELETE", reqURL, nil) + if err != nil { + return err + } + authorization := u.Auth.Authorization("DELETE", u.BucketName, state.keyName, req.Header) + req.Header.Add("authorization", authorization) + return u.request(req) +} + +//InitiateMultipartUpload 初始化分片上传,返回一个 state 用于后续的 UploadPart, FinishMultipartUpload, AbortMultipartUpload 的接口。 +// +//keyName 表示传到 ufile 的文件名。 +// +//mimeType 表示文件的 mimeType, 传空会报错,你可以使用 GetFileMimeType 方法检测文件的 mimeType。如果您上传的不是文件,您可以使用 http.DetectContentType https://golang.org/src/net/http/sniff.go?s=646:688#L11进行检测。 +func (u *UFileRequest) InitiateMultipartUpload(keyName, mimeType string) (*MultipartState, error) { + reqURL := u.genFileURL(keyName) + "?uploads" + req, err := http.NewRequest("POST", reqURL, nil) + if err != nil { + return nil, err + } + // if mimeType == "" { + // return nil, fmt.Errorf("Mime Type 不能为空!!!") + // } + req.Header.Add("Content-Type", mimeType) + for k, v := range u.RequestHeader { + for i := 0; i < len(v); i++ { + req.Header.Add(k, v[i]) + } + } + + authorization := u.Auth.Authorization("POST", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + + err = u.request(req) + if err != nil { + return nil, err + } + response := new(MultipartState) + err = json.Unmarshal(u.LastResponseBody, response) + if err != nil { + return nil, err + } + response.keyName = keyName + response.etags = make(map[int]string) + response.mimeType = mimeType + + return response, err +} + +//UploadPart 上传一个分片,buf 就是分片数据,buf 的数据块大小必须为 state.BlkSize,否则会报错。 +//pardNumber 表示第几个分片,从 0 开始。例如一个文件按 state.BlkSize 分为 5 块,那么分片分别是 0,1,2,3,4。 +//state 参数是 InitiateMultipartUpload 返回的 +func (u *UFileRequest) UploadPart(buf *bytes.Buffer, state *MultipartState, partNumber int) error { + query := &url.Values{} + query.Add("uploadId", state.uploadID) + query.Add("partNumber", strconv.Itoa(partNumber)) + + reqURL := u.genFileURL(state.keyName) + "?" + query.Encode() + req, err := http.NewRequest("PUT", reqURL, buf) + if err != nil { + return err + } + if u.verifyUploadMD5 { + md5Str := fmt.Sprintf("%x", md5.Sum(buf.Bytes())) + req.Header.Add("Content-MD5", md5Str) + } + + req.Header.Add("Content-Type", state.mimeType) + authorization := u.Auth.Authorization("PUT", u.BucketName, state.keyName, req.Header) + req.Header.Add("Authorization", authorization) + req.Header.Add("Content-Length", strconv.Itoa(buf.Len())) + + resp, err := u.requestWithResp(req) + if err != nil { + return err + } + defer resp.Body.Close() + + etag := strings.Trim(resp.Header.Get("Etag"), "\"") //为保证线程安全,这里就不保留 lastResponse + if etag == "" { + etag = strings.Trim(resp.Header.Get("ETag"), "\"") //为保证线程安全,这里就不保留 lastResponse + } + state.mux.Lock() + state.etags[partNumber] = etag + state.mux.Unlock() + return nil +} + +//FinishMultipartUpload 完成分片上传。分片上传必须要调用的接口。 +//state 参数是 InitiateMultipartUpload 返回的 +func (u *UFileRequest) FinishMultipartUpload(state *MultipartState) error { + query := &url.Values{} + query.Add("uploadId", state.uploadID) + reqURL := u.genFileURL(state.keyName) + "?" + query.Encode() + var etagsStr string + etagLen := len(state.etags) + for i := 0; i != etagLen; i++ { + etagsStr += state.etags[i] + if i != etagLen-1 { + etagsStr += "," + } + } + + req, err := http.NewRequest("POST", reqURL, strings.NewReader(etagsStr)) + if err != nil { + return err + } + req.Header.Add("Content-Type", state.mimeType) + authorization := u.Auth.Authorization("POST", u.BucketName, state.keyName, req.Header) + req.Header.Add("Authorization", authorization) + req.Header.Add("Content-Length", strconv.Itoa(len(etagsStr))) + + return u.request(req) +} + +func divideCeil(a, b int64) int { + div := float64(a) / float64(b) + c := math.Ceil(div) + return int(c) +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload_with_policy.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload_with_policy.go new file mode 100644 index 000000000..51d29fa0d --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/file_mutipart_upload_with_policy.go @@ -0,0 +1,166 @@ +package ufsdk + +import ( + "bytes" + "io" + "encoding/base64" + "net/http" + "net/url" + "strconv" + "strings" + "sync" +) + +//带回调策略的mput 接口,一些基础函数依赖于 file_mput 定义的函数 + +//MPut 分片上传一个文件,filePath 是本地文件所在的路径,内部会自动对文件进行分片上传,上传的方式是同步一片一片的上传。 +//mimeType 如果为空的话,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//大于 100M 的文件推荐使用本接口上传。 +func (u *UFileRequest) MPutWithPolicy(filePath, keyName, mimeType string, policy_json string) error { + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + if mimeType == "" { + mimeType = getMimeType(file) + } + + state, err := u.InitiateMultipartUpload(keyName, mimeType) + if err != nil { + return err + } + + chunk := make([]byte, state.BlkSize) + var pos int + for { + bytesRead, fileErr := file.Read(chunk) + if fileErr == io.EOF || bytesRead == 0 { //后面直接读到了结尾 + break + } + buf := bytes.NewBuffer(chunk[:bytesRead]) + err := u.UploadPart(buf, state, pos) + if err != nil { + u.AbortMultipartUpload(state) + return err + } + pos++ + } + + return u.FinishMultipartUploadWithPolicy(state, policy_json) +} + +//AsyncMPut 异步分片上传一个文件,filePath 是本地文件所在的路径,内部会自动对文件进行分片上传,上传的方式是使用异步的方式同时传多个分片的块。 +//mimeType 如果为空的话,会调用 net/http 里面的 DetectContentType 进行检测。 +//keyName 表示传到 ufile 的文件名。 +//大于 100M 的文件推荐使用本接口上传。 +//同时并发上传的分片数量为10 +func (u *UFileRequest) AsyncMPutWithPolicy(filePath, keyName, mimeType string, policy_json string) error { + return u.AsyncUploadWithPolicy(filePath, keyName, mimeType, 10, policy_json) +} + +//AsyncUpload AsyncMPut 的升级版, jobs 表示同时并发的数量。 +func (u *UFileRequest) AsyncUploadWithPolicy(filePath, keyName, mimeType string, jobs int, policy_json string) error { + if jobs <= 0 { + jobs = 1 + } + + if jobs >= 30 { + jobs = 10 + } + + file, err := openFile(filePath) + if err != nil { + return err + } + defer file.Close() + if mimeType == "" { + mimeType = getMimeType(file) + } + + state, err := u.InitiateMultipartUpload(keyName, mimeType) + if err != nil { + return err + } + fsize := getFileSize(file) + chunkCount := divideCeil(fsize, int64(state.BlkSize)) //向上取整 + concurrentChan := make(chan error, jobs) + for i := 0; i != jobs; i++ { + concurrentChan <- nil + } + + wg := &sync.WaitGroup{} + for i := 0; i != chunkCount; i++ { + uploadErr := <-concurrentChan //最初允许启动 10 个 goroutine,超出10个后,有分片返回才会开新的goroutine. + if uploadErr != nil { + err = uploadErr + break // 中间如果出现错误立即停止继续上传 + } + wg.Add(1) + go func(pos int) { + defer wg.Done() + offset := int64(state.BlkSize * pos) + chunk := make([]byte, state.BlkSize) + bytesRead, _ := file.ReadAt(chunk, offset) + e := u.UploadPart(bytes.NewBuffer(chunk[:bytesRead]), state, pos) + concurrentChan <- e //跑完一个 goroutine 后,发信号表示可以开启新的 goroutine。 + }(i) + } + wg.Wait() //等待所有任务返回 + if err == nil { //再次检查剩余上传完的分片是否有错误 + loopCheck: + for { + select { + case e := <-concurrentChan: + err = e + if err != nil { + break loopCheck + } + default: + break loopCheck + } + } + } + close(concurrentChan) + if err != nil { + u.AbortMultipartUpload(state) + return err + } + + return u.FinishMultipartUploadWithPolicy(state, policy_json) +} + + +//FinishMultipartUpload 完成分片上传。分片上传必须要调用的接口。 +//state 参数是 InitiateMultipartUpload 返回的 +func (u *UFileRequest) FinishMultipartUploadWithPolicy(state *MultipartState, policy_json string) error { + query := &url.Values{} + query.Add("uploadId", state.uploadID) + reqURL := u.genFileURL(state.keyName) + "?" + query.Encode() + var etagsStr string + etagLen := len(state.etags) + for i := 0; i != etagLen; i++ { + etagsStr += state.etags[i] + if i != etagLen-1 { + etagsStr += "," + } + } + + req, err := http.NewRequest("POST", reqURL, strings.NewReader(etagsStr)) + if err != nil { + return err + } + + req.Header.Add("Content-Type", state.mimeType) + + policy := base64.URLEncoding.EncodeToString([]byte(policy_json)) + authorization := u.Auth.AuthorizationPolicy("POST", u.BucketName, state.keyName, policy, req.Header) + + req.Header.Add("Authorization", authorization) + req.Header.Add("Content-Length", strconv.Itoa(len(etagsStr))) + + return u.request(req) +} + + diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/io.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/io.go new file mode 100644 index 000000000..097d3dc01 --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/io.go @@ -0,0 +1,104 @@ +package ufsdk + +import ( + "bytes" + "errors" + "io" + "io/ioutil" + "net/http" + "strings" + "sync" +) + +// IOPut 流式 put 上传接口,你必须确保你的 reader 接口每次调用是递进式的调用,也就是像文件那样的读取方式。 +// mimeType 在这里的检测不会很准确,你可以手动指定更精确的 mimetype。 +// 这里的 reader 接口会把数据全部读到 HTTP Body 里面,如果你接口的数据特别大,请使用 IOMutipartAsyncUpload 接口。 +func (u *UFileRequest) IOPut(reader io.Reader, keyName, mimeType string) (err error) { + if keyName == "" { + err = errors.New("keyName cannot be empty") + return + } + + switch reader.(type) { + case *bytes.Buffer, *bytes.Reader, *strings.Reader: + break + default: + b, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + reader = bytes.NewReader(b) + } + + reqURL := u.genFileURL(keyName) + req, err := http.NewRequest("PUT", reqURL, reader) + if err != nil { + return err + } + + req.Header.Add("Content-Type", mimeType) + + authorization := u.Auth.Authorization("PUT", u.BucketName, keyName, req.Header) + req.Header.Add("authorization", authorization) + + return u.request(req) +} + +// +// IOMutipartAsyncUpload 流式分片上传接口,你必须确保你的 reader 接口每次调用是递进式的调用,也就是像文件那样的读取方式。 +// mimeType 在这里的检测不会很准确,你可以手动指定更精确的 mimetype。 +// 这里的会每次读取4M 的数据到 buffer 里面,适用于大量数据上传。 +func (u *UFileRequest) IOMutipartAsyncUpload(reader io.Reader, keyName, mimeType string) (err error) { + if keyName == "" { + err = errors.New("keyName cannot be empty") + return + } + state, err := u.InitiateMultipartUpload(keyName, mimeType) + if err != nil { + return + } + + maxJobRunning := 10 //最多允许 10 个线程同时跑 + concurrentChan := make(chan error, maxJobRunning) + for i := 0; i != maxJobRunning; i++ { + concurrentChan <- nil + } + wg := &sync.WaitGroup{} + for i := 0; ; i++ { + uploadErr := <-concurrentChan //最初允许启动 10 个 goroutine,超出10个后,有分片返回才会开新的goroutine. + if uploadErr != nil { + u.AbortMultipartUpload(state) + return uploadErr // 中间如果出现错误立即停止继续上传 + } + + chunk := make([]byte, state.BlkSize) + bytesRead, readErr := reader.Read(chunk) + if readErr == io.EOF || bytesRead == 0 { + break + } + if readErr != nil { + u.AbortMultipartUpload(state) + return uploadErr // 检查读文件是否出现错误。 + } + wg.Add(1) + go func(pos int, buf *bytes.Buffer) { + defer wg.Done() + e := u.UploadPart(buf, state, pos) + concurrentChan <- e //跑完一个 goroutine 后,发信号表示可以开启新的 goroutine。 + }(i, bytes.NewBuffer(chunk[:bytesRead])) + } + + go func() { + wg.Wait() + close(concurrentChan) //close channel, when all upload goroutines has finished. + }() + + for err = range concurrentChan { //waitting for all goroutine finished. It will blocked until the channel has been closed. + if err != nil { + u.AbortMultipartUpload(state) + return err + } + } + + return u.FinishMultipartUpload(state) +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/request.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/request.go new file mode 100644 index 000000000..f69c2bc52 --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/request.go @@ -0,0 +1,181 @@ +package ufsdk + +import ( + "bytes" + "context" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +//UFileRequest SDK 主要的 request 模块。本 SDK 遵从以下原则: +// +//1.接口尽可能简洁,隐藏所有复杂实现。 +// +//2.本 SDK 主要的作用是封装 HTTP 请求,不做过多其他的封装(如 HTTP Body 序列化,详细的错误检查)。 +// +//3.只简单封装 HTTP 请求所需要的参数,给接口使用者提供所有原生的 HTTP response header,body,status code 返回,以便排错。 +// +//4.远端请求返回值统一返回一个 error,如果为 nil 表示无错。LastResponseStatus,LastResponseHeader,LastResponseBody 可以查看具体的 HTTP 返回信息()。如果你想少敲几行代码可以直接调用 DumpResponse(true) 查看详细返回。 +// +type UFileRequest struct { + Auth Auth + BucketName string + Host string + Client *http.Client + Context context.Context + baseURL *url.URL + RequestHeader http.Header + + LastResponseStatus int + LastResponseHeader http.Header + LastResponseBody []byte + verifyUploadMD5 bool + lastResponse *http.Response +} + +//NewFileRequest 创建一个用于管理文件的 request,管理文件的 url 与 管理 bucket 接口不一样, +//请将 bucket 和文件管理所需要的分开,NewUBucketRequest 是用来管理 bucket 的。 +//Request 创建后的 instance 不是线程安全的,如果你需要做并发的操作,请创建多个 UFileRequest。 +//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。 +//client 这里你可以传空,会使用默认的 http.Client。如果你需要设置超时以及一些其他相关的网络配置选项请传入一个自定义的 client。 +func NewFileRequest(config *Config, client *http.Client) (*UFileRequest, error) { + config.BucketName = strings.TrimSpace(config.BucketName) + config.FileHost = strings.TrimSpace(config.FileHost) + if config.BucketName == "" || config.FileHost == "" { + return nil, errors.New("管理文件上传必须要提供 bucket 名字和所在地域的 Host 域名") + } + req := newRequest(config.PublicKey, config.PrivateKey, + config.BucketName, config.FileHost, client) + req.verifyUploadMD5 = config.VerifyUploadMD5 + if req.baseURL.Scheme == "" { //用户传了非自定义域名 + req.baseURL.Host = req.BucketName + "." + req.Host + req.baseURL.Scheme = "http" + } + return req, nil +} + +//NewFileRequestWithHeader 创建一个可携带用户自定义http请求头的 request +//Request 创建后的 instance 不是线程安全的,如果你需要做并发的操作,请创建多个 UFileRequest。 +//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。 +//header 自定义http请求头 +//client 这里你可以传空,会使用默认的 http.Client。如果你需要设置超时以及一些其他相关的网络配置选项请传入一个自定义的 client。 +func NewFileRequestWithHeader(config *Config, header http.Header, client *http.Client) (*UFileRequest, error) { + req, err := NewFileRequest(config, client) + if err != nil{ + return nil, err + } + req.RequestHeader = header + return req, nil +} + +//NewBucketRequest 创建一个用于管理 bucket 的 request。 +//注意:不要拿它去调用文件管理的 request,我文件管理和 bucket 管理接口放到一个 request 里面的目的就是让接口更统一,代码更清晰,简洁。 +//config 参数里面包含了公私钥,以及其他必填的参数。详情见 config 相关文档。 +func NewBucketRequest(config *Config, client *http.Client) (*UFileRequest, error) { + config.BucketHost = strings.TrimSpace(config.BucketHost) + if config.BucketHost == "" { + return nil, errors.New("管理 Bucket 必须要提供对应的 API host") + } + req := newRequest(config.PublicKey, config.PrivateKey, "", config.BucketHost, client) + req.verifyUploadMD5 = config.VerifyUploadMD5 + if req.baseURL.Scheme == "" { + req.baseURL.Scheme = "http" + } + return req, nil +} + +//DumpResponse dump 当前请求的返回结果,里面有一个 print 函数,会把 body,header,status code 直接输出到 stdout。 +//如果你需要 Dump 到其他的地方,直接拿返回值即可。 +func (u *UFileRequest) DumpResponse(isDumpBody bool) []byte { + var b bytes.Buffer + if u.lastResponse == nil { + return nil + } + b.WriteString(fmt.Sprintf("%s %d\n", u.lastResponse.Proto, u.LastResponseStatus)) + for k, vs := range u.LastResponseHeader { + str := k + ": " + for i, v := range vs { + if i != 0 { + str += "; " + v + } else { + str += v + } + } + b.WriteString(str) + } + if isDumpBody { + b.Write(u.LastResponseBody) + } + return b.Bytes() +} + +func newRequest(publicKey, privateKey, bucket, host string, client *http.Client) *UFileRequest { + req := new(UFileRequest) + req.Auth = NewAuth(publicKey, privateKey) + req.BucketName = bucket + req.Host = strings.TrimSpace(host) + req.baseURL = new(url.URL) + req.baseURL.Host = req.Host + req.baseURL.Path = "/" //for default usage. + + if client == nil { + client = new(http.Client) + } + req.Client = client + req.Context = context.TODO() + return req +} + +func (u *UFileRequest) responseParse(resp *http.Response) error { + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + defer resp.Body.Close() + + u.LastResponseStatus = resp.StatusCode + u.LastResponseHeader = resp.Header + u.LastResponseBody = resBody + u.lastResponse = resp + return nil +} + +func (u *UFileRequest) request(req *http.Request) error { + resp, err := u.requestWithResp(req) + if err != nil { + return err + } + + err = u.responseParse(resp) + if err != nil { + return err + } + + if !VerifyHTTPCode(resp.StatusCode) { + return fmt.Errorf("Remote response code is %d - %s not 2xx call DumpResponse(true) show details", + resp.StatusCode, http.StatusText(resp.StatusCode)) + } + + return nil +} + +func (u *UFileRequest) requestWithResp(req *http.Request) (resp *http.Response, err error) { + req.Header.Set("User-Agent", "UFile-GoSDK-Client/2.0") + + resp, err = u.Client.Do(req.WithContext(u.Context)) + // If we got an error, and the context has been canceled, + // the context's error is probably more useful. + if err != nil { + select { + case <-u.Context.Done(): + err = u.Context.Err() + default: + } + return + } + return +} diff --git a/vendor/github.com/ufilesdk-dev/ufile-gosdk/utils.go b/vendor/github.com/ufilesdk-dev/ufile-gosdk/utils.go new file mode 100644 index 000000000..99c11847f --- /dev/null +++ b/vendor/github.com/ufilesdk-dev/ufile-gosdk/utils.go @@ -0,0 +1,183 @@ +package ufsdk + +import ( + "bytes" + "crypto/md5" + "crypto/sha1" + "encoding/base64" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "time" +) + +const ( + blkSIZE = 2 << 21 +) + +//Config 配置文件序列化所需的全部字段 +type Config struct { + PublicKey string `json:"public_key"` + PrivateKey string `json:"private_key"` + BucketName string `json:"bucket_name"` + FileHost string `json:"file_host"` + BucketHost string `json:"bucket_host"` + VerifyUploadMD5 bool `json:"verfiy_upload_md5"` +} + +//LoadConfig 从配置文件加载一个配置。 +func LoadConfig(jsonPath string) (*Config, error) { + file, err := openFile(jsonPath) + if err != nil { + return nil, err + } + defer file.Close() + configBytes, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + c := new(Config) + err = json.Unmarshal(configBytes, c) + if err != nil { + return nil, err + } + return c, nil +} + +//VerifyHTTPCode 检查 HTTP 的返回值是否为 2XX,如果不是就返回 false。 +func VerifyHTTPCode(code int) bool { + if code < http.StatusOK || code > http.StatusIMUsed { + return false + } + return true +} + +//GetFileMimeType 获取文件的 mime type 值,接收文件路径作为参数。如果检测不到,则返回空。 +func GetFileMimeType(path string) string { + f, err := openFile(path) + if err != nil { + return "" + } + defer f.Close() + return getMimeType(f) +} + +func getMimeType(f *os.File) string { + buffer := make([]byte, 512) + + _, err := f.Read(buffer) + defer func() { f.Seek(0, 0) }() //revert file's seek + if err != nil { + return "plain/text" + } + + return http.DetectContentType(buffer) +} + +func openFile(path string) (*os.File, error) { + return os.Open(path) +} + +//getFileSize get opened file size +func getFileSize(f *os.File) int64 { + fi, err := f.Stat() + if err != nil { + panic(err.Error()) + } + return fi.Size() +} + +//GetFileEtag 获取文件的 etag 值 +func GetFileEtag(path string) string { + f, err := openFile(path) + if err != nil { + return "" + } + defer f.Close() + return calculateEtag(f) +} + +//Calculatek 计算文件的 etag 值。 +func calculateEtag(f *os.File) string { + fsize := getFileSize(f) + blkcnt := uint32(fsize / blkSIZE) + if fsize%blkSIZE != 0 { + blkcnt++ + } + + bs := make([]byte, 4) + binary.LittleEndian.PutUint32(bs, blkcnt) + + h := sha1.New() + buf := make([]byte, 0, 24) + buf = append(buf, bs...) + if fsize <= blkSIZE { + io.Copy(h, f) + } else { + var i uint32 + for i = 0; i < blkcnt; i++ { + shaBlk := sha1.New() + io.Copy(shaBlk, io.LimitReader(f, blkSIZE)) + io.Copy(h, bytes.NewReader(shaBlk.Sum(nil))) + } + } + buf = h.Sum(buf) + etag := base64.URLEncoding.EncodeToString(buf) + return etag +} + +func makeBoundry() string { + h := md5.New() + t := time.Now() + io.WriteString(h, t.String()) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +func makeFormBody(authorization, boundry, keyName, mimeType string, verifyMD5 bool, file *os.File) *bytes.Buffer { + boundry = "--" + boundry + "\r\n" + boundryBytes := []byte(boundry) + body := new(bytes.Buffer) + + body.Write(boundryBytes) + body.Write(makeFormField("Authorization", authorization)) + body.Write(boundryBytes) + body.Write(makeFormField("Content-Type", mimeType)) + body.Write(boundryBytes) + body.Write(makeFormField("FileName", keyName)) + body.Write(boundryBytes) + + if verifyMD5 { + h := md5.New() + io.Copy(h, file) + md5Str := fmt.Sprintf("%x", h.Sum(nil)) + body.Write(makeFormField("Content-MD5", md5Str)) + body.Write(boundryBytes) + } + + addtionalStr := fmt.Sprintf("Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", keyName) + addtionalStr += fmt.Sprintf("Content-Type: %s\r\n\r\n", mimeType) + body.Write([]byte(addtionalStr)) + body.ReadFrom(file) + body.Write([]byte("\r\n")) + body.Write(boundryBytes) + + return body +} + +func makeFormField(key, value string) []byte { + keyStr := fmt.Sprintf("Content-Disposition: form-data; name=\"%s\"\r\n\r\n", key) + valueStr := fmt.Sprintf("%s\r\n", value) + return []byte(keyStr + valueStr) +} + +func structPrettyStr(data interface{}) string { + bytes, err := json.MarshalIndent(data, "", " ") + if err == nil { + return fmt.Sprintf("%s\n", bytes) + } + return "" +} diff --git a/vendor/modules.txt b/vendor/modules.txt index fd6a5ea29..a23af1afd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -508,10 +508,11 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312 -# github.com/ucloud/ucloud-sdk-go v0.8.7 +# github.com/ucloud/ucloud-sdk-go v0.12.0 github.com/ucloud/ucloud-sdk-go/private/protocol/http github.com/ucloud/ucloud-sdk-go/private/utils github.com/ucloud/ucloud-sdk-go/services/uaccount +github.com/ucloud/ucloud-sdk-go/services/ufile github.com/ucloud/ucloud-sdk-go/services/uhost github.com/ucloud/ucloud-sdk-go/services/unet github.com/ucloud/ucloud-sdk-go/services/vpc @@ -522,6 +523,8 @@ github.com/ucloud/ucloud-sdk-go/ucloud/log github.com/ucloud/ucloud-sdk-go/ucloud/request github.com/ucloud/ucloud-sdk-go/ucloud/response github.com/ucloud/ucloud-sdk-go/ucloud/version +# github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 +github.com/ufilesdk-dev/ufile-gosdk # github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 github.com/ugorji/go/codec # github.com/ulikunitz/xz v0.5.5 diff --git a/website/source/docs/builders/ucloud-uhost.html.md b/website/source/docs/builders/ucloud-uhost.html.md index 0bdce3f72..9d9a51669 100644 --- a/website/source/docs/builders/ucloud-uhost.html.md +++ b/website/source/docs/builders/ucloud-uhost.html.md @@ -26,7 +26,7 @@ In addition to the options listed here, a [communicator](../templates/communicator.html) can be configured for this builder. -\~> **Note:** The bulider doesn't support Windows images for now and only supports CentOS and Ubuntu images via SSH anthentication with `ssh_username` (Required) and `ssh_password` (Optional). The `ssh_username` must be `root` for CentOS images and `ubuntu` for Ubuntu images. The `ssh_password` may contain 8-30 characters, and must consist of at least 2 items out of the capital letters, lower case letters, numbers and special characters. The special characters include `()~!@#$%^&*-+=_|{}\[]:;'<>,.?/. +\~> **Note:** The builder doesn't support Windows images for now and only supports CentOS and Ubuntu images via SSH authentication with `ssh_username` (Required) and `ssh_password` (Optional). The `ssh_username` must be `root` for CentOS images and `ubuntu` for Ubuntu images. The `ssh_password` may contain 8-30 characters, and must consist of at least 2 items out of the capital letters, lower case letters, numbers and special characters. The special characters include `()~!@#$%^&*-+=_|{}\[]:;'<>,.?/. ### Required: @@ -48,22 +48,19 @@ builder. ### Optional: +- `base_url` - (string) This is the base url. (Default: `https://api.ucloud.cn`). + - `use_ssh_private_ip` - (boolean) - If this value is true, packer will connect to the created UHost instance via a private ip instead of allocating an EIP (elastic public ip).(Default: `false`). \~> **Note:** By default (`use_ssh_private_ip` is `false`), the launched uhost instance will be connecting with extranet by bounding with an EIP (elastic public ip) automatically, which bandwidth is 30 Mb by default and paid by traffic. -- `internet_bandwidth` - (string) Maximum bandwidth to the EIP (elastic public ip), measured in Mbps (Mega bit per second). - The ranges for bandwidth are: 1-200 to pay by traffic, 1-800 to pay by bandwith. (Default: `1`). - -- `internet_charge_mode` -(Optional) The EIP (elastic public ip) charge mode associated to UHost instance. Possible values are: `traffic` as pay by traffic, `bandwidth` as pay by bandwidth. (Default: `traffic`). - - `vpc_id` - (string) The ID of VPC linked to the UHost instance. If not defined `vpc_id`, the instance will use the default VPC in the current region. -- `subnet_id` - (string) The ID of subnet under VPC. If `vpc_id` is defined, the `subnet_id` is mandatory required. If `vpc_id` and `subnet_id` are not defined, the instance will use the default subnet in the current region. +- `subnet_id` - (string) The ID of subnet under the VPC. If `vpc_id` is defined, the `subnet_id` is mandatory required. If `vpc_id` and `subnet_id` are not defined, the instance will use the default subnet in the current region. -- `security_group` - (string) The ID of the fire wall associated to UHost instance. If `security_group` is not defined, +- `security_group_id` - (string) The ID of the fire wall associated to UHost instance. If `security_group_id` is not defined, the instance will use the non-recommended web fire wall, and open port include 22, 3389 by default. It is supported by ICMP fire wall protocols. - You may refer to [security group](https://docs.ucloud.cn/network/firewall/firewall.html). + You may refer to [security group_id](https://docs.ucloud.cn/network/firewall/firewall.html). - `image_description` (string) - The description of the image. @@ -83,6 +80,8 @@ builder. - `name` (string) - The copied image name. If not defined, builder will use `image_name` as default name. - `description` (number) - The copied image description. + +- `wait_image_ready_timeout` (int)Timeout of creating image or copying image. The default timeout is 3600 seconds if this option is not set or is set to 0. ## Examples diff --git a/website/source/docs/post-processors/ucloud-import.html.md b/website/source/docs/post-processors/ucloud-import.html.md new file mode 100644 index 000000000..bdf778ce0 --- /dev/null +++ b/website/source/docs/post-processors/ucloud-import.html.md @@ -0,0 +1,78 @@ +--- +description: | + The Packer UCloud Import post-processor takes the RAW, VHD, VMDK, or qcow2 artifact from various builders and imports it to UCloud customized image list for UHost Instance. +layout: docs +page_title: 'UCloud Import Post-Processors' +sidebar_current: 'docs-post-processors-ucloud-import' +--- + +# UCloud Import Post-Processor + +Type: `ucloud-import` + + The Packer UCloud Import post-processor takes the RAW, VHD, VMDK, or qcow2 artifact from various builders and imports it to UCloud customized image list for UHost Instance. + +~> **Note** Some regions don't support image import. You may refer to [ucloud console](https://console.ucloud.cn/uhost/uimage) for more detail. If you want to import to unsupported regions, please import the image in `cn-bj2` first, and then copy the image to the target region. + +## How Does it Work? + +The import process operates by making a temporary copy of the RAW, VHD, VMDK, or qcow2 to an UFile bucket, and calling an import task in UHost on the RAW, VHD, VMDK, or qcow2 file. Once completed, an UCloud UHost Image is returned. The temporary RAW, VHD, VMDK, or qcow2 copy in UFile can be discarded after the import is complete. + +## Configuration + +There are some configuration options available for the post-processor. There +are two categories: required and optional parameters. + +### Required: + +- `public_key` - (string) This is the UCloud public key. It must be provided, but it can also be sourced from the `UCLOUD_PUBLIC_KEY` environment variable. + +- `private_key` - (string) This is the UCloud private key. It must be provided, but it can also be sourced from the `UCLOUD_PRIVATE_KEY` environment variable. + +- `project_id` - (string) This is the UCloud project id. It must be provided, but it can also be sourced from the `UCLOUD_PROJECT_ID` environment variable. + +- `region` - (string) This is the UCloud region. It must be provided, but it can also be sourced from the `UCLOUD_REGION` environment variable. + +- `image_name` - (string) The name of the user-defined image, which contains 1-63 characters and only supports Chinese, English, numbers, '-_,.:[]'. + +- `ufile_bucket_name` (string) - The name of the UFile bucket where the RAW, VHD, VMDK, or qcow2 file will be copied to for import. This bucket must exist when the post-processor is run. + +- `image_os_type` (string) - Type of the OS. Possible values are: `CentOS`, `Ubuntu`, `Windows`, `RedHat`, `Debian`, `Other`. You may refer to [ucloud_api_docs](https://docs.ucloud.cn/api/uhost-api/import_custom_image) for detail. + +- `image_os_name` (string) - The name of OS. Such as: `CentOS 7.2 64位`, set `Other` When `image_os_type` is `Other`. You may refer to [ucloud_api_docs](https://docs.ucloud.cn/api/uhost-api/import_custom_image) for detail. + +- `format` (string) - The format of the import image , Possible values are: `raw`, `vhd`, `vmdk`, or `qcow2`. + +### Optional: + +- `base_url` - (string) This is the base url. (Default: `https://api.ucloud.cn`). + +- `ufile_key_name` (string) - The name of the object key in `ufile_bucket_name` where the RAW, VHD, VMDK, or qcow2 file will be copied to import. + +- `skip_clean` (boolean) - Whether we should skip removing the RAW, VHD, VMDK, or qcow2 file uploaded to UFile after the import process has completed. Possible values are: `true` to leave it in the UFile bucket, `false` to remove it. (Default: `false`). + +- `image_description` (string) - The description of the image. + +- `wait_image_ready_timeout`(number) - Timeout of importing image. The default timeout is 3600 seconds if this option is not set or is set + to 0. + +## Basic Example + +Here is a basic example. This assumes that the builder has produced a RAW artifact for us to work with. This will take the RAW image generated by a builder and upload it to UFile. Once uploaded, the import process will start, creating an UCloud UHost image to the region `cn-bj2`. + +``` json +"post-processors":[ + { + "type":"ucloud-import", + "public_key": "{{user `ucloud_public_key`}}", + "private_key": "{{user `ucloud_private_key`}}", + "project_id": "{{user `ucloud_project_id`}}", + "region":"cn-bj2", + "ufile_bucket_name": "packer-import", + "image_name": "packer_import", + "image_os_type": "CentOS", + "image_os_name": "CentOS 6.10 64位", + "format": "raw" + } + ] +```