diff --git a/builder/alicloud/ecs/access_config.go b/builder/alicloud/ecs/access_config.go
new file mode 100644
index 000000000..33d4ff52d
--- /dev/null
+++ b/builder/alicloud/ecs/access_config.go
@@ -0,0 +1,83 @@
+package ecs
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/template/interpolate"
+)
+
+// Config of alicloud
+type AlicloudAccessConfig struct {
+ AlicloudAccessKey string `mapstructure:"access_key"`
+ AlicloudSecretKey string `mapstructure:"secret_key"`
+ AlicloudRegion string `mapstructure:"region"`
+ AlicloudSkipValidation bool `mapstructure:"skip_region_validation"`
+}
+
+// Client for AlicloudClient
+func (c *AlicloudAccessConfig) Client() (*ecs.Client, error) {
+ if err := c.loadAndValidate(); err != nil {
+ return nil, err
+ }
+ client := ecs.NewClient(c.AlicloudAccessKey, c.AlicloudSecretKey)
+ client.SetBusinessInfo("Packer")
+ if _, err := client.DescribeRegions(); err != nil {
+ return nil, err
+ }
+ return client, nil
+}
+
+func (c *AlicloudAccessConfig) Prepare(ctx *interpolate.Context) []error {
+ var errs []error
+ if err := c.Config(); err != nil {
+ errs = append(errs, err)
+ }
+
+ if c.AlicloudRegion != "" && !c.AlicloudSkipValidation {
+ if c.validateRegion() != nil {
+ errs = append(errs, fmt.Errorf("Unknown alicloud region: %s", c.AlicloudRegion))
+ }
+ }
+
+ if len(errs) > 0 {
+ return errs
+ }
+
+ return nil
+}
+
+func (c *AlicloudAccessConfig) Config() error {
+ if c.AlicloudAccessKey == "" {
+ c.AlicloudAccessKey = os.Getenv("ALICLOUD_ACCESS_KEY")
+ }
+ if c.AlicloudSecretKey == "" {
+ c.AlicloudSecretKey = os.Getenv("ALICLOUD_SECRET_KEY")
+ }
+ if c.AlicloudAccessKey == "" || c.AlicloudSecretKey == "" {
+ return fmt.Errorf("ALICLOUD_ACCESS_KEY and ALICLOUD_SECRET_KEY must be set in template file or environment variables.")
+ }
+ return nil
+
+}
+
+func (c *AlicloudAccessConfig) loadAndValidate() error {
+ if err := c.validateRegion(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *AlicloudAccessConfig) validateRegion() error {
+
+ for _, valid := range common.ValidRegions {
+ if c.AlicloudRegion == string(valid) {
+ return nil
+ }
+ }
+
+ return fmt.Errorf("Not a valid alicloud region: %s", c.AlicloudRegion)
+}
diff --git a/builder/alicloud/ecs/access_config_test.go b/builder/alicloud/ecs/access_config_test.go
new file mode 100644
index 000000000..23b0c9b50
--- /dev/null
+++ b/builder/alicloud/ecs/access_config_test.go
@@ -0,0 +1,44 @@
+package ecs
+
+import (
+ "testing"
+)
+
+func testAlicloudAccessConfig() *AlicloudAccessConfig {
+ return &AlicloudAccessConfig{
+ AlicloudAccessKey: "ak",
+ AlicloudSecretKey: "acs",
+ }
+
+}
+
+func TestAlicloudAccessConfigPrepareRegion(t *testing.T) {
+ c := testAlicloudAccessConfig()
+ c.AlicloudRegion = ""
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+
+ c.AlicloudRegion = "cn-beijing-3"
+ if err := c.Prepare(nil); err == nil {
+ t.Fatal("should have error")
+ }
+
+ c.AlicloudRegion = "cn-beijing"
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+
+ c.AlicloudRegion = "unknown"
+ if err := c.Prepare(nil); err == nil {
+ t.Fatalf("should have err")
+ }
+
+ c.AlicloudRegion = "unknown"
+ c.AlicloudSkipValidation = true
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+ c.AlicloudSkipValidation = false
+
+}
diff --git a/builder/alicloud/ecs/artifact.go b/builder/alicloud/ecs/artifact.go
new file mode 100644
index 000000000..f22735e3f
--- /dev/null
+++ b/builder/alicloud/ecs/artifact.go
@@ -0,0 +1,135 @@
+package ecs
+
+import (
+ "fmt"
+ "log"
+ "sort"
+ "strings"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+)
+
+type Artifact struct {
+ // A map of regions to alicloud image IDs.
+ AlicloudImages map[string]string
+
+ // BuilderId is the unique ID for the builder that created this alicloud image
+ BuilderIdValue string
+
+ // Alcloud connection for performing API stuff.
+ Client *ecs.Client
+}
+
+func (a *Artifact) BuilderId() string {
+ return a.BuilderIdValue
+}
+
+func (*Artifact) Files() []string {
+ // We have no files
+ return nil
+}
+
+func (a *Artifact) Id() string {
+ parts := make([]string, 0, len(a.AlicloudImages))
+ for region, ecsImageId := range a.AlicloudImages {
+ parts = append(parts, fmt.Sprintf("%s:%s", region, ecsImageId))
+ }
+
+ sort.Strings(parts)
+ return strings.Join(parts, ",")
+}
+
+func (a *Artifact) String() string {
+ alicloudImageStrings := make([]string, 0, len(a.AlicloudImages))
+ for region, id := range a.AlicloudImages {
+ single := fmt.Sprintf("%s: %s", region, id)
+ alicloudImageStrings = append(alicloudImageStrings, single)
+ }
+
+ sort.Strings(alicloudImageStrings)
+ return fmt.Sprintf("Alicloud images were created:\n\n%s", strings.Join(alicloudImageStrings, "\n"))
+}
+
+func (a *Artifact) State(name string) interface{} {
+ switch name {
+ case "atlas.artifact.metadata":
+ return a.stateAtlasMetadata()
+ default:
+ return nil
+ }
+}
+
+func (a *Artifact) Destroy() error {
+ errors := make([]error, 0)
+
+ for region, imageId := range a.AlicloudImages {
+ log.Printf("Delete alicloud image ID (%s) from region (%s)", imageId, region)
+
+ // Get alicloud image metadata
+ images, _, err := a.Client.DescribeImages(&ecs.DescribeImagesArgs{
+ RegionId: common.Region(region),
+ ImageId: imageId})
+ if err != nil {
+ errors = append(errors, err)
+ }
+ if len(images) == 0 {
+ err := fmt.Errorf("Error retrieving details for alicloud image(%s), no alicloud images found", imageId)
+ errors = append(errors, err)
+ continue
+ }
+ //Unshared the shared account before destroy
+ sharePermissions, err := a.Client.DescribeImageSharePermission(&ecs.ModifyImageSharePermissionArgs{RegionId: common.Region(region), ImageId: imageId})
+ if err != nil {
+ errors = append(errors, err)
+ }
+ accountsNumber := len(sharePermissions.Accounts.Account)
+ if accountsNumber > 0 {
+ accounts := make([]string, accountsNumber)
+ for index, account := range sharePermissions.Accounts.Account {
+ accounts[index] = account.AliyunId
+ }
+ err := a.Client.ModifyImageSharePermission(&ecs.ModifyImageSharePermissionArgs{
+
+ RegionId: common.Region(region),
+ ImageId: imageId,
+ RemoveAccount: accounts,
+ })
+ if err != nil {
+ errors = append(errors, err)
+ }
+ }
+ // Delete alicloud images
+ if err := a.Client.DeleteImage(common.Region(region), imageId); err != nil {
+ errors = append(errors, err)
+ }
+ //Delete the snapshot of this images
+ for _, diskDevices := range images[0].DiskDeviceMappings.DiskDeviceMapping {
+ if err := a.Client.DeleteSnapshot(diskDevices.SnapshotId); err != nil {
+ errors = append(errors, err)
+ }
+ }
+
+ }
+
+ if len(errors) > 0 {
+ if len(errors) == 1 {
+ return errors[0]
+ } else {
+ return &packer.MultiError{Errors: errors}
+ }
+ }
+
+ return nil
+}
+
+func (a *Artifact) stateAtlasMetadata() interface{} {
+ metadata := make(map[string]string)
+ for region, imageId := range a.AlicloudImages {
+ k := fmt.Sprintf("region.%s", region)
+ metadata[k] = imageId
+ }
+
+ return metadata
+}
diff --git a/builder/alicloud/ecs/artifact_test.go b/builder/alicloud/ecs/artifact_test.go
new file mode 100644
index 000000000..44e56531a
--- /dev/null
+++ b/builder/alicloud/ecs/artifact_test.go
@@ -0,0 +1,47 @@
+package ecs
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/hashicorp/packer/packer"
+)
+
+func TestArtifact_Impl(t *testing.T) {
+ var _ packer.Artifact = new(Artifact)
+}
+
+func TestArtifactId(t *testing.T) {
+ expected := `east:foo,west:bar`
+
+ ecsImages := make(map[string]string)
+ ecsImages["east"] = "foo"
+ ecsImages["west"] = "bar"
+
+ a := &Artifact{
+ AlicloudImages: ecsImages,
+ }
+
+ result := a.Id()
+ if result != expected {
+ t.Fatalf("bad: %s", result)
+ }
+}
+
+func TestArtifactState_atlasMetadata(t *testing.T) {
+ a := &Artifact{
+ AlicloudImages: map[string]string{
+ "east": "foo",
+ "west": "bar",
+ },
+ }
+
+ actual := a.State("atlas.artifact.metadata")
+ expected := map[string]string{
+ "region.east": "foo",
+ "region.west": "bar",
+ }
+ if !reflect.DeepEqual(actual, expected) {
+ t.Fatalf("bad: %#v", actual)
+ }
+}
diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go
new file mode 100644
index 000000000..de56c0cab
--- /dev/null
+++ b/builder/alicloud/ecs/builder.go
@@ -0,0 +1,223 @@
+// The alicloud contains a packer.Builder implementation that
+// builds ecs images for alicloud.
+package ecs
+
+import (
+ "log"
+
+ "github.com/hashicorp/packer/common"
+ "github.com/hashicorp/packer/helper/communicator"
+ "github.com/hashicorp/packer/helper/config"
+ "github.com/hashicorp/packer/packer"
+ "github.com/hashicorp/packer/template/interpolate"
+ "github.com/mitchellh/multistep"
+)
+
+// The unique ID for this builder
+const BuilderId = "alibaba.alicloud"
+
+type Config struct {
+ common.PackerConfig `mapstructure:",squash"`
+ AlicloudAccessConfig `mapstructure:",squash"`
+ AlicloudImageConfig `mapstructure:",squash"`
+ RunConfig `mapstructure:",squash"`
+
+ ctx interpolate.Context
+}
+
+type Builder struct {
+ config Config
+ runner multistep.Runner
+}
+
+type InstanceNetWork string
+
+const (
+ ClassicNet = InstanceNetWork("classic")
+ VpcNet = InstanceNetWork("vpc")
+ ALICLOUD_DEFAULT_SHORT_TIMEOUT = 180
+ ALICLOUD_DEFAULT_TIMEOUT = 1800
+ ALICLOUD_DEFAULT_LONG_TIMEOUT = 3600
+)
+
+func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
+ err := config.Decode(&b.config, &config.DecodeOpts{
+ Interpolate: true,
+ InterpolateContext: &b.config.ctx,
+ InterpolateFilter: &interpolate.RenderFilter{
+ Exclude: []string{
+ "run_command",
+ },
+ },
+ }, raws...)
+ b.config.ctx.EnableEnv = true
+ if err != nil {
+ return nil, err
+ }
+
+ // Accumulate any errors
+ var errs *packer.MultiError
+ errs = packer.MultiErrorAppend(errs, b.config.AlicloudAccessConfig.Prepare(&b.config.ctx)...)
+ errs = packer.MultiErrorAppend(errs, b.config.AlicloudImageConfig.Prepare(&b.config.ctx)...)
+ errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
+
+ if errs != nil && len(errs.Errors) > 0 {
+ return nil, errs
+ }
+
+ log.Println(common.ScrubConfig(b.config, b.config.AlicloudAccessKey, b.config.AlicloudSecretKey))
+ return nil, nil
+}
+
+func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
+
+ client, err := b.config.Client()
+ if err != nil {
+ return nil, err
+ }
+ state := new(multistep.BasicStateBag)
+ state.Put("config", b.config)
+ state.Put("client", client)
+ state.Put("hook", hook)
+ state.Put("ui", ui)
+ state.Put("networktype", b.chooseNetworkType())
+ var steps []multistep.Step
+
+ // Build the steps
+ steps = []multistep.Step{
+ &stepPreValidate{
+ AlicloudDestImageName: b.config.AlicloudImageName,
+ ForceDelete: b.config.AlicloudImageForceDetele,
+ },
+ &stepCheckAlicloudSourceImage{
+ SourceECSImageId: b.config.AlicloudSourceImage,
+ },
+ &StepConfigAlicloudKeyPair{
+ Debug: b.config.PackerDebug,
+ KeyPairName: b.config.SSHKeyPairName,
+ PrivateKeyFile: b.config.Comm.SSHPrivateKey,
+ TemporaryKeyPairName: b.config.TemporaryKeyPairName,
+ SSHAgentAuth: b.config.Comm.SSHAgentAuth,
+ //DebugKeyPath: b.config.Com
+ RegionId: b.config.AlicloudRegion,
+ },
+ }
+ if b.chooseNetworkType() == VpcNet {
+ steps = append(steps,
+ &stepConfigAlicloudVPC{
+ VpcId: b.config.VpcId,
+ CidrBlock: b.config.CidrBlock,
+ VpcName: b.config.VpcName,
+ },
+ &stepConfigAlicloudVSwitch{
+ VSwitchId: b.config.VSwitchId,
+ ZoneId: b.config.ZoneId,
+ CidrBlock: b.config.CidrBlock,
+ VSwitchName: b.config.VSwitchName,
+ })
+ }
+ steps = append(steps,
+ &stepConfigAlicloudSecurityGroup{
+ SecurityGroupId: b.config.SecurityGroupId,
+ SecurityGroupName: b.config.SecurityGroupId,
+ RegionId: b.config.AlicloudRegion,
+ },
+ &stepCreateAlicloudInstance{
+ IOOptimized: b.config.IOOptimized,
+ InstanceType: b.config.InstanceType,
+ UserData: b.config.UserData,
+ UserDataFile: b.config.UserDataFile,
+ RegionId: b.config.AlicloudRegion,
+ InternetChargeType: b.config.InternetChargeType,
+ InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
+ InstnaceName: b.config.InstanceName,
+ ZoneId: b.config.ZoneId,
+ })
+ if b.chooseNetworkType() == VpcNet {
+ steps = append(steps, &setpConfigAlicloudEIP{
+ AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
+ RegionId: b.config.AlicloudRegion,
+ InternetChargeType: b.config.InternetChargeType,
+ })
+ } else {
+ steps = append(steps, &stepConfigAlicloudPublicIP{
+ RegionId: b.config.AlicloudRegion,
+ })
+ }
+ steps = append(steps,
+ &stepRunAlicloudInstance{},
+ &stepMountAlicloudDisk{},
+ &stepAttachKeyPar{},
+ &communicator.StepConnect{
+ Config: &b.config.RunConfig.Comm,
+ Host: SSHHost(
+ client,
+ b.config.SSHPrivateIp),
+ SSHConfig: SSHConfig(
+ b.config.RunConfig.Comm.SSHAgentAuth,
+ b.config.RunConfig.Comm.SSHUsername,
+ b.config.RunConfig.Comm.SSHPassword),
+ },
+ &common.StepProvision{},
+ &stepStopAlicloudInstance{
+ ForceStop: b.config.ForceStopInstance,
+ },
+ &stepDeleteAlicloudImageSnapshots{
+ AlicloudImageForceDeteleSnapshots: b.config.AlicloudImageForceDeteleSnapshots,
+ AlicloudImageForceDetele: b.config.AlicloudImageForceDetele,
+ AlicloudImageName: b.config.AlicloudImageName,
+ },
+ &stepCreateAlicloudImage{},
+ &setpRegionCopyAlicloudImage{
+ AlicloudImageDestinationRegions: b.config.AlicloudImageDestinationRegions,
+ AlicloudImageDestinationNames: b.config.AlicloudImageDestinationNames,
+ RegionId: b.config.AlicloudRegion,
+ },
+ &setpShareAlicloudImage{
+ AlicloudImageShareAccounts: b.config.AlicloudImageShareAccounts,
+ AlicloudImageUNShareAccounts: b.config.AlicloudImageUNShareAccounts,
+ RegionId: b.config.AlicloudRegion,
+ })
+
+ // Run!
+ b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
+ b.runner.Run(state)
+
+ // If there was an error, return that
+ if rawErr, ok := state.GetOk("error"); ok {
+ return nil, rawErr.(error)
+ }
+
+ // If there are no ECS images, then just return
+ if _, ok := state.GetOk("alicloudimages"); !ok {
+ return nil, nil
+ }
+
+ // Build the artifact and return it
+ artifact := &Artifact{
+ AlicloudImages: state.Get("alicloudimages").(map[string]string),
+ BuilderIdValue: BuilderId,
+ Client: client,
+ }
+
+ return artifact, nil
+}
+
+func (b *Builder) Cancel() {
+ if b.runner != nil {
+ log.Println("Cancelling the step runner...")
+ b.runner.Cancel()
+ }
+}
+
+func (b *Builder) chooseNetworkType() InstanceNetWork {
+ //Alicloud userdata require vpc network and public key require userdata, so besides user specific vpc network,
+ //choose vpc networks in those cases
+ if b.config.RunConfig.Comm.SSHPrivateKey != "" || b.config.UserData != "" || b.config.UserDataFile != "" ||
+ b.config.VpcId != "" || b.config.VSwitchId != "" || b.config.TemporaryKeyPairName != "" {
+ return VpcNet
+ } else {
+ return ClassicNet
+ }
+
+}
diff --git a/builder/alicloud/ecs/builder_acc_test.go b/builder/alicloud/ecs/builder_acc_test.go
new file mode 100644
index 000000000..415bfcf8b
--- /dev/null
+++ b/builder/alicloud/ecs/builder_acc_test.go
@@ -0,0 +1,331 @@
+package ecs
+
+import (
+ "os"
+ "testing"
+
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ builderT "github.com/hashicorp/packer/helper/builder/testing"
+ "github.com/hashicorp/packer/packer"
+)
+
+func TestBuilderAcc_basic(t *testing.T) {
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: testBuilderAccBasic,
+ })
+}
+
+//func TestBuilderAcc_windows(t *testing.T) {
+// builderT.Test(t, builderT.TestCase{
+// PreCheck: func() {
+// testAccPreCheck(t)
+// },
+// Builder: &Builder{},
+// Template: testBuilderAccWindows,
+// })
+//}
+
+//func TestBuilderAcc_regionCopy(t *testing.T) {
+// builderT.Test(t, builderT.TestCase{
+// PreCheck: func() {
+// testAccPreCheck(t)
+// },
+// Builder: &Builder{},
+// Template: testBuilderAccRegionCopy,
+// Check: checkRegionCopy([]string{"cn-hangzhou", "cn-shenzhen"}),
+// })
+//}
+
+func TestBuilderAcc_forceDelete(t *testing.T) {
+ // Build the same alicloud image twice, with ecs_image_force_delete on the second run
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: buildForceDeregisterConfig("false", "delete"),
+ SkipArtifactTeardown: true,
+ })
+
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: buildForceDeregisterConfig("true", "delete"),
+ })
+}
+
+func TestBuilderAcc_ECSImageSharing(t *testing.T) {
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: testBuilderAccSharing,
+ Check: checkECSImageSharing("1309208528360047"),
+ })
+}
+
+func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
+ destImageName := "delete"
+
+ // Build the same alicloud image name twice, with force_delete_snapshot on the second run
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: buildForceDeleteSnapshotConfig("false", destImageName),
+ SkipArtifactTeardown: true,
+ })
+
+ // Get image data by image image name
+ client, _ := testAliyunClient()
+ images, _, _ := client.DescribeImages(&ecs.DescribeImagesArgs{
+ ImageName: "packer-test-" + destImageName,
+ RegionId: common.Region("cn-beijing")})
+
+ image := images[0]
+
+ // Get snapshot ids for image
+ snapshotIds := []string{}
+ for _, device := range image.DiskDeviceMappings.DiskDeviceMapping {
+ if device.Device != "" && device.SnapshotId != "" {
+ snapshotIds = append(snapshotIds, device.SnapshotId)
+ }
+ }
+
+ builderT.Test(t, builderT.TestCase{
+ PreCheck: func() {
+ testAccPreCheck(t)
+ },
+ Builder: &Builder{},
+ Template: buildForceDeleteSnapshotConfig("true", destImageName),
+ Check: checkSnapshotsDeleted(snapshotIds),
+ })
+}
+
+func checkSnapshotsDeleted(snapshotIds []string) builderT.TestCheckFunc {
+ return func(artifacts []packer.Artifact) error {
+ // Verify the snapshots are gone
+ client, _ := testAliyunClient()
+ snapshotResp, _, err := client.DescribeSnapshots(
+ &ecs.DescribeSnapshotsArgs{RegionId: common.Region("cn-beijing"), SnapshotIds: snapshotIds},
+ )
+ if err != nil {
+ return fmt.Errorf("Query snapshot failed %v", err)
+ }
+ if len(snapshotResp) > 0 {
+ return fmt.Errorf("Snapshots weren't successfully deleted by " +
+ "`ecs_image_force_delete_snapshots`")
+ }
+ return nil
+ }
+}
+
+func checkECSImageSharing(uid string) builderT.TestCheckFunc {
+ return func(artifacts []packer.Artifact) error {
+ if len(artifacts) > 1 {
+ return fmt.Errorf("more than 1 artifact")
+ }
+
+ // Get the actual *Artifact pointer so we can access the AMIs directly
+ artifactRaw := artifacts[0]
+ artifact, ok := artifactRaw.(*Artifact)
+ if !ok {
+ return fmt.Errorf("unknown artifact: %#v", artifactRaw)
+ }
+
+ // describe the image, get block devices with a snapshot
+ client, _ := testAliyunClient()
+ imageSharePermissionResponse, err := client.DescribeImageSharePermission(
+ &ecs.ModifyImageSharePermissionArgs{
+ RegionId: "cn-beijing",
+ ImageId: artifact.AlicloudImages["cn-beijing"],
+ })
+
+ if err != nil {
+ return fmt.Errorf("Error retrieving Image Attributes for ECS Image Artifact (%#v) "+
+ "in ECS Image Sharing Test: %s", artifact, err)
+ }
+
+ if len(imageSharePermissionResponse.Accounts.Account) != 1 &&
+ imageSharePermissionResponse.Accounts.Account[0].AliyunId != uid {
+ return fmt.Errorf("share account is incorrect %d",
+ len(imageSharePermissionResponse.Accounts.Account))
+ }
+
+ return nil
+ }
+}
+
+func checkRegionCopy(regions []string) builderT.TestCheckFunc {
+ return func(artifacts []packer.Artifact) error {
+ if len(artifacts) > 1 {
+ return fmt.Errorf("more than 1 artifact")
+ }
+
+ // Get the actual *Artifact pointer so we can access the AMIs directly
+ artifactRaw := artifacts[0]
+ artifact, ok := artifactRaw.(*Artifact)
+ if !ok {
+ return fmt.Errorf("unknown artifact: %#v", artifactRaw)
+ }
+
+ // Verify that we copied to only the regions given
+ regionSet := make(map[string]struct{})
+ for _, r := range regions {
+ regionSet[r] = struct{}{}
+ }
+ for r := range artifact.AlicloudImages {
+ if r == "cn-beijing" {
+ delete(regionSet, r)
+ continue
+ }
+ if _, ok := regionSet[r]; !ok {
+ return fmt.Errorf("unknown region: %s", r)
+ }
+
+ delete(regionSet, r)
+ }
+ if len(regionSet) > 0 {
+ return fmt.Errorf("didn't copy to: %#v", regionSet)
+ }
+ client, _ := testAliyunClient()
+ for key, value := range artifact.AlicloudImages {
+ client.WaitForImageReady(common.Region(key), value, 1800)
+ }
+ return nil
+ }
+}
+
+func testAccPreCheck(t *testing.T) {
+ if v := os.Getenv("ALICLOUD_ACCESS_KEY"); v == "" {
+ t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests")
+ }
+
+ if v := os.Getenv("ALICLOUD_SECRET_KEY"); v == "" {
+ t.Fatal("ALICLOUD_SECRET_KEY must be set for acceptance tests")
+ }
+}
+
+func testAliyunClient() (*ecs.Client, error) {
+ access := &AlicloudAccessConfig{AlicloudRegion: "cn-beijing"}
+ err := access.Config()
+ if err != nil {
+ return nil, err
+ }
+ client, err := access.Client()
+ if err != nil {
+ return nil, err
+ }
+
+ return client, nil
+}
+
+const testBuilderAccBasic = `
+{ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "ssh_username": "ubuntu",
+ "io_optimized":"true",
+ "ssh_username":"root",
+ "image_name": "packer-test_{{timestamp}}"
+ }]
+}`
+
+const testBuilderAccRegionCopy = `
+{
+ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "io_optimized":"true",
+ "ssh_username":"root",
+ "image_name": "packer-test_{{timestamp}}",
+ "image_copy_regions": ["cn-hangzhou", "cn-shenzhen"]
+ }]
+}
+`
+
+const testBuilderAccForceDelete = `
+{
+ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "io_optimized":"true",
+ "ssh_username":"root",
+ "image_force_delete": "%s",
+ "image_name": "packer-test_%s"
+ }]
+}
+`
+
+const testBuilderAccForceDeleteSnapshot = `
+{
+ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "io_optimized":"true",
+ "ssh_username":"root",
+ "image_force_delete_snapshots": "%s",
+ "image_force_delete": "%s",
+ "image_name": "packer-test-%s"
+ }]
+}
+`
+
+// share with catsby
+const testBuilderAccSharing = `
+{
+ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "io_optimized":"true",
+ "ssh_username":"root",
+ "image_name": "packer-test_{{timestamp}}",
+ "image_share_account":["1309208528360047"]
+ }]
+}
+`
+
+func buildForceDeregisterConfig(val, name string) string {
+ return fmt.Sprintf(testBuilderAccForceDelete, val, name)
+}
+
+func buildForceDeleteSnapshotConfig(val, name string) string {
+ return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
+}
+
+const testBuilderAccWindows = `
+{ "builders": [{
+ "type": "test",
+ "region": "cn-beijing",
+ "instance_type": "ecs.n1.tiny",
+ "source_image":"win2008_64_ent_r2_zh-cn_40G_alibase_20170301.vhd",
+ "io_optimized":"true",
+ "image_force_delete":"true",
+ "communicator": "winrm",
+ "winrm_port": 5985,
+ "winrm_username": "Administrator",
+ "winrm_password": "Test1234",
+ "image_name": "packer-test_{{timestamp}}"
+ }]
+}`
diff --git a/builder/alicloud/ecs/builder_test.go b/builder/alicloud/ecs/builder_test.go
new file mode 100644
index 000000000..552c77f01
--- /dev/null
+++ b/builder/alicloud/ecs/builder_test.go
@@ -0,0 +1,95 @@
+package ecs
+
+import (
+ "testing"
+
+ "github.com/hashicorp/packer/packer"
+)
+
+func testBuilderConfig() map[string]interface{} {
+ return map[string]interface{}{
+ "access_key": "foo",
+ "secret_key": "bar",
+ "source_image": "foo",
+ "instance_type": "ecs.n1.tiny",
+ "region": "cn-beijing",
+ "ssh_username": "root",
+ "image_name": "foo",
+ "io_optimized": true,
+ }
+}
+
+func TestBuilder_ImplementsBuilder(t *testing.T) {
+ var raw interface{}
+ raw = &Builder{}
+ if _, ok := raw.(packer.Builder); !ok {
+ t.Fatalf("Builder should be a builder")
+ }
+}
+
+func TestBuilder_Prepare_BadType(t *testing.T) {
+ b := &Builder{}
+ c := map[string]interface{}{
+ "access_key": []string{},
+ }
+
+ warnings, err := b.Prepare(c)
+ if len(warnings) > 0 {
+ t.Fatalf("bad: %#v", warnings)
+ }
+ if err == nil {
+ t.Fatalf("prepare should fail")
+ }
+}
+
+func TestBuilderPrepare_ECSImageName(t *testing.T) {
+ var b Builder
+ config := testBuilderConfig()
+
+ // Test good
+ config["image_name"] = "ecs.n1.tiny"
+ warnings, err := b.Prepare(config)
+ if len(warnings) > 0 {
+ t.Fatalf("bad: %#v", warnings)
+ }
+ if err != nil {
+ t.Fatalf("should not have error: %s", err)
+ }
+
+ // Test bad
+ config["ecs_image_name"] = "foo {{"
+ b = Builder{}
+ warnings, err = b.Prepare(config)
+ if len(warnings) > 0 {
+ t.Fatalf("bad: %#v", warnings)
+ }
+ if err == nil {
+ t.Fatal("should have error")
+ }
+
+ // Test bad
+ delete(config, "image_name")
+ b = Builder{}
+ warnings, err = b.Prepare(config)
+ if len(warnings) > 0 {
+ t.Fatalf("bad: %#v", warnings)
+ }
+ if err == nil {
+ t.Fatal("should have error")
+ }
+}
+
+func TestBuilderPrepare_InvalidKey(t *testing.T) {
+ var b Builder
+ config := testBuilderConfig()
+
+ // Add a random key
+ config["i_should_not_be_valid"] = true
+ warnings, err := b.Prepare(config)
+ if len(warnings) > 0 {
+ t.Fatalf("bad: %#v", warnings)
+ }
+ if err == nil {
+ t.Fatal("should have error")
+ }
+}
diff --git a/builder/alicloud/ecs/image_config.go b/builder/alicloud/ecs/image_config.go
new file mode 100644
index 000000000..c7fed2af6
--- /dev/null
+++ b/builder/alicloud/ecs/image_config.go
@@ -0,0 +1,99 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/hashicorp/packer/template/interpolate"
+ "regexp"
+ "strings"
+)
+
+type AlicloudDiskDevice struct {
+ DiskName string `mapstructure:"disk_name"`
+ DiskCategory string `mapstructure:"disk_category"`
+ DiskSize int `mapstructure:"disk_size"`
+ SnapshotId string `mapstructure:"disk_snapshot_id"`
+ Description string `mapstructure:"disk_description"`
+ DeleteWithInstance bool `mapstructure:"disk_delete_with_instance"`
+ Device string `mapstructure:"disk_device"`
+}
+
+type AlicloudDiskDevices struct {
+ ECSImagesDiskMappings []AlicloudDiskDevice `mapstructure:"image_disk_mappings"`
+}
+
+type AlicloudImageConfig struct {
+ AlicloudImageName string `mapstructure:"image_name"`
+ AlicloudImageVersion string `mapstructure:"image_version"`
+ AlicloudImageDescription string `mapstructure:"image_description"`
+ AlicloudImageShareAccounts []string `mapstructure:"image_share_account"`
+ AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"`
+ AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
+ AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"`
+ AlicloudImageForceDetele bool `mapstructure:"image_force_delete"`
+ AlicloudImageForceDeteleSnapshots bool `mapstructure:"image_force_delete_snapshots"`
+ AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"`
+ AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation"`
+ AlicloudDiskDevices `mapstructure:",squash"`
+}
+
+func (c *AlicloudImageConfig) Prepare(ctx *interpolate.Context) []error {
+ var errs []error
+ if c.AlicloudImageName == "" {
+ errs = append(errs, fmt.Errorf("image_name must be specified"))
+ } else if len(c.AlicloudImageName) < 2 || len(c.AlicloudImageName) > 128 {
+ errs = append(errs, fmt.Errorf("image_name must less than 128 letters and more than 1 letters"))
+ } else if strings.HasPrefix(c.AlicloudImageName, "http://") ||
+ strings.HasPrefix(c.AlicloudImageName, "https://") {
+ errs = append(errs, fmt.Errorf("image_name can't start with 'http://' or 'https://'"))
+ }
+ reg := regexp.MustCompile("\\s+")
+ if reg.FindString(c.AlicloudImageName) != "" {
+ errs = append(errs, fmt.Errorf("image_name can't include spaces"))
+ }
+
+ if len(c.AlicloudImageDestinationRegions) > 0 {
+ regionSet := make(map[string]struct{})
+ regions := make([]string, 0, len(c.AlicloudImageDestinationRegions))
+
+ for _, region := range c.AlicloudImageDestinationRegions {
+ // If we already saw the region, then don't look again
+ if _, ok := regionSet[region]; ok {
+ continue
+ }
+
+ // Mark that we saw the region
+ regionSet[region] = struct{}{}
+
+ if !c.AlicloudImageSkipRegionValidation {
+ // Verify the region is real
+ if valid := validateRegion(region); valid != nil {
+ errs = append(errs, fmt.Errorf("Unknown region: %s", region))
+ continue
+ }
+ }
+
+ regions = append(regions, region)
+ }
+
+ c.AlicloudImageDestinationRegions = regions
+ }
+
+ if len(errs) > 0 {
+ return errs
+ }
+
+ return nil
+}
+
+func validateRegion(region string) error {
+
+ for _, valid := range common.ValidRegions {
+ if region == string(valid) {
+ return nil
+ }
+ }
+
+ return fmt.Errorf("Not a valid alicloud region: %s", region)
+}
diff --git a/builder/alicloud/ecs/image_config_test.go b/builder/alicloud/ecs/image_config_test.go
new file mode 100644
index 000000000..bb4581871
--- /dev/null
+++ b/builder/alicloud/ecs/image_config_test.go
@@ -0,0 +1,64 @@
+package ecs
+
+import (
+ "testing"
+
+ "github.com/denverdino/aliyungo/common"
+)
+
+func testAlicloudImageConfig() *AlicloudImageConfig {
+ return &AlicloudImageConfig{
+ AlicloudImageName: "foo",
+ }
+}
+
+func TestECSImageConfigPrepare_name(t *testing.T) {
+ c := testAlicloudImageConfig()
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+
+ c.AlicloudImageName = ""
+ if err := c.Prepare(nil); err == nil {
+ t.Fatal("should have error")
+ }
+}
+
+func TestAMIConfigPrepare_regions(t *testing.T) {
+ c := testAlicloudImageConfig()
+ c.AlicloudImageDestinationRegions = nil
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+
+ c.AlicloudImageDestinationRegions = regionsToString()
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("shouldn't have err: %s", err)
+ }
+
+ c.AlicloudImageDestinationRegions = []string{"foo"}
+ if err := c.Prepare(nil); err == nil {
+ t.Fatal("should have error")
+ }
+
+ c.AlicloudImageDestinationRegions = []string{"cn-beijing", "cn-hangzhou", "eu-central-1"}
+ if err := c.Prepare(nil); err != nil {
+ t.Fatalf("bad: %s", err)
+ }
+
+ c.AlicloudImageDestinationRegions = []string{"unknow"}
+ c.AlicloudImageSkipRegionValidation = true
+ if err := c.Prepare(nil); err != nil {
+ t.Fatal("shouldn't have error")
+ }
+ c.AlicloudImageSkipRegionValidation = false
+
+}
+
+func regionsToString() []string {
+ var regions []string
+ for _, region := range common.ValidRegions {
+ regions = append(regions, string(region))
+ }
+ return regions
+}
diff --git a/builder/alicloud/ecs/packer_helper.go b/builder/alicloud/ecs/packer_helper.go
new file mode 100644
index 000000000..2486b81cf
--- /dev/null
+++ b/builder/alicloud/ecs/packer_helper.go
@@ -0,0 +1,22 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+func message(state multistep.StateBag, module string) {
+ _, cancelled := state.GetOk(multistep.StateCancelled)
+ _, halted := state.GetOk(multistep.StateHalted)
+
+ ui := state.Get("ui").(packer.Ui)
+
+ if cancelled || halted {
+ ui.Say(fmt.Sprintf("Deleting %s because of cancellation or error...", module))
+ } else {
+ ui.Say(fmt.Sprintf("Cleaning up '%s'", module))
+ }
+
+}
diff --git a/builder/alicloud/ecs/run_config.go b/builder/alicloud/ecs/run_config.go
new file mode 100644
index 000000000..59b8695e9
--- /dev/null
+++ b/builder/alicloud/ecs/run_config.go
@@ -0,0 +1,72 @@
+package ecs
+
+import (
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/hashicorp/packer/common/uuid"
+ "github.com/hashicorp/packer/helper/communicator"
+ "github.com/hashicorp/packer/template/interpolate"
+ "strings"
+)
+
+type RunConfig struct {
+ AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
+ ZoneId string `mapstructure:"zone_id"`
+ IOOptimized bool `mapstructure:"io_optimized"`
+ InstanceType string `mapstructure:"instance_type"`
+ Description string `mapstructure:"description"`
+ AlicloudSourceImage string `mapstructure:"source_image"`
+ ForceStopInstance bool `mapstructure:"force_stop_instance"`
+ SecurityGroupId string `mapstructure:"security_group_id"`
+ SecurityGroupName string `mapstructure:"security_group_name"`
+ UserData string `mapstructure:"user_data"`
+ UserDataFile string `mapstructure:"user_data_file"`
+ VpcId string `mapstructure:"vpc_id"`
+ VpcName string `mapstructure:"vpc_name"`
+ CidrBlock string `mapstructure:"vpc_cidr_block"`
+ VSwitchId string `mapstructure:"vswitch_id"`
+ VSwitchName string `mapstructure:"vswitch_id"`
+ InstanceName string `mapstructure:"instance_name"`
+ InternetChargeType string `mapstructure:"internet_charge_type"`
+ InternetMaxBandwidthOut int `mapstructure:"internet_max_bandwith_out"`
+ TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
+
+ // Communicator settings
+ Comm communicator.Config `mapstructure:",squash"`
+ SSHKeyPairName string `mapstructure:"ssh_keypair_name"`
+ SSHPrivateIp bool `mapstructure:"ssh_private_ip"`
+}
+
+func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
+ if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" &&
+ c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" && c.Comm.WinRMPassword == "" {
+
+ c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
+ }
+
+ // Validation
+ errs := c.Comm.Prepare(ctx)
+ if c.AlicloudSourceImage == "" {
+ errs = append(errs, errors.New("A source_image must be specified"))
+ }
+
+ if strings.TrimSpace(c.AlicloudSourceImage) != c.AlicloudSourceImage {
+ errs = append(errs, errors.New("The source_image can't include spaces"))
+ }
+
+ if c.InstanceType == "" {
+ errs = append(errs, errors.New("An aliclod_instance_type must be specified"))
+ }
+
+ if c.UserData != "" && c.UserDataFile != "" {
+ errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
+ } else if c.UserDataFile != "" {
+ if _, err := os.Stat(c.UserDataFile); err != nil {
+ errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
+ }
+ }
+
+ return errs
+}
diff --git a/builder/alicloud/ecs/run_config_test.go b/builder/alicloud/ecs/run_config_test.go
new file mode 100644
index 000000000..d62f60b38
--- /dev/null
+++ b/builder/alicloud/ecs/run_config_test.go
@@ -0,0 +1,122 @@
+package ecs
+
+import (
+ "io/ioutil"
+ "testing"
+
+ "github.com/hashicorp/packer/helper/communicator"
+)
+
+func testConfig() *RunConfig {
+ return &RunConfig{
+ AlicloudSourceImage: "alicloud_images",
+ InstanceType: "ecs.n1.tiny",
+ Comm: communicator.Config{
+ SSHUsername: "alicloud",
+ },
+ }
+}
+
+func TestRunConfigPrepare(t *testing.T) {
+ c := testConfig()
+ err := c.Prepare(nil)
+ if len(err) > 0 {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestRunConfigPrepare_InstanceType(t *testing.T) {
+ c := testConfig()
+ c.InstanceType = ""
+ if err := c.Prepare(nil); len(err) != 1 {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestRunConfigPrepare_SourceECSImage(t *testing.T) {
+ c := testConfig()
+ c.AlicloudSourceImage = ""
+ if err := c.Prepare(nil); len(err) != 1 {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestRunConfigPrepare_SSHPort(t *testing.T) {
+ c := testConfig()
+ c.Comm.SSHPort = 0
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+
+ if c.Comm.SSHPort != 22 {
+ t.Fatalf("invalid value: %d", c.Comm.SSHPort)
+ }
+
+ c.Comm.SSHPort = 44
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+
+ if c.Comm.SSHPort != 44 {
+ t.Fatalf("invalid value: %d", c.Comm.SSHPort)
+ }
+}
+
+func TestRunConfigPrepare_UserData(t *testing.T) {
+ c := testConfig()
+ tf, err := ioutil.TempFile("", "packer")
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+ defer tf.Close()
+
+ c.UserData = "foo"
+ c.UserDataFile = tf.Name()
+ if err := c.Prepare(nil); len(err) != 1 {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestRunConfigPrepare_UserDataFile(t *testing.T) {
+ c := testConfig()
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+
+ c.UserDataFile = "idontexistidontthink"
+ if err := c.Prepare(nil); len(err) != 1 {
+ t.Fatalf("err: %s", err)
+ }
+
+ tf, err := ioutil.TempFile("", "packer")
+ if err != nil {
+ t.Fatalf("err: %s", err)
+ }
+ defer tf.Close()
+
+ c.UserDataFile = tf.Name()
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+}
+
+func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
+ c := testConfig()
+ c.TemporaryKeyPairName = ""
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+
+ if c.TemporaryKeyPairName == "" {
+ t.Fatal("keypair name is empty")
+ }
+
+ c.TemporaryKeyPairName = "ssh-key-123"
+ if err := c.Prepare(nil); len(err) != 0 {
+ t.Fatalf("err: %s", err)
+ }
+
+ if c.TemporaryKeyPairName != "ssh-key-123" {
+ t.Fatal("keypair name does not match")
+ }
+}
diff --git a/builder/alicloud/ecs/ssh_helper.go b/builder/alicloud/ecs/ssh_helper.go
new file mode 100644
index 000000000..d3d6b8b1c
--- /dev/null
+++ b/builder/alicloud/ecs/ssh_helper.go
@@ -0,0 +1,79 @@
+package ecs
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "time"
+
+ packerssh "github.com/hashicorp/packer/communicator/ssh"
+ "github.com/mitchellh/multistep"
+ "golang.org/x/crypto/ssh"
+ "golang.org/x/crypto/ssh/agent"
+)
+
+var (
+ // modified in tests
+ sshHostSleepDuration = time.Second
+)
+
+type alicloudSSHHelper interface {
+}
+
+// SSHHost returns a function that can be given to the SSH communicator
+func SSHHost(e alicloudSSHHelper, private bool) func(multistep.StateBag) (string, error) {
+ return func(state multistep.StateBag) (string, error) {
+ ipAddress := state.Get("ipaddress").(string)
+ return ipAddress, nil
+ }
+}
+
+// SSHConfig returns a function that can be used for the SSH communicator
+// config for connecting to the instance created over SSH using the private key
+// or password.
+func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
+ return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
+ if useAgent {
+ authSock := os.Getenv("SSH_AUTH_SOCK")
+ if authSock == "" {
+ return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
+ }
+
+ sshAgent, err := net.Dial("unix", authSock)
+ if err != nil {
+ return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
+ }
+
+ return &ssh.ClientConfig{
+ User: username,
+ Auth: []ssh.AuthMethod{
+ ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
+ },
+ }, nil
+ }
+
+ privateKey, hasKey := state.GetOk("privateKey")
+ if hasKey {
+
+ signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
+ if err != nil {
+ return nil, fmt.Errorf("Error setting up SSH config: %s", err)
+ }
+ return &ssh.ClientConfig{
+ User: username,
+ Auth: []ssh.AuthMethod{
+ ssh.PublicKeys(signer),
+ },
+ }, nil
+
+ } else {
+ return &ssh.ClientConfig{
+ User: username,
+ Auth: []ssh.AuthMethod{
+ ssh.Password(password),
+ ssh.KeyboardInteractive(
+ packerssh.PasswordKeyboardInteractive(password)),
+ }}, nil
+ }
+ }
+}
diff --git a/builder/alicloud/ecs/step_attach_keypair.go b/builder/alicloud/ecs/step_attach_keypair.go
new file mode 100644
index 000000000..551c74cdf
--- /dev/null
+++ b/builder/alicloud/ecs/step_attach_keypair.go
@@ -0,0 +1,72 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepAttachKeyPar struct {
+}
+
+func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
+ keyPairName := state.Get("keyPair").(string)
+ if keyPairName == "" {
+ return multistep.ActionContinue
+ }
+ ui := state.Get("ui").(packer.Ui)
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ retry_times := 3
+ for {
+ err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
+ KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
+ if err != nil {
+ e, _ := err.(*common.Error)
+ if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
+ e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
+ retry_times > 0 {
+ retry_times = retry_times - 1
+ continue
+ }
+ err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",
+ keyPairName, instance.InstanceId, err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ break
+ }
+
+ ui.Message(fmt.Sprintf("Attach keypair %s to instance: %s", keyPairName, instance.InstanceId))
+
+ return multistep.ActionContinue
+}
+
+func (s *stepAttachKeyPar) Cleanup(state multistep.StateBag) {
+ keyPairName := state.Get("keyPair").(string)
+ if keyPairName == "" {
+ return
+ }
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+
+ err := client.DetachKeyPair(&ecs.DetachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
+ KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
+ if err != nil {
+ err := fmt.Errorf("Error Detaching keypair %s to instance %s : %s", keyPairName,
+ instance.InstanceId, err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return
+ }
+
+ ui.Message(fmt.Sprintf("Detach keypair %s from instance: %s", keyPairName, instance.InstanceId))
+
+}
diff --git a/builder/alicloud/ecs/step_check_source_image.go b/builder/alicloud/ecs/step_check_source_image.go
new file mode 100644
index 000000000..4990f99d5
--- /dev/null
+++ b/builder/alicloud/ecs/step_check_source_image.go
@@ -0,0 +1,42 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepCheckAlicloudSourceImage struct {
+ SourceECSImageId string
+}
+
+func (s *stepCheckAlicloudSourceImage) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ ui := state.Get("ui").(packer.Ui)
+ images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{RegionId: common.Region(config.AlicloudRegion),
+ ImageId: config.AlicloudSourceImage})
+ if err != nil {
+ err := fmt.Errorf("Error querying alicloud image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ if len(images) == 0 {
+ err := fmt.Errorf("No alicloud image was found matching filters: %v", config.AlicloudSourceImage)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ ui.Message(fmt.Sprintf("Found image ID: %s", images[0].ImageId))
+
+ state.Put("source_image", &images[0])
+ return multistep.ActionContinue
+}
+
+func (s *stepCheckAlicloudSourceImage) Cleanup(multistep.StateBag) {}
diff --git a/builder/alicloud/ecs/step_config_eip.go b/builder/alicloud/ecs/step_config_eip.go
new file mode 100644
index 000000000..b0845ba2a
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_eip.go
@@ -0,0 +1,79 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type setpConfigAlicloudEIP struct {
+ AssociatePublicIpAddress bool
+ RegionId string
+ InternetChargeType string
+ allocatedId string
+}
+
+func (s *setpConfigAlicloudEIP) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ ui.Say("Allocating eip")
+ ipaddress, allocateId, err := client.AllocateEipAddress(&ecs.AllocateEipAddressArgs{
+ RegionId: common.Region(s.RegionId), InternetChargeType: common.InternetChargeType(s.InternetChargeType),
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error allocating eip: %s", err))
+ return multistep.ActionHalt
+ }
+ s.allocatedId = allocateId
+ if err = client.WaitForEip(common.Region(s.RegionId), allocateId,
+ ecs.EipStatusAvailable, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error allocating eip: %s", err))
+ return multistep.ActionHalt
+ }
+
+ if err = client.AssociateEipAddress(allocateId, instance.InstanceId); err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error binding eip: %s", err))
+ return multistep.ActionHalt
+ }
+
+ if err = client.WaitForEip(common.Region(s.RegionId), allocateId,
+ ecs.EipStatusInUse, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error associating eip: %s", err))
+ return multistep.ActionHalt
+ }
+ ui.Say(fmt.Sprintf("Allocated eip %s", ipaddress))
+ state.Put("ipaddress", ipaddress)
+ return multistep.ActionContinue
+}
+
+func (s *setpConfigAlicloudEIP) Cleanup(state multistep.StateBag) {
+ if len(s.allocatedId) == 0 {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ ui := state.Get("ui").(packer.Ui)
+
+ message(state, "EIP")
+
+ if err := client.UnassociateEipAddress(s.allocatedId, instance.InstanceId); err != nil {
+ ui.Say(fmt.Sprintf("Failed to unassociate eip."))
+ }
+
+ if err := client.WaitForEip(common.Region(s.RegionId), s.allocatedId, ecs.EipStatusAvailable, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
+ ui.Say(fmt.Sprintf("Timeout while unassociating eip."))
+ }
+ if err := client.ReleaseEipAddress(s.allocatedId); err != nil {
+ ui.Say(fmt.Sprintf("Failed to release eip."))
+ }
+
+}
diff --git a/builder/alicloud/ecs/step_config_key_pair.go b/builder/alicloud/ecs/step_config_key_pair.go
new file mode 100644
index 000000000..22ccd28b6
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_key_pair.go
@@ -0,0 +1,139 @@
+package ecs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "runtime"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type StepConfigAlicloudKeyPair struct {
+ Debug bool
+ SSHAgentAuth bool
+ DebugKeyPath string
+ TemporaryKeyPairName string
+ KeyPairName string
+ PrivateKeyFile string
+ RegionId string
+
+ keyName string
+}
+
+func (s *StepConfigAlicloudKeyPair) Run(state multistep.StateBag) multistep.StepAction {
+ ui := state.Get("ui").(packer.Ui)
+
+ if s.PrivateKeyFile != "" {
+ ui.Say("Using existing SSH private key")
+ privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
+ if err != nil {
+ state.Put("error", fmt.Errorf(
+ "Error loading configured private key file: %s", err))
+ return multistep.ActionHalt
+ }
+
+ state.Put("keyPair", s.KeyPairName)
+ state.Put("privateKey", string(privateKeyBytes))
+
+ return multistep.ActionContinue
+ }
+
+ if s.SSHAgentAuth && s.KeyPairName == "" {
+ ui.Say("Using SSH Agent with key pair in source image")
+ return multistep.ActionContinue
+ }
+
+ if s.SSHAgentAuth && s.KeyPairName != "" {
+ ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName))
+ state.Put("keyPair", s.KeyPairName)
+ return multistep.ActionContinue
+ }
+
+ if s.TemporaryKeyPairName == "" {
+ ui.Say("Not using temporary keypair")
+ state.Put("keyPair", "")
+ return multistep.ActionContinue
+ }
+
+ client := state.Get("client").(*ecs.Client)
+
+ ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.TemporaryKeyPairName))
+ keyResp, err := client.CreateKeyPair(&ecs.CreateKeyPairArgs{
+ KeyPairName: s.TemporaryKeyPairName,
+ RegionId: common.Region(s.RegionId),
+ })
+ if err != nil {
+ state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
+ return multistep.ActionHalt
+ }
+
+ // Set the keyname so we know to delete it later
+ s.keyName = s.TemporaryKeyPairName
+
+ // Set some state data for use in future steps
+ state.Put("keyPair", s.keyName)
+ state.Put("privateKey", keyResp.PrivateKeyBody)
+
+ // If we're in debug mode, output the private key to the working
+ // directory.
+ if s.Debug {
+ ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
+ f, err := os.Create(s.DebugKeyPath)
+ if err != nil {
+ state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
+ return multistep.ActionHalt
+ }
+ defer f.Close()
+
+ // Write the key out
+ if _, err := f.Write([]byte(keyResp.PrivateKeyBody)); err != nil {
+ state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
+ return multistep.ActionHalt
+ }
+
+ // Chmod it so that it is SSH ready
+ if runtime.GOOS != "windows" {
+ if err := f.Chmod(0600); err != nil {
+ state.Put("error", fmt.Errorf("Error setting permissions of debug key: %s", err))
+ return multistep.ActionHalt
+ }
+ }
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *StepConfigAlicloudKeyPair) Cleanup(state multistep.StateBag) {
+ // If no key name is set, then we never created it, so just return
+ // If we used an SSH private key file, do not go about deleting
+ // keypairs
+ if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+
+ // Remove the keypair
+ ui.Say("Deleting temporary keypair...")
+ err := client.DeleteKeyPairs(&ecs.DeleteKeyPairsArgs{
+ RegionId: common.Region(s.RegionId),
+ KeyPairNames: "[\"" + s.keyName + "\"]",
+ })
+ if err != nil {
+ ui.Error(fmt.Sprintf(
+ "Error cleaning up keypair. Please delete the key manually: %s", s.keyName))
+ }
+
+ // Also remove the physical key if we're debugging.
+ if s.Debug {
+ if err := os.Remove(s.DebugKeyPath); err != nil {
+ ui.Error(fmt.Sprintf(
+ "Error removing debug key '%s': %s", s.DebugKeyPath, err))
+ }
+ }
+}
diff --git a/builder/alicloud/ecs/step_config_public_ip.go b/builder/alicloud/ecs/step_config_public_ip.go
new file mode 100644
index 000000000..65b7f3fe2
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_public_ip.go
@@ -0,0 +1,35 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepConfigAlicloudPublicIP struct {
+ publicIPAdress string
+ RegionId string
+}
+
+func (s *stepConfigAlicloudPublicIP) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+
+ ipaddress, err := client.AllocatePublicIpAddress(instance.InstanceId)
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error allocating public ip: %s", err))
+ return multistep.ActionHalt
+ }
+ s.publicIPAdress = ipaddress
+ ui.Say(fmt.Sprintf("Allocated public ip address %s.", ipaddress))
+ state.Put("ipaddress", ipaddress)
+ return multistep.ActionContinue
+}
+
+func (s *stepConfigAlicloudPublicIP) Cleanup(state multistep.StateBag) {
+
+}
diff --git a/builder/alicloud/ecs/step_config_security_group.go b/builder/alicloud/ecs/step_config_security_group.go
new file mode 100644
index 000000000..6bfbb386a
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_security_group.go
@@ -0,0 +1,137 @@
+package ecs
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepConfigAlicloudSecurityGroup struct {
+ SecurityGroupId string
+ SecurityGroupName string
+ Description string
+ VpcId string
+ RegionId string
+ isCreate bool
+}
+
+func (s *stepConfigAlicloudSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ networkType := state.Get("networktype").(InstanceNetWork)
+
+ var securityGroupItems []ecs.SecurityGroupItemType
+ var err error
+ if len(s.SecurityGroupId) != 0 {
+ if networkType == VpcNet {
+ vpcId := state.Get("vpcid").(string)
+ securityGroupItems, _, err = client.DescribeSecurityGroups(&ecs.DescribeSecurityGroupsArgs{
+ VpcId: vpcId,
+ RegionId: common.Region(s.RegionId),
+ })
+ } else {
+ securityGroupItems, _, err = client.DescribeSecurityGroups(&ecs.DescribeSecurityGroupsArgs{
+ RegionId: common.Region(s.RegionId),
+ })
+ }
+
+ if err != nil {
+ ui.Say(fmt.Sprintf("Failed querying security group: %s", err))
+ state.Put("error", err)
+ return multistep.ActionHalt
+ }
+ for _, securityGroupItem := range securityGroupItems {
+ if securityGroupItem.SecurityGroupId == s.SecurityGroupId {
+ state.Put("securitygroupid", s.SecurityGroupId)
+ s.isCreate = false
+ return multistep.ActionContinue
+ }
+ }
+ s.isCreate = false
+ message := fmt.Sprintf("The specified security group {%s} doesn't exist.", s.SecurityGroupId)
+ state.Put("error", errors.New(message))
+ ui.Say(message)
+ return multistep.ActionHalt
+
+ }
+ var securityGroupId string
+ ui.Say("Creating security groups...")
+ if networkType == VpcNet {
+ vpcId := state.Get("vpcid").(string)
+ securityGroupId, err = client.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
+ RegionId: common.Region(s.RegionId),
+ SecurityGroupName: s.SecurityGroupName,
+ VpcId: vpcId,
+ })
+ } else {
+ securityGroupId, err = client.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
+ RegionId: common.Region(s.RegionId),
+ SecurityGroupName: s.SecurityGroupName,
+ })
+ }
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed creating security group %s.", err))
+ return multistep.ActionHalt
+ }
+ state.Put("securitygroupid", securityGroupId)
+ s.isCreate = true
+ s.SecurityGroupId = securityGroupId
+ err = client.AuthorizeSecurityGroupEgress(&ecs.AuthorizeSecurityGroupEgressArgs{
+ SecurityGroupId: securityGroupId,
+ RegionId: common.Region(s.RegionId),
+ IpProtocol: ecs.IpProtocolAll,
+ PortRange: "-1/-1",
+ NicType: ecs.NicTypeInternet,
+ DestCidrIp: "0.0.0.0/0", //The input parameter "DestGroupId" or "DestCidrIp" cannot be both blank.
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed authorizing security group: %s", err))
+ return multistep.ActionHalt
+ }
+ err = client.AuthorizeSecurityGroup(&ecs.AuthorizeSecurityGroupArgs{
+ SecurityGroupId: securityGroupId,
+ RegionId: common.Region(s.RegionId),
+ IpProtocol: ecs.IpProtocolAll,
+ PortRange: "-1/-1",
+ NicType: ecs.NicTypeInternet,
+ SourceCidrIp: "0.0.0.0/0", //The input parameter "SourceGroupId" or "SourceCidrIp" cannot be both blank.
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed authorizing security group: %s", err))
+ return multistep.ActionHalt
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *stepConfigAlicloudSecurityGroup) Cleanup(state multistep.StateBag) {
+ if !s.isCreate {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+
+ message(state, "security group")
+ start := time.Now().Add(10 * time.Second)
+ for {
+ if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
+ e, _ := err.(*common.Error)
+ if e.Code == "DependencyViolation" && time.Now().Before(start) {
+ time.Sleep(1 * time.Second)
+ continue
+ }
+ ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))
+ return
+ }
+ break
+ }
+}
diff --git a/builder/alicloud/ecs/step_config_vpc.go b/builder/alicloud/ecs/step_config_vpc.go
new file mode 100644
index 000000000..0dce325a4
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_vpc.go
@@ -0,0 +1,96 @@
+package ecs
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepConfigAlicloudVPC struct {
+ VpcId string
+ CidrBlock string //192.168.0.0/16 or 172.16.0.0/16 (default)
+ VpcName string
+ isCreate bool
+}
+
+func (s *stepConfigAlicloudVPC) Run(state multistep.StateBag) multistep.StepAction {
+ config := state.Get("config").(Config)
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+
+ if len(s.VpcId) != 0 {
+ vpcs, _, err := client.DescribeVpcs(&ecs.DescribeVpcsArgs{
+ VpcId: s.VpcId,
+ RegionId: common.Region(config.AlicloudRegion),
+ })
+ if err != nil {
+ ui.Say(fmt.Sprintf("Failed querying vpcs: %s", err))
+ state.Put("error", err)
+ return multistep.ActionHalt
+ }
+ if len(vpcs) > 0 {
+ vpc := vpcs[0]
+ state.Put("vpcid", vpc.VpcId)
+ s.isCreate = false
+ return multistep.ActionContinue
+ }
+ message := fmt.Sprintf("The specified vpc {%s} doesn't exist.", s.VpcId)
+ state.Put("error", errors.New(message))
+ ui.Say(message)
+ return multistep.ActionHalt
+
+ }
+ ui.Say("Creating vpc")
+ vpc, err := client.CreateVpc(&ecs.CreateVpcArgs{
+ RegionId: common.Region(config.AlicloudRegion),
+ CidrBlock: s.CidrBlock,
+ VpcName: s.VpcName,
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed creating vpc: %s", err))
+ return multistep.ActionHalt
+ }
+ err = client.WaitForVpcAvailable(common.Region(config.AlicloudRegion), vpc.VpcId, ALICLOUD_DEFAULT_SHORT_TIMEOUT)
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed waiting for vpc to become available: %s", err))
+ return multistep.ActionHalt
+ }
+
+ state.Put("vpcid", vpc.VpcId)
+ s.isCreate = true
+ s.VpcId = vpc.VpcId
+ return multistep.ActionContinue
+}
+
+func (s *stepConfigAlicloudVPC) Cleanup(state multistep.StateBag) {
+ if !s.isCreate {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+
+ message(state, "VPC")
+ start := time.Now().Add(10 * time.Second)
+ for {
+ if err := client.DeleteVpc(s.VpcId); err != nil {
+ e, _ := err.(*common.Error)
+ if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
+ e.Code == "DependencyViolation.VSwitch" ||
+ e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(start) {
+ time.Sleep(1 * time.Second)
+ continue
+ }
+ ui.Error(fmt.Sprintf("Error deleting vpc, it may still be around: %s", err))
+ return
+ }
+ break
+ }
+}
diff --git a/builder/alicloud/ecs/step_config_vswitch.go b/builder/alicloud/ecs/step_config_vswitch.go
new file mode 100644
index 000000000..0705fb45a
--- /dev/null
+++ b/builder/alicloud/ecs/step_config_vswitch.go
@@ -0,0 +1,148 @@
+package ecs
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepConfigAlicloudVSwitch struct {
+ VSwitchId string
+ ZoneId string
+ isCreate bool
+ CidrBlock string
+ VSwitchName string
+}
+
+func (s *stepConfigAlicloudVSwitch) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ vpcId := state.Get("vpcid").(string)
+ config := state.Get("config").(Config)
+
+ if len(s.VSwitchId) != 0 {
+ vswitchs, _, err := client.DescribeVSwitches(&ecs.DescribeVSwitchesArgs{
+ VpcId: vpcId,
+ VSwitchId: s.VSwitchId,
+ ZoneId: s.ZoneId,
+ })
+ if err != nil {
+ ui.Say(fmt.Sprintf("Failed querying vswitch: %s", err))
+ state.Put("error", err)
+ return multistep.ActionHalt
+ }
+ if len(vswitchs) > 0 {
+ vswitch := vswitchs[0]
+ state.Put("vswitchid", vswitch.VSwitchId)
+ s.isCreate = false
+ return multistep.ActionContinue
+ }
+ s.isCreate = false
+ message := fmt.Sprintf("The specified vswitch {%s} doesn't exist.", s.VSwitchId)
+ state.Put("error", errors.New(message))
+ ui.Say(message)
+ return multistep.ActionHalt
+
+ }
+ if s.ZoneId == "" {
+
+ zones, err := client.DescribeZones(common.Region(config.AlicloudRegion))
+ if err != nil {
+ ui.Say(fmt.Sprintf("Query for available zones failed: %s", err))
+ state.Put("error", err)
+ return multistep.ActionHalt
+ }
+ var instanceTypes []string
+ for _, zone := range zones {
+ isVSwitchSupported := false
+ for _, resourceType := range zone.AvailableResourceCreation.ResourceTypes {
+ if resourceType == ecs.ResourceTypeVSwitch {
+ isVSwitchSupported = true
+ }
+ }
+ if isVSwitchSupported {
+ for _, instanceType := range zone.AvailableInstanceTypes.InstanceTypes {
+ if instanceType == config.InstanceType {
+ s.ZoneId = zone.ZoneId
+ break
+ }
+ instanceTypes = append(instanceTypes, instanceType)
+ }
+ }
+ }
+
+ if s.ZoneId == "" {
+ if len(instanceTypes) > 0 {
+ ui.Say(fmt.Sprintf("The instance type %s isn't available in this region."+
+ "\n You can either change the instance to one of following: %v \n"+
+ "or choose another region.", config.InstanceType, instanceTypes))
+
+ state.Put("error", fmt.Errorf("The instance type %s isn't available in this region."+
+ "\n You can either change the instance to one of following: %v \n"+
+ "or choose another region.", config.InstanceType, instanceTypes))
+ return multistep.ActionHalt
+ } else {
+ ui.Say(fmt.Sprintf("The instance type %s isn't available in this region."+
+ "\n You can change to other regions.", config.InstanceType))
+
+ state.Put("error", fmt.Errorf("The instance type %s isn't available in this region."+
+ "\n You can change to other regions.", config.InstanceType))
+ return multistep.ActionHalt
+ }
+ }
+ }
+ if config.CidrBlock == "" {
+ s.CidrBlock = "172.16.0.0/24" //use the default CirdBlock
+ }
+ ui.Say("Creating vswitch...")
+ vswitchId, err := client.CreateVSwitch(&ecs.CreateVSwitchArgs{
+ CidrBlock: s.CidrBlock,
+ ZoneId: s.ZoneId,
+ VpcId: vpcId,
+ VSwitchName: s.VSwitchName,
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Create vswitch failed %v", err))
+ return multistep.ActionHalt
+ }
+ if err := client.WaitForVSwitchAvailable(vpcId, s.VSwitchId, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
+ state.Put("error", err)
+ ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become avaiable: %v", err))
+ return multistep.ActionHalt
+ }
+ state.Put("vswitchid", vswitchId)
+ s.isCreate = true
+ s.VSwitchId = vswitchId
+ return multistep.ActionContinue
+}
+
+func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) {
+ if !s.isCreate {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ message(state, "vSwitch")
+ start := time.Now().Add(10 * time.Second)
+ for {
+ if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
+ e, _ := err.(*common.Error)
+ if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
+ e.Code == "DependencyViolation.HaVip" ||
+ e.Code == "IncorretRouteEntryStatus") && time.Now().Before(start) {
+ time.Sleep(1 * time.Second)
+ continue
+ }
+ ui.Error(fmt.Sprintf("Error deleting vswitch, it may still be around: %s", err))
+ return
+ }
+ break
+ }
+}
diff --git a/builder/alicloud/ecs/step_create_image.go b/builder/alicloud/ecs/step_create_image.go
new file mode 100644
index 000000000..5060c78ca
--- /dev/null
+++ b/builder/alicloud/ecs/step_create_image.go
@@ -0,0 +1,94 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepCreateAlicloudImage struct {
+ image *ecs.ImageType
+}
+
+func (s *stepCreateAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
+ config := state.Get("config").(Config)
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+
+ // Create the alicloud image
+ ui.Say(fmt.Sprintf("Creating image: %s", config.AlicloudImageName))
+ var imageId string
+ var err error
+
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ imageId, err = client.CreateImage(&ecs.CreateImageArgs{
+ RegionId: common.Region(config.AlicloudRegion),
+ InstanceId: instance.InstanceId,
+ ImageName: config.AlicloudImageName,
+ ImageVersion: config.AlicloudImageVersion,
+ Description: config.AlicloudImageDescription})
+
+ if err != nil {
+ err := fmt.Errorf("Error creating image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ err = client.WaitForImageReady(common.Region(config.AlicloudRegion),
+ imageId, ALICLOUD_DEFAULT_LONG_TIMEOUT)
+ if err != nil {
+ err := fmt.Errorf("Timeout waiting for image to be created: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
+ RegionId: common.Region(config.AlicloudRegion),
+ ImageId: imageId})
+ if err != nil {
+ err := fmt.Errorf("Error querying created image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ if len(images) == 0 {
+ err := fmt.Errorf("Unable to find created image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ s.image = &images[0]
+
+ state.Put("alicloudimage", imageId)
+ alicloudImages := make(map[string]string)
+ alicloudImages[config.AlicloudRegion] = images[0].ImageId
+ state.Put("alicloudimages", alicloudImages)
+
+ return multistep.ActionContinue
+}
+
+func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) {
+ if s.image == nil {
+ return
+ }
+ _, cancelled := state.GetOk(multistep.StateCancelled)
+ _, halted := state.GetOk(multistep.StateHalted)
+ if !cancelled && !halted {
+ return
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ config := state.Get("config").(Config)
+
+ ui.Say("Deleting the image because of cancellation or error...")
+ if err := client.DeleteImage(common.Region(config.AlicloudRegion), s.image.ImageId); err != nil {
+ ui.Error(fmt.Sprintf("Error deleting image, it may still be around: %s", err))
+ return
+ }
+}
diff --git a/builder/alicloud/ecs/step_create_instance.go b/builder/alicloud/ecs/step_create_instance.go
new file mode 100644
index 000000000..c4f33f19c
--- /dev/null
+++ b/builder/alicloud/ecs/step_create_instance.go
@@ -0,0 +1,163 @@
+package ecs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepCreateAlicloudInstance struct {
+ IOOptimized bool
+ InstanceType string
+ UserData string
+ UserDataFile string
+ instanceId string
+ RegionId string
+ InternetChargeType string
+ InternetMaxBandwidthOut int
+ InstnaceName string
+ ZoneId string
+ instance *ecs.InstanceAttributesType
+}
+
+func (s *stepCreateAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ ui := state.Get("ui").(packer.Ui)
+ source_image := state.Get("source_image").(*ecs.ImageType)
+ network_type := state.Get("networktype").(InstanceNetWork)
+ securityGroupId := state.Get("securitygroupid").(string)
+ var instanceId string
+ var err error
+
+ ioOptimized := ecs.IoOptimizedNone
+ if s.IOOptimized {
+ ioOptimized = ecs.IoOptimizedOptimized
+ }
+ password := config.Comm.SSHPassword
+ if password == "" && config.Comm.WinRMPassword != "" {
+ password = config.Comm.WinRMPassword
+ }
+ ui.Say("Creating instance.")
+ if network_type == VpcNet {
+ userData, err := s.getUserData(state)
+ if err != nil {
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ vswitchId := state.Get("vswitchid").(string)
+ instanceId, err = client.CreateInstance(&ecs.CreateInstanceArgs{
+ RegionId: common.Region(s.RegionId),
+ ImageId: source_image.ImageId,
+ InstanceType: s.InstanceType,
+ InternetChargeType: common.InternetChargeType(s.InternetChargeType), //"PayByTraffic",
+ InternetMaxBandwidthOut: s.InternetMaxBandwidthOut,
+ UserData: userData,
+ IoOptimized: ioOptimized,
+ VSwitchId: vswitchId,
+ SecurityGroupId: securityGroupId,
+ InstanceName: s.InstnaceName,
+ Password: password,
+ ZoneId: s.ZoneId,
+ DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings),
+ })
+ if err != nil {
+ err := fmt.Errorf("Error creating instance: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ } else {
+ if s.InstanceType == "" {
+ s.InstanceType = "PayByTraffic"
+ }
+ if s.InternetMaxBandwidthOut == 0 {
+ s.InternetMaxBandwidthOut = 5
+ }
+ instanceId, err = client.CreateInstance(&ecs.CreateInstanceArgs{
+ RegionId: common.Region(s.RegionId),
+ ImageId: source_image.ImageId,
+ InstanceType: s.InstanceType,
+ InternetChargeType: common.InternetChargeType(s.InternetChargeType), //"PayByTraffic",
+ InternetMaxBandwidthOut: s.InternetMaxBandwidthOut,
+ IoOptimized: ioOptimized,
+ SecurityGroupId: securityGroupId,
+ InstanceName: s.InstnaceName,
+ Password: password,
+ ZoneId: s.ZoneId,
+ DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings),
+ })
+ if err != nil {
+ err := fmt.Errorf("Error creating instance: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+ err = client.WaitForInstance(instanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
+ if err != nil {
+ err := fmt.Errorf("Error creating instance: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ instance, err := client.DescribeInstanceAttribute(instanceId)
+ if err != nil {
+ ui.Say(err.Error())
+ return multistep.ActionHalt
+ }
+ s.instance = instance
+ state.Put("instance", instance)
+
+ return multistep.ActionContinue
+}
+
+func (s *stepCreateAlicloudInstance) Cleanup(state multistep.StateBag) {
+ if s.instance == nil {
+ return
+ }
+ message(state, "instance")
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ err := client.DeleteInstance(s.instance.InstanceId)
+ if err != nil {
+ ui.Say(fmt.Sprintf("Failed to clean up instance %s: %v", s.instance.InstanceId, err.Error()))
+ }
+
+}
+
+func (s *stepCreateAlicloudInstance) getUserData(state multistep.StateBag) (string, error) {
+ userData := s.UserData
+ if s.UserDataFile != "" {
+ data, err := ioutil.ReadFile(s.UserDataFile)
+ if err != nil {
+ return "", err
+ }
+ userData = string(data)
+ }
+ log.Printf(userData)
+ return userData, nil
+
+}
+
+func diskDeviceToDiskType(diskDevices []AlicloudDiskDevice) []ecs.DataDiskType {
+ result := make([]ecs.DataDiskType, len(diskDevices))
+ for _, diskDevice := range diskDevices {
+ result = append(result, ecs.DataDiskType{
+ DiskName: diskDevice.DiskName,
+ Category: ecs.DiskCategory(diskDevice.DiskCategory),
+ Size: diskDevice.DiskSize,
+ SnapshotId: diskDevice.SnapshotId,
+ Description: diskDevice.Description,
+ DeleteWithInstance: diskDevice.DeleteWithInstance,
+ Device: diskDevice.Device,
+ })
+ }
+ return result
+}
diff --git a/builder/alicloud/ecs/step_delete_images_snapshots.go b/builder/alicloud/ecs/step_delete_images_snapshots.go
new file mode 100644
index 000000000..24104fef0
--- /dev/null
+++ b/builder/alicloud/ecs/step_delete_images_snapshots.go
@@ -0,0 +1,63 @@
+package ecs
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepDeleteAlicloudImageSnapshots struct {
+ AlicloudImageForceDetele bool
+ AlicloudImageForceDeteleSnapshots bool
+ AlicloudImageName string
+}
+
+func (s *stepDeleteAlicloudImageSnapshots) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ config := state.Get("config").(Config)
+ ui.Say("Deleting image snapshots.")
+ // Check for force delete
+ if s.AlicloudImageForceDetele {
+ images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
+ RegionId: common.Region(config.AlicloudRegion),
+ ImageName: s.AlicloudImageName,
+ })
+ if len(images) < 1 {
+ return multistep.ActionContinue
+ }
+ for _, image := range images {
+ if image.ImageOwnerAlias != string(ecs.ImageOwnerSelf) {
+ log.Printf("You can only delete instances based on customized images %s ", image.ImageId)
+ continue
+ }
+ err = client.DeleteImage(common.Region(config.AlicloudRegion), image.ImageId)
+ if err != nil {
+ err := fmt.Errorf("Failed to delete image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ if s.AlicloudImageForceDeteleSnapshots {
+ for _, diskDevice := range image.DiskDeviceMappings.DiskDeviceMapping {
+ if err := client.DeleteSnapshot(diskDevice.SnapshotId); err != nil {
+ err := fmt.Errorf("Deleting ECS snapshot failed: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+ }
+ }
+
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *stepDeleteAlicloudImageSnapshots) Cleanup(state multistep.StateBag) {
+}
diff --git a/builder/alicloud/ecs/step_mount_disk.go b/builder/alicloud/ecs/step_mount_disk.go
new file mode 100644
index 000000000..222b27f08
--- /dev/null
+++ b/builder/alicloud/ecs/step_mount_disk.go
@@ -0,0 +1,71 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepMountAlicloudDisk struct {
+}
+
+func (s *stepMountAlicloudDisk) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ alicloudDiskDevices := config.ECSImagesDiskMappings
+ if len(config.ECSImagesDiskMappings) == 0 {
+ return multistep.ActionContinue
+ }
+ ui.Say("Mounting disks.")
+ disks, _, err := client.DescribeDisks(&ecs.DescribeDisksArgs{InstanceId: instance.InstanceId,
+ RegionId: instance.RegionId})
+ if err != nil {
+ err := fmt.Errorf("Error querying disks: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ for _, disk := range disks {
+ if disk.Status == ecs.DiskStatusAvailable {
+ if err := client.AttachDisk(&ecs.AttachDiskArgs{DiskId: disk.DiskId,
+ InstanceId: instance.InstanceId,
+ Device: getDevice(&disk, alicloudDiskDevices),
+ }); err != nil {
+ err := fmt.Errorf("Error mounting disks: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+ }
+ for _, disk := range disks {
+ if err := client.WaitForDisk(instance.RegionId, disk.DiskId, ecs.DiskStatusInUse, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
+ err := fmt.Errorf("Timeout waiting for mount: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+ ui.Say("Finished mounting disks.")
+ return multistep.ActionContinue
+}
+
+func (s *stepMountAlicloudDisk) Cleanup(state multistep.StateBag) {
+
+}
+
+func getDevice(disk *ecs.DiskItemType, diskDevices []AlicloudDiskDevice) string {
+ if disk.Device != "" {
+ return disk.Device
+ }
+ for _, alicloudDiskDevice := range diskDevices {
+ if alicloudDiskDevice.DiskName == disk.DiskName || alicloudDiskDevice.SnapshotId == disk.SourceSnapshotId {
+ return alicloudDiskDevice.Device
+ }
+ }
+ return ""
+}
diff --git a/builder/alicloud/ecs/step_pre_validate.go b/builder/alicloud/ecs/step_pre_validate.go
new file mode 100644
index 000000000..527d84fbf
--- /dev/null
+++ b/builder/alicloud/ecs/step_pre_validate.go
@@ -0,0 +1,48 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepPreValidate struct {
+ AlicloudDestImageName string
+ ForceDelete bool
+}
+
+func (s *stepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
+ ui := state.Get("ui").(packer.Ui)
+ if s.ForceDelete {
+ ui.Say("Force delete flag found, skipping prevalidating image name.")
+ return multistep.ActionContinue
+ }
+
+ client := state.Get("client").(*ecs.Client)
+ config := state.Get("config").(Config)
+ ui.Say("Prevalidating image name...")
+ images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
+ ImageName: s.AlicloudDestImageName,
+ RegionId: common.Region(config.AlicloudRegion)})
+
+ if err != nil {
+ err := fmt.Errorf("Error querying alicloud image: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ if len(images) > 0 {
+ err := fmt.Errorf("Error: name conflicts with an existing alicloud image: %s", images[0].ImageId)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *stepPreValidate) Cleanup(multistep.StateBag) {}
diff --git a/builder/alicloud/ecs/step_region_copy_image.go b/builder/alicloud/ecs/step_region_copy_image.go
new file mode 100644
index 000000000..5f60cafab
--- /dev/null
+++ b/builder/alicloud/ecs/step_region_copy_image.go
@@ -0,0 +1,71 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type setpRegionCopyAlicloudImage struct {
+ AlicloudImageDestinationRegions []string
+ AlicloudImageDestinationNames []string
+ RegionId string
+}
+
+func (s *setpRegionCopyAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
+ if len(s.AlicloudImageDestinationRegions) == 0 {
+ return multistep.ActionContinue
+ }
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ imageId := state.Get("alicloudimage").(string)
+ alicloudImages := state.Get("alicloudimages").(map[string]string)
+ region := common.Region(s.RegionId)
+
+ numberOfName := len(s.AlicloudImageDestinationNames)
+ for index, destinationRegion := range s.AlicloudImageDestinationRegions {
+ if destinationRegion == s.RegionId {
+ continue
+ }
+ ecsImageName := ""
+ if numberOfName > 0 && index < numberOfName {
+ ecsImageName = s.AlicloudImageDestinationNames[index]
+ }
+ imageId, err := client.CopyImage(
+ &ecs.CopyImageArgs{
+ RegionId: region,
+ ImageId: imageId,
+ DestinationRegionId: common.Region(destinationRegion),
+ DestinationImageName: ecsImageName,
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Error copying images: %s", err))
+ return multistep.ActionHalt
+ }
+ alicloudImages[destinationRegion] = imageId
+ }
+ return multistep.ActionContinue
+}
+
+func (s *setpRegionCopyAlicloudImage) Cleanup(state multistep.StateBag) {
+ _, cancelled := state.GetOk(multistep.StateCancelled)
+ _, halted := state.GetOk(multistep.StateHalted)
+ if cancelled || halted {
+ ui := state.Get("ui").(packer.Ui)
+ client := state.Get("client").(*ecs.Client)
+ alicloudImages := state.Get("alicloudimages").(map[string]string)
+ ui.Say(fmt.Sprintf("Stopping copy image because cancellation or error..."))
+ for copyedRegionId, copyedImageId := range alicloudImages {
+ if copyedRegionId == s.RegionId {
+ continue
+ }
+ if err := client.CancelCopyImage(common.Region(copyedRegionId), copyedImageId); err != nil {
+ ui.Say(fmt.Sprintf("Error cancelling copy image: %v", err))
+ }
+ }
+ }
+}
diff --git a/builder/alicloud/ecs/step_run_instance.go b/builder/alicloud/ecs/step_run_instance.go
new file mode 100644
index 000000000..a92637648
--- /dev/null
+++ b/builder/alicloud/ecs/step_run_instance.go
@@ -0,0 +1,56 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepRunAlicloudInstance struct {
+}
+
+func (s *stepRunAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+
+ err := client.StartInstance(instance.InstanceId)
+ if err != nil {
+ err := fmt.Errorf("Error starting instance: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ ui.Say("Starting instance.")
+ err = client.WaitForInstance(instance.InstanceId, ecs.Running, ALICLOUD_DEFAULT_TIMEOUT)
+ if err != nil {
+ err := fmt.Errorf("Timeout waiting for instance to start: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *stepRunAlicloudInstance) Cleanup(state multistep.StateBag) {
+ _, cancelled := state.GetOk(multistep.StateCancelled)
+ _, halted := state.GetOk(multistep.StateHalted)
+ if cancelled || halted {
+ ui := state.Get("ui").(packer.Ui)
+ client := state.Get("client").(*ecs.Client)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ instanceAttrubite, _ := client.DescribeInstanceAttribute(instance.InstanceId)
+ if instanceAttrubite.Status == ecs.Starting || instanceAttrubite.Status == ecs.Running {
+ if err := client.StopInstance(instance.InstanceId, true); err != nil {
+ ui.Say(fmt.Sprintf("Error stopping instance %s, it may still be around %s", instance.InstanceId, err))
+ return
+ }
+ if err := client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
+ ui.Say(fmt.Sprintf("Error stopping instance %s, it may still be around %s", instance.InstanceId, err))
+ }
+ }
+ }
+}
diff --git a/builder/alicloud/ecs/step_share_image.go b/builder/alicloud/ecs/step_share_image.go
new file mode 100644
index 000000000..bdd8d1245
--- /dev/null
+++ b/builder/alicloud/ecs/step_share_image.go
@@ -0,0 +1,60 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type setpShareAlicloudImage struct {
+ AlicloudImageShareAccounts []string
+ AlicloudImageUNShareAccounts []string
+ RegionId string
+}
+
+func (s *setpShareAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ ui := state.Get("ui").(packer.Ui)
+ alicloudImages := state.Get("alicloudimages").(map[string]string)
+ for copyedRegion, copyedImageId := range alicloudImages {
+ err := client.ModifyImageSharePermission(
+ &ecs.ModifyImageSharePermissionArgs{
+ RegionId: common.Region(copyedRegion),
+ ImageId: copyedImageId,
+ AddAccount: s.AlicloudImageShareAccounts,
+ RemoveAccount: s.AlicloudImageUNShareAccounts,
+ })
+ if err != nil {
+ state.Put("error", err)
+ ui.Say(fmt.Sprintf("Failed modifying image share permissions: %s", err))
+ return multistep.ActionHalt
+ }
+ }
+ return multistep.ActionContinue
+}
+
+func (s *setpShareAlicloudImage) Cleanup(state multistep.StateBag) {
+ _, cancelled := state.GetOk(multistep.StateCancelled)
+ _, halted := state.GetOk(multistep.StateHalted)
+ if cancelled || halted {
+ ui := state.Get("ui").(packer.Ui)
+ client := state.Get("client").(*ecs.Client)
+ alicloudImages := state.Get("alicloudimages").(map[string]string)
+ ui.Say("Restoring image share permission because cancellations or error...")
+ for copyedRegion, copyedImageId := range alicloudImages {
+ err := client.ModifyImageSharePermission(
+ &ecs.ModifyImageSharePermissionArgs{
+ RegionId: common.Region(copyedRegion),
+ ImageId: copyedImageId,
+ AddAccount: s.AlicloudImageUNShareAccounts,
+ RemoveAccount: s.AlicloudImageShareAccounts,
+ })
+ if err != nil {
+ ui.Say(fmt.Sprintf("Restoring image share permission failed: %s", err))
+ }
+ }
+ }
+}
diff --git a/builder/alicloud/ecs/step_stop_instance.go b/builder/alicloud/ecs/step_stop_instance.go
new file mode 100644
index 000000000..b12a74c63
--- /dev/null
+++ b/builder/alicloud/ecs/step_stop_instance.go
@@ -0,0 +1,43 @@
+package ecs
+
+import (
+ "fmt"
+
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/hashicorp/packer/packer"
+ "github.com/mitchellh/multistep"
+)
+
+type stepStopAlicloudInstance struct {
+ ForceStop bool
+}
+
+func (s *stepStopAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
+ client := state.Get("client").(*ecs.Client)
+ instance := state.Get("instance").(*ecs.InstanceAttributesType)
+ ui := state.Get("ui").(packer.Ui)
+
+ err := client.StopInstance(instance.InstanceId, s.ForceStop)
+ if err != nil {
+ if err != nil {
+ err := fmt.Errorf("Error stopping alicloud instance: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+
+ err = client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
+ if err != nil {
+ err := fmt.Errorf("Error waiting for alicloud instance to stop: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+
+ return multistep.ActionContinue
+}
+
+func (s *stepStopAlicloudInstance) Cleanup(multistep.StateBag) {
+ // No cleanup...
+}
diff --git a/command/plugin.go b/command/plugin.go
index ede63501f..4bc8b1936 100644
--- a/command/plugin.go
+++ b/command/plugin.go
@@ -13,6 +13,7 @@ import (
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/packer/plugin"
+ alicloudecsbuilder "github.com/hashicorp/packer/builder/alicloud/ecs"
amazonchrootbuilder "github.com/hashicorp/packer/builder/amazon/chroot"
amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs"
amazonebssurrogatebuilder "github.com/hashicorp/packer/builder/amazon/ebssurrogate"
@@ -37,6 +38,7 @@ import (
virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf"
vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso"
vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx"
+ alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
atlaspostprocessor "github.com/hashicorp/packer/post-processor/atlas"
@@ -73,6 +75,7 @@ type PluginCommand struct {
}
var Builders = map[string]packer.Builder{
+ "alicloud-ecs": new(alicloudecsbuilder.Builder),
"amazon-chroot": new(amazonchrootbuilder.Builder),
"amazon-ebs": new(amazonebsbuilder.Builder),
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
@@ -117,6 +120,7 @@ var Provisioners = map[string]packer.Provisioner{
}
var PostProcessors = map[string]packer.PostProcessor{
+ "alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
"artifice": new(artificepostprocessor.PostProcessor),
"atlas": new(atlaspostprocessor.PostProcessor),
diff --git a/examples/alicloud/basic/alicloud.json b/examples/alicloud/basic/alicloud.json
new file mode 100644
index 000000000..9c7384d98
--- /dev/null
+++ b/examples/alicloud/basic/alicloud.json
@@ -0,0 +1,24 @@
+{
+ "variables": {
+ "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
+ "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
+ },
+ "builders": [{
+ "type":"alicloud-ecs",
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "region":"cn-beijing",
+ "image_name":"packer_basi",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "ssh_username":"root",
+ "instance_type":"ecs.n1.tiny",
+ "io_optimized":"true"
+ }],
+ "provisioners": [{
+ "type": "shell",
+ "inline": [
+ "sleep 30",
+ "apt-get update -yy"
+ ]
+ }]
+}
diff --git a/examples/alicloud/basic/alicloud_windows.json b/examples/alicloud/basic/alicloud_windows.json
new file mode 100644
index 000000000..bb1f16773
--- /dev/null
+++ b/examples/alicloud/basic/alicloud_windows.json
@@ -0,0 +1,25 @@
+{
+ "variables": {
+ "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
+ "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
+ },
+ "builders": [{
+ "type":"alicloud-ecs",
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "region":"cn-beijing",
+ "image_name":"packer_test",
+ "source_image":"win2012_64_datactr_r2_en_40G_alibase_20160622.vhd",
+ "instance_type":"ecs.n1.tiny",
+ "io_optimized":"true",
+ "image_force_delete":"true",
+ "communicator": "winrm",
+ "winrm_port": 5985,
+ "winrm_username": "Administrator",
+ "winrm_password": "Test1234"
+ }],
+ "provisioners": [{
+ "type": "powershell",
+ "inline": ["dir c:\\"]
+ }]
+}
diff --git a/examples/alicloud/basic/alicloud_with_data_disk.json b/examples/alicloud/basic/alicloud_with_data_disk.json
new file mode 100644
index 000000000..2d99af8fb
--- /dev/null
+++ b/examples/alicloud/basic/alicloud_with_data_disk.json
@@ -0,0 +1,25 @@
+{
+ "variables": {
+ "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
+ "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
+ },
+ "builders": [{
+ "type":"alicloud-ecs",
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "region":"cn-beijing",
+ "image_name":"packer_with_data_disk",
+ "source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
+ "ssh_username":"root",
+ "instance_type":"ecs.n1.tiny",
+ "io_optimized":"true",
+ "image_disk_mappings":[{"disk_name":"data1","disk_size":20},{"disk_name":"data1","disk_size":20,"disk_device":"/dev/xvdz"}]
+ }],
+ "provisioners": [{
+ "type": "shell",
+ "inline": [
+ "sleep 30",
+ "apt-get update -yy"
+ ]
+ }]
+}
diff --git a/examples/alicloud/chef/alicloud.json b/examples/alicloud/chef/alicloud.json
new file mode 100644
index 000000000..8bfcfbc8b
--- /dev/null
+++ b/examples/alicloud/chef/alicloud.json
@@ -0,0 +1,33 @@
+{
+ "variables": {
+ "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
+ "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
+ },
+ "builders": [{
+ "type":"alicloud-ecs",
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "region":"cn-beijing",
+ "image_name":"packer_chef2",
+ "source_image":"ubuntu_14_0405_64_40G_base_20170222.vhd",
+ "ssh_username":"root",
+ "instance_type":"ecs.n1.medium",
+ "io_optimized":"true",
+ "image_force_delete":"true",
+ "ssh_password":"Test1234",
+ "user_data_file":"examples/alicloud/chef/user_data.sh"
+ }],
+ "provisioners": [{
+ "type": "file",
+ "source": "examples/alicloud/chef/chef.sh",
+ "destination": "/root/"
+ },{
+ "type": "shell",
+ "inline": [
+ "cd /root/",
+ "chmod 755 chef.sh",
+ "./chef.sh",
+ "chef-server-ctl reconfigure"
+ ]
+ }]
+}
diff --git a/examples/alicloud/chef/chef.sh b/examples/alicloud/chef/chef.sh
new file mode 100644
index 000000000..6d7ecac86
--- /dev/null
+++ b/examples/alicloud/chef/chef.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+HOSTNAME=`ifconfig eth1|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
+if [ not $HOSTNAME ] ; then
+ HOSTNAME=`ifconfig eth0|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
+fi
+CHEF_SERVER_URL='http://dubbo.oss-cn-shenzhen.aliyuncs.com/chef-server-core_12.8.0-1_amd64.deb'
+CHEF_CONSOLE_URL='http://dubbo.oss-cn-shenzhen.aliyuncs.com/chef-manage_2.4.3-1_amd64.deb'
+CHEF_SERVER_ADMIN='admin'
+CHEF_SERVER_ADMIN_PASSWORD='vmADMIN123'
+ORGANIZATION='aliyun'
+ORGANIZATION_FULL_NAME='Aliyun, Inc'
+#specify hostname
+hostname $HOSTNAME
+
+mkdir ~/.pemfile
+#install chef server
+wget $CHEF_SERVER_URL
+sudo dpkg -i chef-server-core_*.deb
+sudo chef-server-ctl reconfigure
+
+#create admin user
+sudo chef-server-ctl user-create $CHEF_SERVER_ADMIN $CHEF_SERVER_ADMIN $CHEF_SERVER_ADMIN 641002259@qq.com $CHEF_SERVER_ADMIN_PASSWORD -f ~/.pemfile/admin.pem
+
+#create aliyun organization
+sudo chef-server-ctl org-create $ORGANIZATION $ORGANIZATION_FULL_NAME --association_user $CHEF_SERVER_ADMIN -f ~/.pemfile/aliyun-validator.pem
+
+#install chef management console
+wget $CHEF_CONSOLE_URL
+sudo dpkg -i chef-manage_*.deb
+sudo chef-server-ctl reconfigure
+
+type expect >/dev/null 2>&1 || { echo >&2 "Install Expect..."; apt-get -y install expect; }
+echo "spawn sudo chef-manage-ctl reconfigure" >> chef-manage-confirm.exp
+echo "expect \"*Press any key to continue\"" >> chef-manage-confirm.exp
+echo "send \"a\\\n\"" >> chef-manage-confirm.exp
+echo "expect \".*chef-manage 2.4.3 license: \\\"Chef-MLSA\\\".*\"" >> chef-manage-confirm.exp
+echo "send \"q\"" >> chef-manage-confirm.exp
+echo "expect \".*Type 'yes' to accept the software license agreement, or anything else to cancel.\"" >> chef-manage-confirm.exp
+echo "send \"yes\\\n\"" >> chef-manage-confirm.exp
+echo "interact" >> chef-manage-confirm.exp
+expect chef-manage-confirm.exp
+rm -f chef-manage-confirm.exp
+
+#clean
+rm -rf chef-manage_2.4.3-1_amd64.deb
+rm -rf chef-server-core_12.8.0-1_amd64.deb
diff --git a/examples/alicloud/chef/user_data.sh b/examples/alicloud/chef/user_data.sh
new file mode 100644
index 000000000..c58826616
--- /dev/null
+++ b/examples/alicloud/chef/user_data.sh
@@ -0,0 +1,6 @@
+HOSTNAME=`ifconfig eth1|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
+if [ not $HOSTNAME ] ; then
+ HOSTNAME=`ifconfig eth0|grep 'inet addr'|cut -d ":" -f2|cut -d " " -f1`
+fi
+hostname $HOSTNAME
+chef-server-ctl reconfigure
diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go
new file mode 100644
index 000000000..5bbf591aa
--- /dev/null
+++ b/post-processor/alicloud-import/post-processor.go
@@ -0,0 +1,363 @@
+package alicloudimport
+
+import (
+ "fmt"
+ "log"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/aliyun/aliyun-oss-go-sdk/oss"
+ packercommon "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/ecs"
+ "github.com/denverdino/aliyungo/ram"
+ packerecs "github.com/hashicorp/packer/builder/alicloud/ecs"
+ "github.com/hashicorp/packer/common"
+ "github.com/hashicorp/packer/helper/config"
+ "github.com/hashicorp/packer/packer"
+ "github.com/hashicorp/packer/template/interpolate"
+)
+
+const (
+ BuilderId = "packer.post-processor.alicloud-import"
+ OSSSuffix = "oss-"
+ RAWFileFormat = "raw"
+ VHDFileFormat = "vhd"
+ BUSINESSINFO = "packer"
+ AliyunECSImageImportDefaultRolePolicy = `{
+ "Statement": [
+ {
+ "Action": "sts:AssumeRole",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": [
+ "ecs.aliyuncs.com"
+ ]
+ }
+ }
+ ],
+ "Version": "1"
+}`
+)
+
+// Configuration of this post processor
+type Config struct {
+ common.PackerConfig `mapstructure:",squash"`
+ packerecs.Config `mapstructure:",squash"`
+
+ // Variables specific to this post processor
+ OSSBucket string `mapstructure:"oss_bucket_name"`
+ OSSKey string `mapstructure:"oss_key_name"`
+ SkipClean bool `mapstructure:"skip_clean"`
+ Tags map[string]string `mapstructure:"tags"`
+ AlicloudImageName string `mapstructure:"image_name"`
+ AlicloudImageVersion string `mapstructure:"image_version"`
+ AlicloudImageDescription string `mapstructure:"image_description"`
+ AlicloudImageShareAccounts []string `mapstructure:"image_share_account"`
+ AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
+ OSType string `mapstructure:"image_os_type"`
+ Platform string `mapstructure:"image_platform"`
+ Architecture string `mapstructure:"image_architecture"`
+ Size string `mapstructure:"image_system_size"`
+ Format string `mapstructure:"format"`
+ AlicloudImageForceDetele bool `mapstructure:"image_force_delete"`
+
+ ctx interpolate.Context
+}
+
+type PostProcessor struct {
+ config Config
+ DiskDeviceMapping []ecs.DiskDeviceMapping
+}
+
+// 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{
+ "oss_key_name",
+ },
+ },
+ }, raws...)
+ if err != nil {
+ return err
+ }
+
+ errs := new(packer.MultiError)
+
+ // Check and render oss_key_name
+ if err = interpolate.Validate(p.config.OSSKey, &p.config.ctx); err != nil {
+ errs = packer.MultiErrorAppend(
+ errs, fmt.Errorf("Error parsing oss_key_name template: %s", err))
+ }
+
+ // Check we have alicloud access variables defined somewhere
+ errs = packer.MultiErrorAppend(errs, p.config.AlicloudAccessConfig.Prepare(&p.config.ctx)...)
+
+ // define all our required parameters
+ templates := map[string]*string{
+ "oss_bucket_name": &p.config.OSSBucket,
+ }
+ // Check out required params are defined
+ for key, ptr := range templates {
+ if *ptr == "" {
+ errs = packer.MultiErrorAppend(
+ errs, fmt.Errorf("%s must be set", key))
+ }
+ }
+
+ // Anything which flagged return back up the stack
+ if len(errs.Errors) > 0 {
+ return errs
+ }
+
+ log.Println(common.ScrubConfig(p.config, p.config.AlicloudAccessKey, p.config.AlicloudSecretKey))
+ return nil
+}
+
+func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
+ var err error
+
+ // Render this key since we didn't in the configure phase
+ p.config.OSSKey, err = interpolate.Render(p.config.OSSKey, &p.config.ctx)
+ if err != nil {
+ return nil, false, fmt.Errorf("Error rendering oss_key_name template: %s", err)
+ }
+ if p.config.OSSKey == "" {
+ p.config.OSSKey = "Packer_" + strconv.Itoa(time.Now().Nanosecond())
+ }
+ log.Printf("Rendered oss_key_name as %s", p.config.OSSKey)
+
+ log.Println("Looking for RAW or VHD in artifact")
+ // Locate the files output from the builder
+ source := ""
+ for _, path := range artifact.Files() {
+ if strings.HasSuffix(path, VHDFileFormat) || strings.HasSuffix(path, RAWFileFormat) {
+ source = path
+ break
+ }
+ }
+
+ // Hope we found something useful
+ if source == "" {
+ return nil, false, fmt.Errorf("No vhd or raw file found in artifact from builder")
+ }
+
+ ecsClient, err := p.config.AlicloudAccessConfig.Client()
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to connect alicloud ecs %s", err)
+ }
+ ecsClient.SetBusinessInfo(BUSINESSINFO)
+
+ images, _, err := ecsClient.DescribeImages(&ecs.DescribeImagesArgs{
+ RegionId: packercommon.Region(p.config.AlicloudRegion),
+ ImageName: p.config.AlicloudImageName,
+ })
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+
+ if len(images) > 0 && !p.config.AlicloudImageForceDetele {
+ return nil, false, fmt.Errorf("Duplicated image exists, please delete the existing images " +
+ "or set the 'image_force_delete' value as true")
+ }
+
+ // Set up the OSS client
+ log.Println("Creating OSS Client")
+ client, err := oss.New(getEndPonit(p.config.AlicloudRegion), p.config.AlicloudAccessKey,
+ p.config.AlicloudSecretKey)
+ if err != nil {
+ return nil, false, fmt.Errorf("Creating oss connection failed: %s", err)
+ }
+ bucket, err := queryOrCreateBucket(p.config.OSSBucket, client)
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to query or create bucket %s: %s", p.config.OSSBucket, err)
+ }
+
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to open %s: %s", source, err)
+ }
+
+ err = bucket.PutObjectFromFile(p.config.OSSKey, source)
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to upload image %s: %s", source, err)
+ }
+ if len(images) > 0 && p.config.AlicloudImageForceDetele {
+ if err = ecsClient.DeleteImage(packercommon.Region(p.config.AlicloudRegion),
+ images[0].ImageId); err != nil {
+ return nil, false, fmt.Errorf("Delete duplicated image %s failed", images[0].ImageName)
+ }
+ }
+
+ diskDeviceMapping := ecs.DiskDeviceMapping{
+ Size: p.config.Size,
+ Format: p.config.Format,
+ OSSBucket: p.config.OSSBucket,
+ OSSObject: p.config.OSSKey,
+ }
+ imageImageArgs := &ecs.ImportImageArgs{
+ RegionId: packercommon.Region(p.config.AlicloudRegion),
+ ImageName: p.config.AlicloudImageName,
+ ImageVersion: p.config.AlicloudImageVersion,
+ Description: p.config.AlicloudImageDescription,
+ Architecture: p.config.Architecture,
+ OSType: p.config.OSType,
+ Platform: p.config.Platform,
+ }
+ imageImageArgs.DiskDeviceMappings.DiskDeviceMapping = []ecs.DiskDeviceMapping{
+ diskDeviceMapping,
+ }
+ imageId, err := ecsClient.ImportImage(imageImageArgs)
+
+ if err != nil {
+ e, _ := err.(*packercommon.Error)
+ if e.Code == "NoSetRoletoECSServiceAcount" {
+ ramClient := ram.NewClient(p.config.AlicloudAccessKey, p.config.AlicloudSecretKey)
+ roleResponse, err := ramClient.GetRole(ram.RoleQueryRequest{
+ RoleName: "AliyunECSImageImportDefaultRole",
+ })
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ if roleResponse.Role.RoleId == "" {
+ if _, err = ramClient.CreateRole(ram.RoleRequest{
+ RoleName: "AliyunECSImageImportDefaultRole",
+ AssumeRolePolicyDocument: AliyunECSImageImportDefaultRolePolicy,
+ }); err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ if _, err := ramClient.AttachPolicyToRole(ram.AttachPolicyToRoleRequest{
+ ram.PolicyRequest{
+ PolicyName: "AliyunECSImageImportRolePolicy",
+ PolicyType: "System",
+ }, "AliyunECSImageImportDefaultRole",
+ }); err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ } else {
+ policyListResponse, err := ramClient.ListPoliciesForRole(ram.RoleQueryRequest{
+ "AliyunECSImageImportDefaultRole",
+ })
+ if err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ isAliyunECSImageImportRolePolicyNotExit := true
+ for _, policy := range policyListResponse.Policies.Policy {
+ if policy.PolicyName == "AliyunECSImageImportRolePolicy" &&
+ policy.PolicyType == "System" {
+ isAliyunECSImageImportRolePolicyNotExit = false
+ break
+ }
+ }
+ if isAliyunECSImageImportRolePolicyNotExit {
+ if _, err := ramClient.AttachPolicyToRole(ram.AttachPolicyToRoleRequest{
+ ram.PolicyRequest{
+ PolicyName: "AliyunECSImageImportRolePolicy",
+ PolicyType: "System",
+ }, "AliyunECSImageImportDefaultRole",
+ }); err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ }
+ if _, err = ramClient.UpdateRole(
+ ram.UpdateRoleRequest{
+ RoleName: "AliyunECSImageImportDefaultRole",
+ NewAssumeRolePolicyDocument: AliyunECSImageImportDefaultRolePolicy,
+ }); err != nil {
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ }
+ for i := 10; i > 0; i = i - 1 {
+ imageId, err = ecsClient.ImportImage(imageImageArgs)
+ if err != nil {
+ e, _ = err.(*packercommon.Error)
+ if e.Code == "NoSetRoletoECSServiceAcount" {
+ time.Sleep(5 * time.Second)
+ continue
+ } else if e.Code == "ImageIsImporting" ||
+ e.Code == "InvalidImageName.Duplicated" {
+ break
+ }
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ break
+ }
+
+ } else {
+
+ return nil, false, fmt.Errorf("Failed to start import from %s/%s: %s",
+ getEndPonit(p.config.OSSBucket), p.config.OSSKey, err)
+ }
+ }
+
+ err = ecsClient.WaitForImageReady(packercommon.Region(p.config.AlicloudRegion),
+ imageId, packerecs.ALICLOUD_DEFAULT_LONG_TIMEOUT)
+ // Add the reported Alicloud image ID to the artifact list
+ log.Printf("Importing created alicloud image ID %s in region %s Finished.", imageId, p.config.AlicloudRegion)
+ artifact = &packerecs.Artifact{
+ AlicloudImages: map[string]string{
+ p.config.AlicloudRegion: imageId,
+ },
+ BuilderIdValue: BuilderId,
+ Client: ecsClient,
+ }
+
+ if !p.config.SkipClean {
+ ui.Message(fmt.Sprintf("Deleting import source %s/%s/%s",
+ getEndPonit(p.config.AlicloudRegion), p.config.OSSBucket, p.config.OSSKey))
+ if err = bucket.DeleteObject(p.config.OSSKey); err != nil {
+ return nil, false, fmt.Errorf("Failed to delete %s/%s/%s: %s",
+ getEndPonit(p.config.AlicloudRegion), p.config.OSSBucket, p.config.OSSKey, err)
+ }
+ }
+
+ return artifact, false, nil
+}
+
+func queryOrCreateBucket(bucketName string, client *oss.Client) (*oss.Bucket, error) {
+ isExist, err := client.IsBucketExist(bucketName)
+ if err != nil {
+ return nil, err
+ }
+ if !isExist {
+ err = client.CreateBucket(bucketName)
+ if err != nil {
+ return nil, err
+ }
+ }
+ bucket, err := client.Bucket(bucketName)
+ if err != nil {
+ return nil, err
+ }
+ return bucket, nil
+
+}
+
+func getEndPonit(region string) string {
+ return "https://" + GetOSSRegion(region) + ".aliyuncs.com"
+}
+
+func GetOSSRegion(region string) string {
+ if strings.HasPrefix(region, OSSSuffix) {
+ return region
+ }
+ return OSSSuffix + region
+}
+
+func GetECSRegion(region string) string {
+ if strings.HasPrefix(region, OSSSuffix) {
+ return strings.TrimSuffix(region, OSSSuffix)
+ }
+ return region
+
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go
new file mode 100644
index 000000000..02c1f144e
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go
@@ -0,0 +1,92 @@
+package oss
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base64"
+ "hash"
+ "io"
+ "net/http"
+ "sort"
+ "strings"
+)
+
+// 用于signHeader的字典排序存放容器。
+type headerSorter struct {
+ Keys []string
+ Vals []string
+}
+
+// 生成签名方法(直接设置请求的Header)。
+func (conn Conn) signHeader(req *http.Request, canonicalizedResource string) {
+ // Find out the "x-oss-"'s address in this request'header
+ temp := make(map[string]string)
+
+ for k, v := range req.Header {
+ if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
+ temp[strings.ToLower(k)] = v[0]
+ }
+ }
+ hs := newHeaderSorter(temp)
+
+ // Sort the temp by the Ascending Order
+ hs.Sort()
+
+ // Get the CanonicalizedOSSHeaders
+ canonicalizedOSSHeaders := ""
+ for i := range hs.Keys {
+ canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
+ }
+
+ // Give other parameters values
+ date := req.Header.Get(HTTPHeaderDate)
+ contentType := req.Header.Get(HTTPHeaderContentType)
+ contentMd5 := req.Header.Get(HTTPHeaderContentMD5)
+
+ signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
+ h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(conn.config.AccessKeySecret))
+ io.WriteString(h, signStr)
+ signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+ // Get the final Authorization' string
+ authorizationStr := "OSS " + conn.config.AccessKeyID + ":" + signedStr
+
+ // Give the parameter "Authorization" value
+ req.Header.Set(HTTPHeaderAuthorization, authorizationStr)
+}
+
+// Additional function for function SignHeader.
+func newHeaderSorter(m map[string]string) *headerSorter {
+ hs := &headerSorter{
+ Keys: make([]string, 0, len(m)),
+ Vals: make([]string, 0, len(m)),
+ }
+
+ for k, v := range m {
+ hs.Keys = append(hs.Keys, k)
+ hs.Vals = append(hs.Vals, v)
+ }
+ return hs
+}
+
+// Additional function for function SignHeader.
+func (hs *headerSorter) Sort() {
+ sort.Sort(hs)
+}
+
+// Additional function for function SignHeader.
+func (hs *headerSorter) Len() int {
+ return len(hs.Vals)
+}
+
+// Additional function for function SignHeader.
+func (hs *headerSorter) Less(i, j int) bool {
+ return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
+}
+
+// Additional function for function SignHeader.
+func (hs *headerSorter) Swap(i, j int) {
+ hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
+ hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go
new file mode 100644
index 000000000..1da3c0765
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/bucket.go
@@ -0,0 +1,633 @@
+package oss
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/xml"
+ "hash"
+ "hash/crc64"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+)
+
+// Bucket implements the operations of object.
+type Bucket struct {
+ Client Client
+ BucketName string
+}
+
+//
+// PutObject 新建Object,如果Object已存在,覆盖原有Object。
+//
+// objectKey 上传对象的名称,使用UTF-8编码、长度必须在1-1023字节之间、不能以“/”或者“\”字符开头。
+// reader io.Reader读取object的数据。
+// options 上传对象时可以指定对象的属性,可用选项有CacheControl、ContentDisposition、ContentEncoding、
+// Expires、ServerSideEncryption、ObjectACL、Meta,具体含义请参看
+// https://help.aliyun.com/document_detail/oss/api-reference/object/PutObject.html
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
+ opts := addContentType(options, objectKey)
+
+ request := &PutObjectRequest{
+ ObjectKey: objectKey,
+ Reader: reader,
+ }
+ resp, err := bucket.DoPutObject(request, opts)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+//
+// PutObjectFromFile 新建Object,内容从本地文件中读取。
+//
+// objectKey 上传对象的名称。
+// filePath 本地文件,上传对象的值为该文件内容。
+// options 上传对象时可以指定对象的属性。详见PutObject的options。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ opts := addContentType(options, filePath, objectKey)
+
+ request := &PutObjectRequest{
+ ObjectKey: objectKey,
+ Reader: fd,
+ }
+ resp, err := bucket.DoPutObject(request, opts)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ return err
+}
+
+//
+// DoPutObject 上传文件。
+//
+// request 上传请求。
+// options 上传选项。
+//
+// Response 上传请求返回值。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
+ isOptSet, _, _ := isOptionSet(options, HTTPHeaderContentType)
+ if !isOptSet {
+ options = addContentType(options, request.ObjectKey)
+ }
+
+ listener := getProgressListener(options)
+
+ resp, err := bucket.do("PUT", request.ObjectKey, "", "", options, request.Reader, listener)
+ if err != nil {
+ return nil, err
+ }
+
+ if bucket.getConfig().IsEnableCRC {
+ err = checkCRC(resp, "DoPutObject")
+ if err != nil {
+ return resp, err
+ }
+ }
+
+ err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
+
+ return resp, err
+}
+
+//
+// GetObject 下载文件。
+//
+// objectKey 下载的文件名称。
+// options 对象的属性限制项,可选值有Range、IfModifiedSince、IfUnmodifiedSince、IfMatch、
+// IfNoneMatch、AcceptEncoding,详细请参考
+// https://help.aliyun.com/document_detail/oss/api-reference/object/GetObject.html
+//
+// io.ReadCloser reader,读取数据后需要close。error为nil时有效。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
+ result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
+ if err != nil {
+ return nil, err
+ }
+ return result.Response.Body, nil
+}
+
+//
+// GetObjectToFile 下载文件。
+//
+// objectKey 下载的文件名称。
+// filePath 下载对象的内容写到该本地文件。
+// options 对象的属性限制项。详见GetObject的options。
+//
+// error 操作无错误时返回error为nil,非nil为错误说明。
+//
+func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
+ tempFilePath := filePath + TempFileSuffix
+
+ // 读取Object内容
+ result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
+ if err != nil {
+ return err
+ }
+ defer result.Response.Body.Close()
+
+ // 如果文件不存在则创建,存在则清空
+ fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
+ if err != nil {
+ return err
+ }
+
+ // 存储数据到文件
+ _, err = io.Copy(fd, result.Response.Body)
+ fd.Close()
+ if err != nil {
+ return err
+ }
+
+ // 比较CRC值
+ hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
+ if bucket.getConfig().IsEnableCRC && !hasRange {
+ result.Response.ClientCRC = result.ClientCRC.Sum64()
+ err = checkCRC(result.Response, "GetObjectToFile")
+ if err != nil {
+ os.Remove(tempFilePath)
+ return err
+ }
+ }
+
+ return os.Rename(tempFilePath, filePath)
+}
+
+//
+// DoGetObject 下载文件
+//
+// request 下载请求
+// options 对象的属性限制项。详见GetObject的options。
+//
+// GetObjectResult 下载请求返回值。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
+ resp, err := bucket.do("GET", request.ObjectKey, "", "", options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &GetObjectResult{
+ Response: resp,
+ }
+
+ // crc
+ var crcCalc hash.Hash64
+ hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
+ if bucket.getConfig().IsEnableCRC && !hasRange {
+ crcCalc = crc64.New(crcTable())
+ result.ServerCRC = resp.ServerCRC
+ result.ClientCRC = crcCalc
+ }
+
+ // progress
+ listener := getProgressListener(options)
+
+ contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
+ resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
+
+ return result, nil
+}
+
+//
+// CopyObject 同一个bucket内拷贝Object。
+//
+// srcObjectKey Copy的源对象。
+// destObjectKey Copy的目标对象。
+// options Copy对象时,您可以指定源对象的限制条件,满足限制条件时copy,不满足时返回错误,您可以选择如下选项CopySourceIfMatch、
+// CopySourceIfNoneMatch、CopySourceIfModifiedSince、CopySourceIfUnmodifiedSince、MetadataDirective。
+// Copy对象时,您可以指定目标对象的属性,如CacheControl、ContentDisposition、ContentEncoding、Expires、
+// ServerSideEncryption、ObjectACL、Meta,选项的含义请参看
+// https://help.aliyun.com/document_detail/oss/api-reference/object/CopyObject.html
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ var out CopyObjectResult
+ options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
+ resp, err := bucket.do("PUT", destObjectKey, "", "", options, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// CopyObjectTo bucket间拷贝object。
+//
+// srcObjectKey 源Object名称。源Bucket名称为Bucket.BucketName。
+// destBucketName 目标Bucket名称。
+// destObjectKey 目标Object名称。
+// options Copy选项,详见CopyObject的options。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
+ return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
+}
+
+//
+// CopyObjectFrom bucket间拷贝object。
+//
+// srcBucketName 源Bucket名称。
+// srcObjectKey 源Object名称。
+// destObjectKey 目标Object名称。目标Bucket名称为Bucket.BucketName。
+// options Copy选项,详见CopyObject的options。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ destBucketName := bucket.BucketName
+ var out CopyObjectResult
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ if err != nil {
+ return out, err
+ }
+
+ return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
+}
+
+func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
+ var out CopyObjectResult
+ options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
+ headers := make(map[string]string)
+ err := handleOptions(headers, options)
+ if err != nil {
+ return out, err
+ }
+ resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, "", "", headers, nil, 0, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// AppendObject 追加方式上传。
+//
+// AppendObject参数必须包含position,其值指定从何处进行追加。首次追加操作的position必须为0,
+// 后续追加操作的position是Object的当前长度。例如,第一次Append Object请求指定position值为0,
+// content-length是65536;那么,第二次Append Object需要指定position为65536。
+// 每次操作成功后,响应头部x-oss-next-append-position也会标明下一次追加的position。
+//
+// objectKey 需要追加的Object。
+// reader io.Reader,读取追的内容。
+// appendPosition object追加的起始位置。
+// destObjectProperties 第一次追加时指定新对象的属性,如CacheControl、ContentDisposition、ContentEncoding、
+// Expires、ServerSideEncryption、ObjectACL。
+//
+// int64 下次追加的开始位置,error为nil空时有效。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
+ request := &AppendObjectRequest{
+ ObjectKey: objectKey,
+ Reader: reader,
+ Position: appendPosition,
+ }
+
+ result, err := bucket.DoAppendObject(request, options)
+
+ return result.NextPosition, err
+}
+
+//
+// DoAppendObject 追加上传。
+//
+// request 追加上传请求。
+// options 追加上传选项。
+//
+// AppendObjectResult 追加上传请求返回值。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
+ params := "append&position=" + strconv.FormatInt(request.Position, 10)
+ headers := make(map[string]string)
+
+ opts := addContentType(options, request.ObjectKey)
+ handleOptions(headers, opts)
+
+ var initCRC uint64
+ isCRCSet, initCRCOpt, _ := isOptionSet(options, initCRC64)
+ if isCRCSet {
+ initCRC = initCRCOpt.(uint64)
+ }
+
+ listener := getProgressListener(options)
+
+ handleOptions(headers, opts)
+ resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, params, headers,
+ request.Reader, initCRC, listener)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
+ result := &AppendObjectResult{
+ NextPosition: nextPosition,
+ CRC: resp.ServerCRC,
+ }
+
+ if bucket.getConfig().IsEnableCRC && isCRCSet {
+ err = checkCRC(resp, "AppendObject")
+ if err != nil {
+ return result, err
+ }
+ }
+
+ return result, nil
+}
+
+//
+// DeleteObject 删除Object。
+//
+// objectKey 待删除Object。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DeleteObject(objectKey string) error {
+ resp, err := bucket.do("DELETE", objectKey, "", "", nil, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// DeleteObjects 批量删除object。
+//
+// objectKeys 待删除object类表。
+// options 删除选项,DeleteObjectsQuiet,是否是安静模式,默认不使用。
+//
+// DeleteObjectsResult 非安静模式的的返回值。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
+ out := DeleteObjectsResult{}
+ dxml := deleteXML{}
+ for _, key := range objectKeys {
+ dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
+ }
+ isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
+ dxml.Quiet = isQuiet.(bool)
+ encode := "&encoding-type=url"
+
+ bs, err := xml.Marshal(dxml)
+ if err != nil {
+ return out, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ options = append(options, ContentType(contentType))
+ sum := md5.Sum(bs)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ options = append(options, ContentMD5(b64))
+ resp, err := bucket.do("POST", "", "delete"+encode, "delete", options, buffer, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ if !dxml.Quiet {
+ if err = xmlUnmarshal(resp.Body, &out); err == nil {
+ err = decodeDeleteObjectsResult(&out)
+ }
+ }
+ return out, err
+}
+
+//
+// IsObjectExist object是否存在。
+//
+// bool object是否存在,true存在,false不存在。error为nil时有效。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) IsObjectExist(objectKey string) (bool, error) {
+ listRes, err := bucket.ListObjects(Prefix(objectKey), MaxKeys(1))
+ if err != nil {
+ return false, err
+ }
+
+ if len(listRes.Objects) == 1 && listRes.Objects[0].Key == objectKey {
+ return true, nil
+ }
+ return false, nil
+}
+
+//
+// ListObjects 获得Bucket下筛选后所有的object的列表。
+//
+// options ListObject的筛选行为。Prefix指定的前缀、MaxKeys最大数目、Marker第一个开始、Delimiter对Object名字进行分组的字符。
+//
+// 您有如下8个object,my-object-1, my-object-11, my-object-2, my-object-21,
+// my-object-22, my-object-3, my-object-31, my-object-32。如果您指定了Prefix为my-object-2,
+// 则返回my-object-2, my-object-21, my-object-22三个object。如果您指定了Marker为my-object-22,
+// 则返回my-object-3, my-object-31, my-object-32三个object。如果您指定MaxKeys则每次最多返回MaxKeys个,
+// 最后一次可能不足。这三个参数可以组合使用,实现分页等功能。如果把prefix设为某个文件夹名,就可以罗列以此prefix开头的文件,
+// 即该文件夹下递归的所有的文件和子文件夹。如果再把delimiter设置为"/"时,返回值就只罗列该文件夹下的文件,该文件夹下的子文件名
+// 返回在CommonPrefixes部分,子文件夹下递归的文件和文件夹不被显示。例如一个bucket存在三个object,fun/test.jpg、
+// fun/movie/001.avi、fun/movie/007.avi。若设定prefix为"fun/",则返回三个object;如果增加设定
+// delimiter为"/",则返回文件"fun/test.jpg"和前缀"fun/movie/",即实现了文件夹的逻辑。
+//
+// 常用场景,请参数示例sample/list_object.go。
+//
+// ListObjectsResponse 操作成功后的返回值,成员Objects为bucket中对象列表。error为nil时该返回值有效。
+//
+func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
+ var out ListObjectsResult
+
+ options = append(options, EncodingType("url"))
+ params, err := handleParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := bucket.do("GET", "", params, "", nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+
+ err = decodeListObjectsResult(&out)
+ return out, err
+}
+
+//
+// SetObjectMeta 设置Object的Meta。
+//
+// objectKey object
+// options 指定对象的属性,有以下可选项CacheControl、ContentDisposition、ContentEncoding、Expires、
+// ServerSideEncryption、Meta。
+//
+// error 操作无错误时error为nil,非nil为错误信息。
+//
+func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
+ options = append(options, MetadataDirective(MetaReplace))
+ _, err := bucket.CopyObject(objectKey, objectKey, options...)
+ return err
+}
+
+//
+// GetObjectDetailedMeta 查询Object的头信息。
+//
+// objectKey object名称。
+// objectPropertyConstraints 对象的属性限制项,满足时正常返回,不满足时返回错误。现在项有IfModifiedSince、IfUnmodifiedSince、
+// IfMatch、IfNoneMatch。具体含义请参看 https://help.aliyun.com/document_detail/oss/api-reference/object/HeadObject.html
+//
+// http.Header 对象的meta,error为nil时有效。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
+ resp, err := bucket.do("HEAD", objectKey, "", "", options, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return resp.Headers, nil
+}
+
+//
+// GetObjectMeta 查询Object的头信息。
+//
+// GetObjectMeta相比GetObjectDetailedMeta更轻量,仅返回指定Object的少量基本meta信息,
+// 包括该Object的ETag、Size(对象大小)、LastModified,其中Size由响应头Content-Length的数值表示。
+//
+// objectKey object名称。
+//
+// http.Header 对象的meta,error为nil时有效。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
+ resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ return resp.Headers, nil
+}
+
+//
+// SetObjectACL 修改Object的ACL权限。
+//
+// 只有Bucket Owner才有权限调用PutObjectACL来修改Object的ACL。Object ACL优先级高于Bucket ACL。
+// 例如Bucket ACL是private的,而Object ACL是public-read-write的,则访问这个Object时,
+// 先判断Object的ACL,所以所有用户都拥有这个Object的访问权限,即使这个Bucket是private bucket。
+// 如果某个Object从来没设置过ACL,则访问权限遵循Bucket ACL。
+//
+// Object的读操作包括GetObject,HeadObject,CopyObject和UploadPartCopy中的对source object的读;
+// Object的写操作包括:PutObject,PostObject,AppendObject,DeleteObject,
+// DeleteMultipleObjects,CompleteMultipartUpload以及CopyObject对新的Object的写。
+//
+// objectKey 设置权限的object。
+// objectAcl 对象权限。可选值PrivateACL(私有读写)、PublicReadACL(公共读私有写)、PublicReadWriteACL(公共读写)。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
+ options := []Option{ObjectACL(objectACL)}
+ resp, err := bucket.do("PUT", objectKey, "acl", "acl", options, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// GetObjectACL 获取对象的ACL权限。
+//
+// objectKey 获取权限的object。
+//
+// GetObjectAclResponse 获取权限操作返回值,error为nil时有效。GetObjectAclResponse.Acl为对象的权限。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
+ var out GetObjectACLResult
+ resp, err := bucket.do("GET", objectKey, "acl", "acl", nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+// Private
+func (bucket Bucket) do(method, objectName, urlParams, subResource string, options []Option,
+ data io.Reader, listener ProgressListener) (*Response, error) {
+ headers := make(map[string]string)
+ err := handleOptions(headers, options)
+ if err != nil {
+ return nil, err
+ }
+ return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
+ urlParams, subResource, headers, data, 0, listener)
+}
+
+func (bucket Bucket) getConfig() *Config {
+ return bucket.Client.Config
+}
+
+func addContentType(options []Option, keys ...string) []Option {
+ typ := TypeByExtension("")
+ for _, key := range keys {
+ typ = TypeByExtension(key)
+ if typ != "" {
+ break
+ }
+ }
+
+ if typ == "" {
+ typ = "application/octet-stream"
+ }
+
+ opts := []Option{ContentType(typ)}
+ opts = append(opts, options...)
+
+ return opts
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go
new file mode 100644
index 000000000..23df9bc1a
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/client.go
@@ -0,0 +1,748 @@
+// Package oss implements functions for access oss service.
+// It has two main struct Client and Bucket.
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+)
+
+//
+// Client Sdk的入口,Client的方法可以完成bucket的各种操作,如create/delete bucket,
+// set/get acl/lifecycle/referer/logging/website等。文件(object)的上传下载通过Bucket完成。
+// 用户用oss.New创建Client。
+//
+type (
+ // Client oss client
+ Client struct {
+ Config *Config // Oss Client configure
+ Conn *Conn // Send http request
+ }
+
+ // ClientOption client option such as UseCname, Timeout, SecurityToken.
+ ClientOption func(*Client)
+)
+
+//
+// New 生成一个新的Client。
+//
+// endpoint 用户Bucket所在数据中心的访问域名,如http://oss-cn-hangzhou.aliyuncs.com。
+// accessKeyId 用户标识。
+// accessKeySecret 用户密钥。
+//
+// Client 生成的新Client。error为nil时有效。
+// error 操作无错误时为nil,非nil时表示操作出错。
+//
+func New(endpoint, accessKeyID, accessKeySecret string, options ...ClientOption) (*Client, error) {
+ // configuration
+ config := getDefaultOssConfig()
+ config.Endpoint = endpoint
+ config.AccessKeyID = accessKeyID
+ config.AccessKeySecret = accessKeySecret
+
+ // url parse
+ url := &urlMaker{}
+ url.Init(config.Endpoint, config.IsCname, config.IsUseProxy)
+
+ // http connect
+ conn := &Conn{config: config, url: url}
+
+ // oss client
+ client := &Client{
+ config,
+ conn,
+ }
+
+ // client options parse
+ for _, option := range options {
+ option(client)
+ }
+
+ // create http connect
+ err := conn.init(config, url)
+
+ return client, err
+}
+
+//
+// Bucket 取存储空间(Bucket)的对象实例。
+//
+// bucketName 存储空间名称。
+// Bucket 新的Bucket。error为nil时有效。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) Bucket(bucketName string) (*Bucket, error) {
+ return &Bucket{
+ client,
+ bucketName,
+ }, nil
+}
+
+//
+// CreateBucket 创建Bucket。
+//
+// bucketName bucket名称,在整个OSS中具有全局唯一性,且不能修改。bucket名称的只能包括小写字母,数字和短横线-,
+// 必须以小写字母或者数字开头,长度必须在3-255字节之间。
+// options 创建bucket的选项。您可以使用选项ACL,指定bucket的访问权限。Bucket有以下三种访问权限,私有读写(ACLPrivate)、
+// 公共读私有写(ACLPublicRead),公共读公共写(ACLPublicReadWrite),默认访问权限是私有读写。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) CreateBucket(bucketName string, options ...Option) error {
+ headers := make(map[string]string)
+ handleOptions(headers, options)
+
+ resp, err := client.do("PUT", bucketName, "", "", headers, nil)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// ListBuckets 获取当前用户下的bucket。
+//
+// options 指定ListBuckets的筛选行为,Prefix、Marker、MaxKeys三个选项。Prefix限定前缀。
+// Marker设定从Marker之后的第一个开始返回。MaxKeys限定此次返回的最大数目,默认为100。
+// 常用使用场景的实现,参数示例程序list_bucket.go。
+// ListBucketsResponse 操作成功后的返回值,error为nil时该返回值有效。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) ListBuckets(options ...Option) (ListBucketsResult, error) {
+ var out ListBucketsResult
+
+ params, err := handleParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := client.do("GET", "", params, "", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// IsBucketExist Bucket是否存在。
+//
+// bucketName 存储空间名称。
+//
+// bool 存储空间是否存在。error为nil时有效。
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) IsBucketExist(bucketName string) (bool, error) {
+ listRes, err := client.ListBuckets(Prefix(bucketName), MaxKeys(1))
+ if err != nil {
+ return false, err
+ }
+
+ if len(listRes.Buckets) == 1 && listRes.Buckets[0].Name == bucketName {
+ return true, nil
+ }
+ return false, nil
+}
+
+//
+// DeleteBucket 删除空存储空间。非空时请先清理Object、Upload。
+//
+// bucketName 存储空间名称。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) DeleteBucket(bucketName string) error {
+ resp, err := client.do("DELETE", bucketName, "", "", nil, nil)
+ if err != nil {
+ return err
+ }
+
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// GetBucketLocation 查看Bucket所属数据中心位置的信息。
+//
+// 如果您想了解"访问域名和数据中心"详细信息,请参看
+// https://help.aliyun.com/document_detail/oss/user_guide/oss_concept/endpoint.html
+//
+// bucketName 存储空间名称。
+//
+// string Bucket所属的数据中心位置信息。
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) GetBucketLocation(bucketName string) (string, error) {
+ resp, err := client.do("GET", bucketName, "location", "location", nil, nil)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ var LocationConstraint string
+ err = xmlUnmarshal(resp.Body, &LocationConstraint)
+ return LocationConstraint, err
+}
+
+//
+// SetBucketACL 修改Bucket的访问权限。
+//
+// bucketName 存储空间名称。
+// bucketAcl bucket的访问权限。Bucket有以下三种访问权限,Bucket有以下三种访问权限,私有读写(ACLPrivate)、
+// 公共读私有写(ACLPublicRead),公共读公共写(ACLPublicReadWrite)。
+//
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) SetBucketACL(bucketName string, bucketACL ACLType) error {
+ headers := map[string]string{HTTPHeaderOssACL: string(bucketACL)}
+ resp, err := client.do("PUT", bucketName, "", "", headers, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// GetBucketACL 获得Bucket的访问权限。
+//
+// bucketName 存储空间名称。
+//
+// GetBucketAclResponse 操作成功后的返回值,error为nil时该返回值有效。
+// error 操作无错误时返回nil,非nil为错误信息。
+//
+func (client Client) GetBucketACL(bucketName string) (GetBucketACLResult, error) {
+ var out GetBucketACLResult
+ resp, err := client.do("GET", bucketName, "acl", "acl", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// SetBucketLifecycle 修改Bucket的生命周期设置。
+//
+// OSS提供Object生命周期管理来为用户管理对象。用户可以为某个Bucket定义生命周期配置,来为该Bucket的Object定义各种规则。
+// Bucket的拥有者可以通过SetBucketLifecycle来设置Bucket的Lifecycle配置。Lifecycle开启后,OSS将按照配置,
+// 定期自动删除与Lifecycle规则相匹配的Object。如果您想了解更多的生命周期的信息,请参看
+// https://help.aliyun.com/document_detail/oss/user_guide/manage_object/object_lifecycle.html
+//
+// bucketName 存储空间名称。
+// rules 生命周期规则列表。生命周期规则有两种格式,指定绝对和相对过期时间,分布由days和year/month/day控制。
+// 具体用法请参考示例程序sample/bucket_lifecycle.go。
+//
+// error 操作无错误时返回error为nil,非nil为错误信息。
+//
+func (client Client) SetBucketLifecycle(bucketName string, rules []LifecycleRule) error {
+ lxml := lifecycleXML{Rules: convLifecycleRule(rules)}
+ bs, err := xml.Marshal(lxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, "lifecycle", "lifecycle", headers, buffer)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// DeleteBucketLifecycle 删除Bucket的生命周期设置。
+//
+//
+// bucketName 存储空间名称。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) DeleteBucketLifecycle(bucketName string) error {
+ resp, err := client.do("DELETE", bucketName, "lifecycle", "lifecycle", nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// GetBucketLifecycle 查看Bucket的生命周期设置。
+//
+// bucketName 存储空间名称。
+//
+// GetBucketLifecycleResponse 操作成功的返回值,error为nil时该返回值有效。Rules为该bucket上的规则列表。
+// error 操作无错误时为nil,非nil为错误信息。
+//
+func (client Client) GetBucketLifecycle(bucketName string) (GetBucketLifecycleResult, error) {
+ var out GetBucketLifecycleResult
+ resp, err := client.do("GET", bucketName, "lifecycle", "lifecycle", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// SetBucketReferer 设置bucket的referer访问白名单和是否允许referer字段为空的请求访问。
+//
+// 防止用户在OSS上的数据被其他人盗用,OSS支持基于HTTP header中表头字段referer的防盗链方法。可以通过OSS控制台或者API的方式对
+// 一个bucket设置referer字段的白名单和是否允许referer字段为空的请求访问。例如,对于一个名为oss-example的bucket,
+// 设置其referer白名单为http://www.aliyun.com。则所有referer为http://www.aliyun.com的请求才能访问oss-example
+// 这个bucket中的object。如果您还需要了解更多信息,请参看
+// https://help.aliyun.com/document_detail/oss/user_guide/security_management/referer.html
+//
+// bucketName 存储空间名称。
+// referers 访问白名单列表。一个bucket可以支持多个referer参数。referer参数支持通配符"*"和"?"。
+// 用法请参看示例sample/bucket_referer.go
+// allowEmptyReferer 指定是否允许referer字段为空的请求访问。 默认为true。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) SetBucketReferer(bucketName string, referers []string, allowEmptyReferer bool) error {
+ rxml := RefererXML{}
+ rxml.AllowEmptyReferer = allowEmptyReferer
+ if referers == nil {
+ rxml.RefererList = append(rxml.RefererList, "")
+ } else {
+ for _, referer := range referers {
+ rxml.RefererList = append(rxml.RefererList, referer)
+ }
+ }
+
+ bs, err := xml.Marshal(rxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, "referer", "referer", headers, buffer)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// GetBucketReferer 获得Bucket的白名单地址。
+//
+// bucketName 存储空间名称。
+//
+// GetBucketRefererResponse 操作成功的返回值,error为nil时该返回值有效。
+// error 操作无错误时为nil,非nil为错误信息。
+//
+func (client Client) GetBucketReferer(bucketName string) (GetBucketRefererResult, error) {
+ var out GetBucketRefererResult
+ resp, err := client.do("GET", bucketName, "referer", "referer", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// SetBucketLogging 修改Bucket的日志设置。
+//
+// OSS为您提供自动保存访问日志记录功能。Bucket的拥有者可以开启访问日志记录功能。当一个bucket开启访问日志记录功能后,
+// OSS自动将访问这个bucket的请求日志,以小时为单位,按照固定的命名规则,生成一个Object写入用户指定的bucket中。
+// 如果您需要更多,请参看 https://help.aliyun.com/document_detail/oss/user_guide/security_management/logging.html
+//
+// bucketName 需要记录访问日志的Bucket。
+// targetBucket 访问日志记录到的Bucket。
+// targetPrefix bucketName中需要存储访问日志记录的object前缀。为空记录所有object的访问日志。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) SetBucketLogging(bucketName, targetBucket, targetPrefix string,
+ isEnable bool) error {
+ var err error
+ var bs []byte
+ if isEnable {
+ lxml := LoggingXML{}
+ lxml.LoggingEnabled.TargetBucket = targetBucket
+ lxml.LoggingEnabled.TargetPrefix = targetPrefix
+ bs, err = xml.Marshal(lxml)
+ } else {
+ lxml := loggingXMLEmpty{}
+ bs, err = xml.Marshal(lxml)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, "logging", "logging", headers, buffer)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// DeleteBucketLogging 删除Bucket的日志设置。
+//
+// bucketName 需要删除访问日志的Bucket。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) DeleteBucketLogging(bucketName string) error {
+ resp, err := client.do("DELETE", bucketName, "logging", "logging", nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// GetBucketLogging 获得Bucket的日志设置。
+//
+// bucketName 需要删除访问日志的Bucket。
+// GetBucketLoggingResponse 操作成功的返回值,error为nil时该返回值有效。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) GetBucketLogging(bucketName string) (GetBucketLoggingResult, error) {
+ var out GetBucketLoggingResult
+ resp, err := client.do("GET", bucketName, "logging", "logging", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// SetBucketWebsite 设置/修改Bucket的默认首页以及错误页。
+//
+// OSS支持静态网站托管,Website操作可以将一个bucket设置成静态网站托管模式 。您可以将自己的Bucket配置成静态网站托管模式。
+// 如果您需要更多,请参看 https://help.aliyun.com/document_detail/oss/user_guide/static_host_website.html
+//
+// bucketName 需要设置Website的Bucket。
+// indexDocument 索引文档。
+// errorDocument 错误文档。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) SetBucketWebsite(bucketName, indexDocument, errorDocument string) error {
+ wxml := WebsiteXML{}
+ wxml.IndexDocument.Suffix = indexDocument
+ wxml.ErrorDocument.Key = errorDocument
+
+ bs, err := xml.Marshal(wxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := make(map[string]string)
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, "website", "website", headers, buffer)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// DeleteBucketWebsite 删除Bucket的Website设置。
+//
+// bucketName 需要删除website设置的Bucket。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) DeleteBucketWebsite(bucketName string) error {
+ resp, err := client.do("DELETE", bucketName, "website", "website", nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// GetBucketWebsite 获得Bucket的默认首页以及错误页。
+//
+// bucketName 存储空间名称。
+//
+// GetBucketWebsiteResponse 操作成功的返回值,error为nil时该返回值有效。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) GetBucketWebsite(bucketName string) (GetBucketWebsiteResult, error) {
+ var out GetBucketWebsiteResult
+ resp, err := client.do("GET", bucketName, "website", "website", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// SetBucketCORS 设置Bucket的跨域访问(CORS)规则。
+//
+// 跨域访问的更多信息,请参看 https://help.aliyun.com/document_detail/oss/user_guide/security_management/cors.html
+//
+// bucketName 需要设置Website的Bucket。
+// corsRules 待设置的CORS规则。用法请参看示例代码sample/bucket_cors.go。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) SetBucketCORS(bucketName string, corsRules []CORSRule) error {
+ corsxml := CORSXML{}
+ for _, v := range corsRules {
+ cr := CORSRule{}
+ cr.AllowedMethod = v.AllowedMethod
+ cr.AllowedOrigin = v.AllowedOrigin
+ cr.AllowedHeader = v.AllowedHeader
+ cr.ExposeHeader = v.ExposeHeader
+ cr.MaxAgeSeconds = v.MaxAgeSeconds
+ corsxml.CORSRules = append(corsxml.CORSRules, cr)
+ }
+
+ bs, err := xml.Marshal(corsxml)
+ if err != nil {
+ return err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ contentType := http.DetectContentType(buffer.Bytes())
+ headers := map[string]string{}
+ headers[HTTPHeaderContentType] = contentType
+
+ resp, err := client.do("PUT", bucketName, "cors", "cors", headers, buffer)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+//
+// DeleteBucketCORS 删除Bucket的Website设置。
+//
+// bucketName 需要删除cors设置的Bucket。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) DeleteBucketCORS(bucketName string) error {
+ resp, err := client.do("DELETE", bucketName, "cors", "cors", nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// GetBucketCORS 获得Bucket的CORS设置。
+//
+//
+// bucketName 存储空间名称。
+// GetBucketCORSResult 操作成功的返回值,error为nil时该返回值有效。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) GetBucketCORS(bucketName string) (GetBucketCORSResult, error) {
+ var out GetBucketCORSResult
+ resp, err := client.do("GET", bucketName, "cors", "cors", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// GetBucketInfo 获得Bucket的信息。
+//
+// bucketName 存储空间名称。
+// GetBucketInfoResult 操作成功的返回值,error为nil时该返回值有效。
+//
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (client Client) GetBucketInfo(bucketName string) (GetBucketInfoResult, error) {
+ var out GetBucketInfoResult
+ resp, err := client.do("GET", bucketName, "bucketInfo", "bucketInfo", nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// UseCname 设置是否使用CNAME,默认不使用。
+//
+// isUseCname true设置endpoint格式是cname格式,false为非cname格式,默认false
+//
+func UseCname(isUseCname bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsCname = isUseCname
+ client.Conn.url.Init(client.Config.Endpoint, client.Config.IsCname, client.Config.IsUseProxy)
+ }
+}
+
+//
+// Timeout 设置HTTP超时时间。
+//
+// connectTimeoutSec HTTP链接超时时间,单位是秒,默认10秒。0表示永不超时。
+// readWriteTimeout HTTP发送接受数据超时时间,单位是秒,默认20秒。0表示永不超时。
+//
+func Timeout(connectTimeoutSec, readWriteTimeout int64) ClientOption {
+ return func(client *Client) {
+ client.Config.HTTPTimeout.ConnectTimeout =
+ time.Second * time.Duration(connectTimeoutSec)
+ client.Config.HTTPTimeout.ReadWriteTimeout =
+ time.Second * time.Duration(readWriteTimeout)
+ client.Config.HTTPTimeout.HeaderTimeout =
+ time.Second * time.Duration(readWriteTimeout)
+ client.Config.HTTPTimeout.LongTimeout =
+ time.Second * time.Duration(readWriteTimeout*10)
+ }
+}
+
+//
+// SecurityToken 临时用户设置SecurityToken。
+//
+// token STS token
+//
+func SecurityToken(token string) ClientOption {
+ return func(client *Client) {
+ client.Config.SecurityToken = strings.TrimSpace(token)
+ }
+}
+
+//
+// EnableMD5 是否启用MD5校验,默认启用。
+//
+// isEnableMD5 true启用MD5校验,false不启用MD5校验
+//
+func EnableMD5(isEnableMD5 bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsEnableMD5 = isEnableMD5
+ }
+}
+
+//
+// MD5ThresholdCalcInMemory 使用内存计算MD5值的上限,默认16MB。
+//
+// threshold 单位Byte。上传内容小于threshold在MD5在内存中计算,大于使用临时文件计算MD5
+//
+func MD5ThresholdCalcInMemory(threshold int64) ClientOption {
+ return func(client *Client) {
+ client.Config.MD5Threshold = threshold
+ }
+}
+
+//
+// EnableCRC 上传是否启用CRC校验,默认启用。
+//
+// isEnableCRC true启用CRC校验,false不启用CRC校验
+//
+func EnableCRC(isEnableCRC bool) ClientOption {
+ return func(client *Client) {
+ client.Config.IsEnableCRC = isEnableCRC
+ }
+}
+
+//
+// UserAgent 指定UserAgent,默认如下aliyun-sdk-go/1.2.0 (windows/-/amd64;go1.5.2)。
+//
+// userAgent user agent字符串。
+//
+func UserAgent(userAgent string) ClientOption {
+ return func(client *Client) {
+ client.Config.UserAgent = userAgent
+ }
+}
+
+//
+// Proxy 设置代理服务器,默认不使用代理。
+//
+// proxyHost 代理服务器地址,格式是host或host:port
+//
+func Proxy(proxyHost string) ClientOption {
+ return func(client *Client) {
+ client.Config.IsUseProxy = true
+ client.Config.ProxyHost = proxyHost
+ client.Conn.url.Init(client.Config.Endpoint, client.Config.IsCname, client.Config.IsUseProxy)
+ }
+}
+
+//
+// AuthProxy 设置需要认证的代理服务器,默认不使用代理。
+//
+// proxyHost 代理服务器地址,格式是host或host:port
+// proxyUser 代理服务器认证的用户名
+// proxyPassword 代理服务器认证的用户密码
+//
+func AuthProxy(proxyHost, proxyUser, proxyPassword string) ClientOption {
+ return func(client *Client) {
+ client.Config.IsUseProxy = true
+ client.Config.ProxyHost = proxyHost
+ client.Config.IsAuthProxy = true
+ client.Config.ProxyUser = proxyUser
+ client.Config.ProxyPassword = proxyPassword
+ client.Conn.url.Init(client.Config.Endpoint, client.Config.IsCname, client.Config.IsUseProxy)
+ }
+}
+
+// Private
+func (client Client) do(method, bucketName, urlParams, subResource string,
+ headers map[string]string, data io.Reader) (*Response, error) {
+ return client.Conn.Do(method, bucketName, "", urlParams,
+ subResource, headers, data, 0, nil)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go
new file mode 100644
index 000000000..e8bee299e
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go
@@ -0,0 +1,67 @@
+package oss
+
+import (
+ "time"
+)
+
+// HTTPTimeout http timeout
+type HTTPTimeout struct {
+ ConnectTimeout time.Duration
+ ReadWriteTimeout time.Duration
+ HeaderTimeout time.Duration
+ LongTimeout time.Duration
+}
+
+// Config oss configure
+type Config struct {
+ Endpoint string // oss地址
+ AccessKeyID string // accessId
+ AccessKeySecret string // accessKey
+ RetryTimes uint // 失败重试次数,默认5
+ UserAgent string // SDK名称/版本/系统信息
+ IsDebug bool // 是否开启调试模式,默认false
+ Timeout uint // 超时时间,默认60s
+ SecurityToken string // STS Token
+ IsCname bool // Endpoint是否是CNAME
+ HTTPTimeout HTTPTimeout // HTTP的超时时间设置
+ IsUseProxy bool // 是否使用代理
+ ProxyHost string // 代理服务器地址
+ IsAuthProxy bool // 代理服务器是否使用用户认证
+ ProxyUser string // 代理服务器认证用户名
+ ProxyPassword string // 代理服务器认证密码
+ IsEnableMD5 bool // 上传数据时是否启用MD5校验
+ MD5Threshold int64 // 内存中计算MD5的上线大小,大于该值启用临时文件,单位Byte
+ IsEnableCRC bool // 上传数据时是否启用CRC64校验
+}
+
+// 获取默认配置
+func getDefaultOssConfig() *Config {
+ config := Config{}
+
+ config.Endpoint = ""
+ config.AccessKeyID = ""
+ config.AccessKeySecret = ""
+ config.RetryTimes = 5
+ config.IsDebug = false
+ config.UserAgent = userAgent
+ config.Timeout = 60 // seconds
+ config.SecurityToken = ""
+ config.IsCname = false
+
+ config.HTTPTimeout.ConnectTimeout = time.Second * 30 // 30s
+ config.HTTPTimeout.ReadWriteTimeout = time.Second * 60 // 60s
+ config.HTTPTimeout.HeaderTimeout = time.Second * 60 // 60s
+ config.HTTPTimeout.LongTimeout = time.Second * 300 // 300s
+
+ config.IsUseProxy = false
+ config.ProxyHost = ""
+ config.IsAuthProxy = false
+ config.ProxyUser = ""
+ config.ProxyPassword = ""
+
+ config.MD5Threshold = 16 * 1024 * 1024 // 16MB
+ config.IsEnableMD5 = false
+ config.IsEnableCRC = true
+
+ return &config
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go
new file mode 100644
index 000000000..77707bd92
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go
@@ -0,0 +1,437 @@
+package oss
+
+import (
+ "bytes"
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/xml"
+ "fmt"
+ "hash"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Conn oss conn
+type Conn struct {
+ config *Config
+ url *urlMaker
+ client *http.Client
+}
+
+// init 初始化Conn
+func (conn *Conn) init(config *Config, urlMaker *urlMaker) error {
+ httpTimeOut := conn.config.HTTPTimeout
+
+ // new Transport
+ transport := &http.Transport{
+ Dial: func(netw, addr string) (net.Conn, error) {
+ conn, err := net.DialTimeout(netw, addr, httpTimeOut.ConnectTimeout)
+ if err != nil {
+ return nil, err
+ }
+ return newTimeoutConn(conn, httpTimeOut.ReadWriteTimeout, httpTimeOut.LongTimeout), nil
+ },
+ ResponseHeaderTimeout: httpTimeOut.HeaderTimeout,
+ }
+
+ // Proxy
+ if conn.config.IsUseProxy {
+ proxyURL, err := url.Parse(config.ProxyHost)
+ if err != nil {
+ return err
+ }
+ transport.Proxy = http.ProxyURL(proxyURL)
+ }
+
+ conn.config = config
+ conn.url = urlMaker
+ conn.client = &http.Client{Transport: transport}
+
+ return nil
+}
+
+// Do 处理请求,返回响应结果。
+func (conn Conn) Do(method, bucketName, objectName, urlParams, subResource string, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ uri := conn.url.getURL(bucketName, objectName, urlParams)
+ resource := conn.url.getResource(bucketName, objectName, subResource)
+ return conn.doRequest(method, uri, resource, headers, data, initCRC, listener)
+}
+
+func (conn Conn) doRequest(method string, uri *url.URL, canonicalizedResource string, headers map[string]string,
+ data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
+ method = strings.ToUpper(method)
+ if !conn.config.IsUseProxy {
+ uri.Opaque = uri.Path
+ }
+ req := &http.Request{
+ Method: method,
+ URL: uri,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: uri.Host,
+ }
+
+ tracker := &readerTracker{completedBytes: 0}
+ fd, crc := conn.handleBody(req, data, initCRC, listener, tracker)
+ if fd != nil {
+ defer func() {
+ fd.Close()
+ os.Remove(fd.Name())
+ }()
+ }
+
+ if conn.config.IsAuthProxy {
+ auth := conn.config.ProxyUser + ":" + conn.config.ProxyPassword
+ basic := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
+ req.Header.Set("Proxy-Authorization", basic)
+ }
+
+ date := time.Now().UTC().Format(http.TimeFormat)
+ req.Header.Set(HTTPHeaderDate, date)
+ req.Header.Set(HTTPHeaderHost, conn.config.Endpoint)
+ req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
+ if conn.config.SecurityToken != "" {
+ req.Header.Set(HTTPHeaderOssSecurityToken, conn.config.SecurityToken)
+ }
+
+ if headers != nil {
+ for k, v := range headers {
+ req.Header.Set(k, v)
+ }
+ }
+
+ conn.signHeader(req, canonicalizedResource)
+
+ // transfer started
+ event := newProgressEvent(TransferStartedEvent, 0, req.ContentLength)
+ publishProgress(listener, event)
+
+ resp, err := conn.client.Do(req)
+ if err != nil {
+ // transfer failed
+ event = newProgressEvent(TransferFailedEvent, tracker.completedBytes, req.ContentLength)
+ publishProgress(listener, event)
+ return nil, err
+ }
+
+ // transfer completed
+ event = newProgressEvent(TransferCompletedEvent, tracker.completedBytes, req.ContentLength)
+ publishProgress(listener, event)
+
+ return conn.handleResponse(resp, crc)
+}
+
+// handle request body
+func (conn Conn) handleBody(req *http.Request, body io.Reader, initCRC uint64,
+ listener ProgressListener, tracker *readerTracker) (*os.File, hash.Hash64) {
+ var file *os.File
+ var crc hash.Hash64
+ reader := body
+
+ // length
+ switch v := body.(type) {
+ case *bytes.Buffer:
+ req.ContentLength = int64(v.Len())
+ case *bytes.Reader:
+ req.ContentLength = int64(v.Len())
+ case *strings.Reader:
+ req.ContentLength = int64(v.Len())
+ case *os.File:
+ req.ContentLength = tryGetFileSize(v)
+ case *io.LimitedReader:
+ req.ContentLength = int64(v.N)
+ }
+ req.Header.Set(HTTPHeaderContentLength, strconv.FormatInt(req.ContentLength, 10))
+
+ // md5
+ if body != nil && conn.config.IsEnableMD5 && req.Header.Get(HTTPHeaderContentMD5) == "" {
+ md5 := ""
+ reader, md5, file, _ = calcMD5(body, req.ContentLength, conn.config.MD5Threshold)
+ req.Header.Set(HTTPHeaderContentMD5, md5)
+ }
+
+ // crc
+ if reader != nil && conn.config.IsEnableCRC {
+ crc = NewCRC(crcTable(), initCRC)
+ reader = TeeReader(reader, crc, req.ContentLength, listener, tracker)
+ }
+
+ // http body
+ rc, ok := reader.(io.ReadCloser)
+ if !ok && reader != nil {
+ rc = ioutil.NopCloser(reader)
+ }
+ req.Body = rc
+
+ return file, crc
+}
+
+func tryGetFileSize(f *os.File) int64 {
+ fInfo, _ := f.Stat()
+ return fInfo.Size()
+}
+
+// handle response
+func (conn Conn) handleResponse(resp *http.Response, crc hash.Hash64) (*Response, error) {
+ var cliCRC uint64
+ var srvCRC uint64
+
+ statusCode := resp.StatusCode
+ if statusCode >= 400 && statusCode <= 505 {
+ // 4xx and 5xx indicate that the operation has error occurred
+ var respBody []byte
+ respBody, err := readResponseBody(resp)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(respBody) == 0 {
+ // no error in response body
+ err = fmt.Errorf("oss: service returned without a response body (%s)", resp.Status)
+ } else {
+ // response contains storage service error object, unmarshal
+ srvErr, errIn := serviceErrFromXML(respBody, resp.StatusCode,
+ resp.Header.Get(HTTPHeaderOssRequestID))
+ if err != nil { // error unmarshaling the error response
+ err = errIn
+ }
+ err = srvErr
+ }
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: ioutil.NopCloser(bytes.NewReader(respBody)), // restore the body
+ }, err
+ } else if statusCode >= 300 && statusCode <= 307 {
+ // oss use 3xx, but response has no body
+ err := fmt.Errorf("oss: service returned %d,%s", resp.StatusCode, resp.Status)
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: resp.Body,
+ }, err
+ }
+
+ if conn.config.IsEnableCRC && crc != nil {
+ cliCRC = crc.Sum64()
+ }
+ srvCRC, _ = strconv.ParseUint(resp.Header.Get(HTTPHeaderOssCRC64), 10, 64)
+
+ // 2xx, successful
+ return &Response{
+ StatusCode: resp.StatusCode,
+ Headers: resp.Header,
+ Body: resp.Body,
+ ClientCRC: cliCRC,
+ ServerCRC: srvCRC,
+ }, nil
+}
+
+func calcMD5(body io.Reader, contentLen, md5Threshold int64) (reader io.Reader, b64 string, tempFile *os.File, err error) {
+ if contentLen == 0 || contentLen > md5Threshold {
+ // huge body, use temporary file
+ tempFile, err = ioutil.TempFile(os.TempDir(), TempFilePrefix)
+ if tempFile != nil {
+ io.Copy(tempFile, body)
+ tempFile.Seek(0, os.SEEK_SET)
+ md5 := md5.New()
+ io.Copy(md5, tempFile)
+ sum := md5.Sum(nil)
+ b64 = base64.StdEncoding.EncodeToString(sum[:])
+ tempFile.Seek(0, os.SEEK_SET)
+ reader = tempFile
+ }
+ } else {
+ // small body, use memory
+ buf, _ := ioutil.ReadAll(body)
+ sum := md5.Sum(buf)
+ b64 = base64.StdEncoding.EncodeToString(sum[:])
+ reader = bytes.NewReader(buf)
+ }
+ return
+}
+
+func readResponseBody(resp *http.Response) ([]byte, error) {
+ defer resp.Body.Close()
+ out, err := ioutil.ReadAll(resp.Body)
+ if err == io.EOF {
+ err = nil
+ }
+ return out, err
+}
+
+func serviceErrFromXML(body []byte, statusCode int, requestID string) (ServiceError, error) {
+ var storageErr ServiceError
+ if err := xml.Unmarshal(body, &storageErr); err != nil {
+ return storageErr, err
+ }
+ storageErr.StatusCode = statusCode
+ storageErr.RequestID = requestID
+ storageErr.RawMessage = string(body)
+ return storageErr, nil
+}
+
+func xmlUnmarshal(body io.Reader, v interface{}) error {
+ data, err := ioutil.ReadAll(body)
+ if err != nil {
+ return err
+ }
+ return xml.Unmarshal(data, v)
+}
+
+// Handle http timeout
+type timeoutConn struct {
+ conn net.Conn
+ timeout time.Duration
+ longTimeout time.Duration
+}
+
+func newTimeoutConn(conn net.Conn, timeout time.Duration, longTimeout time.Duration) *timeoutConn {
+ conn.SetReadDeadline(time.Now().Add(longTimeout))
+ return &timeoutConn{
+ conn: conn,
+ timeout: timeout,
+ longTimeout: longTimeout,
+ }
+}
+
+func (c *timeoutConn) Read(b []byte) (n int, err error) {
+ c.SetReadDeadline(time.Now().Add(c.timeout))
+ n, err = c.conn.Read(b)
+ c.SetReadDeadline(time.Now().Add(c.longTimeout))
+ return n, err
+}
+
+func (c *timeoutConn) Write(b []byte) (n int, err error) {
+ c.SetWriteDeadline(time.Now().Add(c.timeout))
+ n, err = c.conn.Write(b)
+ c.SetReadDeadline(time.Now().Add(c.longTimeout))
+ return n, err
+}
+
+func (c *timeoutConn) Close() error {
+ return c.conn.Close()
+}
+
+func (c *timeoutConn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+func (c *timeoutConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+func (c *timeoutConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+
+func (c *timeoutConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+func (c *timeoutConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// UrlMaker - build url and resource
+const (
+ urlTypeCname = 1
+ urlTypeIP = 2
+ urlTypeAliyun = 3
+)
+
+type urlMaker struct {
+ Scheme string // http or https
+ NetLoc string // host or ip
+ Type int // 1 CNAME 2 IP 3 ALIYUN
+ IsProxy bool // proxy
+}
+
+// Parse endpoint
+func (um *urlMaker) Init(endpoint string, isCname bool, isProxy bool) {
+ if strings.HasPrefix(endpoint, "http://") {
+ um.Scheme = "http"
+ um.NetLoc = endpoint[len("http://"):]
+ } else if strings.HasPrefix(endpoint, "https://") {
+ um.Scheme = "https"
+ um.NetLoc = endpoint[len("https://"):]
+ } else {
+ um.Scheme = "http"
+ um.NetLoc = endpoint
+ }
+
+ host, _, err := net.SplitHostPort(um.NetLoc)
+ if err != nil {
+ host = um.NetLoc
+ }
+ ip := net.ParseIP(host)
+ if ip != nil {
+ um.Type = urlTypeIP
+ } else if isCname {
+ um.Type = urlTypeCname
+ } else {
+ um.Type = urlTypeAliyun
+ }
+ um.IsProxy = isProxy
+}
+
+// Build URL
+func (um urlMaker) getURL(bucket, object, params string) *url.URL {
+ var host = ""
+ var path = ""
+
+ if !um.IsProxy {
+ object = url.QueryEscape(object)
+ }
+
+ if um.Type == urlTypeCname {
+ host = um.NetLoc
+ path = "/" + object
+ } else if um.Type == urlTypeIP {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = um.NetLoc
+ path = fmt.Sprintf("/%s/%s", bucket, object)
+ }
+ } else {
+ if bucket == "" {
+ host = um.NetLoc
+ path = "/"
+ } else {
+ host = bucket + "." + um.NetLoc
+ path = "/" + object
+ }
+ }
+
+ uri := &url.URL{
+ Scheme: um.Scheme,
+ Host: host,
+ Path: path,
+ RawQuery: params,
+ }
+
+ return uri
+}
+
+// Canonicalized Resource
+func (um urlMaker) getResource(bucketName, objectName, subResource string) string {
+ if subResource != "" {
+ subResource = "?" + subResource
+ }
+ if bucketName == "" {
+ return fmt.Sprintf("/%s%s", bucketName, subResource)
+ }
+ return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go
new file mode 100644
index 000000000..7120763d3
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/const.go
@@ -0,0 +1,89 @@
+package oss
+
+import "os"
+
+// ACLType Bucket/Object的访问控制
+type ACLType string
+
+const (
+ // ACLPrivate 私有读写
+ ACLPrivate ACLType = "private"
+
+ // ACLPublicRead 公共读私有写
+ ACLPublicRead ACLType = "public-read"
+
+ // ACLPublicReadWrite 公共读写
+ ACLPublicReadWrite ACLType = "public-read-write"
+
+ // ACLDefault Object默认权限,Bucket无此权限
+ ACLDefault ACLType = "default"
+)
+
+// MetadataDirectiveType 对象COPY时新对象是否使用原对象的Meta
+type MetadataDirectiveType string
+
+const (
+ // MetaCopy 目标对象使用源对象的META
+ MetaCopy MetadataDirectiveType = "COPY"
+
+ // MetaReplace 目标对象使用自定义的META
+ MetaReplace MetadataDirectiveType = "REPLACE"
+)
+
+// Http头标签
+const (
+ HTTPHeaderAcceptEncoding string = "Accept-Encoding"
+ HTTPHeaderAuthorization = "Authorization"
+ HTTPHeaderCacheControl = "Cache-Control"
+ HTTPHeaderContentDisposition = "Content-Disposition"
+ HTTPHeaderContentEncoding = "Content-Encoding"
+ HTTPHeaderContentLength = "Content-Length"
+ HTTPHeaderContentMD5 = "Content-MD5"
+ HTTPHeaderContentType = "Content-Type"
+ HTTPHeaderContentLanguage = "Content-Language"
+ HTTPHeaderDate = "Date"
+ HTTPHeaderEtag = "ETag"
+ HTTPHeaderExpires = "Expires"
+ HTTPHeaderHost = "Host"
+ HTTPHeaderLastModified = "Last-Modified"
+ HTTPHeaderRange = "Range"
+ HTTPHeaderLocation = "Location"
+ HTTPHeaderOrigin = "Origin"
+ HTTPHeaderServer = "Server"
+ HTTPHeaderUserAgent = "User-Agent"
+ HTTPHeaderIfModifiedSince = "If-Modified-Since"
+ HTTPHeaderIfUnmodifiedSince = "If-Unmodified-Since"
+ HTTPHeaderIfMatch = "If-Match"
+ HTTPHeaderIfNoneMatch = "If-None-Match"
+
+ HTTPHeaderOssACL = "X-Oss-Acl"
+ HTTPHeaderOssMetaPrefix = "X-Oss-Meta-"
+ HTTPHeaderOssObjectACL = "X-Oss-Object-Acl"
+ HTTPHeaderOssSecurityToken = "X-Oss-Security-Token"
+ HTTPHeaderOssServerSideEncryption = "X-Oss-Server-Side-Encryption"
+ HTTPHeaderOssCopySource = "X-Oss-Copy-Source"
+ HTTPHeaderOssCopySourceRange = "X-Oss-Copy-Source-Range"
+ HTTPHeaderOssCopySourceIfMatch = "X-Oss-Copy-Source-If-Match"
+ HTTPHeaderOssCopySourceIfNoneMatch = "X-Oss-Copy-Source-If-None-Match"
+ HTTPHeaderOssCopySourceIfModifiedSince = "X-Oss-Copy-Source-If-Modified-Since"
+ HTTPHeaderOssCopySourceIfUnmodifiedSince = "X-Oss-Copy-Source-If-Unmodified-Since"
+ HTTPHeaderOssMetadataDirective = "X-Oss-Metadata-Directive"
+ HTTPHeaderOssNextAppendPosition = "X-Oss-Next-Append-Position"
+ HTTPHeaderOssRequestID = "X-Oss-Request-Id"
+ HTTPHeaderOssCRC64 = "X-Oss-Hash-Crc64ecma"
+)
+
+// 其它常量
+const (
+ MaxPartSize = 5 * 1024 * 1024 * 1024 // 文件片最大值,5GB
+ MinPartSize = 100 * 1024 // 文件片最小值,100KBß
+
+ FilePermMode = os.FileMode(0664) // 新建文件默认权限
+
+ TempFilePrefix = "oss-go-temp-" // 临时文件前缀
+ TempFileSuffix = ".temp" // 临时文件后缀
+
+ CheckpointFileSuffix = ".cp" // Checkpoint文件后缀
+
+ Version = "1.3.0" // Go sdk版本
+)
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go
new file mode 100644
index 000000000..4f0958f42
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go
@@ -0,0 +1,44 @@
+package oss
+
+import (
+ "hash"
+ "hash/crc64"
+)
+
+// digest represents the partial evaluation of a checksum.
+type digest struct {
+ crc uint64
+ tab *crc64.Table
+}
+
+// NewCRC creates a new hash.Hash64 computing the CRC-64 checksum
+// using the polynomial represented by the Table.
+func NewCRC(tab *crc64.Table, init uint64) hash.Hash64 { return &digest{init, tab} }
+
+// Size returns the number of bytes Sum will return.
+func (d *digest) Size() int { return crc64.Size }
+
+// BlockSize returns the hash's underlying block size.
+// The Write method must be able to accept any amount
+// of data, but it may operate more efficiently if all writes
+// are a multiple of the block size.
+func (d *digest) BlockSize() int { return 1 }
+
+// Reset resets the Hash to its initial state.
+func (d *digest) Reset() { d.crc = 0 }
+
+// Write (via the embedded io.Writer interface) adds more data to the running hash.
+// It never returns an error.
+func (d *digest) Write(p []byte) (n int, err error) {
+ d.crc = crc64.Update(d.crc, d.tab, p)
+ return len(p), nil
+}
+
+// Sum64 returns crc64 value.
+func (d *digest) Sum64() uint64 { return d.crc }
+
+// Sum returns hash value.
+func (d *digest) Sum(in []byte) []byte {
+ s := d.Sum64()
+ return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go
new file mode 100644
index 000000000..ec3fe8b0c
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/download.go
@@ -0,0 +1,464 @@
+package oss
+
+import (
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "os"
+ "strconv"
+)
+
+//
+// DownloadFile 分片下载文件
+//
+// objectKey object key。
+// filePath 本地文件。objectKey下载到文件。
+// partSize 本次上传文件片的大小,字节数。比如100 * 1024为每片100KB。
+// options Object的属性限制项。详见GetObject。
+//
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) DownloadFile(objectKey, filePath string, partSize int64, options ...Option) error {
+ if partSize < 1 || partSize > MaxPartSize {
+ return errors.New("oss: part size invalid range (1, 5GB]")
+ }
+
+ cpConf, err := getCpConfig(options, filePath)
+ if err != nil {
+ return err
+ }
+
+ routines := getRoutines(options)
+
+ if cpConf.IsEnable {
+ return bucket.downloadFileWithCp(objectKey, filePath, partSize, options, cpConf.FilePath, routines)
+ }
+
+ return bucket.downloadFile(objectKey, filePath, partSize, options, routines)
+}
+
+// ----- 并发无断点的下载 -----
+
+// 工作协程参数
+type downloadWorkerArg struct {
+ bucket *Bucket
+ key string
+ filePath string
+ options []Option
+ hook downloadPartHook
+}
+
+// Hook用于测试
+type downloadPartHook func(part downloadPart) error
+
+var downloadPartHooker downloadPartHook = defaultDownloadPartHook
+
+func defaultDownloadPartHook(part downloadPart) error {
+ return nil
+}
+
+// 默认ProgressListener,屏蔽GetObject的Options中ProgressListener
+type defaultDownloadProgressListener struct {
+}
+
+// ProgressChanged 静默处理
+func (listener *defaultDownloadProgressListener) ProgressChanged(event *ProgressEvent) {
+}
+
+// 工作协程
+func downloadWorker(id int, arg downloadWorkerArg, jobs <-chan downloadPart, results chan<- downloadPart, failed chan<- error, die <-chan bool) {
+ for part := range jobs {
+ if err := arg.hook(part); err != nil {
+ failed <- err
+ break
+ }
+
+ // resolve options
+ r := Range(part.Start, part.End)
+ p := Progress(&defaultDownloadProgressListener{})
+ opts := make([]Option, len(arg.options)+2)
+ // append orderly, can not be reversed!
+ opts = append(opts, arg.options...)
+ opts = append(opts, r, p)
+
+ rd, err := arg.bucket.GetObject(arg.key, opts...)
+ if err != nil {
+ failed <- err
+ break
+ }
+ defer rd.Close()
+
+ select {
+ case <-die:
+ return
+ default:
+ }
+
+ fd, err := os.OpenFile(arg.filePath, os.O_WRONLY, FilePermMode)
+ if err != nil {
+ failed <- err
+ break
+ }
+ defer fd.Close()
+
+ _, err = fd.Seek(part.Start, os.SEEK_SET)
+ if err != nil {
+ failed <- err
+ break
+ }
+
+ _, err = io.Copy(fd, rd)
+ if err != nil {
+ failed <- err
+ break
+ }
+
+ results <- part
+ }
+}
+
+// 调度协程
+func downloadScheduler(jobs chan downloadPart, parts []downloadPart) {
+ for _, part := range parts {
+ jobs <- part
+ }
+ close(jobs)
+}
+
+// 下载片
+type downloadPart struct {
+ Index int // 片序号,从0开始编号
+ Start int64 // 片起始位置
+ End int64 // 片结束位置
+}
+
+// 文件分片
+func getDownloadParts(bucket *Bucket, objectKey string, partSize int64) ([]downloadPart, error) {
+ meta, err := bucket.GetObjectDetailedMeta(objectKey)
+ if err != nil {
+ return nil, err
+ }
+
+ parts := []downloadPart{}
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ part := downloadPart{}
+ i := 0
+ for offset := int64(0); offset < objectSize; offset += partSize {
+ part.Index = i
+ part.Start = offset
+ part.End = GetPartEnd(offset, objectSize, partSize)
+ parts = append(parts, part)
+ i++
+ }
+ return parts, nil
+}
+
+// 文件大小
+func getObjectBytes(parts []downloadPart) int64 {
+ var ob int64
+ for _, part := range parts {
+ ob += (part.End - part.Start + 1)
+ }
+ return ob
+}
+
+// 并发无断点续传的下载
+func (bucket Bucket) downloadFile(objectKey, filePath string, partSize int64, options []Option, routines int) error {
+ tempFilePath := filePath + TempFileSuffix
+ listener := getProgressListener(options)
+
+ // 如果文件不存在则创建,存在不清空,下载分片会重写文件内容
+ fd, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE, FilePermMode)
+ if err != nil {
+ return err
+ }
+ fd.Close()
+
+ // 分割文件
+ parts, err := getDownloadParts(&bucket, objectKey, partSize)
+ if err != nil {
+ return err
+ }
+
+ jobs := make(chan downloadPart, len(parts))
+ results := make(chan downloadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getObjectBytes(parts)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := downloadWorkerArg{&bucket, objectKey, tempFilePath, options, downloadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go downloadWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发上传分片
+ go downloadScheduler(jobs, parts)
+
+ // 等待分片下载完成
+ completed := 0
+ ps := make([]downloadPart, len(parts))
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ ps[part.Index] = part
+ completedBytes += (part.End - part.Start + 1)
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+
+ return os.Rename(tempFilePath, filePath)
+}
+
+// ----- 并发有断点的下载 -----
+
+const downloadCpMagic = "92611BED-89E2-46B6-89E5-72F273D4B0A3"
+
+type downloadCheckpoint struct {
+ Magic string // magic
+ MD5 string // cp内容的MD5
+ FilePath string // 本地文件
+ Object string // key
+ ObjStat objectStat // 文件状态
+ Parts []downloadPart // 全部分片
+ PartStat []bool // 分片下载是否完成
+}
+
+type objectStat struct {
+ Size int64 // 大小
+ LastModified string // 最后修改时间
+ Etag string // etag
+}
+
+// CP数据是否有效,CP有效且Object没有更新时有效
+func (cp downloadCheckpoint) isValid(bucket *Bucket, objectKey string) (bool, error) {
+ // 比较CP的Magic及MD5
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != downloadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ // 确认object没有更新
+ meta, err := bucket.GetObjectDetailedMeta(objectKey)
+ if err != nil {
+ return false, err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return false, err
+ }
+
+ // 比较Object的大小/最后修改时间/etag
+ if cp.ObjStat.Size != objectSize ||
+ cp.ObjStat.LastModified != meta.Get(HTTPHeaderLastModified) ||
+ cp.ObjStat.Etag != meta.Get(HTTPHeaderEtag) {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// 从文件中load
+func (cp *downloadCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// dump到文件
+func (cp *downloadCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // 计算MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // 序列化
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// 未完成的分片
+func (cp downloadCheckpoint) todoParts() []downloadPart {
+ dps := []downloadPart{}
+ for i, ps := range cp.PartStat {
+ if !ps {
+ dps = append(dps, cp.Parts[i])
+ }
+ }
+ return dps
+}
+
+// 完成的字节数
+func (cp downloadCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for i, part := range cp.Parts {
+ if cp.PartStat[i] {
+ completedBytes += (part.End - part.Start + 1)
+ }
+ }
+ return completedBytes
+}
+
+// 初始化下载任务
+func (cp *downloadCheckpoint) prepare(bucket *Bucket, objectKey, filePath string, partSize int64) error {
+ // cp
+ cp.Magic = downloadCpMagic
+ cp.FilePath = filePath
+ cp.Object = objectKey
+
+ // object
+ meta, err := bucket.GetObjectDetailedMeta(objectKey)
+ if err != nil {
+ return err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return err
+ }
+
+ cp.ObjStat.Size = objectSize
+ cp.ObjStat.LastModified = meta.Get(HTTPHeaderLastModified)
+ cp.ObjStat.Etag = meta.Get(HTTPHeaderEtag)
+
+ // parts
+ cp.Parts, err = getDownloadParts(bucket, objectKey, partSize)
+ if err != nil {
+ return err
+ }
+ cp.PartStat = make([]bool, len(cp.Parts))
+ for i := range cp.PartStat {
+ cp.PartStat[i] = false
+ }
+
+ return nil
+}
+
+func (cp *downloadCheckpoint) complete(cpFilePath, downFilepath string) error {
+ os.Remove(cpFilePath)
+ return os.Rename(downFilepath, cp.FilePath)
+}
+
+// 并发带断点的下载
+func (bucket Bucket) downloadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int) error {
+ tempFilePath := filePath + TempFileSuffix
+ listener := getProgressListener(options)
+
+ // LOAD CP数据
+ dcp := downloadCheckpoint{}
+ err := dcp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // LOAD出错或数据无效重新初始化下载
+ valid, err := dcp.isValid(&bucket, objectKey)
+ if err != nil || !valid {
+ if err = dcp.prepare(&bucket, objectKey, filePath, partSize); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ // 如果文件不存在则创建,存在不清空,下载分片会重写文件内容
+ fd, err := os.OpenFile(tempFilePath, os.O_WRONLY|os.O_CREATE, FilePermMode)
+ if err != nil {
+ return err
+ }
+ fd.Close()
+
+ // 未完成的分片
+ parts := dcp.todoParts()
+ jobs := make(chan downloadPart, len(parts))
+ results := make(chan downloadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := dcp.getCompletedBytes()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, dcp.ObjStat.Size)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := downloadWorkerArg{&bucket, objectKey, tempFilePath, options, downloadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go downloadWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发下载分片
+ go downloadScheduler(jobs, parts)
+
+ // 等待分片下载完成
+ completed := 0
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ dcp.PartStat[part.Index] = true
+ dcp.dump(cpFilePath)
+ completedBytes += (part.End - part.Start + 1)
+ event = newProgressEvent(TransferDataEvent, completedBytes, dcp.ObjStat.Size)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, dcp.ObjStat.Size)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, dcp.ObjStat.Size)
+ publishProgress(listener, event)
+
+ return dcp.complete(cpFilePath, tempFilePath)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go
new file mode 100644
index 000000000..5c06df92b
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/error.go
@@ -0,0 +1,82 @@
+package oss
+
+import (
+ "encoding/xml"
+ "fmt"
+ "net/http"
+ "strings"
+)
+
+// ServiceError contains fields of the error response from Oss Service REST API.
+type ServiceError struct {
+ XMLName xml.Name `xml:"Error"`
+ Code string `xml:"Code"` // OSS返回给用户的错误码
+ Message string `xml:"Message"` // OSS给出的详细错误信息
+ RequestID string `xml:"RequestId"` // 用于唯一标识该次请求的UUID
+ HostID string `xml:"HostId"` // 用于标识访问的OSS集群
+ RawMessage string // OSS返回的原始消息内容
+ StatusCode int // HTTP状态码
+}
+
+// Implement interface error
+func (e ServiceError) Error() string {
+ return fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s",
+ e.StatusCode, e.Code, e.Message, e.RequestID)
+}
+
+// UnexpectedStatusCodeError is returned when a storage service responds with neither an error
+// nor with an HTTP status code indicating success.
+type UnexpectedStatusCodeError struct {
+ allowed []int // 预期OSS返回HTTP状态码
+ got int // OSS实际返回HTTP状态码
+}
+
+// Implement interface error
+func (e UnexpectedStatusCodeError) Error() string {
+ s := func(i int) string { return fmt.Sprintf("%d %s", i, http.StatusText(i)) }
+
+ got := s(e.got)
+ expected := []string{}
+ for _, v := range e.allowed {
+ expected = append(expected, s(v))
+ }
+ return fmt.Sprintf("oss: status code from service response is %s; was expecting %s",
+ got, strings.Join(expected, " or "))
+}
+
+// Got is the actual status code returned by oss.
+func (e UnexpectedStatusCodeError) Got() int {
+ return e.got
+}
+
+// checkRespCode returns UnexpectedStatusError if the given response code is not
+// one of the allowed status codes; otherwise nil.
+func checkRespCode(respCode int, allowed []int) error {
+ for _, v := range allowed {
+ if respCode == v {
+ return nil
+ }
+ }
+ return UnexpectedStatusCodeError{allowed, respCode}
+}
+
+// CRCCheckError is returned when crc check is inconsistent between client and server
+type CRCCheckError struct {
+ clientCRC uint64 // 客户端计算的CRC64值
+ serverCRC uint64 // 服务端计算的CRC64值
+ operation string // 上传操作,如PutObject/AppendObject/UploadPart等
+ requestID string // 本次操作的RequestID
+}
+
+// Implement interface error
+func (e CRCCheckError) Error() string {
+ return fmt.Sprintf("oss: the crc of %s is inconsistent, client %d but server %d; request id is %s",
+ e.operation, e.clientCRC, e.serverCRC, e.requestID)
+}
+
+func checkCRC(resp *Response, operation string) error {
+ if resp.Headers.Get(HTTPHeaderOssCRC64) == "" || resp.ClientCRC == resp.ServerCRC {
+ return nil
+ }
+ return CRCCheckError{resp.ClientCRC, resp.ServerCRC, operation, resp.Headers.Get(HTTPHeaderOssRequestID)}
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go
new file mode 100644
index 000000000..e2ed9ce10
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go
@@ -0,0 +1,245 @@
+package oss
+
+import (
+ "mime"
+ "path"
+ "strings"
+)
+
+var extToMimeType = map[string]string{
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ ".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ ".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+ ".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ ".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ ".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
+ ".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
+ ".apk": "application/vnd.android.package-archive",
+ ".hqx": "application/mac-binhex40",
+ ".cpt": "application/mac-compactpro",
+ ".doc": "application/msword",
+ ".ogg": "application/ogg",
+ ".pdf": "application/pdf",
+ ".rtf": "text/rtf",
+ ".mif": "application/vnd.mif",
+ ".xls": "application/vnd.ms-excel",
+ ".ppt": "application/vnd.ms-powerpoint",
+ ".odc": "application/vnd.oasis.opendocument.chart",
+ ".odb": "application/vnd.oasis.opendocument.database",
+ ".odf": "application/vnd.oasis.opendocument.formula",
+ ".odg": "application/vnd.oasis.opendocument.graphics",
+ ".otg": "application/vnd.oasis.opendocument.graphics-template",
+ ".odi": "application/vnd.oasis.opendocument.image",
+ ".odp": "application/vnd.oasis.opendocument.presentation",
+ ".otp": "application/vnd.oasis.opendocument.presentation-template",
+ ".ods": "application/vnd.oasis.opendocument.spreadsheet",
+ ".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+ ".odt": "application/vnd.oasis.opendocument.text",
+ ".odm": "application/vnd.oasis.opendocument.text-master",
+ ".ott": "application/vnd.oasis.opendocument.text-template",
+ ".oth": "application/vnd.oasis.opendocument.text-web",
+ ".sxw": "application/vnd.sun.xml.writer",
+ ".stw": "application/vnd.sun.xml.writer.template",
+ ".sxc": "application/vnd.sun.xml.calc",
+ ".stc": "application/vnd.sun.xml.calc.template",
+ ".sxd": "application/vnd.sun.xml.draw",
+ ".std": "application/vnd.sun.xml.draw.template",
+ ".sxi": "application/vnd.sun.xml.impress",
+ ".sti": "application/vnd.sun.xml.impress.template",
+ ".sxg": "application/vnd.sun.xml.writer.global",
+ ".sxm": "application/vnd.sun.xml.math",
+ ".sis": "application/vnd.symbian.install",
+ ".wbxml": "application/vnd.wap.wbxml",
+ ".wmlc": "application/vnd.wap.wmlc",
+ ".wmlsc": "application/vnd.wap.wmlscriptc",
+ ".bcpio": "application/x-bcpio",
+ ".torrent": "application/x-bittorrent",
+ ".bz2": "application/x-bzip2",
+ ".vcd": "application/x-cdlink",
+ ".pgn": "application/x-chess-pgn",
+ ".cpio": "application/x-cpio",
+ ".csh": "application/x-csh",
+ ".dvi": "application/x-dvi",
+ ".spl": "application/x-futuresplash",
+ ".gtar": "application/x-gtar",
+ ".hdf": "application/x-hdf",
+ ".jar": "application/x-java-archive",
+ ".jnlp": "application/x-java-jnlp-file",
+ ".js": "application/x-javascript",
+ ".ksp": "application/x-kspread",
+ ".chrt": "application/x-kchart",
+ ".kil": "application/x-killustrator",
+ ".latex": "application/x-latex",
+ ".rpm": "application/x-rpm",
+ ".sh": "application/x-sh",
+ ".shar": "application/x-shar",
+ ".swf": "application/x-shockwave-flash",
+ ".sit": "application/x-stuffit",
+ ".sv4cpio": "application/x-sv4cpio",
+ ".sv4crc": "application/x-sv4crc",
+ ".tar": "application/x-tar",
+ ".tcl": "application/x-tcl",
+ ".tex": "application/x-tex",
+ ".man": "application/x-troff-man",
+ ".me": "application/x-troff-me",
+ ".ms": "application/x-troff-ms",
+ ".ustar": "application/x-ustar",
+ ".src": "application/x-wais-source",
+ ".zip": "application/zip",
+ ".m3u": "audio/x-mpegurl",
+ ".ra": "audio/x-pn-realaudio",
+ ".wav": "audio/x-wav",
+ ".wma": "audio/x-ms-wma",
+ ".wax": "audio/x-ms-wax",
+ ".pdb": "chemical/x-pdb",
+ ".xyz": "chemical/x-xyz",
+ ".bmp": "image/bmp",
+ ".gif": "image/gif",
+ ".ief": "image/ief",
+ ".png": "image/png",
+ ".wbmp": "image/vnd.wap.wbmp",
+ ".ras": "image/x-cmu-raster",
+ ".pnm": "image/x-portable-anymap",
+ ".pbm": "image/x-portable-bitmap",
+ ".pgm": "image/x-portable-graymap",
+ ".ppm": "image/x-portable-pixmap",
+ ".rgb": "image/x-rgb",
+ ".xbm": "image/x-xbitmap",
+ ".xpm": "image/x-xpixmap",
+ ".xwd": "image/x-xwindowdump",
+ ".css": "text/css",
+ ".rtx": "text/richtext",
+ ".tsv": "text/tab-separated-values",
+ ".jad": "text/vnd.sun.j2me.app-descriptor",
+ ".wml": "text/vnd.wap.wml",
+ ".wmls": "text/vnd.wap.wmlscript",
+ ".etx": "text/x-setext",
+ ".mxu": "video/vnd.mpegurl",
+ ".flv": "video/x-flv",
+ ".wm": "video/x-ms-wm",
+ ".wmv": "video/x-ms-wmv",
+ ".wmx": "video/x-ms-wmx",
+ ".wvx": "video/x-ms-wvx",
+ ".avi": "video/x-msvideo",
+ ".movie": "video/x-sgi-movie",
+ ".ice": "x-conference/x-cooltalk",
+ ".3gp": "video/3gpp",
+ ".ai": "application/postscript",
+ ".aif": "audio/x-aiff",
+ ".aifc": "audio/x-aiff",
+ ".aiff": "audio/x-aiff",
+ ".asc": "text/plain",
+ ".atom": "application/atom+xml",
+ ".au": "audio/basic",
+ ".bin": "application/octet-stream",
+ ".cdf": "application/x-netcdf",
+ ".cgm": "image/cgm",
+ ".class": "application/octet-stream",
+ ".dcr": "application/x-director",
+ ".dif": "video/x-dv",
+ ".dir": "application/x-director",
+ ".djv": "image/vnd.djvu",
+ ".djvu": "image/vnd.djvu",
+ ".dll": "application/octet-stream",
+ ".dmg": "application/octet-stream",
+ ".dms": "application/octet-stream",
+ ".dtd": "application/xml-dtd",
+ ".dv": "video/x-dv",
+ ".dxr": "application/x-director",
+ ".eps": "application/postscript",
+ ".exe": "application/octet-stream",
+ ".ez": "application/andrew-inset",
+ ".gram": "application/srgs",
+ ".grxml": "application/srgs+xml",
+ ".gz": "application/x-gzip",
+ ".htm": "text/html",
+ ".html": "text/html",
+ ".ico": "image/x-icon",
+ ".ics": "text/calendar",
+ ".ifb": "text/calendar",
+ ".iges": "model/iges",
+ ".igs": "model/iges",
+ ".jp2": "image/jp2",
+ ".jpe": "image/jpeg",
+ ".jpeg": "image/jpeg",
+ ".jpg": "image/jpeg",
+ ".kar": "audio/midi",
+ ".lha": "application/octet-stream",
+ ".lzh": "application/octet-stream",
+ ".m4a": "audio/mp4a-latm",
+ ".m4p": "audio/mp4a-latm",
+ ".m4u": "video/vnd.mpegurl",
+ ".m4v": "video/x-m4v",
+ ".mac": "image/x-macpaint",
+ ".mathml": "application/mathml+xml",
+ ".mesh": "model/mesh",
+ ".mid": "audio/midi",
+ ".midi": "audio/midi",
+ ".mov": "video/quicktime",
+ ".mp2": "audio/mpeg",
+ ".mp3": "audio/mpeg",
+ ".mp4": "video/mp4",
+ ".mpe": "video/mpeg",
+ ".mpeg": "video/mpeg",
+ ".mpg": "video/mpeg",
+ ".mpga": "audio/mpeg",
+ ".msh": "model/mesh",
+ ".nc": "application/x-netcdf",
+ ".oda": "application/oda",
+ ".ogv": "video/ogv",
+ ".pct": "image/pict",
+ ".pic": "image/pict",
+ ".pict": "image/pict",
+ ".pnt": "image/x-macpaint",
+ ".pntg": "image/x-macpaint",
+ ".ps": "application/postscript",
+ ".qt": "video/quicktime",
+ ".qti": "image/x-quicktime",
+ ".qtif": "image/x-quicktime",
+ ".ram": "audio/x-pn-realaudio",
+ ".rdf": "application/rdf+xml",
+ ".rm": "application/vnd.rn-realmedia",
+ ".roff": "application/x-troff",
+ ".sgm": "text/sgml",
+ ".sgml": "text/sgml",
+ ".silo": "model/mesh",
+ ".skd": "application/x-koan",
+ ".skm": "application/x-koan",
+ ".skp": "application/x-koan",
+ ".skt": "application/x-koan",
+ ".smi": "application/smil",
+ ".smil": "application/smil",
+ ".snd": "audio/basic",
+ ".so": "application/octet-stream",
+ ".svg": "image/svg+xml",
+ ".t": "application/x-troff",
+ ".texi": "application/x-texinfo",
+ ".texinfo": "application/x-texinfo",
+ ".tif": "image/tiff",
+ ".tiff": "image/tiff",
+ ".tr": "application/x-troff",
+ ".txt": "text/plain",
+ ".vrml": "model/vrml",
+ ".vxml": "application/voicexml+xml",
+ ".webm": "video/webm",
+ ".wrl": "model/vrml",
+ ".xht": "application/xhtml+xml",
+ ".xhtml": "application/xhtml+xml",
+ ".xml": "application/xml",
+ ".xsl": "application/xml",
+ ".xslt": "application/xslt+xml",
+ ".xul": "application/vnd.mozilla.xul+xml",
+}
+
+// TypeByExtension returns the MIME type associated with the file extension ext.
+// 获取文件类型,选项ContentType使用
+func TypeByExtension(filePath string) string {
+ typ := mime.TypeByExtension(path.Ext(filePath))
+ if typ == "" {
+ typ = extToMimeType[strings.ToLower(path.Ext(filePath))]
+ }
+ return typ
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go
new file mode 100644
index 000000000..7c71b0181
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/model.go
@@ -0,0 +1,60 @@
+package oss
+
+import (
+ "hash"
+ "io"
+ "net/http"
+)
+
+// Response Http response from oss
+type Response struct {
+ StatusCode int
+ Headers http.Header
+ Body io.ReadCloser
+ ClientCRC uint64
+ ServerCRC uint64
+}
+
+// PutObjectRequest The request of DoPutObject
+type PutObjectRequest struct {
+ ObjectKey string
+ Reader io.Reader
+}
+
+// GetObjectRequest The request of DoGetObject
+type GetObjectRequest struct {
+ ObjectKey string
+}
+
+// GetObjectResult The result of DoGetObject
+type GetObjectResult struct {
+ Response *Response
+ ClientCRC hash.Hash64
+ ServerCRC uint64
+}
+
+// AppendObjectRequest The requtest of DoAppendObject
+type AppendObjectRequest struct {
+ ObjectKey string
+ Reader io.Reader
+ Position int64
+}
+
+// AppendObjectResult The result of DoAppendObject
+type AppendObjectResult struct {
+ NextPosition int64
+ CRC uint64
+}
+
+// UploadPartRequest The request of DoUploadPart
+type UploadPartRequest struct {
+ InitResult *InitiateMultipartUploadResult
+ Reader io.Reader
+ PartSize int64
+ PartNumber int
+}
+
+// UploadPartResult The result of DoUploadPart
+type UploadPartResult struct {
+ Part UploadPart
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go
new file mode 100644
index 000000000..a33b48870
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multicopy.go
@@ -0,0 +1,461 @@
+package oss
+
+import (
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+)
+
+//
+// CopyFile 分片复制文件
+//
+// srcBucketName 源Bucket名称。
+// srcObjectKey 源Object名称。
+// destObjectKey 目标Object名称。目标Bucket名称为Bucket.BucketName。
+// partSize 复制文件片的大小,字节数。比如100 * 1024为每片100KB。
+// options Object的属性限制项。详见InitiateMultipartUpload。
+//
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) CopyFile(srcBucketName, srcObjectKey, destObjectKey string, partSize int64, options ...Option) error {
+ destBucketName := bucket.BucketName
+ if partSize < MinPartSize || partSize > MaxPartSize {
+ return errors.New("oss: part size invalid range (1024KB, 5GB]")
+ }
+
+ cpConf, err := getCpConfig(options, filepath.Base(destObjectKey))
+ if err != nil {
+ return err
+ }
+
+ routines := getRoutines(options)
+
+ if cpConf.IsEnable {
+ return bucket.copyFileWithCp(srcBucketName, srcObjectKey, destBucketName, destObjectKey,
+ partSize, options, cpConf.FilePath, routines)
+ }
+
+ return bucket.copyFile(srcBucketName, srcObjectKey, destBucketName, destObjectKey,
+ partSize, options, routines)
+}
+
+// ----- 并发无断点的下载 -----
+
+// 工作协程参数
+type copyWorkerArg struct {
+ bucket *Bucket
+ imur InitiateMultipartUploadResult
+ srcBucketName string
+ srcObjectKey string
+ options []Option
+ hook copyPartHook
+}
+
+// Hook用于测试
+type copyPartHook func(part copyPart) error
+
+var copyPartHooker copyPartHook = defaultCopyPartHook
+
+func defaultCopyPartHook(part copyPart) error {
+ return nil
+}
+
+// 工作协程
+func copyWorker(id int, arg copyWorkerArg, jobs <-chan copyPart, results chan<- UploadPart, failed chan<- error, die <-chan bool) {
+ for chunk := range jobs {
+ if err := arg.hook(chunk); err != nil {
+ failed <- err
+ break
+ }
+ chunkSize := chunk.End - chunk.Start + 1
+ part, err := arg.bucket.UploadPartCopy(arg.imur, arg.srcBucketName, arg.srcObjectKey,
+ chunk.Start, chunkSize, chunk.Number, arg.options...)
+ if err != nil {
+ failed <- err
+ break
+ }
+ select {
+ case <-die:
+ return
+ default:
+ }
+ results <- part
+ }
+}
+
+// 调度协程
+func copyScheduler(jobs chan copyPart, parts []copyPart) {
+ for _, part := range parts {
+ jobs <- part
+ }
+ close(jobs)
+}
+
+// 分片
+type copyPart struct {
+ Number int // 片序号[1, 10000]
+ Start int64 // 片起始位置
+ End int64 // 片结束位置
+}
+
+// 文件分片
+func getCopyParts(bucket *Bucket, objectKey string, partSize int64) ([]copyPart, error) {
+ meta, err := bucket.GetObjectDetailedMeta(objectKey)
+ if err != nil {
+ return nil, err
+ }
+
+ parts := []copyPart{}
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ part := copyPart{}
+ i := 0
+ for offset := int64(0); offset < objectSize; offset += partSize {
+ part.Number = i + 1
+ part.Start = offset
+ part.End = GetPartEnd(offset, objectSize, partSize)
+ parts = append(parts, part)
+ i++
+ }
+ return parts, nil
+}
+
+// 获取源文件大小
+func getSrcObjectBytes(parts []copyPart) int64 {
+ var ob int64
+ for _, part := range parts {
+ ob += (part.End - part.Start + 1)
+ }
+ return ob
+}
+
+// 并发无断点续传的下载
+func (bucket Bucket) copyFile(srcBucketName, srcObjectKey, destBucketName, destObjectKey string,
+ partSize int64, options []Option, routines int) error {
+ descBucket, err := bucket.Client.Bucket(destBucketName)
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ listener := getProgressListener(options)
+
+ // 分割文件
+ parts, err := getCopyParts(srcBucket, srcObjectKey, partSize)
+ if err != nil {
+ return err
+ }
+
+ // 初始化上传任务
+ imur, err := descBucket.InitiateMultipartUpload(destObjectKey, options...)
+ if err != nil {
+ return err
+ }
+
+ jobs := make(chan copyPart, len(parts))
+ results := make(chan UploadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getSrcObjectBytes(parts)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := copyWorkerArg{descBucket, imur, srcBucketName, srcObjectKey, options, copyPartHooker}
+ for w := 1; w <= routines; w++ {
+ go copyWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发上传分片
+ go copyScheduler(jobs, parts)
+
+ // 等待分片下载完成
+ completed := 0
+ ups := make([]UploadPart, len(parts))
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ ups[part.PartNumber-1] = part
+ completedBytes += (parts[part.PartNumber-1].End - parts[part.PartNumber-1].Start + 1)
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ descBucket.AbortMultipartUpload(imur)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+
+ // 提交任务
+ _, err = descBucket.CompleteMultipartUpload(imur, ups)
+ if err != nil {
+ bucket.AbortMultipartUpload(imur)
+ return err
+ }
+ return nil
+}
+
+// ----- 并发有断点的下载 -----
+
+const copyCpMagic = "84F1F18C-FF1D-403B-A1D8-9DEB5F65910A"
+
+type copyCheckpoint struct {
+ Magic string // magic
+ MD5 string // cp内容的MD5
+ SrcBucketName string // 源Bucket
+ SrcObjectKey string // 源Object
+ DestBucketName string // 目标Bucket
+ DestObjectKey string // 目标Bucket
+ CopyID string // copy id
+ ObjStat objectStat // 文件状态
+ Parts []copyPart // 全部分片
+ CopyParts []UploadPart // 分片上传成功后的返回值
+ PartStat []bool // 分片下载是否完成
+}
+
+// CP数据是否有效,CP有效且Object没有更新时有效
+func (cp copyCheckpoint) isValid(bucket *Bucket, objectKey string) (bool, error) {
+ // 比较CP的Magic及MD5
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != downloadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ // 确认object没有更新
+ meta, err := bucket.GetObjectDetailedMeta(objectKey)
+ if err != nil {
+ return false, err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return false, err
+ }
+
+ // 比较Object的大小/最后修改时间/etag
+ if cp.ObjStat.Size != objectSize ||
+ cp.ObjStat.LastModified != meta.Get(HTTPHeaderLastModified) ||
+ cp.ObjStat.Etag != meta.Get(HTTPHeaderEtag) {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// 从文件中load
+func (cp *copyCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// 更新分片状态
+func (cp *copyCheckpoint) update(part UploadPart) {
+ cp.CopyParts[part.PartNumber-1] = part
+ cp.PartStat[part.PartNumber-1] = true
+}
+
+// dump到文件
+func (cp *copyCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // 计算MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // 序列化
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// 未完成的分片
+func (cp copyCheckpoint) todoParts() []copyPart {
+ dps := []copyPart{}
+ for i, ps := range cp.PartStat {
+ if !ps {
+ dps = append(dps, cp.Parts[i])
+ }
+ }
+ return dps
+}
+
+// 完成的字节数
+func (cp copyCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for i, part := range cp.Parts {
+ if cp.PartStat[i] {
+ completedBytes += (part.End - part.Start + 1)
+ }
+ }
+ return completedBytes
+}
+
+// 初始化下载任务
+func (cp *copyCheckpoint) prepare(srcBucket *Bucket, srcObjectKey string, destBucket *Bucket, destObjectKey string,
+ partSize int64, options []Option) error {
+ // cp
+ cp.Magic = copyCpMagic
+ cp.SrcBucketName = srcBucket.BucketName
+ cp.SrcObjectKey = srcObjectKey
+ cp.DestBucketName = destBucket.BucketName
+ cp.DestObjectKey = destObjectKey
+
+ // object
+ meta, err := srcBucket.GetObjectDetailedMeta(srcObjectKey)
+ if err != nil {
+ return err
+ }
+
+ objectSize, err := strconv.ParseInt(meta.Get(HTTPHeaderContentLength), 10, 0)
+ if err != nil {
+ return err
+ }
+
+ cp.ObjStat.Size = objectSize
+ cp.ObjStat.LastModified = meta.Get(HTTPHeaderLastModified)
+ cp.ObjStat.Etag = meta.Get(HTTPHeaderEtag)
+
+ // parts
+ cp.Parts, err = getCopyParts(srcBucket, srcObjectKey, partSize)
+ if err != nil {
+ return err
+ }
+ cp.PartStat = make([]bool, len(cp.Parts))
+ for i := range cp.PartStat {
+ cp.PartStat[i] = false
+ }
+ cp.CopyParts = make([]UploadPart, len(cp.Parts))
+
+ // init copy
+ imur, err := destBucket.InitiateMultipartUpload(destObjectKey, options...)
+ if err != nil {
+ return err
+ }
+ cp.CopyID = imur.UploadID
+
+ return nil
+}
+
+func (cp *copyCheckpoint) complete(bucket *Bucket, parts []UploadPart, cpFilePath string) error {
+ imur := InitiateMultipartUploadResult{Bucket: cp.DestBucketName,
+ Key: cp.DestObjectKey, UploadID: cp.CopyID}
+ _, err := bucket.CompleteMultipartUpload(imur, parts)
+ if err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ return err
+}
+
+// 并发带断点的下载
+func (bucket Bucket) copyFileWithCp(srcBucketName, srcObjectKey, destBucketName, destObjectKey string,
+ partSize int64, options []Option, cpFilePath string, routines int) error {
+ descBucket, err := bucket.Client.Bucket(destBucketName)
+ srcBucket, err := bucket.Client.Bucket(srcBucketName)
+ listener := getProgressListener(options)
+
+ // LOAD CP数据
+ ccp := copyCheckpoint{}
+ err = ccp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // LOAD出错或数据无效重新初始化下载
+ valid, err := ccp.isValid(srcBucket, srcObjectKey)
+ if err != nil || !valid {
+ if err = ccp.prepare(srcBucket, srcObjectKey, descBucket, destObjectKey, partSize, options); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ // 未完成的分片
+ parts := ccp.todoParts()
+ imur := InitiateMultipartUploadResult{
+ Bucket: destBucketName,
+ Key: destObjectKey,
+ UploadID: ccp.CopyID}
+
+ jobs := make(chan copyPart, len(parts))
+ results := make(chan UploadPart, len(parts))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := ccp.getCompletedBytes()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, ccp.ObjStat.Size)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := copyWorkerArg{descBucket, imur, srcBucketName, srcObjectKey, options, copyPartHooker}
+ for w := 1; w <= routines; w++ {
+ go copyWorker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发下载分片
+ go copyScheduler(jobs, parts)
+
+ // 等待分片下载完成
+ completed := 0
+ for completed < len(parts) {
+ select {
+ case part := <-results:
+ completed++
+ ccp.update(part)
+ ccp.dump(cpFilePath)
+ completedBytes += (parts[part.PartNumber-1].End - parts[part.PartNumber-1].Start + 1)
+ event = newProgressEvent(TransferDataEvent, completedBytes, ccp.ObjStat.Size)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, ccp.ObjStat.Size)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(parts) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, ccp.ObjStat.Size)
+ publishProgress(listener, event)
+
+ return ccp.complete(descBucket, ccp.CopyParts, cpFilePath)
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go
new file mode 100644
index 000000000..d06c79160
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/multipart.go
@@ -0,0 +1,281 @@
+package oss
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "net/http"
+ "os"
+ "sort"
+ "strconv"
+)
+
+//
+// InitiateMultipartUpload 初始化分片上传任务。
+//
+// objectKey Object名称。
+// options 上传时可以指定Object的属性,可选属性有CacheControl、ContentDisposition、ContentEncoding、Expires、
+// ServerSideEncryption、Meta,具体含义请参考
+// https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/InitiateMultipartUpload.html
+//
+// InitiateMultipartUploadResult 初始化后操作成功的返回值,用于后面的UploadPartFromFile、UploadPartCopy等操作。error为nil时有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) InitiateMultipartUpload(objectKey string, options ...Option) (InitiateMultipartUploadResult, error) {
+ var imur InitiateMultipartUploadResult
+ opts := addContentType(options, objectKey)
+ resp, err := bucket.do("POST", objectKey, "uploads", "uploads", opts, nil, nil)
+ if err != nil {
+ return imur, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &imur)
+ return imur, err
+}
+
+//
+// UploadPart 上传分片。
+//
+// 初始化一个Multipart Upload之后,可以根据指定的Object名和Upload ID来分片(Part)上传数据。
+// 每一个上传的Part都有一个标识它的号码(part number,范围是1~10000)。对于同一个Upload ID,
+// 该号码不但唯一标识这一片数据,也标识了这片数据在整个文件内的相对位置。如果您用同一个part号码,上传了新的数据,
+// 那么OSS上已有的这个号码的Part数据将被覆盖。除了最后一片Part以外,其他的part最小为100KB;
+// 最后一片Part没有大小限制。
+//
+// imur InitiateMultipartUpload成功后的返回值。
+// reader io.Reader 需要分片上传的reader。
+// size 本次上传片Part的大小。
+// partNumber 本次上传片(Part)的编号,范围是1~10000。如果超出范围,OSS将返回InvalidArgument错误。
+//
+// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片编号,即传入参数partNumber;
+// ETag及上传数据的MD5。error为nil时有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) UploadPart(imur InitiateMultipartUploadResult, reader io.Reader,
+ partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ request := &UploadPartRequest{
+ InitResult: &imur,
+ Reader: reader,
+ PartSize: partSize,
+ PartNumber: partNumber,
+ }
+
+ result, err := bucket.DoUploadPart(request, options)
+
+ return result.Part, err
+}
+
+//
+// UploadPartFromFile 上传分片。
+//
+// imur InitiateMultipartUpload成功后的返回值。
+// filePath 需要分片上传的本地文件。
+// startPosition 本次上传文件片的起始位置。
+// partSize 本次上传文件片的大小。
+// partNumber 本次上传文件片的编号,范围是1~10000。
+//
+// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片编号,传入参数partNumber;
+// ETag上传数据的MD5。error为nil时有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) UploadPartFromFile(imur InitiateMultipartUploadResult, filePath string,
+ startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ var part = UploadPart{}
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return part, err
+ }
+ defer fd.Close()
+ fd.Seek(startPosition, os.SEEK_SET)
+
+ request := &UploadPartRequest{
+ InitResult: &imur,
+ Reader: fd,
+ PartSize: partSize,
+ PartNumber: partNumber,
+ }
+
+ result, err := bucket.DoUploadPart(request, options)
+
+ return result.Part, err
+}
+
+//
+// DoUploadPart 上传分片。
+//
+// request 上传分片请求。
+//
+// UploadPartResult 上传分片请求返回值。
+// error 操作无错误为nil,非nil为错误信息。
+//
+func (bucket Bucket) DoUploadPart(request *UploadPartRequest, options []Option) (*UploadPartResult, error) {
+ listener := getProgressListener(options)
+ params := "partNumber=" + strconv.Itoa(request.PartNumber) + "&uploadId=" + request.InitResult.UploadID
+ opts := []Option{ContentLength(request.PartSize)}
+ resp, err := bucket.do("PUT", request.InitResult.Key, params, params, opts,
+ &io.LimitedReader{R: request.Reader, N: request.PartSize}, listener)
+ if err != nil {
+ return &UploadPartResult{}, err
+ }
+ defer resp.Body.Close()
+
+ part := UploadPart{
+ ETag: resp.Headers.Get(HTTPHeaderEtag),
+ PartNumber: request.PartNumber,
+ }
+
+ if bucket.getConfig().IsEnableCRC {
+ err = checkCRC(resp, "DoUploadPart")
+ if err != nil {
+ return &UploadPartResult{part}, err
+ }
+ }
+
+ return &UploadPartResult{part}, nil
+}
+
+//
+// UploadPartCopy 拷贝分片。
+//
+// imur InitiateMultipartUpload成功后的返回值。
+// copySrc 源Object名称。
+// startPosition 本次拷贝片(Part)在源Object的起始位置。
+// partSize 本次拷贝片的大小。
+// partNumber 本次拷贝片的编号,范围是1~10000。如果超出范围,OSS将返回InvalidArgument错误。
+// options copy时源Object的限制条件,满足限制条件时copy,不满足时返回错误。可选条件有CopySourceIfMatch、
+// CopySourceIfNoneMatch、CopySourceIfModifiedSince CopySourceIfUnmodifiedSince,具体含义请参看
+// https://help.aliyun.com/document_detail/oss/api-reference/multipart-upload/UploadPartCopy.html
+//
+// UploadPart 上传成功的返回值,两个成员PartNumber、ETag。PartNumber片(Part)编号,即传入参数partNumber;
+// ETag及上传数据的MD5。error为nil时有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) UploadPartCopy(imur InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
+ startPosition, partSize int64, partNumber int, options ...Option) (UploadPart, error) {
+ var out UploadPartCopyResult
+ var part UploadPart
+
+ opts := []Option{CopySource(srcBucketName, srcObjectKey),
+ CopySourceRange(startPosition, partSize)}
+ opts = append(opts, options...)
+ params := "partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + imur.UploadID
+ resp, err := bucket.do("PUT", imur.Key, params, params, opts, nil, nil)
+ if err != nil {
+ return part, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return part, err
+ }
+ part.ETag = out.ETag
+ part.PartNumber = partNumber
+
+ return part, nil
+}
+
+//
+// CompleteMultipartUpload 提交分片上传任务。
+//
+// imur InitiateMultipartUpload的返回值。
+// parts UploadPart/UploadPartFromFile/UploadPartCopy返回值组成的数组。
+//
+// CompleteMultipartUploadResponse 操作成功后的返回值。error为nil时有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) CompleteMultipartUpload(imur InitiateMultipartUploadResult,
+ parts []UploadPart) (CompleteMultipartUploadResult, error) {
+ var out CompleteMultipartUploadResult
+
+ sort.Sort(uploadParts(parts))
+ cxml := completeMultipartUploadXML{}
+ cxml.Part = parts
+ bs, err := xml.Marshal(cxml)
+ if err != nil {
+ return out, err
+ }
+ buffer := new(bytes.Buffer)
+ buffer.Write(bs)
+
+ params := "uploadId=" + imur.UploadID
+ resp, err := bucket.do("POST", imur.Key, params, params, nil, buffer, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// AbortMultipartUpload 取消分片上传任务。
+//
+// imur InitiateMultipartUpload的返回值。
+//
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) AbortMultipartUpload(imur InitiateMultipartUploadResult) error {
+ params := "uploadId=" + imur.UploadID
+ resp, err := bucket.do("DELETE", imur.Key, params, params, nil, nil, nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+//
+// ListUploadedParts 列出指定上传任务已经上传的分片。
+//
+// imur InitiateMultipartUpload的返回值。
+//
+// ListUploadedPartsResponse 操作成功后的返回值,成员UploadedParts已经上传/拷贝的片。error为nil时该返回值有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) ListUploadedParts(imur InitiateMultipartUploadResult) (ListUploadedPartsResult, error) {
+ var out ListUploadedPartsResult
+ params := "uploadId=" + imur.UploadID
+ resp, err := bucket.do("GET", imur.Key, params, params, nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ return out, err
+}
+
+//
+// ListMultipartUploads 列出所有未上传完整的multipart任务列表。
+//
+// options ListObject的筛选行为。Prefix返回object的前缀,KeyMarker返回object的起始位置,MaxUploads最大数目默认1000,
+// Delimiter用于对Object名字进行分组的字符,所有名字包含指定的前缀且第一次出现delimiter字符之间的object。
+//
+// ListMultipartUploadResponse 操作成功后的返回值,error为nil时该返回值有效。
+// error 操作成功error为nil,非nil为错误信息。
+//
+func (bucket Bucket) ListMultipartUploads(options ...Option) (ListMultipartUploadResult, error) {
+ var out ListMultipartUploadResult
+
+ options = append(options, EncodingType("url"))
+ params, err := handleParams(options)
+ if err != nil {
+ return out, err
+ }
+
+ resp, err := bucket.do("GET", "", "uploads&"+params, "uploads", nil, nil, nil)
+ if err != nil {
+ return out, err
+ }
+ defer resp.Body.Close()
+
+ err = xmlUnmarshal(resp.Body, &out)
+ if err != nil {
+ return out, err
+ }
+ err = decodeListMultipartUploadResult(&out)
+ return out, err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go
new file mode 100644
index 000000000..8ad932a87
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/option.go
@@ -0,0 +1,351 @@
+package oss
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "time"
+)
+
+type optionType string
+
+const (
+ optionParam optionType = "HTTPParameter" // URL参数
+ optionHTTP optionType = "HTTPHeader" // HTTP头
+ optionArg optionType = "FuncArgument" // 函数参数
+)
+
+const (
+ deleteObjectsQuiet = "delete-objects-quiet"
+ routineNum = "x-routine-num"
+ checkpointConfig = "x-cp-config"
+ initCRC64 = "init-crc64"
+ progressListener = "x-progress-listener"
+)
+
+type (
+ optionValue struct {
+ Value interface{}
+ Type optionType
+ }
+
+ // Option http option
+ Option func(map[string]optionValue) error
+)
+
+// ACL is an option to set X-Oss-Acl header
+func ACL(acl ACLType) Option {
+ return setHeader(HTTPHeaderOssACL, string(acl))
+}
+
+// ContentType is an option to set Content-Type header
+func ContentType(value string) Option {
+ return setHeader(HTTPHeaderContentType, value)
+}
+
+// ContentLength is an option to set Content-Length header
+func ContentLength(length int64) Option {
+ return setHeader(HTTPHeaderContentLength, strconv.FormatInt(length, 10))
+}
+
+// CacheControl is an option to set Cache-Control header
+func CacheControl(value string) Option {
+ return setHeader(HTTPHeaderCacheControl, value)
+}
+
+// ContentDisposition is an option to set Content-Disposition header
+func ContentDisposition(value string) Option {
+ return setHeader(HTTPHeaderContentDisposition, value)
+}
+
+// ContentEncoding is an option to set Content-Encoding header
+func ContentEncoding(value string) Option {
+ return setHeader(HTTPHeaderContentEncoding, value)
+}
+
+// ContentMD5 is an option to set Content-MD5 header
+func ContentMD5(value string) Option {
+ return setHeader(HTTPHeaderContentMD5, value)
+}
+
+// Expires is an option to set Expires header
+func Expires(t time.Time) Option {
+ return setHeader(HTTPHeaderExpires, t.Format(http.TimeFormat))
+}
+
+// Meta is an option to set Meta header
+func Meta(key, value string) Option {
+ return setHeader(HTTPHeaderOssMetaPrefix+key, value)
+}
+
+// Range is an option to set Range header, [start, end]
+func Range(start, end int64) Option {
+ return setHeader(HTTPHeaderRange, fmt.Sprintf("bytes=%d-%d", start, end))
+}
+
+// AcceptEncoding is an option to set Accept-Encoding header
+func AcceptEncoding(value string) Option {
+ return setHeader(HTTPHeaderAcceptEncoding, value)
+}
+
+// IfModifiedSince is an option to set If-Modified-Since header
+func IfModifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderIfModifiedSince, t.Format(http.TimeFormat))
+}
+
+// IfUnmodifiedSince is an option to set If-Unmodified-Since header
+func IfUnmodifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderIfUnmodifiedSince, t.Format(http.TimeFormat))
+}
+
+// IfMatch is an option to set If-Match header
+func IfMatch(value string) Option {
+ return setHeader(HTTPHeaderIfMatch, value)
+}
+
+// IfNoneMatch is an option to set IfNoneMatch header
+func IfNoneMatch(value string) Option {
+ return setHeader(HTTPHeaderIfNoneMatch, value)
+}
+
+// CopySource is an option to set X-Oss-Copy-Source header
+func CopySource(sourceBucket, sourceObject string) Option {
+ return setHeader(HTTPHeaderOssCopySource, "/"+sourceBucket+"/"+sourceObject)
+}
+
+// CopySourceRange is an option to set X-Oss-Copy-Source header
+func CopySourceRange(startPosition, partSize int64) Option {
+ val := "bytes=" + strconv.FormatInt(startPosition, 10) + "-" +
+ strconv.FormatInt((startPosition+partSize-1), 10)
+ return setHeader(HTTPHeaderOssCopySourceRange, val)
+}
+
+// CopySourceIfMatch is an option to set X-Oss-Copy-Source-If-Match header
+func CopySourceIfMatch(value string) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfMatch, value)
+}
+
+// CopySourceIfNoneMatch is an option to set X-Oss-Copy-Source-If-None-Match header
+func CopySourceIfNoneMatch(value string) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfNoneMatch, value)
+}
+
+// CopySourceIfModifiedSince is an option to set X-Oss-CopySource-If-Modified-Since header
+func CopySourceIfModifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfModifiedSince, t.Format(http.TimeFormat))
+}
+
+// CopySourceIfUnmodifiedSince is an option to set X-Oss-Copy-Source-If-Unmodified-Since header
+func CopySourceIfUnmodifiedSince(t time.Time) Option {
+ return setHeader(HTTPHeaderOssCopySourceIfUnmodifiedSince, t.Format(http.TimeFormat))
+}
+
+// MetadataDirective is an option to set X-Oss-Metadata-Directive header
+func MetadataDirective(directive MetadataDirectiveType) Option {
+ return setHeader(HTTPHeaderOssMetadataDirective, string(directive))
+}
+
+// ServerSideEncryption is an option to set X-Oss-Server-Side-Encryption header
+func ServerSideEncryption(value string) Option {
+ return setHeader(HTTPHeaderOssServerSideEncryption, value)
+}
+
+// ObjectACL is an option to set X-Oss-Object-Acl header
+func ObjectACL(acl ACLType) Option {
+ return setHeader(HTTPHeaderOssObjectACL, string(acl))
+}
+
+// Origin is an option to set Origin header
+func Origin(value string) Option {
+ return setHeader(HTTPHeaderOrigin, value)
+}
+
+// Delimiter is an option to set delimiler parameter
+func Delimiter(value string) Option {
+ return addParam("delimiter", value)
+}
+
+// Marker is an option to set marker parameter
+func Marker(value string) Option {
+ return addParam("marker", value)
+}
+
+// MaxKeys is an option to set maxkeys parameter
+func MaxKeys(value int) Option {
+ return addParam("max-keys", strconv.Itoa(value))
+}
+
+// Prefix is an option to set prefix parameter
+func Prefix(value string) Option {
+ return addParam("prefix", value)
+}
+
+// EncodingType is an option to set encoding-type parameter
+func EncodingType(value string) Option {
+ return addParam("encoding-type", value)
+}
+
+// MaxUploads is an option to set max-uploads parameter
+func MaxUploads(value int) Option {
+ return addParam("max-uploads", strconv.Itoa(value))
+}
+
+// KeyMarker is an option to set key-marker parameter
+func KeyMarker(value string) Option {
+ return addParam("key-marker", value)
+}
+
+// UploadIDMarker is an option to set upload-id-marker parameter
+func UploadIDMarker(value string) Option {
+ return addParam("upload-id-marker", value)
+}
+
+// DeleteObjectsQuiet DeleteObjects详细(verbose)模式或简单(quiet)模式,默认详细模式。
+func DeleteObjectsQuiet(isQuiet bool) Option {
+ return addArg(deleteObjectsQuiet, isQuiet)
+}
+
+// 断点续传配置,包括是否启用、cp文件
+type cpConfig struct {
+ IsEnable bool
+ FilePath string
+}
+
+// Checkpoint DownloadFile/UploadFile是否开启checkpoint及checkpoint文件路径
+func Checkpoint(isEnable bool, filePath string) Option {
+ return addArg(checkpointConfig, &cpConfig{isEnable, filePath})
+}
+
+// Routines DownloadFile/UploadFile并发数
+func Routines(n int) Option {
+ return addArg(routineNum, n)
+}
+
+// InitCRC AppendObject CRC的校验的初始值
+func InitCRC(initCRC uint64) Option {
+ return addArg(initCRC64, initCRC)
+}
+
+// Progress set progress listener
+func Progress(listener ProgressListener) Option {
+ return addArg(progressListener, listener)
+}
+
+func setHeader(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionHTTP}
+ return nil
+ }
+}
+
+func addParam(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionParam}
+ return nil
+ }
+}
+
+func addArg(key string, value interface{}) Option {
+ return func(params map[string]optionValue) error {
+ if value == nil {
+ return nil
+ }
+ params[key] = optionValue{value, optionArg}
+ return nil
+ }
+}
+
+func handleOptions(headers map[string]string, options []Option) error {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return err
+ }
+ }
+ }
+
+ for k, v := range params {
+ if v.Type == optionHTTP {
+ headers[k] = v.Value.(string)
+ }
+ }
+ return nil
+}
+
+func handleParams(options []Option) (string, error) {
+ // option
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return "", err
+ }
+ }
+ }
+
+ // sort
+ var buf bytes.Buffer
+ keys := make([]string, 0, len(params))
+ for k, v := range params {
+ if v.Type == optionParam {
+ keys = append(keys, k)
+ }
+ }
+ sort.Strings(keys)
+
+ // serialize
+ for _, k := range keys {
+ vs := params[k]
+ prefix := url.QueryEscape(k) + "="
+
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(prefix)
+ buf.WriteString(url.QueryEscape(vs.Value.(string)))
+ }
+
+ return buf.String(), nil
+}
+
+func findOption(options []Option, param string, defaultVal interface{}) (interface{}, error) {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if val, ok := params[param]; ok {
+ return val.Value, nil
+ }
+ return defaultVal, nil
+}
+
+func isOptionSet(options []Option, option string) (bool, interface{}, error) {
+ params := map[string]optionValue{}
+ for _, option := range options {
+ if option != nil {
+ if err := option(params); err != nil {
+ return false, nil, err
+ }
+ }
+ }
+
+ if val, ok := params[option]; ok {
+ return true, val.Value, nil
+ }
+ return false, nil, nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go
new file mode 100644
index 000000000..0ea897f03
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/progress.go
@@ -0,0 +1,105 @@
+package oss
+
+import "io"
+
+// ProgressEventType transfer progress event type
+type ProgressEventType int
+
+const (
+ // TransferStartedEvent transfer started, set TotalBytes
+ TransferStartedEvent ProgressEventType = 1 + iota
+ // TransferDataEvent transfer data, set ConsumedBytes anmd TotalBytes
+ TransferDataEvent
+ // TransferCompletedEvent transfer completed
+ TransferCompletedEvent
+ // TransferFailedEvent transfer encounters an error
+ TransferFailedEvent
+)
+
+// ProgressEvent progress event
+type ProgressEvent struct {
+ ConsumedBytes int64
+ TotalBytes int64
+ EventType ProgressEventType
+}
+
+// ProgressListener listen progress change
+type ProgressListener interface {
+ ProgressChanged(event *ProgressEvent)
+}
+
+// -------------------- private --------------------
+
+func newProgressEvent(eventType ProgressEventType, consumed, total int64) *ProgressEvent {
+ return &ProgressEvent{
+ ConsumedBytes: consumed,
+ TotalBytes: total,
+ EventType: eventType}
+}
+
+// publishProgress
+func publishProgress(listener ProgressListener, event *ProgressEvent) {
+ if listener != nil && event != nil {
+ listener.ProgressChanged(event)
+ }
+}
+
+type readerTracker struct {
+ completedBytes int64
+}
+
+type teeReader struct {
+ reader io.Reader
+ writer io.Writer
+ listener ProgressListener
+ consumedBytes int64
+ totalBytes int64
+ tracker *readerTracker
+}
+
+// TeeReader returns a Reader that writes to w what it reads from r.
+// All reads from r performed through it are matched with
+// corresponding writes to w. There is no internal buffering -
+// the write must complete before the read completes.
+// Any error encountered while writing is reported as a read error.
+func TeeReader(reader io.Reader, writer io.Writer, totalBytes int64, listener ProgressListener, tracker *readerTracker) io.Reader {
+ return &teeReader{
+ reader: reader,
+ writer: writer,
+ listener: listener,
+ consumedBytes: 0,
+ totalBytes: totalBytes,
+ tracker: tracker,
+ }
+}
+
+func (t *teeReader) Read(p []byte) (n int, err error) {
+ n, err = t.reader.Read(p)
+
+ // read encountered error
+ if err != nil && err != io.EOF {
+ event := newProgressEvent(TransferFailedEvent, t.consumedBytes, t.totalBytes)
+ publishProgress(t.listener, event)
+ }
+
+ if n > 0 {
+ t.consumedBytes += int64(n)
+ // crc
+ if t.writer != nil {
+ if n, err := t.writer.Write(p[:n]); err != nil {
+ return n, err
+ }
+ }
+ // progress
+ if t.listener != nil {
+ event := newProgressEvent(TransferDataEvent, t.consumedBytes, t.totalBytes)
+ publishProgress(t.listener, event)
+ }
+ // track
+ if t.tracker != nil {
+ t.tracker.completedBytes = t.consumedBytes
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go
new file mode 100644
index 000000000..b60564538
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go
@@ -0,0 +1,442 @@
+package oss
+
+import (
+ "encoding/xml"
+ "net/url"
+ "time"
+)
+
+// ListBucketsResult ListBuckets请求返回的结果
+type ListBucketsResult struct {
+ XMLName xml.Name `xml:"ListAllMyBucketsResult"`
+ Prefix string `xml:"Prefix"` // 本次查询结果的前缀
+ Marker string `xml:"Marker"` // 标明查询的起点,未全部返回时有此节点
+ MaxKeys int `xml:"MaxKeys"` // 返回结果的最大数目,未全部返回时有此节点
+ IsTruncated bool `xml:"IsTruncated"` // 所有的结果是否已经全部返回
+ NextMarker string `xml:"NextMarker"` // 表示下一次查询的起点
+ Owner Owner `xml:"Owner"` // 拥有者信息
+ Buckets []BucketProperties `xml:"Buckets>Bucket"` // Bucket列表
+}
+
+// BucketProperties Bucket信息
+type BucketProperties struct {
+ XMLName xml.Name `xml:"Bucket"`
+ Name string `xml:"Name"` // Bucket名称
+ Location string `xml:"Location"` // Bucket所在的数据中心
+ CreationDate time.Time `xml:"CreationDate"` // Bucket创建时间
+}
+
+// GetBucketACLResult GetBucketACL请求返回的结果
+type GetBucketACLResult struct {
+ XMLName xml.Name `xml:"AccessControlPolicy"`
+ ACL string `xml:"AccessControlList>Grant"` // Bucket权限
+ Owner Owner `xml:"Owner"` // Bucket拥有者信息
+}
+
+// LifecycleConfiguration Bucket的Lifecycle配置
+type LifecycleConfiguration struct {
+ XMLName xml.Name `xml:"LifecycleConfiguration"`
+ Rules []LifecycleRule `xml:"Rule"`
+}
+
+// LifecycleRule Lifecycle规则
+type LifecycleRule struct {
+ XMLName xml.Name `xml:"Rule"`
+ ID string `xml:"ID"` // 规则唯一的ID
+ Prefix string `xml:"Prefix"` // 规则所适用Object的前缀
+ Status string `xml:"Status"` // 规则是否生效
+ Expiration LifecycleExpiration `xml:"Expiration"` // 规则的过期属性
+}
+
+// LifecycleExpiration 规则的过期属性
+type LifecycleExpiration struct {
+ XMLName xml.Name `xml:"Expiration"`
+ Days int `xml:"Days,omitempty"` // 最后修改时间过后多少天生效
+ Date time.Time `xml:"Date,omitempty"` // 指定规则何时生效
+}
+
+type lifecycleXML struct {
+ XMLName xml.Name `xml:"LifecycleConfiguration"`
+ Rules []lifecycleRule `xml:"Rule"`
+}
+
+type lifecycleRule struct {
+ XMLName xml.Name `xml:"Rule"`
+ ID string `xml:"ID"`
+ Prefix string `xml:"Prefix"`
+ Status string `xml:"Status"`
+ Expiration lifecycleExpiration `xml:"Expiration"`
+}
+
+type lifecycleExpiration struct {
+ XMLName xml.Name `xml:"Expiration"`
+ Days int `xml:"Days,omitempty"`
+ Date string `xml:"Date,omitempty"`
+}
+
+const expirationDateFormat = "2006-01-02T15:04:05.000Z"
+
+func convLifecycleRule(rules []LifecycleRule) []lifecycleRule {
+ rs := []lifecycleRule{}
+ for _, rule := range rules {
+ r := lifecycleRule{}
+ r.ID = rule.ID
+ r.Prefix = rule.Prefix
+ r.Status = rule.Status
+ if rule.Expiration.Date.IsZero() {
+ r.Expiration.Days = rule.Expiration.Days
+ } else {
+ r.Expiration.Date = rule.Expiration.Date.Format(expirationDateFormat)
+ }
+ rs = append(rs, r)
+ }
+ return rs
+}
+
+// BuildLifecycleRuleByDays 指定过期天数构建Lifecycle规则
+func BuildLifecycleRuleByDays(id, prefix string, status bool, days int) LifecycleRule {
+ var statusStr = "Enabled"
+ if !status {
+ statusStr = "Disabled"
+ }
+ return LifecycleRule{ID: id, Prefix: prefix, Status: statusStr,
+ Expiration: LifecycleExpiration{Days: days}}
+}
+
+// BuildLifecycleRuleByDate 指定过期时间构建Lifecycle规则
+func BuildLifecycleRuleByDate(id, prefix string, status bool, year, month, day int) LifecycleRule {
+ var statusStr = "Enabled"
+ if !status {
+ statusStr = "Disabled"
+ }
+ date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
+ return LifecycleRule{ID: id, Prefix: prefix, Status: statusStr,
+ Expiration: LifecycleExpiration{Date: date}}
+}
+
+// GetBucketLifecycleResult GetBucketLifecycle请求请求结果
+type GetBucketLifecycleResult LifecycleConfiguration
+
+// RefererXML Referer配置
+type RefererXML struct {
+ XMLName xml.Name `xml:"RefererConfiguration"`
+ AllowEmptyReferer bool `xml:"AllowEmptyReferer"` // 是否允许referer字段为空的请求访问
+ RefererList []string `xml:"RefererList>Referer"` // referer访问白名单
+}
+
+// GetBucketRefererResult GetBucketReferer请教返回结果
+type GetBucketRefererResult RefererXML
+
+// LoggingXML Logging配置
+type LoggingXML struct {
+ XMLName xml.Name `xml:"BucketLoggingStatus"`
+ LoggingEnabled LoggingEnabled `xml:"LoggingEnabled"` // 访问日志信息容器
+}
+
+type loggingXMLEmpty struct {
+ XMLName xml.Name `xml:"BucketLoggingStatus"`
+}
+
+// LoggingEnabled 访问日志信息容器
+type LoggingEnabled struct {
+ XMLName xml.Name `xml:"LoggingEnabled"`
+ TargetBucket string `xml:"TargetBucket"` //存放访问日志的Bucket
+ TargetPrefix string `xml:"TargetPrefix"` //保存访问日志的文件前缀
+}
+
+// GetBucketLoggingResult GetBucketLogging请求返回结果
+type GetBucketLoggingResult LoggingXML
+
+// WebsiteXML Website配置
+type WebsiteXML struct {
+ XMLName xml.Name `xml:"WebsiteConfiguration"`
+ IndexDocument IndexDocument `xml:"IndexDocument"` // 目录URL时添加的索引文件
+ ErrorDocument ErrorDocument `xml:"ErrorDocument"` // 404错误时使用的文件
+}
+
+// IndexDocument 目录URL时添加的索引文件
+type IndexDocument struct {
+ XMLName xml.Name `xml:"IndexDocument"`
+ Suffix string `xml:"Suffix"` // 目录URL时添加的索引文件名
+}
+
+// ErrorDocument 404错误时使用的文件
+type ErrorDocument struct {
+ XMLName xml.Name `xml:"ErrorDocument"`
+ Key string `xml:"Key"` // 404错误时使用的文件名
+}
+
+// GetBucketWebsiteResult GetBucketWebsite请求返回结果
+type GetBucketWebsiteResult WebsiteXML
+
+// CORSXML CORS配置
+type CORSXML struct {
+ XMLName xml.Name `xml:"CORSConfiguration"`
+ CORSRules []CORSRule `xml:"CORSRule"` // CORS规则列表
+}
+
+// CORSRule CORS规则
+type CORSRule struct {
+ XMLName xml.Name `xml:"CORSRule"`
+ AllowedOrigin []string `xml:"AllowedOrigin"` // 允许的来源,默认通配符"*"
+ AllowedMethod []string `xml:"AllowedMethod"` // 允许的方法
+ AllowedHeader []string `xml:"AllowedHeader"` // 允许的请求头
+ ExposeHeader []string `xml:"ExposeHeader"` // 允许的响应头
+ MaxAgeSeconds int `xml:"MaxAgeSeconds"` // 最大的缓存时间
+}
+
+// GetBucketCORSResult GetBucketCORS请求返回的结果
+type GetBucketCORSResult CORSXML
+
+// GetBucketInfoResult GetBucketInfo请求返回结果
+type GetBucketInfoResult struct {
+ XMLName xml.Name `xml:"BucketInfo"`
+ BucketInfo BucketInfo `xml:"Bucket"`
+}
+
+// BucketInfo Bucket信息
+type BucketInfo struct {
+ XMLName xml.Name `xml:"Bucket"`
+ Name string `xml:"Name"` // Bucket名称
+ Location string `xml:"Location"` // Bucket所在的数据中心
+ CreationDate time.Time `xml:"CreationDate"` // Bucket创建时间
+ ExtranetEndpoint string `xml:"ExtranetEndpoint"` // Bucket访问的外网域名
+ IntranetEndpoint string `xml:"IntranetEndpoint"` // Bucket访问的内网域名
+ ACL string `xml:"AccessControlList>Grant"` // Bucket权限
+ Owner Owner `xml:"Owner"` // Bucket拥有者信息
+}
+
+// ListObjectsResult ListObjects请求返回结果
+type ListObjectsResult struct {
+ XMLName xml.Name `xml:"ListBucketResult"`
+ Prefix string `xml:"Prefix"` // 本次查询结果的开始前缀
+ Marker string `xml:"Marker"` // 这次查询的起点
+ MaxKeys int `xml:"MaxKeys"` // 请求返回结果的最大数目
+ Delimiter string `xml:"Delimiter"` // 对Object名字进行分组的字符
+ IsTruncated bool `xml:"IsTruncated"` // 是否所有的结果都已经返回
+ NextMarker string `xml:"NextMarker"` // 下一次查询的起点
+ Objects []ObjectProperties `xml:"Contents"` // Object类别
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // 以delimiter结尾并有共同前缀的Object的集合
+}
+
+// ObjectProperties Objecct属性
+type ObjectProperties struct {
+ XMLName xml.Name `xml:"Contents"`
+ Key string `xml:"Key"` // Object的Key
+ Type string `xml:"Type"` // Object Type
+ Size int64 `xml:"Size"` // Object的长度字节数
+ ETag string `xml:"ETag"` // 标示Object的内容
+ Owner Owner `xml:"Owner"` // 保存Object拥有者信息的容器
+ LastModified time.Time `xml:"LastModified"` // Object最后修改时间
+ StorageClass string `xml:"StorageClass"` // Object的存储类型,目前只能是Standard
+}
+
+// Owner Bucket/Object的owner
+type Owner struct {
+ XMLName xml.Name `xml:"Owner"`
+ ID string `xml:"ID"` // 用户ID
+ DisplayName string `xml:"DisplayName"` // Owner名字
+}
+
+// CopyObjectResult CopyObject请求返回的结果
+type CopyObjectResult struct {
+ XMLName xml.Name `xml:"CopyObjectResult"`
+ LastModified time.Time `xml:"LastModified"` // 新Object最后更新时间
+ ETag string `xml:"ETag"` // 新Object的ETag值
+}
+
+// GetObjectACLResult GetObjectACL请求返回的结果
+type GetObjectACLResult GetBucketACLResult
+
+type deleteXML struct {
+ XMLName xml.Name `xml:"Delete"`
+ Objects []DeleteObject `xml:"Object"` // 删除的所有Object
+ Quiet bool `xml:"Quiet"` // 安静响应模式
+}
+
+// DeleteObject 删除的Object
+type DeleteObject struct {
+ XMLName xml.Name `xml:"Object"`
+ Key string `xml:"Key"` // Object名称
+}
+
+// DeleteObjectsResult DeleteObjects请求返回结果
+type DeleteObjectsResult struct {
+ XMLName xml.Name `xml:"DeleteResult"`
+ DeletedObjects []string `xml:"Deleted>Key"` // 删除的Object列表
+}
+
+// InitiateMultipartUploadResult InitiateMultipartUpload请求返回结果
+type InitiateMultipartUploadResult struct {
+ XMLName xml.Name `xml:"InitiateMultipartUploadResult"`
+ Bucket string `xml:"Bucket"` // Bucket名称
+ Key string `xml:"Key"` // 上传Object名称
+ UploadID string `xml:"UploadId"` // 生成的UploadId
+}
+
+// UploadPart 上传/拷贝的分片
+type UploadPart struct {
+ XMLName xml.Name `xml:"Part"`
+ PartNumber int `xml:"PartNumber"` // Part编号
+ ETag string `xml:"ETag"` // ETag缓存码
+}
+
+type uploadParts []UploadPart
+
+func (slice uploadParts) Len() int {
+ return len(slice)
+}
+
+func (slice uploadParts) Less(i, j int) bool {
+ return slice[i].PartNumber < slice[j].PartNumber
+}
+
+func (slice uploadParts) Swap(i, j int) {
+ slice[i], slice[j] = slice[j], slice[i]
+}
+
+// UploadPartCopyResult 拷贝分片请求返回的结果
+type UploadPartCopyResult struct {
+ XMLName xml.Name `xml:"CopyPartResult"`
+ LastModified time.Time `xml:"LastModified"` // 最后修改时间
+ ETag string `xml:"ETag"` // ETag
+}
+
+type completeMultipartUploadXML struct {
+ XMLName xml.Name `xml:"CompleteMultipartUpload"`
+ Part []UploadPart `xml:"Part"`
+}
+
+// CompleteMultipartUploadResult 提交分片上传任务返回结果
+type CompleteMultipartUploadResult struct {
+ XMLName xml.Name `xml:"CompleteMultipartUploadResult"`
+ Location string `xml:"Location"` // Object的URL
+ Bucket string `xml:"Bucket"` // Bucket名称
+ ETag string `xml:"ETag"` // Object的ETag
+ Key string `xml:"Key"` // Object的名字
+}
+
+// ListUploadedPartsResult ListUploadedParts请求返回结果
+type ListUploadedPartsResult struct {
+ XMLName xml.Name `xml:"ListPartsResult"`
+ Bucket string `xml:"Bucket"` // Bucket名称
+ Key string `xml:"Key"` // Object名称
+ UploadID string `xml:"UploadId"` // 上传Id
+ NextPartNumberMarker string `xml:"NextPartNumberMarker"` // 下一个Part的位置
+ MaxParts int `xml:"MaxParts"` // 最大Part个数
+ IsTruncated bool `xml:"IsTruncated"` // 是否完全上传完成
+ UploadedParts []UploadedPart `xml:"Part"` // 已完成的Part
+}
+
+// UploadedPart 该任务已经上传的分片
+type UploadedPart struct {
+ XMLName xml.Name `xml:"Part"`
+ PartNumber int `xml:"PartNumber"` // Part编号
+ LastModified time.Time `xml:"LastModified"` // 最后一次修改时间
+ ETag string `xml:"ETag"` // ETag缓存码
+ Size int `xml:"Size"` // Part大小
+}
+
+// ListMultipartUploadResult ListMultipartUpload请求返回结果
+type ListMultipartUploadResult struct {
+ XMLName xml.Name `xml:"ListMultipartUploadsResult"`
+ Bucket string `xml:"Bucket"` // Bucket名称
+ Delimiter string `xml:"Delimiter"` // 分组分割符
+ Prefix string `xml:"Prefix"` // 筛选前缀
+ KeyMarker string `xml:"KeyMarker"` // 起始Object位置
+ UploadIDMarker string `xml:"UploadIdMarker"` // 起始UploadId位置
+ NextKeyMarker string `xml:"NextKeyMarker"` // 如果没有全部返回,标明接下去的KeyMarker位置
+ NextUploadIDMarker string `xml:"NextUploadIdMarker"` // 如果没有全部返回,标明接下去的UploadId位置
+ MaxUploads int `xml:"MaxUploads"` // 返回最大Upload数目
+ IsTruncated bool `xml:"IsTruncated"` // 是否完全返回
+ Uploads []UncompletedUpload `xml:"Upload"` // 未完成上传的MultipartUpload
+ CommonPrefixes []string `xml:"CommonPrefixes>Prefix"` // 所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组的分组结果
+}
+
+// UncompletedUpload 未完成的Upload任务
+type UncompletedUpload struct {
+ XMLName xml.Name `xml:"Upload"`
+ Key string `xml:"Key"` // Object名称
+ UploadID string `xml:"UploadId"` // 对应UploadId
+ Initiated time.Time `xml:"Initiated"` // 初始化时间,格式2012-02-23T04:18:23.000Z
+}
+
+// 解析URL编码
+func decodeDeleteObjectsResult(result *DeleteObjectsResult) error {
+ var err error
+ for i := 0; i < len(result.DeletedObjects); i++ {
+ result.DeletedObjects[i], err = url.QueryUnescape(result.DeletedObjects[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// 解析URL编码
+func decodeListObjectsResult(result *ListObjectsResult) error {
+ var err error
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+ result.Marker, err = url.QueryUnescape(result.Marker)
+ if err != nil {
+ return err
+ }
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+ result.NextMarker, err = url.QueryUnescape(result.NextMarker)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < len(result.Objects); i++ {
+ result.Objects[i].Key, err = url.QueryUnescape(result.Objects[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// 解析URL编码
+func decodeListMultipartUploadResult(result *ListMultipartUploadResult) error {
+ var err error
+ result.Prefix, err = url.QueryUnescape(result.Prefix)
+ if err != nil {
+ return err
+ }
+ result.Delimiter, err = url.QueryUnescape(result.Delimiter)
+ if err != nil {
+ return err
+ }
+ result.KeyMarker, err = url.QueryUnescape(result.KeyMarker)
+ if err != nil {
+ return err
+ }
+ result.NextKeyMarker, err = url.QueryUnescape(result.NextKeyMarker)
+ if err != nil {
+ return err
+ }
+ for i := 0; i < len(result.Uploads); i++ {
+ result.Uploads[i].Key, err = url.QueryUnescape(result.Uploads[i].Key)
+ if err != nil {
+ return err
+ }
+ }
+ for i := 0; i < len(result.CommonPrefixes); i++ {
+ result.CommonPrefixes[i], err = url.QueryUnescape(result.CommonPrefixes[i])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go
new file mode 100644
index 000000000..049ed82d9
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/upload.go
@@ -0,0 +1,485 @@
+package oss
+
+import (
+ "crypto/md5"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "os"
+ "time"
+)
+
+//
+// UploadFile 分片上传文件
+//
+// objectKey object名称。
+// filePath 本地文件。需要上传的文件。
+// partSize 本次上传文件片的大小,字节数。比如100 * 1024为每片100KB。
+// options 上传Object时可以指定Object的属性。详见InitiateMultipartUpload。
+//
+// error 操作成功为nil,非nil为错误信息。
+//
+func (bucket Bucket) UploadFile(objectKey, filePath string, partSize int64, options ...Option) error {
+ if partSize < MinPartSize || partSize > MaxPartSize {
+ return errors.New("oss: part size invalid range (1024KB, 5GB]")
+ }
+
+ cpConf, err := getCpConfig(options, filePath)
+ if err != nil {
+ return err
+ }
+
+ routines := getRoutines(options)
+
+ if cpConf.IsEnable {
+ return bucket.uploadFileWithCp(objectKey, filePath, partSize, options, cpConf.FilePath, routines)
+ }
+
+ return bucket.uploadFile(objectKey, filePath, partSize, options, routines)
+}
+
+// ----- 并发无断点的上传 -----
+
+// 获取Checkpoint配置
+func getCpConfig(options []Option, filePath string) (*cpConfig, error) {
+ cpc := &cpConfig{}
+ cpcOpt, err := findOption(options, checkpointConfig, nil)
+ if err != nil || cpcOpt == nil {
+ return cpc, err
+ }
+
+ cpc = cpcOpt.(*cpConfig)
+ if cpc.IsEnable && cpc.FilePath == "" {
+ cpc.FilePath = filePath + CheckpointFileSuffix
+ }
+
+ return cpc, nil
+}
+
+// 获取并发数,默认并发数1
+func getRoutines(options []Option) int {
+ rtnOpt, err := findOption(options, routineNum, nil)
+ if err != nil || rtnOpt == nil {
+ return 1
+ }
+
+ rs := rtnOpt.(int)
+ if rs < 1 {
+ rs = 1
+ } else if rs > 100 {
+ rs = 100
+ }
+
+ return rs
+}
+
+// 获取进度回调
+func getProgressListener(options []Option) ProgressListener {
+ isSet, listener, _ := isOptionSet(options, progressListener)
+ if !isSet {
+ return nil
+ }
+ return listener.(ProgressListener)
+}
+
+// 测试使用
+type uploadPartHook func(id int, chunk FileChunk) error
+
+var uploadPartHooker uploadPartHook = defaultUploadPart
+
+func defaultUploadPart(id int, chunk FileChunk) error {
+ return nil
+}
+
+// 工作协程参数
+type workerArg struct {
+ bucket *Bucket
+ filePath string
+ imur InitiateMultipartUploadResult
+ hook uploadPartHook
+}
+
+// 工作协程
+func worker(id int, arg workerArg, jobs <-chan FileChunk, results chan<- UploadPart, failed chan<- error, die <-chan bool) {
+ for chunk := range jobs {
+ if err := arg.hook(id, chunk); err != nil {
+ failed <- err
+ break
+ }
+ part, err := arg.bucket.UploadPartFromFile(arg.imur, arg.filePath, chunk.Offset, chunk.Size, chunk.Number)
+ if err != nil {
+ failed <- err
+ break
+ }
+ select {
+ case <-die:
+ return
+ default:
+ }
+ results <- part
+ }
+}
+
+// 调度协程
+func scheduler(jobs chan FileChunk, chunks []FileChunk) {
+ for _, chunk := range chunks {
+ jobs <- chunk
+ }
+ close(jobs)
+}
+
+func getTotalBytes(chunks []FileChunk) int64 {
+ var tb int64
+ for _, chunk := range chunks {
+ tb += chunk.Size
+ }
+ return tb
+}
+
+// 并发上传,不带断点续传功能
+func (bucket Bucket) uploadFile(objectKey, filePath string, partSize int64, options []Option, routines int) error {
+ listener := getProgressListener(options)
+
+ chunks, err := SplitFileByPartSize(filePath, partSize)
+ if err != nil {
+ return err
+ }
+
+ // 初始化上传任务
+ imur, err := bucket.InitiateMultipartUpload(objectKey, options...)
+ if err != nil {
+ return err
+ }
+
+ jobs := make(chan FileChunk, len(chunks))
+ results := make(chan UploadPart, len(chunks))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ var completedBytes int64
+ totalBytes := getTotalBytes(chunks)
+ event := newProgressEvent(TransferStartedEvent, 0, totalBytes)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := workerArg{&bucket, filePath, imur, uploadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go worker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发上传分片
+ go scheduler(jobs, chunks)
+
+ // 等待分配分片上传完成
+ completed := 0
+ parts := make([]UploadPart, len(chunks))
+ for completed < len(chunks) {
+ select {
+ case part := <-results:
+ completed++
+ parts[part.PartNumber-1] = part
+ completedBytes += chunks[part.PartNumber-1].Size
+ event = newProgressEvent(TransferDataEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+ bucket.AbortMultipartUpload(imur)
+ return err
+ }
+
+ if completed >= len(chunks) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferStartedEvent, completedBytes, totalBytes)
+ publishProgress(listener, event)
+
+ // 提交任务
+ _, err = bucket.CompleteMultipartUpload(imur, parts)
+ if err != nil {
+ bucket.AbortMultipartUpload(imur)
+ return err
+ }
+ return nil
+}
+
+// ----- 并发带断点的上传 -----
+const uploadCpMagic = "FE8BB4EA-B593-4FAC-AD7A-2459A36E2E62"
+
+type uploadCheckpoint struct {
+ Magic string // magic
+ MD5 string // cp内容的MD5
+ FilePath string // 本地文件
+ FileStat cpStat // 文件状态
+ ObjectKey string // key
+ UploadID string // upload id
+ Parts []cpPart // 本地文件的全部分片
+}
+
+type cpStat struct {
+ Size int64 // 文件大小
+ LastModified time.Time // 本地文件最后修改时间
+ MD5 string // 本地文件MD5
+}
+
+type cpPart struct {
+ Chunk FileChunk // 分片
+ Part UploadPart // 上传完成的分片
+ IsCompleted bool // upload是否完成
+}
+
+// CP数据是否有效,CP有效且文件没有更新时有效
+func (cp uploadCheckpoint) isValid(filePath string) (bool, error) {
+ // 比较CP的Magic及MD5
+ cpb := cp
+ cpb.MD5 = ""
+ js, _ := json.Marshal(cpb)
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+
+ if cp.Magic != uploadCpMagic || b64 != cp.MD5 {
+ return false, nil
+ }
+
+ // 确认本地文件是否更新
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return false, err
+ }
+ defer fd.Close()
+
+ st, err := fd.Stat()
+ if err != nil {
+ return false, err
+ }
+
+ md, err := calcFileMD5(filePath)
+ if err != nil {
+ return false, err
+ }
+
+ // 比较文件大小/文件最后更新时间/文件MD5
+ if cp.FileStat.Size != st.Size() ||
+ cp.FileStat.LastModified != st.ModTime() ||
+ cp.FileStat.MD5 != md {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// 从文件中load
+func (cp *uploadCheckpoint) load(filePath string) error {
+ contents, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(contents, cp)
+ return err
+}
+
+// dump到文件
+func (cp *uploadCheckpoint) dump(filePath string) error {
+ bcp := *cp
+
+ // 计算MD5
+ bcp.MD5 = ""
+ js, err := json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+ sum := md5.Sum(js)
+ b64 := base64.StdEncoding.EncodeToString(sum[:])
+ bcp.MD5 = b64
+
+ // 序列化
+ js, err = json.Marshal(bcp)
+ if err != nil {
+ return err
+ }
+
+ // dump
+ return ioutil.WriteFile(filePath, js, FilePermMode)
+}
+
+// 更新分片状态
+func (cp *uploadCheckpoint) updatePart(part UploadPart) {
+ cp.Parts[part.PartNumber-1].Part = part
+ cp.Parts[part.PartNumber-1].IsCompleted = true
+}
+
+// 未完成的分片
+func (cp *uploadCheckpoint) todoParts() []FileChunk {
+ fcs := []FileChunk{}
+ for _, part := range cp.Parts {
+ if !part.IsCompleted {
+ fcs = append(fcs, part.Chunk)
+ }
+ }
+ return fcs
+}
+
+// 所有的分片
+func (cp *uploadCheckpoint) allParts() []UploadPart {
+ ps := []UploadPart{}
+ for _, part := range cp.Parts {
+ ps = append(ps, part.Part)
+ }
+ return ps
+}
+
+// 完成的字节数
+func (cp *uploadCheckpoint) getCompletedBytes() int64 {
+ var completedBytes int64
+ for _, part := range cp.Parts {
+ if part.IsCompleted {
+ completedBytes += part.Chunk.Size
+ }
+ }
+ return completedBytes
+}
+
+// 计算文件文件MD5
+func calcFileMD5(filePath string) (string, error) {
+ return "", nil
+}
+
+// 初始化分片上传
+func prepare(cp *uploadCheckpoint, objectKey, filePath string, partSize int64, bucket *Bucket, options []Option) error {
+ // cp
+ cp.Magic = uploadCpMagic
+ cp.FilePath = filePath
+ cp.ObjectKey = objectKey
+
+ // localfile
+ fd, err := os.Open(filePath)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+
+ st, err := fd.Stat()
+ if err != nil {
+ return err
+ }
+ cp.FileStat.Size = st.Size()
+ cp.FileStat.LastModified = st.ModTime()
+ md, err := calcFileMD5(filePath)
+ if err != nil {
+ return err
+ }
+ cp.FileStat.MD5 = md
+
+ // chunks
+ parts, err := SplitFileByPartSize(filePath, partSize)
+ if err != nil {
+ return err
+ }
+
+ cp.Parts = make([]cpPart, len(parts))
+ for i, part := range parts {
+ cp.Parts[i].Chunk = part
+ cp.Parts[i].IsCompleted = false
+ }
+
+ // init load
+ imur, err := bucket.InitiateMultipartUpload(objectKey, options...)
+ if err != nil {
+ return err
+ }
+ cp.UploadID = imur.UploadID
+
+ return nil
+}
+
+// 提交分片上传,删除CP文件
+func complete(cp *uploadCheckpoint, bucket *Bucket, parts []UploadPart, cpFilePath string) error {
+ imur := InitiateMultipartUploadResult{Bucket: bucket.BucketName,
+ Key: cp.ObjectKey, UploadID: cp.UploadID}
+ _, err := bucket.CompleteMultipartUpload(imur, parts)
+ if err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ return err
+}
+
+// 并发带断点的上传
+func (bucket Bucket) uploadFileWithCp(objectKey, filePath string, partSize int64, options []Option, cpFilePath string, routines int) error {
+ listener := getProgressListener(options)
+
+ // LOAD CP数据
+ ucp := uploadCheckpoint{}
+ err := ucp.load(cpFilePath)
+ if err != nil {
+ os.Remove(cpFilePath)
+ }
+
+ // LOAD出错或数据无效重新初始化上传
+ valid, err := ucp.isValid(filePath)
+ if err != nil || !valid {
+ if err = prepare(&ucp, objectKey, filePath, partSize, &bucket, options); err != nil {
+ return err
+ }
+ os.Remove(cpFilePath)
+ }
+
+ chunks := ucp.todoParts()
+ imur := InitiateMultipartUploadResult{
+ Bucket: bucket.BucketName,
+ Key: objectKey,
+ UploadID: ucp.UploadID}
+
+ jobs := make(chan FileChunk, len(chunks))
+ results := make(chan UploadPart, len(chunks))
+ failed := make(chan error)
+ die := make(chan bool)
+
+ completedBytes := ucp.getCompletedBytes()
+ event := newProgressEvent(TransferStartedEvent, completedBytes, ucp.FileStat.Size)
+ publishProgress(listener, event)
+
+ // 启动工作协程
+ arg := workerArg{&bucket, filePath, imur, uploadPartHooker}
+ for w := 1; w <= routines; w++ {
+ go worker(w, arg, jobs, results, failed, die)
+ }
+
+ // 并发上传分片
+ go scheduler(jobs, chunks)
+
+ // 等待分配分片上传完成
+ completed := 0
+ for completed < len(chunks) {
+ select {
+ case part := <-results:
+ completed++
+ ucp.updatePart(part)
+ ucp.dump(cpFilePath)
+ completedBytes += ucp.Parts[part.PartNumber-1].Chunk.Size
+ event = newProgressEvent(TransferDataEvent, completedBytes, ucp.FileStat.Size)
+ publishProgress(listener, event)
+ case err := <-failed:
+ close(die)
+ event = newProgressEvent(TransferFailedEvent, completedBytes, ucp.FileStat.Size)
+ publishProgress(listener, event)
+ return err
+ }
+
+ if completed >= len(chunks) {
+ break
+ }
+ }
+
+ event = newProgressEvent(TransferCompletedEvent, completedBytes, ucp.FileStat.Size)
+ publishProgress(listener, event)
+
+ // 提交分片上传
+ err = complete(&ucp, &bucket, ucp.allParts(), cpFilePath)
+ return err
+}
diff --git a/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go
new file mode 100644
index 000000000..b4d8e1694
--- /dev/null
+++ b/vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go
@@ -0,0 +1,165 @@
+package oss
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "hash/crc64"
+ "net/http"
+ "os"
+ "os/exec"
+ "runtime"
+ "time"
+)
+
+// Get User Agent
+// Go sdk相关信息,包括sdk版本,操作系统类型,GO版本
+var userAgent = func() string {
+ sys := getSysInfo()
+ return fmt.Sprintf("aliyun-sdk-go/%s (%s/%s/%s;%s)", Version, sys.name,
+ sys.release, sys.machine, runtime.Version())
+}()
+
+type sysInfo struct {
+ name string // 操作系统名称windows/Linux
+ release string // 操作系统版本 2.6.32-220.23.2.ali1089.el5.x86_64等
+ machine string // 机器类型amd64/x86_64
+}
+
+// Get system info
+// 获取操作系统信息、机器类型
+func getSysInfo() sysInfo {
+ name := runtime.GOOS
+ release := "-"
+ machine := runtime.GOARCH
+ if out, err := exec.Command("uname", "-s").CombinedOutput(); err == nil {
+ name = string(bytes.TrimSpace(out))
+ }
+ if out, err := exec.Command("uname", "-r").CombinedOutput(); err == nil {
+ release = string(bytes.TrimSpace(out))
+ }
+ if out, err := exec.Command("uname", "-m").CombinedOutput(); err == nil {
+ machine = string(bytes.TrimSpace(out))
+ }
+ return sysInfo{name: name, release: release, machine: machine}
+}
+
+// GetNowSec returns Unix time, the number of seconds elapsed since January 1, 1970 UTC.
+// 获取当前时间,从UTC开始的秒数。
+func GetNowSec() int64 {
+ return time.Now().Unix()
+}
+
+// GetNowNanoSec returns t as a Unix time, the number of nanoseconds elapsed
+// since January 1, 1970 UTC. The result is undefined if the Unix time
+// in nanoseconds cannot be represented by an int64. Note that this
+// means the result of calling UnixNano on the zero Time is undefined.
+// 获取当前时间,从UTC开始的纳秒。
+func GetNowNanoSec() int64 {
+ return time.Now().UnixNano()
+}
+
+// GetNowGMT 获取当前时间,格式形如"Mon, 02 Jan 2006 15:04:05 GMT",HTTP中使用的时间格式
+func GetNowGMT() string {
+ return time.Now().UTC().Format(http.TimeFormat)
+}
+
+// FileChunk 文件片定义
+type FileChunk struct {
+ Number int // 块序号
+ Offset int64 // 块在文件中的偏移量
+ Size int64 // 块大小
+}
+
+// SplitFileByPartNum Split big file to part by the num of part
+// 按指定的块数分割文件。返回值FileChunk为分割结果,error为nil时有效。
+func SplitFileByPartNum(fileName string, chunkNum int) ([]FileChunk, error) {
+ if chunkNum <= 0 || chunkNum > 10000 {
+ return nil, errors.New("chunkNum invalid")
+ }
+
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ stat, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+
+ if int64(chunkNum) > stat.Size() {
+ return nil, errors.New("oss: chunkNum invalid")
+ }
+
+ var chunks []FileChunk
+ var chunk = FileChunk{}
+ var chunkN = (int64)(chunkNum)
+ for i := int64(0); i < chunkN; i++ {
+ chunk.Number = int(i + 1)
+ chunk.Offset = i * (stat.Size() / chunkN)
+ if i == chunkN-1 {
+ chunk.Size = stat.Size()/chunkN + stat.Size()%chunkN
+ } else {
+ chunk.Size = stat.Size() / chunkN
+ }
+ chunks = append(chunks, chunk)
+ }
+
+ return chunks, nil
+}
+
+// SplitFileByPartSize Split big file to part by the size of part
+// 按块大小分割文件。返回值FileChunk为分割结果,error为nil时有效。
+func SplitFileByPartSize(fileName string, chunkSize int64) ([]FileChunk, error) {
+ if chunkSize <= 0 {
+ return nil, errors.New("chunkSize invalid")
+ }
+
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ stat, err := file.Stat()
+ if err != nil {
+ return nil, err
+ }
+ var chunkN = stat.Size() / chunkSize
+ if chunkN >= 10000 {
+ return nil, errors.New("Too many parts, please increase part size.")
+ }
+
+ var chunks []FileChunk
+ var chunk = FileChunk{}
+ for i := int64(0); i < chunkN; i++ {
+ chunk.Number = int(i + 1)
+ chunk.Offset = i * chunkSize
+ chunk.Size = chunkSize
+ chunks = append(chunks, chunk)
+ }
+
+ if stat.Size()%chunkSize > 0 {
+ chunk.Number = len(chunks) + 1
+ chunk.Offset = int64(len(chunks)) * chunkSize
+ chunk.Size = stat.Size() % chunkSize
+ chunks = append(chunks, chunk)
+ }
+
+ return chunks, nil
+}
+
+// GetPartEnd 计算结束位置
+func GetPartEnd(begin int64, total int64, per int64) int64 {
+ if begin+per > total {
+ return total - 1
+ }
+ return begin + per - 1
+}
+
+// crcTable returns the Table constructed from the specified polynomial
+var crcTable = func() *crc64.Table {
+ return crc64.MakeTable(crc64.ECMA)
+}
diff --git a/vendor/github.com/denverdino/aliyungo/LICENSE.txt b/vendor/github.com/denverdino/aliyungo/LICENSE.txt
new file mode 100644
index 000000000..918297133
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/LICENSE.txt
@@ -0,0 +1,191 @@
+
+ Apache License
+ Version 2.0, January 2004
+ https://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2015-2015 Li Yi (denverdino@gmail.com).
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go
new file mode 100755
index 000000000..d186ebd82
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/client.go
@@ -0,0 +1,345 @@
+package common
+
+import (
+ "bytes"
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "github.com/denverdino/aliyungo/util"
+)
+
+// RemovalPolicy.N add index to array item
+// RemovalPolicy=["a", "b"] => RemovalPolicy.1="a" RemovalPolicy.2="b"
+type FlattenArray []string
+
+// string contains underline which will be replaced with dot
+// SystemDisk_Category => SystemDisk.Category
+type UnderlineString string
+
+// A Client represents a client of ECS services
+type Client struct {
+ AccessKeyId string //Access Key Id
+ AccessKeySecret string //Access Key Secret
+ debug bool
+ httpClient *http.Client
+ endpoint string
+ version string
+ serviceCode string
+ regionID Region
+ businessInfo string
+}
+
+// NewClient creates a new instance of ECS client
+func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret string) {
+ client.AccessKeyId = accessKeyId
+ client.AccessKeySecret = accessKeySecret + "&"
+ client.debug = false
+ client.httpClient = &http.Client{}
+ client.endpoint = endpoint
+ client.version = version
+}
+
+func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) {
+ client.Init(endpoint, version, accessKeyId, accessKeySecret)
+ client.serviceCode = serviceCode
+ client.regionID = regionID
+ client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret)
+}
+
+//NewClient using location service
+func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret string) {
+ locationClient := NewLocationClient(accessKeyId, accessKeySecret)
+ ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode)
+ if ep == "" {
+ ep = loadEndpointFromFile(region, serviceCode)
+ }
+
+ if ep != "" {
+ client.endpoint = ep
+ }
+}
+
+// SetEndpoint sets custom endpoint
+func (client *Client) SetEndpoint(endpoint string) {
+ client.endpoint = endpoint
+}
+
+// SetEndpoint sets custom version
+func (client *Client) SetVersion(version string) {
+ client.version = version
+}
+
+func (client *Client) SetRegionID(regionID Region) {
+ client.regionID = regionID
+}
+
+//SetServiceCode sets serviceCode
+func (client *Client) SetServiceCode(serviceCode string) {
+ client.serviceCode = serviceCode
+}
+
+// SetAccessKeyId sets new AccessKeyId
+func (client *Client) SetAccessKeyId(id string) {
+ client.AccessKeyId = id
+}
+
+// SetAccessKeySecret sets new AccessKeySecret
+func (client *Client) SetAccessKeySecret(secret string) {
+ client.AccessKeySecret = secret + "&"
+}
+
+// SetDebug sets debug mode to log the request/response message
+func (client *Client) SetDebug(debug bool) {
+ client.debug = debug
+}
+
+// SetBusinessInfo sets business info to log the request/response message
+func (client *Client) SetBusinessInfo(businessInfo string) {
+ if strings.HasPrefix(businessInfo, "/") {
+ client.businessInfo = businessInfo
+ } else if businessInfo != "" {
+ client.businessInfo = "/" + businessInfo
+ }
+}
+
+// Invoke sends the raw HTTP request for ECS services
+func (client *Client) Invoke(action string, args interface{}, response interface{}) error {
+
+ request := Request{}
+ request.init(client.version, action, client.AccessKeyId)
+
+ query := util.ConvertToQueryValues(request)
+ util.SetQueryValues(args, &query)
+
+ // Sign request
+ signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret)
+
+ // Generate the request URL
+ requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature)
+
+ httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil)
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ // TODO move to util and add build val flag
+ httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
+
+ t0 := time.Now()
+ httpResp, err := client.httpClient.Do(httpReq)
+ t1 := time.Now()
+ if err != nil {
+ return GetClientError(err)
+ }
+ statusCode := httpResp.StatusCode
+
+ if client.debug {
+ log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
+ }
+
+ defer httpResp.Body.Close()
+ body, err := ioutil.ReadAll(httpResp.Body)
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ if client.debug {
+ var prettyJSON bytes.Buffer
+ err = json.Indent(&prettyJSON, body, "", " ")
+ log.Println(string(prettyJSON.Bytes()))
+ }
+
+ if statusCode >= 400 && statusCode <= 599 {
+ errorResponse := ErrorResponse{}
+ err = json.Unmarshal(body, &errorResponse)
+ ecsError := &Error{
+ ErrorResponse: errorResponse,
+ StatusCode: statusCode,
+ }
+ return ecsError
+ }
+
+ err = json.Unmarshal(body, response)
+ //log.Printf("%++v", response)
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ return nil
+}
+
+// Invoke sends the raw HTTP request for ECS services
+func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) error {
+
+ request := Request{}
+ request.init(client.version, action, client.AccessKeyId)
+
+ query := util.ConvertToQueryValues(request)
+
+ util.SetQueryValueByFlattenMethod(args, &query)
+
+ // Sign request
+ signature := util.CreateSignatureForRequest(ECSRequestMethod, &query, client.AccessKeySecret)
+
+ // Generate the request URL
+ requestURL := client.endpoint + "?" + query.Encode() + "&Signature=" + url.QueryEscape(signature)
+
+ httpReq, err := http.NewRequest(ECSRequestMethod, requestURL, nil)
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ // TODO move to util and add build val flag
+ httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
+
+ t0 := time.Now()
+ httpResp, err := client.httpClient.Do(httpReq)
+ t1 := time.Now()
+ if err != nil {
+ return GetClientError(err)
+ }
+ statusCode := httpResp.StatusCode
+
+ if client.debug {
+ log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
+ }
+
+ defer httpResp.Body.Close()
+ body, err := ioutil.ReadAll(httpResp.Body)
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ if client.debug {
+ var prettyJSON bytes.Buffer
+ err = json.Indent(&prettyJSON, body, "", " ")
+ log.Println(string(prettyJSON.Bytes()))
+ }
+
+ if statusCode >= 400 && statusCode <= 599 {
+ errorResponse := ErrorResponse{}
+ err = json.Unmarshal(body, &errorResponse)
+ ecsError := &Error{
+ ErrorResponse: errorResponse,
+ StatusCode: statusCode,
+ }
+ return ecsError
+ }
+
+ err = json.Unmarshal(body, response)
+ //log.Printf("%++v", response)
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ return nil
+}
+
+// Invoke sends the raw HTTP request for ECS services
+//改进了一下上面那个方法,可以使用各种Http方法
+//2017.1.30 增加了一个path参数,用来拓展访问的地址
+func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error {
+
+ request := Request{}
+ request.init(client.version, action, client.AccessKeyId)
+
+ data := util.ConvertToQueryValues(request)
+ util.SetQueryValues(args, &data)
+
+ // Sign request
+ signature := util.CreateSignatureForRequest(method, &data, client.AccessKeySecret)
+
+ data.Add("Signature", signature)
+ // Generate the request URL
+ var (
+ httpReq *http.Request
+ err error
+ )
+ if method == http.MethodGet {
+ requestURL := client.endpoint + path + "?" + data.Encode()
+ //fmt.Println(requestURL)
+ httpReq, err = http.NewRequest(method, requestURL, nil)
+ } else {
+ //fmt.Println(client.endpoint + path)
+ httpReq, err = http.NewRequest(method, client.endpoint+path, strings.NewReader(data.Encode()))
+ httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ }
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ // TODO move to util and add build val flag
+ httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
+
+ t0 := time.Now()
+ httpResp, err := client.httpClient.Do(httpReq)
+ t1 := time.Now()
+ if err != nil {
+ return GetClientError(err)
+ }
+ statusCode := httpResp.StatusCode
+
+ if client.debug {
+ log.Printf("Invoke %s %s %d (%v) %v", ECSRequestMethod, client.endpoint, statusCode, t1.Sub(t0), data.Encode())
+ }
+
+ defer httpResp.Body.Close()
+ body, err := ioutil.ReadAll(httpResp.Body)
+
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ if client.debug {
+ var prettyJSON bytes.Buffer
+ err = json.Indent(&prettyJSON, body, "", " ")
+ log.Println(string(prettyJSON.Bytes()))
+ }
+
+ if statusCode >= 400 && statusCode <= 599 {
+ errorResponse := ErrorResponse{}
+ err = json.Unmarshal(body, &errorResponse)
+ ecsError := &Error{
+ ErrorResponse: errorResponse,
+ StatusCode: statusCode,
+ }
+ return ecsError
+ }
+
+ err = json.Unmarshal(body, response)
+ //log.Printf("%++v", response)
+ if err != nil {
+ return GetClientError(err)
+ }
+
+ return nil
+}
+
+// GenerateClientToken generates the Client Token with random string
+func (client *Client) GenerateClientToken() string {
+ return util.CreateRandomString()
+}
+
+func GetClientErrorFromString(str string) error {
+ return &Error{
+ ErrorResponse: ErrorResponse{
+ Code: "AliyunGoClientFailure",
+ Message: str,
+ },
+ StatusCode: -1,
+ }
+}
+
+func GetClientError(err error) error {
+ return GetClientErrorFromString(err.Error())
+}
diff --git a/vendor/github.com/denverdino/aliyungo/common/endpoint.go b/vendor/github.com/denverdino/aliyungo/common/endpoint.go
new file mode 100644
index 000000000..16bcbf9d6
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/endpoint.go
@@ -0,0 +1,118 @@
+package common
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+)
+
+const (
+ // LocationDefaultEndpoint is the default API endpoint of Location services
+ locationDefaultEndpoint = "https://location.aliyuncs.com"
+ locationAPIVersion = "2015-06-12"
+ HTTP_PROTOCOL = "http"
+ HTTPS_PROTOCOL = "https"
+)
+
+var (
+ endpoints = make(map[Region]map[string]string)
+)
+
+//init endpoints from file
+func init() {
+
+}
+
+func NewLocationClient(accessKeyId, accessKeySecret string) *Client {
+ endpoint := os.Getenv("LOCATION_ENDPOINT")
+ if endpoint == "" {
+ endpoint = locationDefaultEndpoint
+ }
+
+ client := &Client{}
+ client.Init(endpoint, locationAPIVersion, accessKeyId, accessKeySecret)
+ return client
+}
+
+func (client *Client) DescribeEndpoint(args *DescribeEndpointArgs) (*DescribeEndpointResponse, error) {
+ response := &DescribeEndpointResponse{}
+ err := client.Invoke("DescribeEndpoint", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func getProductRegionEndpoint(region Region, serviceCode string) string {
+ if sp, ok := endpoints[region]; ok {
+ if endpoint, ok := sp[serviceCode]; ok {
+ return endpoint
+ }
+ }
+
+ return ""
+}
+
+func setProductRegionEndpoint(region Region, serviceCode string, endpoint string) {
+ endpoints[region] = map[string]string{
+ serviceCode: endpoint,
+ }
+}
+
+func (client *Client) DescribeOpenAPIEndpoint(region Region, serviceCode string) string {
+ if endpoint := getProductRegionEndpoint(region, serviceCode); endpoint != "" {
+ return endpoint
+ }
+
+ defaultProtocols := HTTP_PROTOCOL
+
+ args := &DescribeEndpointArgs{
+ Id: region,
+ ServiceCode: serviceCode,
+ Type: "openAPI",
+ }
+
+ endpoint, err := client.DescribeEndpoint(args)
+ if err != nil || endpoint.Endpoint == "" {
+ return ""
+ }
+
+ for _, protocol := range endpoint.Protocols.Protocols {
+ if strings.ToLower(protocol) == HTTPS_PROTOCOL {
+ defaultProtocols = HTTPS_PROTOCOL
+ break
+ }
+ }
+
+ ep := fmt.Sprintf("%s://%s", defaultProtocols, endpoint.Endpoint)
+
+ setProductRegionEndpoint(region, serviceCode, ep)
+ return ep
+}
+
+func loadEndpointFromFile(region Region, serviceCode string) string {
+ data, err := ioutil.ReadFile("./endpoints.xml")
+ if err != nil {
+ return ""
+ }
+
+ var endpoints Endpoints
+ err = xml.Unmarshal(data, &endpoints)
+ if err != nil {
+ return ""
+ }
+
+ for _, endpoint := range endpoints.Endpoint {
+ if endpoint.RegionIds.RegionId == string(region) {
+ for _, product := range endpoint.Products.Product {
+ if strings.ToLower(product.ProductName) == serviceCode {
+ return fmt.Sprintf("%s://%s", HTTPS_PROTOCOL, product.DomainName)
+ }
+ }
+ }
+ }
+
+ return ""
+}
diff --git a/vendor/github.com/denverdino/aliyungo/common/endpoints.xml b/vendor/github.com/denverdino/aliyungo/common/endpoints.xml
new file mode 100644
index 000000000..4079bcd2b
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/endpoints.xml
@@ -0,0 +1,1349 @@
+
+
+
+ jp-fudao-1
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ me-east-1
+
+ Rdsrds.me-east-1.aliyuncs.com
+ Ecsecs.me-east-1.aliyuncs.com
+ Vpcvpc.me-east-1.aliyuncs.com
+ Kmskms.me-east-1.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.me-east-1.aliyuncs.com
+
+
+
+ us-east-1
+
+ CScs.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ CFcf.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Stssts.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Onsons.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+
+
+
+ ap-northeast-1
+
+ Rdsrds.ap-northeast-1.aliyuncs.com
+ Kmskms.ap-northeast-1.aliyuncs.com
+ Vpcvpc.ap-northeast-1.aliyuncs.com
+ Ecsecs.ap-northeast-1.aliyuncs.com
+ Cmsmetrics.ap-northeast-1.aliyuncs.com
+ Kvstorer-kvstore.ap-northeast-1.aliyuncs.com
+ Slbslb.ap-northeast-1.aliyuncs.com
+
+
+
+ cn-hangzhou-bj-b01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-hongkong
+
+ Pushcloudpush.aliyuncs.com
+ COScos.aliyuncs.com
+ Onsons.aliyuncs.com
+ Essess.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ CScs.aliyuncs.com
+ Kmskms.cn-hongkong.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ HPChpc.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ CloudAPIapigateway.cn-hongkong.aliyuncs.com
+ Stssts.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-hongkong.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ Ossoss-cn-hongkong.aliyuncs.com
+
+
+
+ cn-beijing-nu16-b01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-beijing-am13-c01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ in-west-antgroup-1
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-guizhou-gov
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ in-west-antgroup-2
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-qingdao-cm9
+
+ CScs.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ AMSams.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Bssbss.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-qingdao.aliyuncs.com
+ CFcf.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Stssts.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Emremr.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ ROSros.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Cdncdn.aliyuncs.com
+
+
+
+ tw-snowcloud-kaohsiung
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-shanghai-finance-1
+
+ Kmskms.cn-shanghai-finance-1.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Rdsrds.aliyuncs.com
+
+
+
+ cn-guizhou
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ cn-qingdao-finance
+
+ Ossoss-cn-qdjbp-a.aliyuncs.com
+
+
+
+ cn-beijing-gov-1
+
+ Ossoss-cn-haidian-a.aliyuncs.com
+ Rdsrds.aliyuncs.com
+
+
+
+ cn-shanghai
+
+ ARMSarms.cn-shanghai.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ CScs.aliyuncs.com
+ Kmskms.cn-shanghai.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.cn-shanghai.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Omsoms.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Apigatewayapigateway.cn-shanghai.aliyuncs.com
+ CloudAPIapigateway.cn-shanghai.aliyuncs.com
+ Stssts.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-shanghai.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Essess.aliyuncs.com
+ Ossoss-cn-shanghai.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ vodvod.cn-shanghai.aliyuncs.com
+
+
+
+ cn-shenzhen-inner
+
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ Onsons.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ AMSams.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Stssts.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-shenzhen.aliyuncs.com
+ CFcf.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.aliyuncs.com
+ CScs.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ ROSros.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-fujian
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Rdsrds.aliyuncs.com
+
+
+
+ in-mumbai-alipay
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ us-west-1
+
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Stssts.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.us-west-1.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Onsons.aliyuncs.com
+ Ossoss-us-west-1.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+
+
+
+ cn-shanghai-inner
+
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.aliyuncs.com
+ Smssms.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-shanghai.aliyuncs.com
+ CFcf.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Stssts.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Onsons.aliyuncs.com
+ Cdncdn.aliyuncs.com
+
+
+
+ cn-anhui-gov-1
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-hangzhou-finance
+
+ Ossoss-cn-hzjbp-b-console.aliyuncs.com
+
+
+
+ cn-hangzhou
+
+ ARMSarms.cn-hangzhou.aliyuncs.com
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Stssts.aliyuncs.com
+ Smssms.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Livelive.aliyuncs.com
+ Kmskms.cn-hangzhou.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Hpchpc.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.cn-hangzhou.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ Apigatewayapigateway.cn-hangzhou.aliyuncs.com
+ CloudAPIapigateway.cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-hangzhou.aliyuncs.com
+ Oascn-hangzhou.oas.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Emremr.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Onsons.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+
+
+
+ cn-beijing-inner
+
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.aliyuncs.com
+ Smssms.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ CScs.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ Essess.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Dmdm.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ Stssts.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-beijing.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ CFcf.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ ROSros.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Cdncdn.aliyuncs.com
+
+
+
+ cn-haidian-cm12-c01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Rdsrds.aliyuncs.com
+
+
+
+ cn-anhui-gov
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ cn-shenzhen
+
+ ARMSarms.cn-shenzhen.aliyuncs.com
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Onsons.aliyuncs.com
+ Essess.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Kmskms.cn-shenzhen.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Iotiot.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Omsoms.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ BatchComputebatchcompute.cn-shenzhen.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Apigatewayapigateway.cn-shenzhen.aliyuncs.com
+ CloudAPIapigateway.cn-shenzhen.aliyuncs.com
+ Stssts.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-shenzhen.aliyuncs.com
+ Oascn-shenzhen.oas.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Ossoss-cn-shenzhen.aliyuncs.com
+
+
+
+ ap-southeast-2
+
+ Rdsrds.ap-southeast-2.aliyuncs.com
+ Kmskms.ap-southeast-2.aliyuncs.com
+ Vpcvpc.ap-southeast-2.aliyuncs.com
+ Ecsecs.ap-southeast-2.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.ap-southeast-2.aliyuncs.com
+
+
+
+ cn-qingdao
+
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.cn-qingdao.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Essess.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.cn-qingdao.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Omsoms.aliyuncs.com
+ Ubsmsubsms.cn-qingdao.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ BatchComputebatchcompute.cn-qingdao.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Apigatewayapigateway.cn-qingdao.aliyuncs.com
+ CloudAPIapigateway.cn-qingdao.aliyuncs.com
+ Stssts.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-qingdao.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ ROSros.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Ossoss-cn-qingdao.aliyuncs.com
+
+
+
+ cn-shenzhen-su18-b02
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-shenzhen-su18-b03
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-shenzhen-su18-b01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ ap-southeast-antgroup-1
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ oss-cn-bjzwy
+
+ Ossoss-cn-bjzwy.aliyuncs.com
+
+
+
+ cn-henan-am12001
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ cn-beijing
+
+ ARMSarms.cn-beijing.aliyuncs.com
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Stssts.aliyuncs.com
+ Smssms.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Oascn-beijing.oas.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Onsons.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Hpchpc.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Omsoms.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Apigatewayapigateway.cn-beijing.aliyuncs.com
+ CloudAPIapigateway.cn-beijing.aliyuncs.com
+ Kmskms.cn-beijing.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.cn-beijing.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Ossoss-cn-beijing.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ vodvod.cn-beijing.aliyuncs.com
+
+
+
+ cn-hangzhou-d
+
+ CScs.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Emremr.aliyuncs.com
+ Smssms.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Iotiot.aliyuncs.com
+ HPChpc.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-hangzhou.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ CFcf.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Stssts.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ ROSros.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+
+
+
+ cn-gansu-am6
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ Rdsrds.aliyuncs.com
+
+
+
+ cn-ningxiazhongwei
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ cn-shanghai-et2-b01
+
+ CScs.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ Onsons.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Jaqjaq.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Ace-opsace-ops.cn-hangzhou.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ AMSams.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Mtsmts.cn-hangzhou.aliyuncs.com
+ CFcf.aliyuncs.com
+ Acsacs.aliyun-inc.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Stssts.aliyuncs.com
+ HPChpc.aliyuncs.com
+ Emremr.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.aliyuncs.com
+ Slbslb.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ ROSros.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Vpc-innervpc-inner.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Ossoss-cn-hangzhou.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+
+
+
+ cn-ningxia-am7-c01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ cn-shenzhen-finance-1
+
+ Kmskms.cn-shenzhen-finance-1.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+
+
+
+ ap-southeast-1
+
+ CScs.aliyuncs.com
+ Riskrisk-cn-hangzhou.aliyuncs.com
+ COScos.aliyuncs.com
+ Essess.aliyuncs.com
+ Billingbilling.aliyuncs.com
+ Dqsdqs.aliyuncs.com
+ Ddsmongodb.aliyuncs.com
+ Alidnsalidns.aliyuncs.com
+ Smssms.aliyuncs.com
+ Drdsdrds.aliyuncs.com
+ Dtsdts.aliyuncs.com
+ Kmskms.ap-southeast-1.aliyuncs.com
+ Locationlocation.aliyuncs.com
+ Msgmsg-inner.aliyuncs.com
+ ChargingServicechargingservice.aliyuncs.com
+ R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com
+ Alertalert.aliyuncs.com
+ Mscmsc-inner.aliyuncs.com
+ HighDDosyd-highddos-cn-hangzhou.aliyuncs.com
+ Yundunyundun-cn-hangzhou.aliyuncs.com
+ Ubsms-innerubsms-inner.aliyuncs.com
+ Ocsm-kvstore.aliyuncs.com
+ Dmdm.aliyuncs.com
+ Greengreen.aliyuncs.com
+ Commondrivercommon.driver.aliyuncs.com
+ oceanbaseoceanbase.aliyuncs.com
+ Workorderworkorder.aliyuncs.com
+ Yundunhsmyundunhsm.aliyuncs.com
+ Iotiot.aliyuncs.com
+ HPChpc.aliyuncs.com
+ jaqjaq.aliyuncs.com
+ Omsoms.aliyuncs.com
+ livelive.aliyuncs.com
+ Ecsecs-cn-hangzhou.aliyuncs.com
+ M-kvstorem-kvstore.aliyuncs.com
+ Vpcvpc.aliyuncs.com
+ BatchComputebatchCompute.aliyuncs.com
+ AMSams.aliyuncs.com
+ ROSros.aliyuncs.com
+ PTSpts.aliyuncs.com
+ Qualitycheckqualitycheck.aliyuncs.com
+ Bssbss.aliyuncs.com
+ Ubsmsubsms.aliyuncs.com
+ Apigatewayapigateway.ap-southeast-1.aliyuncs.com
+ CloudAPIapigateway.ap-southeast-1.aliyuncs.com
+ Stssts.aliyuncs.com
+ CmsSiteMonitorsitemonitor.aliyuncs.com
+ Aceace.cn-hangzhou.aliyuncs.com
+ Mtsmts.ap-southeast-1.aliyuncs.com
+ CFcf.aliyuncs.com
+ Crmcrm-cn-hangzhou.aliyuncs.com
+ Location-innerlocation-inner.aliyuncs.com
+ Aasaas.aliyuncs.com
+ Emremr.ap-southeast-1.aliyuncs.com
+ Httpdnshttpdns-api.aliyuncs.com
+ Drcdrc.aliyuncs.com
+ Pushcloudpush.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.aliyuncs.com
+ YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com
+ Domaindomain.aliyuncs.com
+ Otsots-pop.aliyuncs.com
+ Cdncdn.aliyuncs.com
+ Ramram.aliyuncs.com
+ Salessales.cn-hangzhou.aliyuncs.com
+ Rdsrds.aliyuncs.com
+ OssAdminoss-admin.aliyuncs.com
+ Onsons.aliyuncs.com
+ Ossoss-ap-southeast-1.aliyuncs.com
+
+
+
+ cn-shenzhen-st4-d01
+
+ Ecsecs-cn-hangzhou.aliyuncs.com
+
+
+
+ eu-central-1
+
+ Rdsrds.eu-central-1.aliyuncs.com
+ Ecsecs.eu-central-1.aliyuncs.com
+ Vpcvpc.eu-central-1.aliyuncs.com
+ Kmskms.eu-central-1.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.eu-central-1.aliyuncs.com
+
+
+
+ cn-zhangjiakou
+
+ Rdsrds.cn-zhangjiakou.aliyuncs.com
+ Ecsecs.cn-zhangjiakou.aliyuncs.com
+ Vpcvpc.cn-zhangjiakou.aliyuncs.com
+ Cmsmetrics.cn-hangzhou.aliyuncs.com
+ Slbslb.cn-zhangjiakou.aliyuncs.com
+
+
+
diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go
new file mode 100644
index 000000000..62e6e9d81
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/regions.go
@@ -0,0 +1,34 @@
+package common
+
+// Region represents ECS region
+type Region string
+
+// Constants of region definition
+const (
+ Hangzhou = Region("cn-hangzhou")
+ Qingdao = Region("cn-qingdao")
+ Beijing = Region("cn-beijing")
+ Hongkong = Region("cn-hongkong")
+ Shenzhen = Region("cn-shenzhen")
+ Shanghai = Region("cn-shanghai")
+ Zhangjiakou = Region("cn-zhangjiakou")
+
+ APSouthEast1 = Region("ap-southeast-1")
+ APNorthEast1 = Region("ap-northeast-1")
+ APSouthEast2 = Region("ap-southeast-2")
+
+ USWest1 = Region("us-west-1")
+ USEast1 = Region("us-east-1")
+
+ MEEast1 = Region("me-east-1")
+
+ EUCentral1 = Region("eu-central-1")
+)
+
+var ValidRegions = []Region{
+ Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, Zhangjiakou,
+ USWest1, USEast1,
+ APNorthEast1, APSouthEast1, APSouthEast2,
+ MEEast1,
+ EUCentral1,
+}
diff --git a/vendor/github.com/denverdino/aliyungo/common/request.go b/vendor/github.com/denverdino/aliyungo/common/request.go
new file mode 100644
index 000000000..2a883f19b
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/request.go
@@ -0,0 +1,101 @@
+package common
+
+import (
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/denverdino/aliyungo/util"
+)
+
+// Constants for Aliyun API requests
+const (
+ SignatureVersion = "1.0"
+ SignatureMethod = "HMAC-SHA1"
+ JSONResponseFormat = "JSON"
+ XMLResponseFormat = "XML"
+ ECSRequestMethod = "GET"
+)
+
+type Request struct {
+ Format string
+ Version string
+ AccessKeyId string
+ Signature string
+ SignatureMethod string
+ Timestamp util.ISO6801Time
+ SignatureVersion string
+ SignatureNonce string
+ ResourceOwnerAccount string
+ Action string
+}
+
+func (request *Request) init(version string, action string, AccessKeyId string) {
+ request.Format = JSONResponseFormat
+ request.Timestamp = util.NewISO6801Time(time.Now().UTC())
+ request.Version = version
+ request.SignatureVersion = SignatureVersion
+ request.SignatureMethod = SignatureMethod
+ request.SignatureNonce = util.CreateRandomString()
+ request.Action = action
+ request.AccessKeyId = AccessKeyId
+}
+
+type Response struct {
+ RequestId string
+}
+
+type ErrorResponse struct {
+ Response
+ HostId string
+ Code string
+ Message string
+}
+
+// An Error represents a custom error for Aliyun API failure response
+type Error struct {
+ ErrorResponse
+ StatusCode int //Status Code of HTTP Response
+}
+
+func (e *Error) Error() string {
+ return fmt.Sprintf("Aliyun API Error: RequestId: %s Status Code: %d Code: %s Message: %s", e.RequestId, e.StatusCode, e.Code, e.Message)
+}
+
+type Pagination struct {
+ PageNumber int
+ PageSize int
+}
+
+func (p *Pagination) SetPageSize(size int) {
+ p.PageSize = size
+}
+
+func (p *Pagination) Validate() {
+ if p.PageNumber < 0 {
+ log.Printf("Invalid PageNumber: %d", p.PageNumber)
+ p.PageNumber = 1
+ }
+ if p.PageSize < 0 {
+ log.Printf("Invalid PageSize: %d", p.PageSize)
+ p.PageSize = 10
+ } else if p.PageSize > 50 {
+ log.Printf("Invalid PageSize: %d", p.PageSize)
+ p.PageSize = 50
+ }
+}
+
+// A PaginationResponse represents a response with pagination information
+type PaginationResult struct {
+ TotalCount int
+ PageNumber int
+ PageSize int
+}
+
+// NextPage gets the next page of the result set
+func (r *PaginationResult) NextPage() *Pagination {
+ if r.PageNumber*r.PageSize >= r.TotalCount {
+ return nil
+ }
+ return &Pagination{PageNumber: r.PageNumber + 1, PageSize: r.PageSize}
+}
diff --git a/vendor/github.com/denverdino/aliyungo/common/types.go b/vendor/github.com/denverdino/aliyungo/common/types.go
new file mode 100644
index 000000000..a74e150e9
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/types.go
@@ -0,0 +1,89 @@
+package common
+
+type InternetChargeType string
+
+const (
+ PayByBandwidth = InternetChargeType("PayByBandwidth")
+ PayByTraffic = InternetChargeType("PayByTraffic")
+)
+
+type InstanceChargeType string
+
+const (
+ PrePaid = InstanceChargeType("PrePaid")
+ PostPaid = InstanceChargeType("PostPaid")
+)
+
+type DescribeEndpointArgs struct {
+ Id Region
+ ServiceCode string
+ Type string
+}
+
+type EndpointItem struct {
+ Protocols struct {
+ Protocols []string
+ }
+ Type string
+ Namespace string
+ Id Region
+ SerivceCode string
+ Endpoint string
+}
+
+type DescribeEndpointResponse struct {
+ Response
+ EndpointItem
+}
+
+type NetType string
+
+const (
+ Internet = NetType("Internet")
+ Intranet = NetType("Intranet")
+)
+
+type TimeType string
+
+const (
+ Hour = TimeType("Hour")
+ Day = TimeType("Day")
+ Month = TimeType("Month")
+ Year = TimeType("Year")
+)
+
+type NetworkType string
+
+const (
+ Classic = NetworkType("Classic")
+ VPC = NetworkType("VPC")
+)
+
+type BusinessInfo struct {
+ Pack string `json:"pack,omitempty"`
+ ActivityId string `json:"activityId,omitempty"`
+}
+
+//xml
+type Endpoints struct {
+ Endpoint []Endpoint `xml:"Endpoint"`
+}
+
+type Endpoint struct {
+ Name string `xml:"name,attr"`
+ RegionIds RegionIds `xml:"RegionIds"`
+ Products Products `xml:"Products"`
+}
+
+type RegionIds struct {
+ RegionId string `xml:"RegionId"`
+}
+
+type Products struct {
+ Product []Product `xml:"Product"`
+}
+
+type Product struct {
+ ProductName string `xml:"ProductName"`
+ DomainName string `xml:"DomainName"`
+}
diff --git a/vendor/github.com/denverdino/aliyungo/common/version.go b/vendor/github.com/denverdino/aliyungo/common/version.go
new file mode 100644
index 000000000..7cb3d3aff
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/common/version.go
@@ -0,0 +1,3 @@
+package common
+
+const Version = "0.1"
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go
new file mode 100644
index 000000000..d70a1554e
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go
@@ -0,0 +1,74 @@
+package ecs
+
+import (
+ "os"
+
+ "github.com/denverdino/aliyungo/common"
+)
+
+// Interval for checking status in WaitForXXX method
+const DefaultWaitForInterval = 5
+
+// Default timeout value for WaitForXXX method
+const DefaultTimeout = 60
+
+type Client struct {
+ common.Client
+}
+
+const (
+ // ECSDefaultEndpoint is the default API endpoint of ECS services
+ ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com"
+ ECSAPIVersion = "2014-05-26"
+
+ ECSServiceCode = "ecs"
+
+ VPCDefaultEndpoint = "https://vpc.aliyuncs.com"
+ VPCAPIVersion = "2016-04-28"
+ VPCServiceCode = "vpc"
+)
+
+// NewClient creates a new instance of ECS client
+func NewClient(accessKeyId, accessKeySecret string) *Client {
+ endpoint := os.Getenv("ECS_ENDPOINT")
+ if endpoint == "" {
+ endpoint = ECSDefaultEndpoint
+ }
+ return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
+}
+
+func NewECSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ endpoint := os.Getenv("ECS_ENDPOINT")
+ if endpoint == "" {
+ endpoint = ECSDefaultEndpoint
+ }
+
+ return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID)
+}
+
+func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ client := &Client{}
+ client.NewInit(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret, ECSServiceCode, regionID)
+ return client
+}
+
+func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client {
+ client := &Client{}
+ client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret)
+ return client
+}
+
+func NewVPCClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ endpoint := os.Getenv("VPC_ENDPOINT")
+ if endpoint == "" {
+ endpoint = VPCDefaultEndpoint
+ }
+
+ return NewVPCClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID)
+}
+
+func NewVPCClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ client := &Client{}
+ client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID)
+ return client
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/disks.go b/vendor/github.com/denverdino/aliyungo/ecs/disks.go
new file mode 100644
index 000000000..f1d1e9341
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/disks.go
@@ -0,0 +1,330 @@
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+// Types of disks
+type DiskType string
+
+const (
+ DiskTypeAll = DiskType("all") //Default
+ DiskTypeAllSystem = DiskType("system")
+ DiskTypeAllData = DiskType("data")
+)
+
+// Categories of disks
+type DiskCategory string
+
+const (
+ DiskCategoryAll = DiskCategory("all") //Default
+ DiskCategoryCloud = DiskCategory("cloud")
+ DiskCategoryEphemeral = DiskCategory("ephemeral")
+ DiskCategoryEphemeralSSD = DiskCategory("ephemeral_ssd")
+ DiskCategoryCloudEfficiency = DiskCategory("cloud_efficiency")
+ DiskCategoryCloudSSD = DiskCategory("cloud_ssd")
+)
+
+// Status of disks
+type DiskStatus string
+
+const (
+ DiskStatusInUse = DiskStatus("In_use")
+ DiskStatusAvailable = DiskStatus("Available")
+ DiskStatusAttaching = DiskStatus("Attaching")
+ DiskStatusDetaching = DiskStatus("Detaching")
+ DiskStatusCreating = DiskStatus("Creating")
+ DiskStatusReIniting = DiskStatus("ReIniting")
+ DiskStatusAll = DiskStatus("All") //Default
+)
+
+// Charge type of disks
+type DiskChargeType string
+
+const (
+ PrePaid = DiskChargeType("PrePaid")
+ PostPaid = DiskChargeType("PostPaid")
+)
+
+// A DescribeDisksArgs defines the arguments to describe disks
+type DescribeDisksArgs struct {
+ RegionId common.Region
+ ZoneId string
+ DiskIds []string
+ InstanceId string
+ DiskType DiskType //enum for all(default) | system | data
+ Category DiskCategory //enum for all(default) | cloud | ephemeral
+ Status DiskStatus //enum for In_use | Available | Attaching | Detaching | Creating | ReIniting | All(default)
+ SnapshotId string
+ Name string
+ Portable *bool //optional
+ DeleteWithInstance *bool //optional
+ DeleteAutoSnapshot *bool //optional
+ EnableAutoSnapshot *bool //optional
+ DiskChargeType DiskChargeType
+ Tag map[string]string
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskitemtype
+type DiskItemType struct {
+ DiskId string
+ RegionId common.Region
+ ZoneId string
+ DiskName string
+ Description string
+ Type DiskType
+ Category DiskCategory
+ Size int
+ ImageId string
+ SourceSnapshotId string
+ ProductCode string
+ Portable bool
+ Status DiskStatus
+ OperationLocks OperationLocksType
+ InstanceId string
+ Device string
+ DeleteWithInstance bool
+ DeleteAutoSnapshot bool
+ EnableAutoSnapshot bool
+ CreationTime util.ISO6801Time
+ AttachedTime util.ISO6801Time
+ DetachedTime util.ISO6801Time
+ DiskChargeType DiskChargeType
+}
+
+type DescribeDisksResponse struct {
+ common.Response
+ common.PaginationResult
+ RegionId common.Region
+ Disks struct {
+ Disk []DiskItemType
+ }
+}
+
+// DescribeDisks describes Disks
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&describedisks
+func (client *Client) DescribeDisks(args *DescribeDisksArgs) (disks []DiskItemType, pagination *common.PaginationResult, err error) {
+ response := DescribeDisksResponse{}
+
+ err = client.Invoke("DescribeDisks", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return response.Disks.Disk, &response.PaginationResult, err
+}
+
+type CreateDiskArgs struct {
+ RegionId common.Region
+ ZoneId string
+ DiskName string
+ Description string
+ DiskCategory DiskCategory
+ Size int
+ SnapshotId string
+ ClientToken string
+}
+
+type CreateDisksResponse struct {
+ common.Response
+ DiskId string
+}
+
+// CreateDisk creates a new disk
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&createdisk
+func (client *Client) CreateDisk(args *CreateDiskArgs) (diskId string, err error) {
+ response := CreateDisksResponse{}
+ err = client.Invoke("CreateDisk", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.DiskId, err
+}
+
+type DeleteDiskArgs struct {
+ DiskId string
+}
+
+type DeleteDiskResponse struct {
+ common.Response
+}
+
+// DeleteDisk deletes disk
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&deletedisk
+func (client *Client) DeleteDisk(diskId string) error {
+ args := DeleteDiskArgs{
+ DiskId: diskId,
+ }
+ response := DeleteDiskResponse{}
+ err := client.Invoke("DeleteDisk", &args, &response)
+ return err
+}
+
+type ReInitDiskArgs struct {
+ DiskId string
+}
+
+type ReInitDiskResponse struct {
+ common.Response
+}
+
+// ReInitDisk reinitizes disk
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&reinitdisk
+func (client *Client) ReInitDisk(diskId string) error {
+ args := ReInitDiskArgs{
+ DiskId: diskId,
+ }
+ response := ReInitDiskResponse{}
+ err := client.Invoke("ReInitDisk", &args, &response)
+ return err
+}
+
+type AttachDiskArgs struct {
+ InstanceId string
+ DiskId string
+ Device string
+ DeleteWithInstance bool
+}
+
+type AttachDiskResponse struct {
+ common.Response
+}
+
+// AttachDisk attaches disk to instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&attachdisk
+func (client *Client) AttachDisk(args *AttachDiskArgs) error {
+ response := AttachDiskResponse{}
+ err := client.Invoke("AttachDisk", args, &response)
+ return err
+}
+
+type DetachDiskArgs struct {
+ InstanceId string
+ DiskId string
+}
+
+type DetachDiskResponse struct {
+ common.Response
+}
+
+// DetachDisk detaches disk from instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&detachdisk
+func (client *Client) DetachDisk(instanceId string, diskId string) error {
+ args := DetachDiskArgs{
+ InstanceId: instanceId,
+ DiskId: diskId,
+ }
+ response := DetachDiskResponse{}
+ err := client.Invoke("DetachDisk", &args, &response)
+ return err
+}
+
+type ResetDiskArgs struct {
+ DiskId string
+ SnapshotId string
+}
+
+type ResetDiskResponse struct {
+ common.Response
+}
+
+// ResetDisk resets disk to original status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&resetdisk
+func (client *Client) ResetDisk(diskId string, snapshotId string) error {
+ args := ResetDiskArgs{
+ SnapshotId: snapshotId,
+ DiskId: diskId,
+ }
+ response := ResetDiskResponse{}
+ err := client.Invoke("ResetDisk", &args, &response)
+ return err
+}
+
+type ModifyDiskAttributeArgs struct {
+ DiskId string
+ DiskName string
+ Description string
+ DeleteWithInstance *bool
+ DeleteAutoSnapshot *bool
+ EnableAutoSnapshot *bool
+}
+
+type ModifyDiskAttributeResponse struct {
+ common.Response
+}
+
+// ModifyDiskAttribute modifies disk attribute
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/disk&modifydiskattribute
+func (client *Client) ModifyDiskAttribute(args *ModifyDiskAttributeArgs) error {
+ response := ModifyDiskAttributeResponse{}
+ err := client.Invoke("ModifyDiskAttribute", args, &response)
+ return err
+}
+
+type ReplaceSystemDiskArgs struct {
+ InstanceId string
+ ImageId string
+ SystemDisk SystemDiskType
+ ClientToken string
+}
+
+type ReplaceSystemDiskResponse struct {
+ common.Response
+ DiskId string
+}
+
+// ReplaceSystemDisk replace system disk
+//
+// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/disk/replacesystemdisk.html
+func (client *Client) ReplaceSystemDisk(args *ReplaceSystemDiskArgs) (diskId string, err error) {
+ response := ReplaceSystemDiskResponse{}
+ err = client.Invoke("ReplaceSystemDisk", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.DiskId, nil
+}
+
+// WaitForDisk waits for disk to given status
+func (client *Client) WaitForDisk(regionId common.Region, diskId string, status DiskStatus, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+ args := DescribeDisksArgs{
+ RegionId: regionId,
+ DiskIds: []string{diskId},
+ }
+
+ for {
+ disks, _, err := client.DescribeDisks(&args)
+ if err != nil {
+ return err
+ }
+ if disks == nil || len(disks) == 0 {
+ return common.GetClientErrorFromString("Not found")
+ }
+ if disks[0].Status == status {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/forward_entry.go b/vendor/github.com/denverdino/aliyungo/ecs/forward_entry.go
new file mode 100644
index 000000000..2a316e18e
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/forward_entry.go
@@ -0,0 +1,104 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type CreateForwardEntryArgs struct {
+ RegionId common.Region
+ ForwardTableId string
+ ExternalIp string
+ ExternalPort string
+ IpProtocol string
+ InternalIp string
+ InternalPort string
+}
+
+type CreateForwardEntryResponse struct {
+ common.Response
+ ForwardEntryId string
+}
+
+type DescribeForwardTableEntriesArgs struct {
+ RegionId common.Region
+ ForwardTableId string
+ common.Pagination
+}
+
+type ForwardTableEntrySetType struct {
+ RegionId common.Region
+ ExternalIp string
+ ExternalPort string
+ ForwardEntryId string
+ ForwardTableId string
+ InternalIp string
+ InternalPort string
+ IpProtocol string
+ Status string
+}
+
+type DescribeForwardTableEntriesResponse struct {
+ common.Response
+ common.PaginationResult
+ ForwardTableEntries struct {
+ ForwardTableEntry []ForwardTableEntrySetType
+ }
+}
+
+type ModifyForwardEntryArgs struct {
+ RegionId common.Region
+ ForwardTableId string
+ ForwardEntryId string
+ ExternalIp string
+ IpProtocol string
+ ExternalPort string
+ InternalIp string
+ InternalPort string
+}
+
+type ModifyForwardEntryResponse struct {
+ common.Response
+}
+
+type DeleteForwardEntryArgs struct {
+ RegionId common.Region
+ ForwardTableId string
+ ForwardEntryId string
+}
+
+type DeleteForwardEntryResponse struct {
+ common.Response
+}
+
+func (client *Client) CreateForwardEntry(args *CreateForwardEntryArgs) (resp *CreateForwardEntryResponse, err error) {
+ response := CreateForwardEntryResponse{}
+ err = client.Invoke("CreateForwardEntry", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+func (client *Client) DescribeForwardTableEntries(args *DescribeForwardTableEntriesArgs) (forwardTableEntries []ForwardTableEntrySetType,
+ pagination *common.PaginationResult, err error) {
+
+ args.Validate()
+ response := DescribeForwardTableEntriesResponse{}
+
+ err = client.Invoke("DescribeForwardTableEntries", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return response.ForwardTableEntries.ForwardTableEntry, &response.PaginationResult, nil
+}
+
+func (client *Client) ModifyForwardEntry(args *ModifyForwardEntryArgs) error {
+ response := ModifyForwardEntryResponse{}
+ return client.Invoke("ModifyForwardEntry", args, &response)
+}
+
+func (client *Client) DeleteForwardEntry(args *DeleteForwardEntryArgs) error {
+ response := DeleteForwardEntryResponse{}
+ err := client.Invoke("DeleteForwardEntry", args, &response)
+ return err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/images.go b/vendor/github.com/denverdino/aliyungo/ecs/images.go
new file mode 100644
index 000000000..0a4e1e2c0
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/images.go
@@ -0,0 +1,317 @@
+package ecs
+
+import (
+ "net/url"
+ "strconv"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+// ImageOwnerAlias represents image owner
+type ImageOwnerAlias string
+
+// Constants of image owner
+const (
+ ImageOwnerSystem = ImageOwnerAlias("system")
+ ImageOwnerSelf = ImageOwnerAlias("self")
+ ImageOwnerOthers = ImageOwnerAlias("others")
+ ImageOwnerMarketplace = ImageOwnerAlias("marketplace")
+ ImageOwnerDefault = ImageOwnerAlias("") //Return the values for system, self, and others
+)
+
+type ImageStatus string
+
+const (
+ ImageStatusAvailable = ImageStatus("Available")
+ ImageStatusUnAvailable = ImageStatus("UnAvailable")
+ ImageStatusCreating = ImageStatus("Creating")
+ ImageStatusCreateFailed = ImageStatus("CreateFailed")
+)
+
+type ImageUsage string
+
+const (
+ ImageUsageInstance = ImageUsage("instance")
+ ImageUsageNone = ImageUsage("none")
+)
+
+// DescribeImagesArgs repsents arguements to describe images
+type DescribeImagesArgs struct {
+ RegionId common.Region
+ ImageId string
+ SnapshotId string
+ ImageName string
+ Status ImageStatus
+ ImageOwnerAlias ImageOwnerAlias
+ common.Pagination
+}
+
+type DescribeImagesResponse struct {
+ common.Response
+ common.PaginationResult
+
+ RegionId common.Region
+ Images struct {
+ Image []ImageType
+ }
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskdevicemapping
+type DiskDeviceMapping struct {
+ SnapshotId string
+ //Why Size Field is string-type.
+ Size string
+ Device string
+ //For import images
+ Format string
+ OSSBucket string
+ OSSObject string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&imagetype
+type ImageType struct {
+ ImageId string
+ ImageVersion string
+ Architecture string
+ ImageName string
+ Description string
+ Size int
+ ImageOwnerAlias string
+ OSName string
+ OSType string
+ Platform string
+ DiskDeviceMappings struct {
+ DiskDeviceMapping []DiskDeviceMapping
+ }
+ ProductCode string
+ IsSubscribed bool
+ IsSelfShared string
+ IsCopied bool
+ IsSupportIoOptimized bool
+ Progress string
+ Usage ImageUsage
+ Status ImageStatus
+ CreationTime util.ISO6801Time
+}
+
+// DescribeImages describes images
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&describeimages
+func (client *Client) DescribeImages(args *DescribeImagesArgs) (images []ImageType, pagination *common.PaginationResult, err error) {
+
+ args.Validate()
+ response := DescribeImagesResponse{}
+ err = client.Invoke("DescribeImages", args, &response)
+ if err != nil {
+ return nil, nil, err
+ }
+ return response.Images.Image, &response.PaginationResult, nil
+}
+
+// CreateImageArgs repsents arguements to create image
+type CreateImageArgs struct {
+ RegionId common.Region
+ SnapshotId string
+ InstanceId string
+ ImageName string
+ ImageVersion string
+ Description string
+ ClientToken string
+}
+
+type CreateImageResponse struct {
+ common.Response
+
+ ImageId string
+}
+
+// CreateImage creates a new image
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&createimage
+func (client *Client) CreateImage(args *CreateImageArgs) (imageId string, err error) {
+ response := &CreateImageResponse{}
+ err = client.Invoke("CreateImage", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.ImageId, nil
+}
+
+type DeleteImageArgs struct {
+ RegionId common.Region
+ ImageId string
+}
+
+type DeleteImageResponse struct {
+ common.Response
+}
+
+// DeleteImage deletes Image
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/image&deleteimage
+func (client *Client) DeleteImage(regionId common.Region, imageId string) error {
+ args := DeleteImageArgs{
+ RegionId: regionId,
+ ImageId: imageId,
+ }
+
+ response := &DeleteImageResponse{}
+ return client.Invoke("DeleteImage", &args, &response)
+}
+
+// ModifyImageSharePermission repsents arguements to share image
+type ModifyImageSharePermissionArgs struct {
+ RegionId common.Region
+ ImageId string
+ AddAccount []string
+ RemoveAccount []string
+}
+
+// You can read doc at http://help.aliyun.com/document_detail/ecs/open-api/image/modifyimagesharepermission.html
+func (client *Client) ModifyImageSharePermission(args *ModifyImageSharePermissionArgs) error {
+ req := url.Values{}
+ req.Add("RegionId", string(args.RegionId))
+ req.Add("ImageId", args.ImageId)
+
+ for i, item := range args.AddAccount {
+ req.Add("AddAccount."+strconv.Itoa(i+1), item)
+ }
+ for i, item := range args.RemoveAccount {
+ req.Add("RemoveAccount."+strconv.Itoa(i+1), item)
+ }
+
+ return client.Invoke("ModifyImageSharePermission", req, &common.Response{})
+}
+
+type AccountType struct {
+ AliyunId string
+}
+type ImageSharePermissionResponse struct {
+ common.Response
+ ImageId string
+ RegionId string
+ Accounts struct {
+ Account []AccountType
+ }
+ TotalCount int
+ PageNumber int
+ PageSize int
+}
+
+func (client *Client) DescribeImageSharePermission(args *ModifyImageSharePermissionArgs) (*ImageSharePermissionResponse, error) {
+ response := ImageSharePermissionResponse{}
+ err := client.Invoke("DescribeImageSharePermission", args, &response)
+ return &response, err
+}
+
+type CopyImageArgs struct {
+ RegionId common.Region
+ ImageId string
+ DestinationRegionId common.Region
+ DestinationImageName string
+ DestinationDescription string
+ ClientToken string
+}
+
+type CopyImageResponse struct {
+ common.Response
+ ImageId string
+}
+
+// You can read doc at https://help.aliyun.com/document_detail/25538.html
+func (client *Client) CopyImage(args *CopyImageArgs) (string, error) {
+ response := &CopyImageResponse{}
+ err := client.Invoke("CopyImage", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.ImageId, nil
+}
+
+
+// ImportImageArgs repsents arguements to import image from oss
+type ImportImageArgs struct {
+ RegionId common.Region
+ ImageName string
+ ImageVersion string
+ Description string
+ ClientToken string
+ Architecture string
+ OSType string
+ Platform string
+ DiskDeviceMappings struct {
+ DiskDeviceMapping []DiskDeviceMapping
+ }
+}
+
+func (client *Client) ImportImage(args *ImportImageArgs) (string, error) {
+ response := &CopyImageResponse{}
+ err := client.Invoke("ImportImage", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.ImageId, nil
+}
+
+type ImportImageResponse struct {
+ common.Response
+ RegionId common.Region
+ ImageId string
+ ImportTaskId string
+}
+
+// Default timeout value for WaitForImageReady method
+const ImageDefaultTimeout = 120
+
+//Wait Image ready
+func (client *Client) WaitForImageReady(regionId common.Region, imageId string, timeout int) error {
+ if timeout <= 0 {
+ timeout = ImageDefaultTimeout
+ }
+ for {
+ args := DescribeImagesArgs{
+ RegionId: regionId,
+ ImageId: imageId,
+ Status: ImageStatusCreating,
+ }
+
+ images, _, err := client.DescribeImages(&args)
+ if err != nil {
+ return err
+ }
+ if images == nil || len(images) == 0 {
+ args.Status = ImageStatusAvailable
+ images, _, er := client.DescribeImages(&args)
+ if er == nil && len(images) == 1 {
+ break
+ } else {
+ return common.GetClientErrorFromString("Not found")
+ }
+ }
+ if images[0].Progress == "100%" {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
+
+type CancelCopyImageRequest struct {
+ regionId common.Region
+ ImageId string
+}
+
+// You can read doc at https://help.aliyun.com/document_detail/25539.html
+func (client *Client) CancelCopyImage(regionId common.Region, imageId string) error {
+ response := &common.Response{}
+ err := client.Invoke("CancelCopyImage", &CancelCopyImageRequest{regionId, imageId}, &response)
+ return err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go
new file mode 100644
index 000000000..a133806f1
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/instance_types.go
@@ -0,0 +1,82 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type DescribeInstanceTypesArgs struct {
+ InstanceTypeFamily string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype
+type InstanceTypeItemType struct {
+ InstanceTypeId string
+ CpuCoreCount int
+ MemorySize float64
+ InstanceTypeFamily string
+}
+
+type DescribeInstanceTypesResponse struct {
+ common.Response
+ InstanceTypes struct {
+ InstanceType []InstanceTypeItemType
+ }
+}
+
+// DescribeInstanceTypes describes all instance types
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/other&describeinstancetypes
+func (client *Client) DescribeInstanceTypes() (instanceTypes []InstanceTypeItemType, err error) {
+ response := DescribeInstanceTypesResponse{}
+
+ err = client.Invoke("DescribeInstanceTypes", &DescribeInstanceTypesArgs{}, &response)
+
+ if err != nil {
+ return []InstanceTypeItemType{}, err
+ }
+ return response.InstanceTypes.InstanceType, nil
+
+}
+
+// support user args
+func (client *Client) DescribeInstanceTypesNew(args *DescribeInstanceTypesArgs) (instanceTypes []InstanceTypeItemType, err error) {
+ response := DescribeInstanceTypesResponse{}
+
+ err = client.Invoke("DescribeInstanceTypes", args, &response)
+
+ if err != nil {
+ return []InstanceTypeItemType{}, err
+ }
+ return response.InstanceTypes.InstanceType, nil
+
+}
+
+type DescribeInstanceTypeFamiliesArgs struct {
+ RegionId common.Region
+ Generation string
+}
+
+type InstanceTypeFamilies struct {
+ InstanceTypeFamily []InstanceTypeFamily
+}
+
+type InstanceTypeFamily struct {
+ InstanceTypeFamilyId string
+ Generation string
+}
+
+type DescribeInstanceTypeFamiliesResponse struct {
+ common.Response
+
+ InstanceTypeFamilies InstanceTypeFamilies
+}
+
+func (client *Client) DescribeInstanceTypeFamilies(args *DescribeInstanceTypeFamiliesArgs) (*DescribeInstanceTypeFamiliesResponse, error) {
+ response := &DescribeInstanceTypeFamiliesResponse{}
+
+ err := client.Invoke("DescribeInstanceTypeFamilies", args, response)
+ if err != nil {
+ return nil, err
+ }
+
+ return response, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go
new file mode 100644
index 000000000..d127d55ab
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go
@@ -0,0 +1,607 @@
+package ecs
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "strconv"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+// InstanceStatus represents instance status
+type InstanceStatus string
+
+// Constants of InstanceStatus
+const (
+ Creating = InstanceStatus("Creating") // For backward compatability
+ Pending = InstanceStatus("Pending")
+ Running = InstanceStatus("Running")
+ Starting = InstanceStatus("Starting")
+
+ Stopped = InstanceStatus("Stopped")
+ Stopping = InstanceStatus("Stopping")
+ Deleted = InstanceStatus("Deleted")
+)
+
+type LockReason string
+
+const (
+ LockReasonFinancial = LockReason("financial")
+ LockReasonSecurity = LockReason("security")
+)
+
+type LockReasonType struct {
+ LockReason LockReason
+}
+
+type DescribeUserdataArgs struct {
+ RegionId common.Region
+ InstanceId string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
+type DescribeUserdataItemType struct {
+ UserData string
+ InstanceId string
+ RegionId string
+}
+
+type DescribeUserdataResponse struct {
+ common.Response
+ DescribeUserdataItemType
+}
+
+// DescribeInstanceStatus describes instance status
+//
+// You can read doc at https://intl.aliyun.com/help/doc-detail/49227.htm
+func (client *Client) DescribeUserdata(args *DescribeUserdataArgs) (userData *DescribeUserdataItemType, err error) {
+ response := DescribeUserdataResponse{}
+
+ err = client.Invoke("DescribeUserdata", args, &response)
+
+ if err == nil {
+ return &response.DescribeUserdataItemType, nil
+ }
+
+ return nil, err
+}
+
+type DescribeInstanceStatusArgs struct {
+ RegionId common.Region
+ ZoneId string
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancestatusitemtype
+type InstanceStatusItemType struct {
+ InstanceId string
+ Status InstanceStatus
+}
+
+type DescribeInstanceStatusResponse struct {
+ common.Response
+ common.PaginationResult
+ InstanceStatuses struct {
+ InstanceStatus []InstanceStatusItemType
+ }
+}
+
+// DescribeInstanceStatus describes instance status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancestatus
+func (client *Client) DescribeInstanceStatus(args *DescribeInstanceStatusArgs) (instanceStatuses []InstanceStatusItemType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeInstanceStatusResponse{}
+
+ err = client.Invoke("DescribeInstanceStatus", args, &response)
+
+ if err == nil {
+ return response.InstanceStatuses.InstanceStatus, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type StopInstanceArgs struct {
+ InstanceId string
+ ForceStop bool
+}
+
+type StopInstanceResponse struct {
+ common.Response
+}
+
+// StopInstance stops instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&stopinstance
+func (client *Client) StopInstance(instanceId string, forceStop bool) error {
+ args := StopInstanceArgs{
+ InstanceId: instanceId,
+ ForceStop: forceStop,
+ }
+ response := StopInstanceResponse{}
+ err := client.Invoke("StopInstance", &args, &response)
+ return err
+}
+
+type StartInstanceArgs struct {
+ InstanceId string
+}
+
+type StartInstanceResponse struct {
+ common.Response
+}
+
+// StartInstance starts instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&startinstance
+func (client *Client) StartInstance(instanceId string) error {
+ args := StartInstanceArgs{InstanceId: instanceId}
+ response := StartInstanceResponse{}
+ err := client.Invoke("StartInstance", &args, &response)
+ return err
+}
+
+type RebootInstanceArgs struct {
+ InstanceId string
+ ForceStop bool
+}
+
+type RebootInstanceResponse struct {
+ common.Response
+}
+
+// RebootInstance reboot instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&rebootinstance
+func (client *Client) RebootInstance(instanceId string, forceStop bool) error {
+ request := RebootInstanceArgs{
+ InstanceId: instanceId,
+ ForceStop: forceStop,
+ }
+ response := RebootInstanceResponse{}
+ err := client.Invoke("RebootInstance", &request, &response)
+ return err
+}
+
+type DescribeInstanceAttributeArgs struct {
+ InstanceId string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&operationlockstype
+type OperationLocksType struct {
+ LockReason []LockReasonType //enum for financial, security
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupidsettype
+type SecurityGroupIdSetType struct {
+ SecurityGroupId string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&ipaddresssettype
+type IpAddressSetType struct {
+ IpAddress []string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcattributestype
+type VpcAttributesType struct {
+ VpcId string
+ VSwitchId string
+ PrivateIpAddress IpAddressSetType
+ NatIpAddress string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddressassociatetype
+type EipAddressAssociateType struct {
+ AllocationId string
+ IpAddress string
+ Bandwidth int
+ InternetChargeType common.InternetChargeType
+}
+
+// Experimental feature
+type SpotStrategyType string
+
+// Constants of SpotStrategyType
+const (
+ NoSpot = SpotStrategyType("NoSpot")
+ SpotWithPriceLimit = SpotStrategyType("SpotWithPriceLimit")
+)
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
+type InstanceAttributesType struct {
+ InstanceId string
+ InstanceName string
+ Description string
+ ImageId string
+ RegionId common.Region
+ ZoneId string
+ CPU int
+ Memory int
+ ClusterId string
+ InstanceType string
+ InstanceTypeFamily string
+ HostName string
+ SerialNumber string
+ Status InstanceStatus
+ OperationLocks OperationLocksType
+ SecurityGroupIds struct {
+ SecurityGroupId []string
+ }
+ PublicIpAddress IpAddressSetType
+ InnerIpAddress IpAddressSetType
+ InstanceNetworkType string //enum Classic | Vpc
+ InternetMaxBandwidthIn int
+ InternetMaxBandwidthOut int
+ InternetChargeType common.InternetChargeType
+ CreationTime util.ISO6801Time //time.Time
+ VpcAttributes VpcAttributesType
+ EipAddress EipAddressAssociateType
+ IoOptimized StringOrBool
+ InstanceChargeType common.InstanceChargeType
+ ExpiredTime util.ISO6801Time
+ Tags struct {
+ Tag []TagItemType
+ }
+ SpotStrategy SpotStrategyType
+}
+
+type DescribeInstanceAttributeResponse struct {
+ common.Response
+ InstanceAttributesType
+}
+
+// DescribeInstanceAttribute describes instance attribute
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstanceattribute
+func (client *Client) DescribeInstanceAttribute(instanceId string) (instance *InstanceAttributesType, err error) {
+ args := DescribeInstanceAttributeArgs{InstanceId: instanceId}
+
+ response := DescribeInstanceAttributeResponse{}
+ err = client.Invoke("DescribeInstanceAttribute", &args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response.InstanceAttributesType, err
+}
+
+type ModifyInstanceAttributeArgs struct {
+ InstanceId string
+ InstanceName string
+ Description string
+ Password string
+ HostName string
+ UserData string
+}
+
+type ModifyInstanceAttributeResponse struct {
+ common.Response
+}
+
+//ModifyInstanceAttribute modify instance attrbute
+//
+// You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/modifyinstanceattribute.html
+func (client *Client) ModifyInstanceAttribute(args *ModifyInstanceAttributeArgs) error {
+ response := ModifyInstanceAttributeResponse{}
+ err := client.Invoke("ModifyInstanceAttribute", args, &response)
+ return err
+}
+
+// Default timeout value for WaitForInstance method
+const InstanceDefaultTimeout = 120
+
+// WaitForInstance waits for instance to given status
+func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error {
+ if timeout <= 0 {
+ timeout = InstanceDefaultTimeout
+ }
+ for {
+ instance, err := client.DescribeInstanceAttribute(instanceId)
+ if err != nil {
+ return err
+ }
+ if instance.Status == status {
+ //TODO
+ //Sleep one more time for timing issues
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+
+ }
+ return nil
+}
+
+// WaitForInstance waits for instance to given status
+// when instance.NotFound wait until timeout
+func (client *Client) WaitForInstanceAsyn(instanceId string, status InstanceStatus, timeout int) error {
+ if timeout <= 0 {
+ timeout = InstanceDefaultTimeout
+ }
+ for {
+ instance, err := client.DescribeInstanceAttribute(instanceId)
+ if err != nil {
+ e, _ := err.(*common.Error)
+ if e.Code != "InvalidInstanceId.NotFound" && e.Code != "Forbidden.InstanceNotFound" {
+ return err
+ }
+ } else if instance != nil && instance.Status == status {
+ //TODO
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+
+ }
+ return nil
+}
+
+type DescribeInstanceVncUrlArgs struct {
+ RegionId common.Region
+ InstanceId string
+}
+
+type DescribeInstanceVncUrlResponse struct {
+ common.Response
+ VncUrl string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstancevncurl
+func (client *Client) DescribeInstanceVncUrl(args *DescribeInstanceVncUrlArgs) (string, error) {
+ response := DescribeInstanceVncUrlResponse{}
+
+ err := client.Invoke("DescribeInstanceVncUrl", args, &response)
+
+ if err == nil {
+ return response.VncUrl, nil
+ }
+
+ return "", err
+}
+
+type DescribeInstancesArgs struct {
+ RegionId common.Region
+ VpcId string
+ VSwitchId string
+ ZoneId string
+ InstanceIds string
+ InstanceNetworkType string
+ InstanceName string
+ Status InstanceStatus
+ PrivateIpAddresses string
+ InnerIpAddresses string
+ PublicIpAddresses string
+ SecurityGroupId string
+ Tag map[string]string
+ InstanceType string
+ SpotStrategy SpotStrategyType
+ common.Pagination
+}
+
+type DescribeInstancesResponse struct {
+ common.Response
+ common.PaginationResult
+ Instances struct {
+ Instance []InstanceAttributesType
+ }
+}
+
+// DescribeInstances describes instances
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&describeinstances
+func (client *Client) DescribeInstances(args *DescribeInstancesArgs) (instances []InstanceAttributesType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeInstancesResponse{}
+
+ err = client.Invoke("DescribeInstances", args, &response)
+
+ if err == nil {
+ return response.Instances.Instance, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type DeleteInstanceArgs struct {
+ InstanceId string
+}
+
+type DeleteInstanceResponse struct {
+ common.Response
+}
+
+// DeleteInstance deletes instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&deleteinstance
+func (client *Client) DeleteInstance(instanceId string) error {
+ args := DeleteInstanceArgs{InstanceId: instanceId}
+ response := DeleteInstanceResponse{}
+ err := client.Invoke("DeleteInstance", &args, &response)
+ return err
+}
+
+type DataDiskType struct {
+ Size int
+ Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
+ SnapshotId string
+ DiskName string
+ Description string
+ Device string
+ DeleteWithInstance bool
+}
+
+type SystemDiskType struct {
+ Size int
+ Category DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
+ DiskName string
+ Description string
+}
+
+type IoOptimized string
+
+type StringOrBool struct {
+ Value bool
+}
+
+// UnmarshalJSON implements the json.Unmarshaller interface.
+func (io *StringOrBool) UnmarshalJSON(value []byte) error {
+ if value[0] == '"' {
+ var str string
+ err := json.Unmarshal(value, &str)
+ if err == nil {
+ io.Value = (str == "true" || str == "optimized")
+ }
+ return err
+ }
+ var boolVal bool
+ err := json.Unmarshal(value, &boolVal)
+ if err == nil {
+ io.Value = boolVal
+ }
+ return err
+}
+
+func (io StringOrBool) Bool() bool {
+ return io.Value
+}
+
+func (io StringOrBool) String() string {
+ return strconv.FormatBool(io.Value)
+}
+
+var (
+ IoOptimizedNone = IoOptimized("none")
+ IoOptimizedOptimized = IoOptimized("optimized")
+)
+
+type CreateInstanceArgs struct {
+ RegionId common.Region
+ ZoneId string
+ ImageId string
+ InstanceType string
+ SecurityGroupId string
+ InstanceName string
+ Description string
+ InternetChargeType common.InternetChargeType
+ InternetMaxBandwidthIn int
+ InternetMaxBandwidthOut int
+ HostName string
+ Password string
+ IoOptimized IoOptimized
+ SystemDisk SystemDiskType
+ DataDisk []DataDiskType
+ VSwitchId string
+ PrivateIpAddress string
+ ClientToken string
+ InstanceChargeType common.InstanceChargeType
+ Period int
+ UserData string
+ AutoRenew bool
+ AutoRenewPeriod int
+ SpotStrategy SpotStrategyType
+}
+
+type CreateInstanceResponse struct {
+ common.Response
+ InstanceId string
+}
+
+// CreateInstance creates instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
+func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId string, err error) {
+ if args.UserData != "" {
+ // Encode to base64 string
+ args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData))
+ }
+ response := CreateInstanceResponse{}
+ err = client.Invoke("CreateInstance", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.InstanceId, err
+}
+
+type RunInstanceArgs struct {
+ CreateInstanceArgs
+ MinAmount int
+ MaxAmount int
+ AutoReleaseTime string
+ NetworkType string
+ InnerIpAddress string
+ BusinessInfo string
+}
+
+type RunInstanceResponse struct {
+ common.Response
+ InstanceIdSets InstanceIdSets
+}
+
+type InstanceIdSets struct {
+ InstanceIdSet []string
+}
+
+type BusinessInfo struct {
+ Pack string `json:"pack,omitempty"`
+ ActivityId string `json:"activityId,omitempty"`
+}
+
+func (client *Client) RunInstances(args *RunInstanceArgs) (instanceIdSet []string, err error) {
+ if args.UserData != "" {
+ // Encode to base64 string
+ args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData))
+ }
+ response := RunInstanceResponse{}
+ err = client.Invoke("RunInstances", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return response.InstanceIdSets.InstanceIdSet, err
+}
+
+type SecurityGroupArgs struct {
+ InstanceId string
+ SecurityGroupId string
+}
+
+type SecurityGroupResponse struct {
+ common.Response
+}
+
+//JoinSecurityGroup
+//
+//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/joinsecuritygroup.html
+func (client *Client) JoinSecurityGroup(instanceId string, securityGroupId string) error {
+ args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
+ response := SecurityGroupResponse{}
+ err := client.Invoke("JoinSecurityGroup", &args, &response)
+ return err
+}
+
+//LeaveSecurityGroup
+//
+//You can read doc at https://help.aliyun.com/document_detail/ecs/open-api/instance/leavesecuritygroup.html
+func (client *Client) LeaveSecurityGroup(instanceId string, securityGroupId string) error {
+ args := SecurityGroupArgs{InstanceId: instanceId, SecurityGroupId: securityGroupId}
+ response := SecurityGroupResponse{}
+ err := client.Invoke("LeaveSecurityGroup", &args, &response)
+ return err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go
new file mode 100644
index 000000000..6123d28b7
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/monitoring.go
@@ -0,0 +1,136 @@
+package ecs
+
+import (
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type DescribeInstanceMonitorDataArgs struct {
+ InstanceId string
+ StartTime util.ISO6801Time
+ EndTime util.ISO6801Time
+ Period int //Default 60s
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancemonitordatatype
+type InstanceMonitorDataType struct {
+ InstanceId string
+ CPU int
+ IntranetRX int
+ IntranetTX int
+ IntranetBandwidth int
+ InternetRX int
+ InternetTX int
+ InternetBandwidth int
+ IOPSRead int
+ IOPSWrite int
+ BPSRead int
+ BPSWrite int
+ TimeStamp util.ISO6801Time
+}
+
+type DescribeInstanceMonitorDataResponse struct {
+ common.Response
+ MonitorData struct {
+ InstanceMonitorData []InstanceMonitorDataType
+ }
+}
+
+// DescribeInstanceMonitorData describes instance monitoring data
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeinstancemonitordata
+func (client *Client) DescribeInstanceMonitorData(args *DescribeInstanceMonitorDataArgs) (monitorData []InstanceMonitorDataType, err error) {
+ if args.Period == 0 {
+ args.Period = 60
+ }
+ response := DescribeInstanceMonitorDataResponse{}
+ err = client.Invoke("DescribeInstanceMonitorData", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return response.MonitorData.InstanceMonitorData, err
+}
+
+type DescribeEipMonitorDataArgs struct {
+ AllocationId string
+ StartTime util.ISO6801Time
+ EndTime util.ISO6801Time
+ Period int //Default 60s
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipmonitordatatype
+type EipMonitorDataType struct {
+ EipRX int
+ EipTX int
+ EipFlow int
+ EipBandwidth int
+ EipPackets int
+ TimeStamp util.ISO6801Time
+}
+
+type DescribeEipMonitorDataResponse struct {
+ common.Response
+ EipMonitorDatas struct {
+ EipMonitorData []EipMonitorDataType
+ }
+}
+
+// DescribeEipMonitorData describes EIP monitoring data
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describeeipmonitordata
+func (client *Client) DescribeEipMonitorData(args *DescribeEipMonitorDataArgs) (monitorData []EipMonitorDataType, err error) {
+ if args.Period == 0 {
+ args.Period = 60
+ }
+ response := DescribeEipMonitorDataResponse{}
+ err = client.Invoke("DescribeEipMonitorData", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return response.EipMonitorDatas.EipMonitorData, err
+}
+
+type DescribeDiskMonitorDataArgs struct {
+ DiskId string
+ StartTime util.ISO6801Time
+ EndTime util.ISO6801Time
+ Period int //Default 60s
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&diskmonitordatatype
+type DiskMonitorDataType struct {
+ DiskId string
+ IOPSRead int
+ IOPSWrite int
+ IOPSTotal int
+ BPSRead int
+ BPSWrite int
+ BPSTotal int
+ TimeStamp util.ISO6801Time
+}
+
+type DescribeDiskMonitorDataResponse struct {
+ common.Response
+ TotalCount int
+ MonitorData struct {
+ DiskMonitorData []DiskMonitorDataType
+ }
+}
+
+// DescribeDiskMonitorData describes disk monitoring data
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/monitor&describediskmonitordata
+func (client *Client) DescribeDiskMonitorData(args *DescribeDiskMonitorDataArgs) (monitorData []DiskMonitorDataType, totalCount int, err error) {
+ if args.Period == 0 {
+ args.Period = 60
+ }
+ response := DescribeDiskMonitorDataResponse{}
+ err = client.Invoke("DescribeDiskMonitorData", args, &response)
+ if err != nil {
+ return nil, 0, err
+ }
+ return response.MonitorData.DiskMonitorData, response.TotalCount, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go b/vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go
new file mode 100644
index 000000000..dfcb74d32
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go
@@ -0,0 +1,202 @@
+package ecs
+
+import (
+ "github.com/denverdino/aliyungo/common"
+)
+
+type BandwidthPackageType struct {
+ IpCount int
+ Bandwidth int
+ Zone string
+}
+
+type CreateNatGatewayArgs struct {
+ RegionId common.Region
+ VpcId string
+ Spec string
+ BandwidthPackage []BandwidthPackageType
+ Name string
+ Description string
+ ClientToken string
+}
+
+type ForwardTableIdType struct {
+ ForwardTableId []string
+}
+
+type SnatTableIdType struct {
+ SnatTableId []string
+}
+
+type BandwidthPackageIdType struct {
+ BandwidthPackageId []string
+}
+
+type CreateNatGatewayResponse struct {
+ common.Response
+ NatGatewayId string
+ ForwardTableIds ForwardTableIdType
+ BandwidthPackageIds BandwidthPackageIdType
+}
+
+// CreateNatGateway creates Virtual Private Cloud
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc
+func (client *Client) CreateNatGateway(args *CreateNatGatewayArgs) (resp *CreateNatGatewayResponse, err error) {
+ response := CreateNatGatewayResponse{}
+ err = client.Invoke("CreateNatGateway", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+type NatGatewaySetType struct {
+ BusinessStatus string
+ Description string
+ BandwidthPackageIds BandwidthPackageIdType
+ ForwardTableIds ForwardTableIdType
+ SnatTableIds SnatTableIdType
+ InstanceChargeType string
+ Name string
+ NatGatewayId string
+ RegionId common.Region
+ Spec string
+ Status string
+ VpcId string
+}
+
+type DescribeNatGatewayResponse struct {
+ common.Response
+ common.PaginationResult
+ NatGateways struct {
+ NatGateway []NatGatewaySetType
+ }
+}
+
+type DescribeNatGatewaysArgs struct {
+ RegionId common.Region
+ NatGatewayId string
+ VpcId string
+ common.Pagination
+}
+
+func (client *Client) DescribeNatGateways(args *DescribeNatGatewaysArgs) (natGateways []NatGatewaySetType,
+ pagination *common.PaginationResult, err error) {
+
+ args.Validate()
+ response := DescribeNatGatewayResponse{}
+
+ err = client.Invoke("DescribeNatGateways", args, &response)
+
+ if err == nil {
+ return response.NatGateways.NatGateway, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type ModifyNatGatewayAttributeArgs struct {
+ RegionId common.Region
+ NatGatewayId string
+ Name string
+ Description string
+}
+
+type ModifyNatGatewayAttributeResponse struct {
+ common.Response
+}
+
+func (client *Client) ModifyNatGatewayAttribute(args *ModifyNatGatewayAttributeArgs) error {
+ response := ModifyNatGatewayAttributeResponse{}
+ return client.Invoke("ModifyNatGatewayAttribute", args, &response)
+}
+
+type ModifyNatGatewaySpecArgs struct {
+ RegionId common.Region
+ NatGatewayId string
+ Spec NatGatewaySpec
+}
+
+func (client *Client) ModifyNatGatewaySpec(args *ModifyNatGatewaySpecArgs) error {
+ response := ModifyNatGatewayAttributeResponse{}
+ return client.Invoke("ModifyNatGatewaySpec", args, &response)
+}
+
+type DeleteNatGatewayArgs struct {
+ RegionId common.Region
+ NatGatewayId string
+}
+
+type DeleteNatGatewayResponse struct {
+ common.Response
+}
+
+func (client *Client) DeleteNatGateway(args *DeleteNatGatewayArgs) error {
+ response := DeleteNatGatewayResponse{}
+ err := client.Invoke("DeleteNatGateway", args, &response)
+ return err
+}
+
+type DescribeBandwidthPackagesArgs struct {
+ RegionId common.Region
+ BandwidthPackageId string
+ NatGatewayId string
+}
+
+type PublicIpAddresseType struct {
+ AllocationId string
+ IpAddress string
+}
+
+type DescribeBandwidthPackageType struct {
+ Bandwidth string
+ BandwidthPackageId string
+ IpCount string
+ PublicIpAddresses struct {
+ PublicIpAddresse []PublicIpAddresseType
+ }
+
+ ZoneId string
+}
+
+type DescribeBandwidthPackagesResponse struct {
+ common.Response
+ BandwidthPackages struct {
+ BandwidthPackage []DescribeBandwidthPackageType
+ }
+}
+
+func (client *Client) DescribeBandwidthPackages(args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) {
+ response := &DescribeBandwidthPackagesResponse{}
+
+ err := client.Invoke("DescribeBandwidthPackages", args, response)
+ if err != nil {
+ return nil, err
+ }
+
+ return response.BandwidthPackages.BandwidthPackage, err
+}
+
+type DeleteBandwidthPackageArgs struct {
+ RegionId common.Region
+ BandwidthPackageId string
+}
+
+type DeleteBandwidthPackageResponse struct {
+ common.Response
+}
+
+func (client *Client) DeleteBandwidthPackage(args *DeleteBandwidthPackageArgs) error {
+ response := DeleteBandwidthPackageResponse{}
+ err := client.Invoke("DeleteBandwidthPackage", args, &response)
+ return err
+}
+
+type NatGatewaySpec string
+
+const (
+ NatGatewaySmallSpec = NatGatewaySpec("Small")
+ NatGatewayMiddleSpec = NatGatewaySpec("Middle")
+ NatGatewayLargeSpec = NatGatewaySpec("Large")
+)
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/networks.go b/vendor/github.com/denverdino/aliyungo/ecs/networks.go
new file mode 100644
index 000000000..fecc7af1a
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/networks.go
@@ -0,0 +1,249 @@
+// API on Network
+
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type AllocatePublicIpAddressArgs struct {
+ InstanceId string
+}
+
+type AllocatePublicIpAddressResponse struct {
+ common.Response
+
+ IpAddress string
+}
+
+// AllocatePublicIpAddress allocates Public Ip Address
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocatepublicipaddress
+func (client *Client) AllocatePublicIpAddress(instanceId string) (ipAddress string, err error) {
+ args := AllocatePublicIpAddressArgs{
+ InstanceId: instanceId,
+ }
+ response := AllocatePublicIpAddressResponse{}
+ err = client.Invoke("AllocatePublicIpAddress", &args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.IpAddress, nil
+}
+
+type ModifyInstanceNetworkSpec struct {
+ InstanceId string
+ InternetMaxBandwidthOut *int
+ InternetMaxBandwidthIn *int
+}
+
+type ModifyInstanceNetworkSpecResponse struct {
+ common.Response
+}
+
+// ModifyInstanceNetworkSpec modifies instance network spec
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyinstancenetworkspec
+func (client *Client) ModifyInstanceNetworkSpec(args *ModifyInstanceNetworkSpec) error {
+
+ response := ModifyInstanceNetworkSpecResponse{}
+ return client.Invoke("ModifyInstanceNetworkSpec", args, &response)
+}
+
+type AllocateEipAddressArgs struct {
+ RegionId common.Region
+ Bandwidth int
+ InternetChargeType common.InternetChargeType
+ ClientToken string
+}
+
+type AllocateEipAddressResponse struct {
+ common.Response
+ EipAddress string
+ AllocationId string
+}
+
+// AllocateEipAddress allocates Eip Address
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&allocateeipaddress
+func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddress string, AllocationId string, err error) {
+ if args.Bandwidth == 0 {
+ args.Bandwidth = 5
+ }
+ response := AllocateEipAddressResponse{}
+ err = client.Invoke("AllocateEipAddress", args, &response)
+ if err != nil {
+ return "", "", err
+ }
+ return response.EipAddress, response.AllocationId, nil
+}
+
+type AssociateEipAddressArgs struct {
+ AllocationId string
+ InstanceId string
+}
+
+type AssociateEipAddressResponse struct {
+ common.Response
+}
+
+// AssociateEipAddress associates EIP address to VM instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress
+func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error {
+ args := AssociateEipAddressArgs{
+ AllocationId: allocationId,
+ InstanceId: instanceId,
+ }
+ response := ModifyInstanceNetworkSpecResponse{}
+ return client.Invoke("AssociateEipAddress", &args, &response)
+}
+
+// Status of disks
+type EipStatus string
+
+const (
+ EipStatusAssociating = EipStatus("Associating")
+ EipStatusUnassociating = EipStatus("Unassociating")
+ EipStatusInUse = EipStatus("InUse")
+ EipStatusAvailable = EipStatus("Available")
+)
+
+type DescribeEipAddressesArgs struct {
+ RegionId common.Region
+ Status EipStatus //enum Associating | Unassociating | InUse | Available
+ EipAddress string
+ AllocationId string
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&eipaddresssettype
+type EipAddressSetType struct {
+ RegionId common.Region
+ IpAddress string
+ AllocationId string
+ Status EipStatus
+ InstanceId string
+ Bandwidth string // Why string
+ InternetChargeType common.InternetChargeType
+ OperationLocks OperationLocksType
+ AllocationTime util.ISO6801Time
+}
+
+type DescribeEipAddressesResponse struct {
+ common.Response
+ common.PaginationResult
+ EipAddresses struct {
+ EipAddress []EipAddressSetType
+ }
+}
+
+// DescribeInstanceStatus describes instance status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&describeeipaddresses
+func (client *Client) DescribeEipAddresses(args *DescribeEipAddressesArgs) (eipAddresses []EipAddressSetType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeEipAddressesResponse{}
+
+ err = client.Invoke("DescribeEipAddresses", args, &response)
+
+ if err == nil {
+ return response.EipAddresses.EipAddress, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type ModifyEipAddressAttributeArgs struct {
+ AllocationId string
+ Bandwidth int
+}
+
+type ModifyEipAddressAttributeResponse struct {
+ common.Response
+}
+
+// ModifyEipAddressAttribute Modifies EIP attribute
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&modifyeipaddressattribute
+func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth int) error {
+ args := ModifyEipAddressAttributeArgs{
+ AllocationId: allocationId,
+ Bandwidth: bandwidth,
+ }
+ response := ModifyEipAddressAttributeResponse{}
+ return client.Invoke("ModifyEipAddressAttribute", &args, &response)
+}
+
+type UnallocateEipAddressArgs struct {
+ AllocationId string
+ InstanceId string
+}
+
+type UnallocateEipAddressResponse struct {
+ common.Response
+}
+
+// UnassociateEipAddress unallocates Eip Address from instance
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&unassociateeipaddress
+func (client *Client) UnassociateEipAddress(allocationId string, instanceId string) error {
+ args := UnallocateEipAddressArgs{
+ AllocationId: allocationId,
+ InstanceId: instanceId,
+ }
+ response := UnallocateEipAddressResponse{}
+ return client.Invoke("UnassociateEipAddress", &args, &response)
+}
+
+type ReleaseEipAddressArgs struct {
+ AllocationId string
+}
+
+type ReleaseEipAddressResponse struct {
+ common.Response
+}
+
+// ReleaseEipAddress releases Eip address
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&releaseeipaddress
+func (client *Client) ReleaseEipAddress(allocationId string) error {
+ args := ReleaseEipAddressArgs{
+ AllocationId: allocationId,
+ }
+ response := ReleaseEipAddressResponse{}
+ return client.Invoke("ReleaseEipAddress", &args, &response)
+}
+
+// WaitForVSwitchAvailable waits for VSwitch to given status
+func (client *Client) WaitForEip(regionId common.Region, allocationId string, status EipStatus, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+ args := DescribeEipAddressesArgs{
+ RegionId: regionId,
+ AllocationId: allocationId,
+ }
+ for {
+ eips, _, err := client.DescribeEipAddresses(&args)
+ if err != nil {
+ return err
+ }
+ if len(eips) == 0 {
+ return common.GetClientErrorFromString("Not found")
+ }
+ if eips[0].Status == status {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/regions.go b/vendor/github.com/denverdino/aliyungo/ecs/regions.go
new file mode 100644
index 000000000..d8ed72e8d
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/regions.go
@@ -0,0 +1,34 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type DescribeRegionsArgs struct {
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype
+type RegionType struct {
+ RegionId common.Region
+ LocalName string
+}
+
+type DescribeRegionsResponse struct {
+ common.Response
+ Regions struct {
+ Region []RegionType
+ }
+}
+
+// DescribeRegions describes regions
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/region&describeregions
+func (client *Client) DescribeRegions() (regions []RegionType, err error) {
+ response := DescribeRegionsResponse{}
+
+ err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response)
+
+ if err != nil {
+ return []RegionType{}, err
+ }
+ return response.Regions.Region, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go
new file mode 100644
index 000000000..01f43127c
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/route_tables.go
@@ -0,0 +1,176 @@
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type DescribeRouteTablesArgs struct {
+ VRouterId string
+ RouteTableId string
+ common.Pagination
+}
+
+type RouteTableType string
+
+const (
+ RouteTableSystem = RouteTableType("System")
+ RouteTableCustom = RouteTableType("Custom")
+)
+
+type RouteEntryStatus string
+
+const (
+ RouteEntryStatusPending = RouteEntryStatus("Pending")
+ RouteEntryStatusAvailable = RouteEntryStatus("Available")
+ RouteEntryStatusModifying = RouteEntryStatus("Modifying")
+)
+
+type NextHopListType struct {
+ NextHopList struct {
+ NextHopItem []NextHopItemType
+ }
+}
+
+type NextHopItemType struct {
+ NextHopType string
+ NextHopId string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routeentrysettype
+type RouteEntrySetType struct {
+ RouteTableId string
+ DestinationCidrBlock string
+ Type RouteTableType
+ NextHopType string
+ NextHopId string
+ NextHopList NextHopListType
+ InstanceId string
+ Status RouteEntryStatus // enum Pending | Available | Modifying
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&routetablesettype
+type RouteTableSetType struct {
+ VRouterId string
+ RouteTableId string
+ RouteEntrys struct {
+ RouteEntry []RouteEntrySetType
+ }
+ RouteTableType RouteTableType
+ CreationTime util.ISO6801Time
+}
+
+type DescribeRouteTablesResponse struct {
+ common.Response
+ common.PaginationResult
+ RouteTables struct {
+ RouteTable []RouteTableSetType
+ }
+}
+
+// DescribeRouteTables describes Virtual Routers
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&describeroutetables
+func (client *Client) DescribeRouteTables(args *DescribeRouteTablesArgs) (routeTables []RouteTableSetType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeRouteTablesResponse{}
+
+ err = client.Invoke("DescribeRouteTables", args, &response)
+
+ if err == nil {
+ return response.RouteTables.RouteTable, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type NextHopType string
+
+const (
+ NextHopIntance = NextHopType("Instance") //Default
+ NextHopTunnel = NextHopType("Tunnel")
+)
+
+type CreateRouteEntryArgs struct {
+ RouteTableId string
+ DestinationCidrBlock string
+ NextHopType NextHopType
+ NextHopId string
+ ClientToken string
+}
+
+type CreateRouteEntryResponse struct {
+ common.Response
+}
+
+// CreateRouteEntry creates route entry
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&createrouteentry
+func (client *Client) CreateRouteEntry(args *CreateRouteEntryArgs) error {
+ response := CreateRouteEntryResponse{}
+ return client.Invoke("CreateRouteEntry", args, &response)
+}
+
+type DeleteRouteEntryArgs struct {
+ RouteTableId string
+ DestinationCidrBlock string
+ NextHopId string
+}
+
+type DeleteRouteEntryResponse struct {
+ common.Response
+}
+
+// DeleteRouteEntry deletes route entry
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/routertable&deleterouteentry
+func (client *Client) DeleteRouteEntry(args *DeleteRouteEntryArgs) error {
+ response := DeleteRouteEntryResponse{}
+ return client.Invoke("DeleteRouteEntry", args, &response)
+}
+
+// WaitForAllRouteEntriesAvailable waits for all route entries to Available status
+func (client *Client) WaitForAllRouteEntriesAvailable(vrouterId string, routeTableId string, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+ args := DescribeRouteTablesArgs{
+ VRouterId: vrouterId,
+ RouteTableId: routeTableId,
+ }
+ for {
+
+ routeTables, _, err := client.DescribeRouteTables(&args)
+
+ if err != nil {
+ return err
+ }
+ if len(routeTables) == 0 {
+ return common.GetClientErrorFromString("Not found")
+ }
+ success := true
+
+ loop:
+ for _, routeTable := range routeTables {
+ for _, routeEntry := range routeTable.RouteEntrys.RouteEntry {
+ if routeEntry.Status != RouteEntryStatusAvailable {
+ success = false
+ break loop
+ }
+ }
+ }
+ if success {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go
new file mode 100644
index 000000000..eaec701de
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go
@@ -0,0 +1,273 @@
+package ecs
+
+import (
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type NicType string
+
+const (
+ NicTypeInternet = NicType("internet")
+ NicTypeIntranet = NicType("intranet")
+)
+
+type IpProtocol string
+
+const (
+ IpProtocolAll = IpProtocol("all")
+ IpProtocolTCP = IpProtocol("tcp")
+ IpProtocolUDP = IpProtocol("udp")
+ IpProtocolICMP = IpProtocol("icmp")
+ IpProtocolGRE = IpProtocol("gre")
+)
+
+type PermissionPolicy string
+
+const (
+ PermissionPolicyAccept = PermissionPolicy("accept")
+ PermissionPolicyDrop = PermissionPolicy("drop")
+)
+
+type DescribeSecurityGroupAttributeArgs struct {
+ SecurityGroupId string
+ RegionId common.Region
+ NicType NicType //enum for internet (default) |intranet
+ Direction string // enum ingress egress
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&permissiontype
+type PermissionType struct {
+ IpProtocol IpProtocol
+ PortRange string
+ SourceCidrIp string
+ SourceGroupId string
+ SourceGroupOwnerAccount string
+ DestCidrIp string
+ DestGroupId string
+ DestGroupOwnerAccount string
+ Policy PermissionPolicy
+ NicType NicType
+ Priority int
+ Direction string
+ Description string
+}
+
+type DescribeSecurityGroupAttributeResponse struct {
+ common.Response
+
+ SecurityGroupId string
+ SecurityGroupName string
+ RegionId common.Region
+ Description string
+ Permissions struct {
+ Permission []PermissionType
+ }
+ VpcId string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroupattribute
+func (client *Client) DescribeSecurityGroupAttribute(args *DescribeSecurityGroupAttributeArgs) (response *DescribeSecurityGroupAttributeResponse, err error) {
+ response = &DescribeSecurityGroupAttributeResponse{}
+ err = client.Invoke("DescribeSecurityGroupAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, nil
+}
+
+type DescribeSecurityGroupsArgs struct {
+ RegionId common.Region
+ VpcId string
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&securitygroupitemtype
+type SecurityGroupItemType struct {
+ SecurityGroupId string
+ SecurityGroupName string
+ Description string
+ VpcId string
+ CreationTime util.ISO6801Time
+}
+
+type DescribeSecurityGroupsResponse struct {
+ common.Response
+ common.PaginationResult
+
+ RegionId common.Region
+ SecurityGroups struct {
+ SecurityGroup []SecurityGroupItemType
+ }
+}
+
+// DescribeSecurityGroups describes security groups
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&describesecuritygroups
+func (client *Client) DescribeSecurityGroups(args *DescribeSecurityGroupsArgs) (securityGroupItems []SecurityGroupItemType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeSecurityGroupsResponse{}
+
+ err = client.Invoke("DescribeSecurityGroups", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return response.SecurityGroups.SecurityGroup, &response.PaginationResult, nil
+}
+
+type CreateSecurityGroupArgs struct {
+ RegionId common.Region
+ SecurityGroupName string
+ Description string
+ VpcId string
+ ClientToken string
+}
+
+type CreateSecurityGroupResponse struct {
+ common.Response
+
+ SecurityGroupId string
+}
+
+// CreateSecurityGroup creates security group
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&createsecuritygroup
+func (client *Client) CreateSecurityGroup(args *CreateSecurityGroupArgs) (securityGroupId string, err error) {
+ response := CreateSecurityGroupResponse{}
+ err = client.Invoke("CreateSecurityGroup", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.SecurityGroupId, err
+}
+
+type DeleteSecurityGroupArgs struct {
+ RegionId common.Region
+ SecurityGroupId string
+}
+
+type DeleteSecurityGroupResponse struct {
+ common.Response
+}
+
+// DeleteSecurityGroup deletes security group
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&deletesecuritygroup
+func (client *Client) DeleteSecurityGroup(regionId common.Region, securityGroupId string) error {
+ args := DeleteSecurityGroupArgs{
+ RegionId: regionId,
+ SecurityGroupId: securityGroupId,
+ }
+ response := DeleteSecurityGroupResponse{}
+ err := client.Invoke("DeleteSecurityGroup", &args, &response)
+ return err
+}
+
+type ModifySecurityGroupAttributeArgs struct {
+ RegionId common.Region
+ SecurityGroupId string
+ SecurityGroupName string
+ Description string
+}
+
+type ModifySecurityGroupAttributeResponse struct {
+ common.Response
+}
+
+// ModifySecurityGroupAttribute modifies attribute of security group
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&modifysecuritygroupattribute
+func (client *Client) ModifySecurityGroupAttribute(args *ModifySecurityGroupAttributeArgs) error {
+ response := ModifySecurityGroupAttributeResponse{}
+ err := client.Invoke("ModifySecurityGroupAttribute", args, &response)
+ return err
+}
+
+type AuthorizeSecurityGroupArgs struct {
+ SecurityGroupId string
+ RegionId common.Region
+ IpProtocol IpProtocol
+ PortRange string
+ SourceGroupId string
+ SourceGroupOwnerAccount string
+ SourceGroupOwnerID string
+ SourceCidrIp string // IPv4 only, default 0.0.0.0/0
+ Policy PermissionPolicy // enum of accept (default) | drop
+ Priority int // 1 - 100, default 1
+ NicType NicType // enum of internet | intranet (default)
+}
+
+type AuthorizeSecurityGroupResponse struct {
+ common.Response
+}
+
+// AuthorizeSecurityGroup authorize permissions to security group
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/securitygroup&authorizesecuritygroup
+func (client *Client) AuthorizeSecurityGroup(args *AuthorizeSecurityGroupArgs) error {
+ response := AuthorizeSecurityGroupResponse{}
+ err := client.Invoke("AuthorizeSecurityGroup", args, &response)
+ return err
+}
+
+type RevokeSecurityGroupArgs struct {
+ AuthorizeSecurityGroupArgs
+}
+
+type RevokeSecurityGroupResponse struct {
+ common.Response
+}
+
+// You can read doc at https://help.aliyun.com/document_detail/25557.html?spm=5176.doc25554.6.755.O6Tjz0
+func (client *Client) RevokeSecurityGroup(args *RevokeSecurityGroupArgs) error {
+ response := RevokeSecurityGroupResponse{}
+ err := client.Invoke("RevokeSecurityGroup", args, &response)
+ return err
+}
+
+type AuthorizeSecurityGroupEgressArgs struct {
+ SecurityGroupId string
+ RegionId common.Region
+ IpProtocol IpProtocol
+ PortRange string
+ DestGroupId string
+ DestGroupOwnerAccount string
+ DestGroupOwnerId string
+ DestCidrIp string // IPv4 only, default 0.0.0.0/0
+ Policy PermissionPolicy // enum of accept (default) | drop
+ Priority int // 1 - 100, default 1
+ NicType NicType // enum of internet | intranet (default)
+}
+
+type AuthorizeSecurityGroupEgressResponse struct {
+ common.Response
+}
+
+// AuthorizeSecurityGroup authorize permissions to security group
+//
+// You can read doc at https://help.aliyun.com/document_detail/25560.html
+func (client *Client) AuthorizeSecurityGroupEgress(args *AuthorizeSecurityGroupEgressArgs) error {
+ response := AuthorizeSecurityGroupEgressResponse{}
+ err := client.Invoke("AuthorizeSecurityGroupEgress", args, &response)
+ return err
+}
+
+type RevokeSecurityGroupEgressArgs struct {
+ AuthorizeSecurityGroupEgressArgs
+}
+
+type RevokeSecurityGroupEgressResponse struct {
+ common.Response
+}
+
+// You can read doc at https://help.aliyun.com/document_detail/25561.html?spm=5176.doc25557.6.759.qcR4Az
+func (client *Client) RevokeSecurityGroupEgress(args *RevokeSecurityGroupEgressArgs) error {
+ response := RevokeSecurityGroupEgressResponse{}
+ err := client.Invoke("RevokeSecurityGroupEgress", args, &response)
+ return err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go
new file mode 100644
index 000000000..fb6f9c8e1
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go
@@ -0,0 +1,131 @@
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type DescribeSnapshotsArgs struct {
+ RegionId common.Region
+ InstanceId string
+ DiskId string
+ SnapshotIds []string //["s-xxxxxxxxx", "s-yyyyyyyyy", ..."s-zzzzzzzzz"]
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&snapshottype
+type SnapshotType struct {
+ SnapshotId string
+ SnapshotName string
+ Description string
+ Progress string
+ SourceDiskId string
+ SourceDiskSize int
+ SourceDiskType string //enum for System | Data
+ ProductCode string
+ CreationTime util.ISO6801Time
+}
+
+type DescribeSnapshotsResponse struct {
+ common.Response
+ common.PaginationResult
+ Snapshots struct {
+ Snapshot []SnapshotType
+ }
+}
+
+// DescribeSnapshots describe snapshots
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&describesnapshots
+func (client *Client) DescribeSnapshots(args *DescribeSnapshotsArgs) (snapshots []SnapshotType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeSnapshotsResponse{}
+
+ err = client.Invoke("DescribeSnapshots", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+ return response.Snapshots.Snapshot, &response.PaginationResult, nil
+
+}
+
+type DeleteSnapshotArgs struct {
+ SnapshotId string
+}
+
+type DeleteSnapshotResponse struct {
+ common.Response
+}
+
+// DeleteSnapshot deletes snapshot
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&deletesnapshot
+func (client *Client) DeleteSnapshot(snapshotId string) error {
+ args := DeleteSnapshotArgs{SnapshotId: snapshotId}
+ response := DeleteSnapshotResponse{}
+
+ return client.Invoke("DeleteSnapshot", &args, &response)
+}
+
+type CreateSnapshotArgs struct {
+ DiskId string
+ SnapshotName string
+ Description string
+ ClientToken string
+}
+
+type CreateSnapshotResponse struct {
+ common.Response
+ SnapshotId string
+}
+
+// CreateSnapshot creates a new snapshot
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/snapshot&createsnapshot
+func (client *Client) CreateSnapshot(args *CreateSnapshotArgs) (snapshotId string, err error) {
+
+ response := CreateSnapshotResponse{}
+
+ err = client.Invoke("CreateSnapshot", args, &response)
+ if err == nil {
+ snapshotId = response.SnapshotId
+ }
+ return snapshotId, err
+}
+
+// Default timeout value for WaitForSnapShotReady method
+const SnapshotDefaultTimeout = 120
+
+// WaitForSnapShotReady waits for snapshot ready
+func (client *Client) WaitForSnapShotReady(regionId common.Region, snapshotId string, timeout int) error {
+ if timeout <= 0 {
+ timeout = SnapshotDefaultTimeout
+ }
+ for {
+ args := DescribeSnapshotsArgs{
+ RegionId: regionId,
+ SnapshotIds: []string{snapshotId},
+ }
+
+ snapshots, _, err := client.DescribeSnapshots(&args)
+ if err != nil {
+ return err
+ }
+ if snapshots == nil || len(snapshots) == 0 {
+ return common.GetClientErrorFromString("Not found")
+ }
+ if snapshots[0].Progress == "100%" {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go b/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go
new file mode 100644
index 000000000..aa75574c3
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go
@@ -0,0 +1,95 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type CreateSnatEntryArgs struct {
+ RegionId common.Region
+ SnatTableId string
+ SourceVSwitchId string
+ SnatIp string
+}
+
+type CreateSnatEntryResponse struct {
+ common.Response
+ SnatEntryId string
+}
+
+type SnatEntrySetType struct {
+ RegionId common.Region
+ SnatEntryId string
+ SnatIp string
+ SnatTableId string
+ SourceCIDR string
+ SourceVSwitchId string
+ Status string
+}
+
+type DescribeSnatTableEntriesArgs struct {
+ RegionId common.Region
+ SnatTableId string
+ common.Pagination
+}
+
+type DescribeSnatTableEntriesResponse struct {
+ common.Response
+ common.PaginationResult
+ SnatTableEntries struct {
+ SnatTableEntry []SnatEntrySetType
+ }
+}
+
+type ModifySnatEntryArgs struct {
+ RegionId common.Region
+ SnatTableId string
+ SnatEntryId string
+ SnatIp string
+}
+
+type ModifySnatEntryResponse struct {
+ common.Response
+}
+
+type DeleteSnatEntryArgs struct {
+ RegionId common.Region
+ SnatTableId string
+ SnatEntryId string
+}
+
+type DeleteSnatEntryResponse struct {
+ common.Response
+}
+
+func (client *Client) CreateSnatEntry(args *CreateSnatEntryArgs) (resp *CreateSnatEntryResponse, err error) {
+ response := CreateSnatEntryResponse{}
+ err = client.Invoke("CreateSnatEntry", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+func (client *Client) DescribeSnatTableEntries(args *DescribeSnatTableEntriesArgs) (snatTableEntries []SnatEntrySetType,
+ pagination *common.PaginationResult, err error) {
+
+ args.Validate()
+ response := DescribeSnatTableEntriesResponse{}
+
+ err = client.Invoke("DescribeSnatTableEntries", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return response.SnatTableEntries.SnatTableEntry, &response.PaginationResult, nil
+}
+
+func (client *Client) ModifySnatEntry(args *ModifySnatEntryArgs) error {
+ response := ModifySnatEntryResponse{}
+ return client.Invoke("ModifySnatEntry", args, &response)
+}
+
+func (client *Client) DeleteSnatEntry(args *DeleteSnatEntryArgs) error {
+ response := DeleteSnatEntryResponse{}
+ err := client.Invoke("DeleteSnatEntry", args, &response)
+ return err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/ssh_key_pair.go b/vendor/github.com/denverdino/aliyungo/ecs/ssh_key_pair.go
new file mode 100644
index 000000000..bd742442e
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/ssh_key_pair.go
@@ -0,0 +1,144 @@
+package ecs
+
+import (
+ "github.com/denverdino/aliyungo/common"
+)
+
+type CreateKeyPairArgs struct {
+ RegionId common.Region
+ KeyPairName string
+}
+
+type CreateKeyPairResponse struct {
+ common.Response
+ KeyPairName string
+ KeyPairFingerPrint string
+ PrivateKeyBody string
+}
+
+// CreateKeyPair creates keypair
+//
+// You can read doc at https://help.aliyun.com/document_detail/51771.html?spm=5176.doc51775.6.910.cedjfr
+func (client *Client) CreateKeyPair(args *CreateKeyPairArgs) (resp *CreateKeyPairResponse,err error) {
+ response := CreateKeyPairResponse{}
+ err = client.Invoke("CreateKeyPair", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+type ImportKeyPairArgs struct {
+ RegionId common.Region
+ PublicKeyBody string
+ KeyPairName string
+}
+
+type ImportKeyPairResponse struct {
+ common.Response
+ KeyPairName string
+ KeyPairFingerPrint string
+}
+
+// ImportKeyPair import keypair
+//
+// You can read doc at https://help.aliyun.com/document_detail/51774.html?spm=5176.doc51771.6.911.BicQq2
+func (client *Client) ImportKeyPair(args *ImportKeyPairArgs) (resp *ImportKeyPairResponse,err error) {
+ response := ImportKeyPairResponse{}
+ err = client.Invoke("ImportKeyPair", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+type DescribeKeyPairsArgs struct {
+ RegionId common.Region
+ KeyPairFingerPrint string
+ KeyPairName string
+ common.Pagination
+}
+
+type KeyPairItemType struct {
+ KeyPairName string
+ KeyPairFingerPrint string
+}
+
+type DescribeKeyPairsResponse struct {
+ common.Response
+ common.PaginationResult
+ RegionId common.Region
+ KeyPairs struct {
+ KeyPair []KeyPairItemType
+ }
+}
+
+// DescribeKeyPairs describe keypairs
+//
+// You can read doc at https://help.aliyun.com/document_detail/51773.html?spm=5176.doc51774.6.912.lyE0iX
+func (client *Client) DescribeKeyPairs(args *DescribeKeyPairsArgs) (KeyPairs []KeyPairItemType, pagination *common.PaginationResult, err error) {
+ response := DescribeKeyPairsResponse{}
+
+ err = client.Invoke("DescribeKeyPairs", args, &response)
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return response.KeyPairs.KeyPair, &response.PaginationResult, err
+}
+
+type AttachKeyPairArgs struct {
+ RegionId common.Region
+ KeyPairName string
+ InstanceIds string
+}
+
+// AttachKeyPair keypars to instances
+//
+// You can read doc at https://help.aliyun.com/document_detail/51775.html?spm=5176.doc51773.6.913.igEem4
+func (client *Client) AttachKeyPair(args *AttachKeyPairArgs) (err error) {
+ response := common.Response{}
+ err = client.Invoke("AttachKeyPair", args, &response)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+type DetachKeyPairArgs struct {
+ RegionId common.Region
+ KeyPairName string
+ InstanceIds string
+}
+
+// DetachKeyPair keyparis from instances
+//
+// You can read doc at https://help.aliyun.com/document_detail/51776.html?spm=5176.doc51775.6.914.DJ7Gmq
+func (client *Client) DetachKeyPair(args *DetachKeyPairArgs) (err error) {
+ response := common.Response{}
+ err = client.Invoke("DetachKeyPair", args, &response)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+type DeleteKeyPairsArgs struct {
+ RegionId common.Region
+ KeyPairNames string
+}
+
+// DeleteKeyPairs delete keypairs
+//
+// You can read doc at https://help.aliyun.com/document_detail/51772.html?spm=5176.doc51776.6.915.Qqcv2Q
+func (client *Client) DeleteKeyPairs(args *DeleteKeyPairsArgs) (err error) {
+ response := common.Response{}
+ err = client.Invoke("DeleteKeyPairs", args, &response)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/tags.go b/vendor/github.com/denverdino/aliyungo/ecs/tags.go
new file mode 100644
index 000000000..5ffd4931a
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/tags.go
@@ -0,0 +1,120 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type TagResourceType string
+
+const (
+ TagResourceImage = TagResourceType("image")
+ TagResourceInstance = TagResourceType("instance")
+ TagResourceSnapshot = TagResourceType("snapshot")
+ TagResourceDisk = TagResourceType("disk")
+)
+
+type AddTagsArgs struct {
+ ResourceId string
+ ResourceType TagResourceType //image, instance, snapshot or disk
+ RegionId common.Region
+ Tag map[string]string
+}
+
+type AddTagsResponse struct {
+ common.Response
+}
+
+// AddTags Add tags to resource
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&addtags
+func (client *Client) AddTags(args *AddTagsArgs) error {
+ response := AddTagsResponse{}
+ err := client.Invoke("AddTags", args, &response)
+ return err
+}
+
+type RemoveTagsArgs struct {
+ ResourceId string
+ ResourceType TagResourceType //image, instance, snapshot or disk
+ RegionId common.Region
+ Tag map[string]string
+}
+
+type RemoveTagsResponse struct {
+ common.Response
+}
+
+// RemoveTags remove tags to resource
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&removetags
+func (client *Client) RemoveTags(args *RemoveTagsArgs) error {
+ response := RemoveTagsResponse{}
+ err := client.Invoke("RemoveTags", args, &response)
+ return err
+}
+
+type ResourceItemType struct {
+ ResourceId string
+ ResourceType TagResourceType
+ RegionId common.Region
+}
+
+type DescribeResourceByTagsArgs struct {
+ ResourceType TagResourceType //image, instance, snapshot or disk
+ RegionId common.Region
+ Tag map[string]string
+ common.Pagination
+}
+
+type DescribeResourceByTagsResponse struct {
+ common.Response
+ common.PaginationResult
+ Resources struct {
+ Resource []ResourceItemType
+ }
+}
+
+// DescribeResourceByTags describe resource by tags
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags
+func (client *Client) DescribeResourceByTags(args *DescribeResourceByTagsArgs) (resources []ResourceItemType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeResourceByTagsResponse{}
+ err = client.Invoke("DescribeResourceByTags", args, &response)
+ if err != nil {
+ return nil, nil, err
+ }
+ return response.Resources.Resource, &response.PaginationResult, nil
+}
+
+type TagItemType struct {
+ TagKey string
+ TagValue string
+}
+
+type DescribeTagsArgs struct {
+ RegionId common.Region
+ ResourceType TagResourceType //image, instance, snapshot or disk
+ ResourceId string
+ Tag map[string]string
+ common.Pagination
+}
+
+type DescribeTagsResponse struct {
+ common.Response
+ common.PaginationResult
+ Tags struct {
+ Tag []TagItemType
+ }
+}
+
+// DescribeResourceByTags describe resource by tags
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/tags&describeresourcebytags
+func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeTagsResponse{}
+ err = client.Invoke("DescribeTags", args, &response)
+ if err != nil {
+ return nil, nil, err
+ }
+ return response.Tags.Tag, &response.PaginationResult, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go
new file mode 100644
index 000000000..80faf21ca
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/vpcs.go
@@ -0,0 +1,152 @@
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type CreateVpcArgs struct {
+ RegionId common.Region
+ CidrBlock string //192.168.0.0/16 or 172.16.0.0/16 (default)
+ VpcName string
+ Description string
+ ClientToken string
+}
+
+type CreateVpcResponse struct {
+ common.Response
+ VpcId string
+ VRouterId string
+ RouteTableId string
+}
+
+// CreateVpc creates Virtual Private Cloud
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc
+func (client *Client) CreateVpc(args *CreateVpcArgs) (resp *CreateVpcResponse, err error) {
+ response := CreateVpcResponse{}
+ err = client.Invoke("CreateVpc", args, &response)
+ if err != nil {
+ return nil, err
+ }
+ return &response, err
+}
+
+type DeleteVpcArgs struct {
+ VpcId string
+}
+
+type DeleteVpcResponse struct {
+ common.Response
+}
+
+// DeleteVpc deletes Virtual Private Cloud
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&deletevpc
+func (client *Client) DeleteVpc(vpcId string) error {
+ args := DeleteVpcArgs{
+ VpcId: vpcId,
+ }
+ response := DeleteVpcResponse{}
+ return client.Invoke("DeleteVpc", &args, &response)
+}
+
+type VpcStatus string
+
+const (
+ VpcStatusPending = VpcStatus("Pending")
+ VpcStatusAvailable = VpcStatus("Available")
+)
+
+type DescribeVpcsArgs struct {
+ VpcId string
+ RegionId common.Region
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vpcsettype
+type VpcSetType struct {
+ VpcId string
+ RegionId common.Region
+ Status VpcStatus // enum Pending | Available
+ VpcName string
+ VSwitchIds struct {
+ VSwitchId []string
+ }
+ CidrBlock string
+ VRouterId string
+ Description string
+ IsDefault bool
+ CreationTime util.ISO6801Time
+}
+
+type DescribeVpcsResponse struct {
+ common.Response
+ common.PaginationResult
+ Vpcs struct {
+ Vpc []VpcSetType
+ }
+}
+
+// DescribeInstanceStatus describes instance status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&describevpcs
+func (client *Client) DescribeVpcs(args *DescribeVpcsArgs) (vpcs []VpcSetType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeVpcsResponse{}
+
+ err = client.Invoke("DescribeVpcs", args, &response)
+
+ if err == nil {
+ return response.Vpcs.Vpc, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type ModifyVpcAttributeArgs struct {
+ VpcId string
+ VpcName string
+ Description string
+}
+
+type ModifyVpcAttributeResponse struct {
+ common.Response
+}
+
+// ModifyVpcAttribute modifies attribute of Virtual Private Cloud
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&modifyvpcattribute
+func (client *Client) ModifyVpcAttribute(args *ModifyVpcAttributeArgs) error {
+ response := ModifyVpcAttributeResponse{}
+ return client.Invoke("ModifyVpcAttribute", args, &response)
+}
+
+// WaitForInstance waits for instance to given status
+func (client *Client) WaitForVpcAvailable(regionId common.Region, vpcId string, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+ args := DescribeVpcsArgs{
+ RegionId: regionId,
+ VpcId: vpcId,
+ }
+ for {
+ vpcs, _, err := client.DescribeVpcs(&args)
+ if err != nil {
+ return err
+ }
+ if len(vpcs) > 0 && vpcs[0].Status == VpcStatusAvailable {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go
new file mode 100644
index 000000000..059a324bd
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/vrouters.go
@@ -0,0 +1,68 @@
+package ecs
+
+import (
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type DescribeVRoutersArgs struct {
+ VRouterId string
+ RegionId common.Region
+ common.Pagination
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vroutersettype
+type VRouterSetType struct {
+ VRouterId string
+ RegionId common.Region
+ VpcId string
+ RouteTableIds struct {
+ RouteTableId []string
+ }
+ VRouterName string
+ Description string
+ CreationTime util.ISO6801Time
+}
+
+type DescribeVRoutersResponse struct {
+ common.Response
+ common.PaginationResult
+ VRouters struct {
+ VRouter []VRouterSetType
+ }
+}
+
+// DescribeVRouters describes Virtual Routers
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&describevrouters
+func (client *Client) DescribeVRouters(args *DescribeVRoutersArgs) (vrouters []VRouterSetType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeVRoutersResponse{}
+
+ err = client.Invoke("DescribeVRouters", args, &response)
+
+ if err == nil {
+ return response.VRouters.VRouter, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type ModifyVRouterAttributeArgs struct {
+ VRouterId string
+ VRouterName string
+ Description string
+}
+
+type ModifyVRouterAttributeResponse struct {
+ common.Response
+}
+
+// ModifyVRouterAttribute modifies attribute of Virtual Router
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vrouter&modifyvrouterattribute
+func (client *Client) ModifyVRouterAttribute(args *ModifyVRouterAttributeArgs) error {
+ response := ModifyVRouterAttributeResponse{}
+ return client.Invoke("ModifyVRouterAttribute", args, &response)
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go
new file mode 100644
index 000000000..8a879ec80
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/vswitches.go
@@ -0,0 +1,153 @@
+package ecs
+
+import (
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type CreateVSwitchArgs struct {
+ ZoneId string
+ CidrBlock string
+ VpcId string
+ VSwitchName string
+ Description string
+ ClientToken string
+}
+
+type CreateVSwitchResponse struct {
+ common.Response
+ VSwitchId string
+}
+
+// CreateVSwitch creates Virtual Switch
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&createvswitch
+func (client *Client) CreateVSwitch(args *CreateVSwitchArgs) (vswitchId string, err error) {
+ response := CreateVSwitchResponse{}
+ err = client.Invoke("CreateVSwitch", args, &response)
+ if err != nil {
+ return "", err
+ }
+ return response.VSwitchId, err
+}
+
+type DeleteVSwitchArgs struct {
+ VSwitchId string
+}
+
+type DeleteVSwitchResponse struct {
+ common.Response
+}
+
+// DeleteVSwitch deletes Virtual Switch
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&deletevswitch
+func (client *Client) DeleteVSwitch(VSwitchId string) error {
+ args := DeleteVSwitchArgs{
+ VSwitchId: VSwitchId,
+ }
+ response := DeleteVSwitchResponse{}
+ return client.Invoke("DeleteVSwitch", &args, &response)
+}
+
+type DescribeVSwitchesArgs struct {
+ VpcId string
+ VSwitchId string
+ ZoneId string
+ common.Pagination
+}
+
+type VSwitchStatus string
+
+const (
+ VSwitchStatusPending = VSwitchStatus("Pending")
+ VSwitchStatusAvailable = VSwitchStatus("Available")
+)
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&vswitchsettype
+type VSwitchSetType struct {
+ VSwitchId string
+ VpcId string
+ Status VSwitchStatus // enum Pending | Available
+ CidrBlock string
+ ZoneId string
+ AvailableIpAddressCount int
+ Description string
+ VSwitchName string
+ IsDefault bool
+ CreationTime util.ISO6801Time
+}
+
+type DescribeVSwitchesResponse struct {
+ common.Response
+ common.PaginationResult
+ VSwitches struct {
+ VSwitch []VSwitchSetType
+ }
+}
+
+// DescribeVSwitches describes Virtual Switches
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&describevswitches
+func (client *Client) DescribeVSwitches(args *DescribeVSwitchesArgs) (vswitches []VSwitchSetType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeVSwitchesResponse{}
+
+ err = client.Invoke("DescribeVSwitches", args, &response)
+
+ if err == nil {
+ return response.VSwitches.VSwitch, &response.PaginationResult, nil
+ }
+
+ return nil, nil, err
+}
+
+type ModifyVSwitchAttributeArgs struct {
+ VSwitchId string
+ VSwitchName string
+ Description string
+}
+
+type ModifyVSwitchAttributeResponse struct {
+ common.Response
+}
+
+// ModifyVSwitchAttribute modifies attribute of Virtual Private Cloud
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vswitch&modifyvswitchattribute
+func (client *Client) ModifyVSwitchAttribute(args *ModifyVSwitchAttributeArgs) error {
+ response := ModifyVSwitchAttributeResponse{}
+ return client.Invoke("ModifyVSwitchAttribute", args, &response)
+}
+
+// WaitForVSwitchAvailable waits for VSwitch to given status
+func (client *Client) WaitForVSwitchAvailable(vpcId string, vswitchId string, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+ args := DescribeVSwitchesArgs{
+ VpcId: vpcId,
+ VSwitchId: vswitchId,
+ }
+ for {
+ vswitches, _, err := client.DescribeVSwitches(&args)
+ if err != nil {
+ return err
+ }
+ if len(vswitches) == 0 {
+ return common.GetClientErrorFromString("Not found")
+ }
+ if vswitches[0].Status == VSwitchStatusAvailable {
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ }
+ return nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ecs/zones.go b/vendor/github.com/denverdino/aliyungo/ecs/zones.go
new file mode 100644
index 000000000..4818c760f
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ecs/zones.go
@@ -0,0 +1,65 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type ResourceType string
+
+const (
+ ResourceTypeInstance = ResourceType("Instance")
+ ResourceTypeDisk = ResourceType("Disk")
+ ResourceTypeVSwitch = ResourceType("VSwitch")
+ ResourceTypeIOOptimizedInstance = ResourceType("IoOptimized")
+)
+
+type DescribeZonesArgs struct {
+ RegionId common.Region
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availableresourcecreationtype
+type AvailableResourceCreationType struct {
+ ResourceTypes []ResourceType //enum for Instance, Disk, VSwitch
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&availablediskcategoriestype
+type AvailableDiskCategoriesType struct {
+ DiskCategories []DiskCategory //enum for cloud, ephemeral, ephemeral_ssd
+}
+
+type AvailableInstanceTypesType struct {
+ InstanceTypes []string
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&zonetype
+type ZoneType struct {
+ ZoneId string
+ LocalName string
+ AvailableInstanceTypes AvailableInstanceTypesType
+ AvailableResourceCreation AvailableResourceCreationType
+ AvailableDiskCategories AvailableDiskCategoriesType
+}
+
+type DescribeZonesResponse struct {
+ common.Response
+ Zones struct {
+ Zone []ZoneType
+ }
+}
+
+// DescribeZones describes zones
+func (client *Client) DescribeZones(regionId common.Region) (zones []ZoneType, err error) {
+ args := DescribeZonesArgs{
+ RegionId: regionId,
+ }
+ response := DescribeZonesResponse{}
+
+ err = client.Invoke("DescribeZones", &args, &response)
+
+ if err == nil {
+ return response.Zones.Zone, nil
+ }
+
+ return []ZoneType{}, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/account.go b/vendor/github.com/denverdino/aliyungo/ram/account.go
new file mode 100644
index 000000000..35ad58c4b
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/account.go
@@ -0,0 +1,78 @@
+package ram
+
+type UserRequest struct {
+ User
+}
+
+type UserResponse struct {
+ RamCommonResponse
+ User User
+}
+
+type UpdateUserRequest struct {
+ UserName string
+ NewUserName string
+ NewDisplayName string
+ NewMobilePhone string
+ NewEmail string
+ NewComments string
+}
+
+type ListUserRequest struct {
+ Marker string
+ MaxItems int8
+}
+
+type ListUserResponse struct {
+ RamCommonResponse
+ IsTruncated bool
+ Marker string
+ Users struct {
+ User []User
+ }
+}
+
+func (client *RamClient) CreateUser(user UserRequest) (UserResponse, error) {
+ var userResponse UserResponse
+ err := client.Invoke("CreateUser", user, &userResponse)
+ if err != nil {
+ return UserResponse{}, err
+ }
+ return userResponse, nil
+}
+
+func (client *RamClient) GetUser(userQuery UserQueryRequest) (UserResponse, error) {
+ var userResponse UserResponse
+ err := client.Invoke("GetUser", userQuery, &userResponse)
+ if err != nil {
+ return UserResponse{}, nil
+ }
+ return userResponse, nil
+}
+
+func (client *RamClient) UpdateUser(newUser UpdateUserRequest) (UserResponse, error) {
+ var userResponse UserResponse
+ err := client.Invoke("UpdateUser", newUser, &userResponse)
+ if err != nil {
+ return UserResponse{}, err
+ }
+ return userResponse, nil
+}
+
+func (client *RamClient) DeleteUser(userQuery UserQueryRequest) (RamCommonResponse, error) {
+ var commonResp RamCommonResponse
+ err := client.Invoke("DeleteUser", userQuery, &commonResp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return commonResp, nil
+}
+
+func (client *RamClient) ListUsers(listParams ListUserRequest) (ListUserResponse, error) {
+ var userList ListUserResponse
+ err := client.Invoke("ListUsers", listParams, &userList)
+ if err != nil {
+ return ListUserResponse{}, err
+ }
+ return userList, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/ak.go b/vendor/github.com/denverdino/aliyungo/ram/ak.go
new file mode 100644
index 000000000..649cb4f23
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/ak.go
@@ -0,0 +1,63 @@
+package ram
+
+/*
+ CreateAccessKey()
+ UpdateAccessKey()
+ DeleteAccessKey()
+ ListAccessKeys()
+*/
+type State string
+
+type AccessKeyResponse struct {
+ RamCommonResponse
+ AccessKey AccessKey
+}
+
+type UpdateAccessKeyRequest struct {
+ UserAccessKeyId string
+ Status State
+ UserName string
+}
+
+type AccessKeyListResponse struct {
+ RamCommonResponse
+ AccessKeys struct {
+ AccessKey []AccessKey
+ }
+}
+
+func (client *RamClient) CreateAccessKey(userQuery UserQueryRequest) (AccessKeyResponse, error) {
+ var accesskeyResp AccessKeyResponse
+ err := client.Invoke("CreateAccessKey", userQuery, &accesskeyResp)
+ if err != nil {
+ return AccessKeyResponse{}, err
+ }
+ return accesskeyResp, nil
+}
+
+func (client *RamClient) UpdateAccessKey(accessKeyRequest UpdateAccessKeyRequest) (RamCommonResponse, error) {
+ var commonResp RamCommonResponse
+ err := client.Invoke("UpdateAccessKey", accessKeyRequest, &commonResp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return commonResp, nil
+}
+
+func (client *RamClient) DeleteAccessKey(accessKeyRequest UpdateAccessKeyRequest) (RamCommonResponse, error) {
+ var commonResp RamCommonResponse
+ err := client.Invoke("DeleteAccessKey", accessKeyRequest, &commonResp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return commonResp, nil
+}
+
+func (client *RamClient) ListAccessKeys(userQuery UserQueryRequest) (AccessKeyListResponse, error) {
+ var accessKeyListResp AccessKeyListResponse
+ err := client.Invoke("ListAccessKeys", userQuery, &accessKeyListResp)
+ if err != nil {
+ return AccessKeyListResponse{}, err
+ }
+ return accessKeyListResp, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/api.go b/vendor/github.com/denverdino/aliyungo/ram/api.go
new file mode 100644
index 000000000..8ad173b78
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/api.go
@@ -0,0 +1,79 @@
+package ram
+
+/*
+ ringtail 2016/1/19
+ All RAM apis provided
+*/
+
+type RamClientInterface interface {
+ //ram user
+ CreateUser(user UserRequest) (UserResponse, error)
+ GetUser(userQuery UserQueryRequest) (UserResponse, error)
+ UpdateUser(newUser UpdateUserRequest) (UserResponse, error)
+ DeleteUser(userQuery UserQueryRequest) (RamCommonResponse, error)
+ ListUsers(listParams ListUserRequest) (ListUserResponse, error)
+
+ //TODO login ram console
+ CreateLoginProfile()
+ GetLoginProfile()
+ DeleteLoginProfile()
+ UpdateLoginProfile()
+
+ //ram ak
+ CreateAccessKey(userQuery UserQueryRequest) (AccessKeyResponse, error)
+ UpdateAccessKey(accessKeyRequest UpdateAccessKeyRequest) (RamCommonResponse, error)
+ DeleteAccessKey(accessKeyRequest UpdateAccessKeyRequest) (RamCommonResponse, error)
+ ListAccessKeys(userQuery UserQueryRequest) (AccessKeyListResponse, error)
+
+ //TODO MFA
+ CreateVirtualMFADevices()
+ ListVirtualMFADevices()
+ DeleteVirtualMFADevices()
+ BindMFADevice()
+ GetUserMFAInfo()
+
+ //TODO group
+ CreateGroup()
+ GetGroup()
+ UpdateGroup()
+ ListGroup()
+ DeleteGroup()
+ AddUserToGroup()
+ RemoveUserFromGroup()
+ ListGroupsForUser()
+ ListUsersForGroup()
+
+ CreateRole(role RoleRequest) (RoleResponse, error)
+ GetRole(roleQuery RoleQueryRequest) (RoleResponse, error)
+ UpdateRole(newRole UpdateRoleRequest) (RoleResponse, error)
+ ListRoles() (ListRoleResponse, error)
+ DeleteRole(roleQuery RoleQueryRequest) (RamCommonResponse, error)
+
+ //DONE policy
+ CreatePolicy(policyReq PolicyRequest) (PolicyResponse, error)
+ GetPolicy(policyReq PolicyRequest) (PolicyResponse, error)
+ DeletePolicy(policyReq PolicyRequest) (RamCommonResponse, error)
+ ListPolicies(policyQuery PolicyQueryRequest) (PolicyQueryResponse, error)
+ ListPoliciesForUser(userQuery UserQueryRequest) (PolicyListResponse, error)
+
+ //TODO policy
+ CreatePolicyVersion(policyReq PolicyRequest) (PolicyVersionResponse, error)
+ GetPolicyVersion(policyReq PolicyRequest) (PolicyVersionResponse, error)
+ DeletePolicyVersion(policyReq PolicyRequest) (RamCommonResponse, error)
+ ListPolicyVersions(policyReq PolicyRequest) (PolicyVersionResponse, error)
+ AttachPolicyToUser(attachPolicyRequest AttachPolicyRequest) (RamCommonResponse, error)
+ DetachPolicyFromUser(attachPolicyRequest AttachPolicyRequest) (RamCommonResponse, error)
+ ListEnitiesForPolicy()
+ SetDefaultPolicyVersion()
+ ListPoliciesForGroup()
+ AttachPolicyToRole(attachPolicyRequest AttachPolicyToRoleRequest) (RamCommonResponse, error)
+ DetachPolicyFromRole(attachPolicyRequest AttachPolicyToRoleRequest) (RamCommonResponse, error)
+ ListPoliciesForRole(roleQuery RoleQueryRequest) (PolicyListResponse, error)
+
+ //TODO security apis
+ SetAccountAlias(accountAlias AccountAlias) (RamCommonResponse, error)
+ GetAccountAlias() (AccountAliasResponse, error)
+ ClearAccountAlias() (RamCommonResponse, error)
+ SetPasswordPolicy(passwordPolicy PasswordPolicyRequest) (PasswordPolicyResponse, error)
+ GetPasswordPolicy(accountAlias AccountAlias) (PasswordPolicyResponse, error)
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/client.go b/vendor/github.com/denverdino/aliyungo/ram/client.go
new file mode 100644
index 000000000..974bc023f
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/client.go
@@ -0,0 +1,30 @@
+package ram
+
+import (
+ "github.com/denverdino/aliyungo/common"
+ "os"
+)
+
+const (
+ // RAMDefaultEndpoint is the default API endpoint of RAM services
+ RAMDefaultEndpoint = "https://ram.aliyuncs.com"
+ RAMAPIVersion = "2015-05-01"
+)
+
+type RamClient struct {
+ common.Client
+}
+
+func NewClient(accessKeyId string, accessKeySecret string) RamClientInterface {
+ endpoint := os.Getenv("RAM_ENDPOINT")
+ if endpoint == "" {
+ endpoint = RAMDefaultEndpoint
+ }
+ return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
+}
+
+func NewClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) RamClientInterface {
+ client := &RamClient{}
+ client.Init(endpoint, RAMAPIVersion, accessKeyId, accessKeySecret)
+ return client
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/error.go b/vendor/github.com/denverdino/aliyungo/ram/error.go
new file mode 100644
index 000000000..30d2f4eb0
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/error.go
@@ -0,0 +1,4 @@
+package ram
+
+//common errors
+var ()
diff --git a/vendor/github.com/denverdino/aliyungo/ram/group.go b/vendor/github.com/denverdino/aliyungo/ram/group.go
new file mode 100644
index 000000000..a64e68fb4
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/group.go
@@ -0,0 +1,11 @@
+package ram
+
+func (client *RamClient) CreateGroup() {}
+func (client *RamClient) GetGroup() {}
+func (client *RamClient) UpdateGroup() {}
+func (client *RamClient) ListGroup() {}
+func (client *RamClient) DeleteGroup() {}
+func (client *RamClient) AddUserToGroup() {}
+func (client *RamClient) RemoveUserFromGroup() {}
+func (client *RamClient) ListGroupsForUser() {}
+func (client *RamClient) ListUsersForGroup() {}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/mfa.go b/vendor/github.com/denverdino/aliyungo/ram/mfa.go
new file mode 100644
index 000000000..07d6b3945
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/mfa.go
@@ -0,0 +1,11 @@
+package ram
+
+func (client *RamClient) CreateVirtualMFADevices() {}
+
+func (client *RamClient) ListVirtualMFADevices() {}
+
+func (client *RamClient) DeleteVirtualMFADevices() {}
+
+func (client *RamClient) BindMFADevice() {}
+
+func (client *RamClient) GetUserMFAInfo() {}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/policy.go b/vendor/github.com/denverdino/aliyungo/ram/policy.go
new file mode 100644
index 000000000..2eaed4f4e
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/policy.go
@@ -0,0 +1,195 @@
+package ram
+
+type PolicyRequest struct {
+ PolicyName string
+ PolicyType string
+ Description string
+ PolicyDocument string
+ SetAsDefault string
+ VersionId string
+}
+type PolicyListResponse struct {
+ RamCommonResponse
+ Policies struct {
+ Policy []Policy
+ }
+}
+
+type PolicyResponse struct {
+ RamCommonResponse
+ Policy Policy
+}
+
+type PolicyQueryRequest struct {
+ PolicyType string
+ Marker string
+ MaxItems int8
+}
+
+type PolicyQueryResponse struct {
+ IsTruncated bool
+ Marker string
+ Policies struct {
+ Policy []Policy
+ }
+}
+
+type PolicyVersionResponse struct {
+ RamCommonResponse
+ IsDefaultVersion bool
+ VersionId string
+ CreateDate string
+ PolicyDocument string
+}
+
+type AttachPolicyRequest struct {
+ PolicyRequest
+ UserName string
+}
+
+type AttachPolicyToRoleRequest struct {
+ PolicyRequest
+ RoleName string
+}
+
+func (client *RamClient) CreatePolicy(policyReq PolicyRequest) (PolicyResponse, error) {
+ var resp PolicyResponse
+ err := client.Invoke("CreatePolicy", policyReq, &resp)
+ if err != nil {
+ return PolicyResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) GetPolicy(policyReq PolicyRequest) (PolicyResponse, error) {
+ var resp PolicyResponse
+ err := client.Invoke("GetPolicy", policyReq, &resp)
+ if err != nil {
+ return PolicyResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) DeletePolicy(policyReq PolicyRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("DeletePolicy", policyReq, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) ListPolicies(policyQuery PolicyQueryRequest) (PolicyQueryResponse, error) {
+ var resp PolicyQueryResponse
+ err := client.Invoke("ListPolicies", policyQuery, &resp)
+ if err != nil {
+ return PolicyQueryResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) CreatePolicyVersion(policyReq PolicyRequest) (PolicyVersionResponse, error) {
+ var resp PolicyVersionResponse
+ err := client.Invoke("CreatePolicyVersion", policyReq, &resp)
+ if err != nil {
+ return PolicyVersionResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) GetPolicyVersion(policyReq PolicyRequest) (PolicyVersionResponse, error) {
+ var resp PolicyVersionResponse
+ err := client.Invoke("GetPolicyVersion", policyReq, &resp)
+ if err != nil {
+ return PolicyVersionResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) DeletePolicyVersion(policyReq PolicyRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("DeletePolicyVersion", policyReq, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) ListPolicyVersions(policyReq PolicyRequest) (PolicyVersionResponse, error) {
+ var resp PolicyVersionResponse
+ err := client.Invoke("ListPolicyVersions", policyReq, &resp)
+ if err != nil {
+ return PolicyVersionResponse{}, err
+ }
+ return resp, nil
+}
+
+//TODO
+func (client *RamClient) SetDefaultPolicyVersion() {}
+
+func (client *RamClient) AttachPolicyToUser(attachPolicyRequest AttachPolicyRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("AttachPolicyToUser", attachPolicyRequest, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) DetachPolicyFromUser(attachPolicyRequest AttachPolicyRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("DetachPolicyFromUser", attachPolicyRequest, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+//TODO
+func (client *RamClient) ListEnitiesForPolicy() {}
+
+func (client *RamClient) ListPoliciesForUser(userQuery UserQueryRequest) (PolicyListResponse, error) {
+ var resp PolicyListResponse
+ err := client.Invoke("ListPoliciesForUser", userQuery, &resp)
+ if err != nil {
+ return PolicyListResponse{}, err
+ }
+ return resp, nil
+}
+
+//
+//Role related
+//
+func (client *RamClient) AttachPolicyToRole(attachPolicyRequest AttachPolicyToRoleRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("AttachPolicyToRole", attachPolicyRequest, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) DetachPolicyFromRole(attachPolicyRequest AttachPolicyToRoleRequest) (RamCommonResponse, error) {
+ var resp RamCommonResponse
+ err := client.Invoke("DetachPolicyFromRole", attachPolicyRequest, &resp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return resp, nil
+}
+
+func (client *RamClient) ListPoliciesForRole(roleQuery RoleQueryRequest) (PolicyListResponse, error) {
+ var resp PolicyListResponse
+ err := client.Invoke("ListPoliciesForRole", roleQuery, &resp)
+ if err != nil {
+ return PolicyListResponse{}, err
+ }
+ return resp, nil
+}
+
+//
+//Group related
+//
+//TODO
+//
+func (client *RamClient) ListPoliciesForGroup() {}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/profile.go b/vendor/github.com/denverdino/aliyungo/ram/profile.go
new file mode 100644
index 000000000..91dd32e31
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/profile.go
@@ -0,0 +1,24 @@
+package ram
+
+/*
+ CreateLoginProfile()
+ GetLoginProfile()
+ DeleteLoginProfile()
+ UpdateLoginProfile()
+*/
+
+func (client *RamClient) CreateLoginProfile() {
+
+}
+
+func (client *RamClient) GetLoginProfile() {
+
+}
+
+func (client *RamClient) DeleteLoginProfile() {
+
+}
+
+func (client *RamClient) UpdateLoginProfile() {
+
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/role.go b/vendor/github.com/denverdino/aliyungo/ram/role.go
new file mode 100644
index 000000000..08ded08fb
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/role.go
@@ -0,0 +1,73 @@
+package ram
+
+type RoleRequest struct {
+ RoleName string
+ AssumeRolePolicyDocument string
+ Description string
+}
+
+type RoleResponse struct {
+ RamCommonResponse
+ Role Role
+}
+
+type RoleQueryRequest struct {
+ RoleName string
+}
+
+type UpdateRoleRequest struct {
+ RoleName string
+ NewAssumeRolePolicyDocument string
+}
+
+type ListRoleResponse struct {
+ RamCommonResponse
+ Roles struct {
+ Role []Role
+ }
+}
+
+func (client *RamClient) CreateRole(role RoleRequest) (RoleResponse, error) {
+ var roleResponse RoleResponse
+ err := client.Invoke("CreateRole", role, &roleResponse)
+ if err != nil {
+ return RoleResponse{}, err
+ }
+ return roleResponse, nil
+}
+
+func (client *RamClient) GetRole(roleQuery RoleQueryRequest) (RoleResponse, error) {
+ var roleResponse RoleResponse
+ err := client.Invoke("GetRole", roleQuery, &roleResponse)
+ if err != nil {
+ return RoleResponse{}, nil
+ }
+ return roleResponse, nil
+}
+
+func (client *RamClient) UpdateRole(newRole UpdateRoleRequest) (RoleResponse, error) {
+ var roleResponse RoleResponse
+ err := client.Invoke("UpdateRole", newRole, &roleResponse)
+ if err != nil {
+ return RoleResponse{}, err
+ }
+ return roleResponse, nil
+}
+
+func (client *RamClient) ListRoles() (ListRoleResponse, error) {
+ var roleList ListRoleResponse
+ err := client.Invoke("ListRoles", struct{}{}, &roleList)
+ if err != nil {
+ return ListRoleResponse{}, err
+ }
+ return roleList, nil
+}
+
+func (client *RamClient) DeleteRole(roleQuery RoleQueryRequest) (RamCommonResponse, error) {
+ var commonResp RamCommonResponse
+ err := client.Invoke("DeleteRole", roleQuery, &commonResp)
+ if err != nil {
+ return RamCommonResponse{}, err
+ }
+ return commonResp, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/security.go b/vendor/github.com/denverdino/aliyungo/ram/security.go
new file mode 100644
index 000000000..da5f4a78e
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/security.go
@@ -0,0 +1,40 @@
+package ram
+
+//TODO implement ram api about security
+/*
+ SetAccountAlias()
+ GetAccountAlias()
+ ClearAccountAlias()
+ SetPasswordPolicy()
+ GetPasswordPolicy()
+*/
+type AccountAliasResponse struct {
+ RamCommonResponse
+ AccountAlias string
+}
+
+type PasswordPolicyResponse struct {
+ RamCommonResponse
+ PasswordPolicy
+}
+
+type PasswordPolicyRequest struct {
+ PasswordPolicy
+}
+
+func (client *RamClient) SetAccountAlias(accountalias AccountAlias) (RamCommonResponse, error) {
+ return RamCommonResponse{}, nil
+}
+
+func (client *RamClient) GetAccountAlias() (AccountAliasResponse, error) {
+ return AccountAliasResponse{}, nil
+}
+func (client *RamClient) ClearAccountAlias() (RamCommonResponse, error) {
+ return RamCommonResponse{}, nil
+}
+func (client *RamClient) SetPasswordPolicy(passwordPolicy PasswordPolicyRequest) (PasswordPolicyResponse, error) {
+ return PasswordPolicyResponse{}, nil
+}
+func (client *RamClient) GetPasswordPolicy(accountAlias AccountAlias) (PasswordPolicyResponse, error) {
+ return PasswordPolicyResponse{}, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/ram/types.go b/vendor/github.com/denverdino/aliyungo/ram/types.go
new file mode 100644
index 000000000..0a82b959c
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/ram/types.go
@@ -0,0 +1,126 @@
+package ram
+
+import (
+ "github.com/denverdino/aliyungo/common"
+)
+
+/*
+ All common struct
+*/
+
+const (
+ Active State = "Active"
+ Inactive State = "Inactive"
+)
+
+/*
+ AccountAlias
+ 类型:String
+ 必须:是
+ 描述:指定云账号的别名, 长度限制为3-63个字符
+ 限制:^[a-z0-9](([a-z0-9]|-(?!-))*[a-z0-9])?$
+*/
+type AccountAlias string
+
+type UserQueryRequest struct {
+ UserName string
+}
+
+type User struct {
+ UserId string
+ UserName string
+ DisplayName string
+ MobilePhone string
+ Email string
+ Comments string
+ CreateDate string
+ UpdateDate string
+ LastLoginDate string
+}
+
+type LoginProfile struct {
+}
+
+type MFADevice struct {
+}
+
+type VirtualMFADevice struct {
+}
+
+type AccessKey struct {
+ AccessKeyId string
+ AccessKeySecret string
+ Status State
+ CreateDate string
+}
+
+type Group struct {
+}
+
+type Role struct {
+ RoleId string
+ RoleName string
+ Arn string
+ Description string
+ AssumeRolePolicyDocument string
+ CreateDate string
+ UpdateDate string
+}
+
+type Policy struct {
+ PolicyName string
+ PolicyType string
+ Description string
+ DefaultVersion string
+ CreateDate string
+ UpdateDate string
+ AttachmentCount int64
+}
+
+type PolicyDocument struct {
+ Statement []PolicyItem
+ Version string
+}
+
+type PolicyItem struct {
+ Action string
+ Effect string
+ Resource string
+}
+
+type AssumeRolePolicyDocument struct {
+ Statement []AssumeRolePolicyItem
+ Version string
+}
+
+type AssumeRolePolicyItem struct {
+ Action string
+ Effect string
+ Principal AssumeRolePolicyPrincpal
+}
+
+type AssumeRolePolicyPrincpal struct {
+ RAM []string
+}
+
+/*
+ "PasswordPolicy": {
+ "MinimumPasswordLength": 12,
+ "RequireLowercaseCharacters": true,
+ "RequireUppercaseCharacters": true,
+ "RequireNumbers": true,
+ "RequireSymbols": true
+ }
+*/
+
+type PasswordPolicy struct {
+ MinimumPasswordLength int8
+ RequireLowercaseCharacters bool
+ RequireUppercaseCharacters bool
+ RequireNumbers bool
+ RequireSymbols bool
+}
+
+type RamCommonResponse struct {
+ common.Response
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/certificates.go b/vendor/github.com/denverdino/aliyungo/slb/certificates.go
new file mode 100644
index 000000000..5cbc4ac09
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/certificates.go
@@ -0,0 +1,108 @@
+package slb
+
+import "github.com/denverdino/aliyungo/common"
+
+type UploadServerCertificateArgs struct {
+ RegionId common.Region
+ ServerCertificate string
+ ServerCertificateName string
+ PrivateKey string
+}
+
+type UploadServerCertificateResponse struct {
+ common.Response
+ ServerCertificateId string
+ ServerCertificateName string
+ Fingerprint string
+}
+
+// UploadServerCertificate Upload server certificate
+//
+// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&UploadServerCertificate
+func (client *Client) UploadServerCertificate(args *UploadServerCertificateArgs) (response *UploadServerCertificateResponse, err error) {
+ response = &UploadServerCertificateResponse{}
+ err = client.Invoke("UploadServerCertificate", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type DeleteServerCertificateArgs struct {
+ RegionId common.Region
+ ServerCertificateId string
+}
+
+type DeleteServerCertificateResponse struct {
+ common.Response
+}
+
+// DeleteServerCertificate Delete server certificate
+//
+// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DeleteServerCertificate
+func (client *Client) DeleteServerCertificate(regionId common.Region, serverCertificateId string) (err error) {
+ args := &DeleteServerCertificateArgs{
+ RegionId: regionId,
+ ServerCertificateId: serverCertificateId,
+ }
+ response := &DeleteServerCertificateResponse{}
+ return client.Invoke("DeleteServerCertificate", args, response)
+}
+
+type SetServerCertificateNameArgs struct {
+ RegionId common.Region
+ ServerCertificateId string
+ ServerCertificateName string
+}
+
+type SetServerCertificateNameResponse struct {
+ common.Response
+}
+
+// SetServerCertificateName Set name of server certificate
+//
+// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&SetServerCertificateName
+func (client *Client) SetServerCertificateName(regionId common.Region, serverCertificateId string, name string) (err error) {
+ args := &SetServerCertificateNameArgs{
+ RegionId: regionId,
+ ServerCertificateId: serverCertificateId,
+ ServerCertificateName: name,
+ }
+ response := &SetServerCertificateNameResponse{}
+ return client.Invoke("SetServerCertificateName", args, response)
+}
+
+type DescribeServerCertificatesArgs struct {
+ RegionId common.Region
+ ServerCertificateId string
+}
+
+type ServerCertificateType struct {
+ RegionId common.Region
+ ServerCertificateId string
+ ServerCertificateName string
+ Fingerprint string
+}
+
+type DescribeServerCertificatesResponse struct {
+ common.Response
+ ServerCertificates struct {
+ ServerCertificate []ServerCertificateType
+ }
+}
+
+// DescribeServerCertificates Describe server certificates
+//
+// You can read doc at http://docs.aliyun.com/#pub/slb/api-reference/api-servercertificate&DescribeServerCertificates
+func (client *Client) DescribeServerCertificatesArgs(regionId common.Region, serverCertificateId string) (serverCertificates []ServerCertificateType, err error) {
+ args := &DescribeServerCertificatesArgs{
+ RegionId: regionId,
+ ServerCertificateId: serverCertificateId,
+ }
+ response := &DescribeServerCertificatesResponse{}
+ err = client.Invoke("DescribeServerCertificates", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response.ServerCertificates.ServerCertificate, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/client.go b/vendor/github.com/denverdino/aliyungo/slb/client.go
new file mode 100644
index 000000000..0f9b705e4
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/client.go
@@ -0,0 +1,49 @@
+package slb
+
+import (
+ "os"
+
+ "github.com/denverdino/aliyungo/common"
+)
+
+type Client struct {
+ common.Client
+}
+
+const (
+ // SLBDefaultEndpoint is the default API endpoint of SLB services
+ SLBDefaultEndpoint = "https://slb.aliyuncs.com"
+ SLBAPIVersion = "2014-05-15"
+
+ SLBServiceCode = "slb"
+)
+
+// NewClient creates a new instance of ECS client
+func NewClient(accessKeyId, accessKeySecret string) *Client {
+ endpoint := os.Getenv("SLB_ENDPOINT")
+ if endpoint == "" {
+ endpoint = SLBDefaultEndpoint
+ }
+ return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
+}
+
+func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client {
+ client := &Client{}
+ client.Init(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret)
+ return client
+}
+
+func NewSLBClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ endpoint := os.Getenv("SLB_ENDPOINT")
+ if endpoint == "" {
+ endpoint = SLBDefaultEndpoint
+ }
+
+ return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID)
+}
+
+func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+ client := &Client{}
+ client.NewInit(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret, SLBServiceCode, regionID)
+ return client
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/listeners.go b/vendor/github.com/denverdino/aliyungo/slb/listeners.go
new file mode 100644
index 000000000..dedc67588
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/listeners.go
@@ -0,0 +1,531 @@
+package slb
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/denverdino/aliyungo/common"
+)
+
+type ListenerStatus string
+
+const (
+ Starting = ListenerStatus("starting")
+ Running = ListenerStatus("running")
+ Configuring = ListenerStatus("configuring")
+ Stopping = ListenerStatus("stopping")
+ Stopped = ListenerStatus("stopped")
+)
+
+type SchedulerType string
+
+const (
+ WRRScheduler = SchedulerType("wrr")
+ WLCScheduler = SchedulerType("wlc")
+)
+
+type FlagType string
+
+const (
+ OnFlag = FlagType("on")
+ OffFlag = FlagType("off")
+)
+
+type StickySessionType string
+
+const (
+ InsertStickySessionType = StickySessionType("insert")
+ ServerStickySessionType = StickySessionType("server")
+)
+
+const BackendServerPort = -520
+
+type HealthCheckHttpCodeType string
+
+const (
+ HTTP_2XX = HealthCheckHttpCodeType("http_2xx")
+ HTTP_3XX = HealthCheckHttpCodeType("http_3xx")
+ HTTP_4XX = HealthCheckHttpCodeType("http_4xx")
+ HTTP_5XX = HealthCheckHttpCodeType("http_5xx")
+)
+
+func EncodeHealthCheckHttpCodeType(healthCheckHttpCodes []HealthCheckHttpCodeType) (HealthCheckHttpCodeType, error) {
+ code := ""
+
+ if nil == healthCheckHttpCodes || len(healthCheckHttpCodes) < 1 {
+ return "", fmt.Errorf("Invalid size of healthCheckHttpCodes")
+ }
+
+ for _, healthCheckHttpCode := range healthCheckHttpCodes {
+ if strings.EqualFold(string(HTTP_2XX), string(healthCheckHttpCode)) ||
+ strings.EqualFold(string(HTTP_3XX), string(healthCheckHttpCode)) ||
+ strings.EqualFold(string(HTTP_4XX), string(healthCheckHttpCode)) ||
+ strings.EqualFold(string(HTTP_5XX), string(healthCheckHttpCode)) {
+ if "" == code {
+ code = string(healthCheckHttpCode)
+ } else {
+ if strings.Contains(code, string(healthCheckHttpCode)) {
+ return "", fmt.Errorf("Duplicates healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes)
+ }
+ code += code + "," + string(healthCheckHttpCode)
+ }
+ } else {
+ return "", fmt.Errorf("Invalid healthCheckHttpCode(%v in %v)", healthCheckHttpCode, healthCheckHttpCodes)
+ }
+ }
+ return HealthCheckHttpCodeType(code), nil
+}
+
+type CommonLoadBalancerListenerResponse struct {
+ common.Response
+}
+
+type HTTPListenerType struct {
+ LoadBalancerId string
+ ListenerPort int
+ BackendServerPort int
+ Bandwidth int
+ Scheduler SchedulerType
+ StickySession FlagType
+ StickySessionType StickySessionType
+ CookieTimeout int
+ Cookie string
+ HealthCheck FlagType
+ HealthCheckDomain string
+ HealthCheckURI string
+ HealthCheckConnectPort int
+ HealthyThreshold int
+ UnhealthyThreshold int
+ HealthCheckTimeout int
+ HealthCheckInterval int
+ HealthCheckHttpCode HealthCheckHttpCodeType
+ VServerGroupId string
+ Gzip FlagType
+}
+type CreateLoadBalancerHTTPListenerArgs HTTPListenerType
+
+// CreateLoadBalancerHTTPListener create HTTP listener on loadbalancer
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPListener
+func (client *Client) CreateLoadBalancerHTTPListener(args *CreateLoadBalancerHTTPListenerArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("CreateLoadBalancerHTTPListener", args, response)
+ return err
+}
+
+type HTTPSListenerType struct {
+ HTTPListenerType
+ ServerCertificateId string
+}
+
+type CreateLoadBalancerHTTPSListenerArgs HTTPSListenerType
+
+// CreateLoadBalancerHTTPSListener create HTTPS listener on loadbalancer
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerHTTPSListener
+func (client *Client) CreateLoadBalancerHTTPSListener(args *CreateLoadBalancerHTTPSListenerArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("CreateLoadBalancerHTTPSListener", args, response)
+ return err
+}
+
+type HealthCheckType string
+
+const (
+ TCPHealthCheckType = HealthCheckType("tcp")
+ HTTPHealthCheckType = HealthCheckType("http")
+)
+
+type TCPListenerType struct {
+ LoadBalancerId string
+ ListenerPort int
+ BackendServerPort int
+ Bandwidth int
+ Scheduler SchedulerType
+ PersistenceTimeout int
+ HealthCheckType HealthCheckType
+ HealthCheckDomain string
+ HealthCheckURI string
+ HealthCheckConnectPort int
+ HealthyThreshold int
+ UnhealthyThreshold int
+ HealthCheckConnectTimeout int
+ HealthCheckInterval int
+ HealthCheckHttpCode HealthCheckHttpCodeType
+ VServerGroupId string
+}
+
+type CreateLoadBalancerTCPListenerArgs TCPListenerType
+
+// CreateLoadBalancerTCPListener create TCP listener on loadbalancer
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerTCPListener
+func (client *Client) CreateLoadBalancerTCPListener(args *CreateLoadBalancerTCPListenerArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("CreateLoadBalancerTCPListener", args, response)
+ return err
+}
+
+type UDPListenerType struct {
+ LoadBalancerId string
+ ListenerPort int
+ BackendServerPort int
+ Bandwidth int
+ Scheduler SchedulerType
+ PersistenceTimeout int
+ HealthCheckConnectPort int
+ HealthyThreshold int
+ UnhealthyThreshold int
+ HealthCheckConnectTimeout int
+ HealthCheckInterval int
+ VServerGroupId string
+}
+type CreateLoadBalancerUDPListenerArgs UDPListenerType
+
+// CreateLoadBalancerUDPListener create UDP listener on loadbalancer
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&CreateLoadBalancerUDPListener
+func (client *Client) CreateLoadBalancerUDPListener(args *CreateLoadBalancerUDPListenerArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("CreateLoadBalancerUDPListener", args, response)
+ return err
+}
+
+type CommonLoadBalancerListenerArgs struct {
+ LoadBalancerId string
+ ListenerPort int
+}
+
+// DeleteLoadBalancerListener Delete listener
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DeleteLoadBalancerListener
+func (client *Client) DeleteLoadBalancerListener(loadBalancerId string, port int) (err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("DeleteLoadBalancerListener", args, response)
+ return err
+}
+
+// StartLoadBalancerListener Start listener
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StartLoadBalancerListener
+func (client *Client) StartLoadBalancerListener(loadBalancerId string, port int) (err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("StartLoadBalancerListener", args, response)
+ return err
+}
+
+// StopLoadBalancerListener Stop listener
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&StopLoadBalancerListener
+func (client *Client) StopLoadBalancerListener(loadBalancerId string, port int) (err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("StopLoadBalancerListener", args, response)
+ return err
+}
+
+type AccessControlStatus string
+
+const (
+ OpenWhileList = AccessControlStatus("open_white_list")
+ Close = AccessControlStatus("close")
+)
+
+type SetListenerAccessControlStatusArgs struct {
+ LoadBalancerId string
+ ListenerPort int
+ AccessControlStatus AccessControlStatus
+}
+
+// SetListenerAccessControlStatus Set listener access control status
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetListenerAccessControlStatus
+func (client *Client) SetListenerAccessControlStatus(loadBalancerId string, port int, status AccessControlStatus) (err error) {
+ args := &SetListenerAccessControlStatusArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ AccessControlStatus: status,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("SetListenerAccessControlStatus", args, response)
+ return err
+}
+
+type CommonListenerWhiteListItemArgs struct {
+ LoadBalancerId string
+ ListenerPort int
+ SourceItems string
+}
+
+// AddListenerWhiteListItem Add listener white-list item
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&AddListenerWhiteListItem
+func (client *Client) AddListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) {
+ args := &CommonListenerWhiteListItemArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ SourceItems: sourceItems,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("AddListenerWhiteListItem", args, response)
+ return err
+}
+
+// RemoveListenerWhiteListItem Remove listener white-list item
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&RemoveListenerWhiteListItem
+func (client *Client) RemoveListenerWhiteListItem(loadBalancerId string, port int, sourceItems string) (err error) {
+ args := &CommonListenerWhiteListItemArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ SourceItems: sourceItems,
+ }
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("RemoveListenerWhiteListItem", args, response)
+ return err
+}
+
+type SetLoadBalancerHTTPListenerAttributeArgs CreateLoadBalancerHTTPListenerArgs
+
+// SetLoadBalancerHTTPListenerAttribute Set HTTP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPListenerAttribute
+func (client *Client) SetLoadBalancerHTTPListenerAttribute(args *SetLoadBalancerHTTPListenerAttributeArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("SetLoadBalancerHTTPListenerAttribute", args, response)
+ return err
+}
+
+type SetLoadBalancerHTTPSListenerAttributeArgs CreateLoadBalancerHTTPSListenerArgs
+
+// SetLoadBalancerHTTPSListenerAttribute Set HTTPS listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerHTTPSListenerAttribute
+func (client *Client) SetLoadBalancerHTTPSListenerAttribute(args *SetLoadBalancerHTTPSListenerAttributeArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("SetLoadBalancerHTTPSListenerAttribute", args, response)
+ return err
+}
+
+type SetLoadBalancerTCPListenerAttributeArgs CreateLoadBalancerTCPListenerArgs
+
+// SetLoadBalancerTCPListenerAttribute Set TCP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerTCPListenerAttribute
+func (client *Client) SetLoadBalancerTCPListenerAttribute(args *SetLoadBalancerTCPListenerAttributeArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("SetLoadBalancerTCPListenerAttribute", args, response)
+ return err
+}
+
+type SetLoadBalancerUDPListenerAttributeArgs CreateLoadBalancerUDPListenerArgs
+
+// SetLoadBalancerUDPListenerAttribute Set UDP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&SetLoadBalancerUDPListenerAttribute
+func (client *Client) SetLoadBalancerUDPListenerAttribute(args *SetLoadBalancerUDPListenerAttributeArgs) (err error) {
+ response := &CommonLoadBalancerListenerResponse{}
+ err = client.Invoke("SetLoadBalancerUDPListenerAttribute", args, response)
+ return err
+}
+
+type DescribeLoadBalancerListenerAttributeResponse struct {
+ common.Response
+ Status ListenerStatus
+}
+
+type DescribeLoadBalancerHTTPListenerAttributeResponse struct {
+ DescribeLoadBalancerListenerAttributeResponse
+ HTTPListenerType
+}
+
+// DescribeLoadBalancerHTTPListenerAttribute Describe HTTP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPListenerAttribute
+func (client *Client) DescribeLoadBalancerHTTPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPListenerAttributeResponse, err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response = &DescribeLoadBalancerHTTPListenerAttributeResponse{}
+ err = client.Invoke("DescribeLoadBalancerHTTPListenerAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type DescribeLoadBalancerHTTPSListenerAttributeResponse struct {
+ DescribeLoadBalancerListenerAttributeResponse
+ HTTPSListenerType
+}
+
+// DescribeLoadBalancerHTTPSListenerAttribute Describe HTTPS listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerHTTPSListenerAttribute
+func (client *Client) DescribeLoadBalancerHTTPSListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerHTTPSListenerAttributeResponse, err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response = &DescribeLoadBalancerHTTPSListenerAttributeResponse{}
+ err = client.Invoke("DescribeLoadBalancerHTTPSListenerAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type DescribeLoadBalancerTCPListenerAttributeResponse struct {
+ DescribeLoadBalancerListenerAttributeResponse
+ TCPListenerType
+}
+
+// DescribeLoadBalancerTCPListenerAttribute Describe TCP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerTCPListenerAttribute
+func (client *Client) DescribeLoadBalancerTCPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerTCPListenerAttributeResponse, err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response = &DescribeLoadBalancerTCPListenerAttributeResponse{}
+ err = client.Invoke("DescribeLoadBalancerTCPListenerAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type DescribeLoadBalancerUDPListenerAttributeResponse struct {
+ DescribeLoadBalancerListenerAttributeResponse
+ UDPListenerType
+}
+
+// DescribeLoadBalancerUDPListenerAttribute Describe UDP listener attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeLoadBalancerUDPListenerAttribute
+func (client *Client) DescribeLoadBalancerUDPListenerAttribute(loadBalancerId string, port int) (response *DescribeLoadBalancerUDPListenerAttributeResponse, err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response = &DescribeLoadBalancerUDPListenerAttributeResponse{}
+ err = client.Invoke("DescribeLoadBalancerUDPListenerAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type ListenerType string
+
+const (
+ UDP = ListenerType("UDP")
+ TCP = ListenerType("TCP")
+ HTTP = ListenerType("HTTP")
+ HTTPS = ListenerType("HTTPS")
+)
+
+const DefaultWaitForInterval = 5 //5 seconds
+const DefaultTimeout = 60 //60 seconds
+
+// WaitForListener waits for listener to given status
+func (client *Client) WaitForListener(loadBalancerId string, port int, listenerType ListenerType) (status ListenerStatus, err error) {
+ timeout := DefaultTimeout
+
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+
+ method := fmt.Sprintf("DescribeLoadBalancer%sListenerAttribute", listenerType)
+ response := &DescribeLoadBalancerListenerAttributeResponse{}
+
+ for {
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return response.Status, common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+ //Sleep first to ensure the previous request is sent
+ err = client.Invoke(method, args, response)
+ if err != nil {
+ return "", err
+ }
+ if response.Status == Running || response.Status == Stopped {
+ break
+ }
+ }
+ return response.Status, nil
+}
+
+// WaitForListener waits for listener to given status
+func (client *Client) WaitForListenerAsyn(loadBalancerId string, port int, listenerType ListenerType, status ListenerStatus, timeout int) error {
+ if timeout <= 0 {
+ timeout = DefaultTimeout
+ }
+
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+
+ method := fmt.Sprintf("DescribeLoadBalancer%sListenerAttribute", listenerType)
+ response := &DescribeLoadBalancerListenerAttributeResponse{}
+
+ for {
+ err := client.Invoke(method, args, response)
+ e, _ := err.(*common.Error)
+ if e != nil {
+ if e.StatusCode == 404 || e.Code == "InvalidLoadBalancerId.NotFound" {
+ continue
+ }
+ return err
+ } else if response != nil && response.Status == status {
+ //TODO
+ break
+ }
+ timeout = timeout - DefaultWaitForInterval
+ if timeout <= 0 {
+ return common.GetClientErrorFromString("Timeout")
+ }
+ time.Sleep(DefaultWaitForInterval * time.Second)
+
+ }
+ return nil
+}
+
+type DescribeListenerAccessControlAttributeResponse struct {
+ common.Response
+ AccessControlStatus AccessControlStatus
+ SourceItems string
+}
+
+// DescribeListenerAccessControlAttribute Describe listener access control attribute
+//
+// You can read doc at https://docs.aliyun.com/#/pub/slb/api-reference/api-related-listener&DescribeListenerAccessControlAttribute
+func (client *Client) DescribeListenerAccessControlAttribute(loadBalancerId string, port int) (response *DescribeListenerAccessControlAttributeResponse, err error) {
+ args := &CommonLoadBalancerListenerArgs{
+ LoadBalancerId: loadBalancerId,
+ ListenerPort: port,
+ }
+ response = &DescribeListenerAccessControlAttributeResponse{}
+ err = client.Invoke("DescribeListenerAccessControlAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go
new file mode 100644
index 000000000..d090570ce
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go
@@ -0,0 +1,238 @@
+package slb
+
+import (
+ "github.com/denverdino/aliyungo/common"
+ "github.com/denverdino/aliyungo/util"
+)
+
+type AddressType string
+
+const (
+ InternetAddressType = AddressType("internet")
+ IntranetAddressType = AddressType("intranet")
+)
+
+type InternetChargeType string
+
+const (
+ PayByBandwidth = InternetChargeType("paybybandwidth")
+ PayByTraffic = InternetChargeType("paybytraffic")
+)
+
+type CreateLoadBalancerArgs struct {
+ RegionId common.Region
+ LoadBalancerName string
+ AddressType AddressType
+ VSwitchId string
+ InternetChargeType InternetChargeType
+ Bandwidth int
+ ClientToken string
+}
+
+type CreateLoadBalancerResponse struct {
+ common.Response
+ LoadBalancerId string
+ Address string
+ NetworkType string
+ VpcId string
+ VSwitchId string
+ LoadBalancerName string
+}
+
+// CreateLoadBalancer create loadbalancer
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&CreateLoadBalancer
+func (client *Client) CreateLoadBalancer(args *CreateLoadBalancerArgs) (response *CreateLoadBalancerResponse, err error) {
+ response = &CreateLoadBalancerResponse{}
+ err = client.Invoke("CreateLoadBalancer", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+type DeleteLoadBalancerArgs struct {
+ LoadBalancerId string
+}
+
+type DeleteLoadBalancerResponse struct {
+ common.Response
+}
+
+// DeleteLoadBalancer delete loadbalancer
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DeleteLoadBalancer
+func (client *Client) DeleteLoadBalancer(loadBalancerId string) (err error) {
+ args := &DeleteLoadBalancerArgs{
+ LoadBalancerId: loadBalancerId,
+ }
+ response := &DeleteLoadBalancerResponse{}
+ err = client.Invoke("DeleteLoadBalancer", args, response)
+ return err
+}
+
+type ModifyLoadBalancerInternetSpecArgs struct {
+ LoadBalancerId string
+ InternetChargeType InternetChargeType
+ Bandwidth int
+}
+
+type ModifyLoadBalancerInternetSpecResponse struct {
+ common.Response
+}
+
+// ModifyLoadBalancerInternetSpec Modify loadbalancer internet spec
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&ModifyLoadBalancerInternetSpec
+
+func (client *Client) ModifyLoadBalancerInternetSpec(args *ModifyLoadBalancerInternetSpecArgs) (err error) {
+ response := &ModifyLoadBalancerInternetSpecResponse{}
+ err = client.Invoke("ModifyLoadBalancerInternetSpec", args, response)
+ return err
+}
+
+type Status string
+
+const InactiveStatus = Status("inactive")
+const ActiveStatus = Status("active")
+const LockedStatus = Status("locked")
+
+type SetLoadBalancerStatusArgs struct {
+ LoadBalancerId string
+ LoadBalancerStatus Status
+}
+
+type SetLoadBalancerStatusResponse struct {
+ common.Response
+}
+
+// SetLoadBalancerStatus Set loadbalancer status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerStatus
+
+func (client *Client) SetLoadBalancerStatus(loadBalancerId string, status Status) (err error) {
+ args := &SetLoadBalancerStatusArgs{
+ LoadBalancerId: loadBalancerId,
+ LoadBalancerStatus: status,
+ }
+ response := &SetLoadBalancerStatusResponse{}
+ err = client.Invoke("SetLoadBalancerStatus", args, response)
+ return err
+}
+
+type SetLoadBalancerNameArgs struct {
+ LoadBalancerId string
+ LoadBalancerName string
+}
+
+type SetLoadBalancerNameResponse struct {
+ common.Response
+}
+
+// SetLoadBalancerName Set loadbalancer name
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&SetLoadBalancerName
+
+func (client *Client) SetLoadBalancerName(loadBalancerId string, name string) (err error) {
+ args := &SetLoadBalancerNameArgs{
+ LoadBalancerId: loadBalancerId,
+ LoadBalancerName: name,
+ }
+ response := &SetLoadBalancerNameResponse{}
+ err = client.Invoke("SetLoadBalancerName", args, response)
+ return err
+}
+
+type DescribeLoadBalancersArgs struct {
+ RegionId common.Region
+ LoadBalancerId string
+ LoadBalancerName string
+ AddressType AddressType
+ NetworkType string
+ VpcId string
+ VSwitchId string
+ Address string
+ InternetChargeType InternetChargeType
+ ServerId string
+}
+
+type ListenerPortAndProtocolType struct {
+ ListenerPort int
+ ListenerProtocol string
+}
+
+type BackendServerType struct {
+ ServerId string
+ Weight int
+}
+
+type LoadBalancerType struct {
+ LoadBalancerId string
+ LoadBalancerName string
+ LoadBalancerStatus string
+ Address string
+ RegionId common.Region
+ RegionIdAlias string
+ AddressType AddressType
+ VSwitchId string
+ VpcId string
+ NetworkType string
+ Bandwidth int
+ InternetChargeType InternetChargeType
+ CreateTime string //Why not ISO 6801
+ CreateTimeStamp util.ISO6801Time
+ ListenerPorts struct {
+ ListenerPort []int
+ }
+ ListenerPortsAndProtocol struct {
+ ListenerPortAndProtocol []ListenerPortAndProtocolType
+ }
+ BackendServers struct {
+ BackendServer []BackendServerType
+ }
+}
+
+type DescribeLoadBalancersResponse struct {
+ common.Response
+ LoadBalancers struct {
+ LoadBalancer []LoadBalancerType
+ }
+}
+
+// DescribeLoadBalancers Describe loadbalancers
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancers
+
+func (client *Client) DescribeLoadBalancers(args *DescribeLoadBalancersArgs) (loadBalancers []LoadBalancerType, err error) {
+ response := &DescribeLoadBalancersResponse{}
+ err = client.Invoke("DescribeLoadBalancers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response.LoadBalancers.LoadBalancer, err
+}
+
+type DescribeLoadBalancerAttributeArgs struct {
+ LoadBalancerId string
+}
+
+type DescribeLoadBalancerAttributeResponse struct {
+ common.Response
+ LoadBalancerType
+}
+
+// DescribeLoadBalancerAttribute Describe loadbalancer attribute
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeLoadBalancerAttribute
+
+func (client *Client) DescribeLoadBalancerAttribute(loadBalancerId string) (loadBalancer *LoadBalancerType, err error) {
+ args := &DescribeLoadBalancersArgs{
+ LoadBalancerId: loadBalancerId,
+ }
+ response := &DescribeLoadBalancerAttributeResponse{}
+ err = client.Invoke("DescribeLoadBalancerAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return &response.LoadBalancerType, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/regions.go b/vendor/github.com/denverdino/aliyungo/slb/regions.go
new file mode 100644
index 000000000..870ffefe7
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/regions.go
@@ -0,0 +1,34 @@
+package slb
+
+import "github.com/denverdino/aliyungo/common"
+
+type DescribeRegionsArgs struct {
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype®iontype
+type RegionType struct {
+ RegionId common.Region
+ LocalName string
+}
+
+type DescribeRegionsResponse struct {
+ common.Response
+ Regions struct {
+ Region []RegionType
+ }
+}
+
+// DescribeRegions describes regions
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-loadbalancer&DescribeRegions
+func (client *Client) DescribeRegions() (regions []RegionType, err error) {
+ response := DescribeRegionsResponse{}
+
+ err = client.Invoke("DescribeRegions", &DescribeRegionsArgs{}, &response)
+
+ if err != nil {
+ return []RegionType{}, err
+ }
+ return response.Regions.Region, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/rules.go b/vendor/github.com/denverdino/aliyungo/slb/rules.go
new file mode 100644
index 000000000..94eb402b6
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/rules.go
@@ -0,0 +1,126 @@
+package slb
+
+import "github.com/denverdino/aliyungo/common"
+
+type CreateRulesResponse struct {
+ common.Response
+}
+
+type CreateRulesArgs struct {
+ RegionId common.Region
+ LoadBalancerId string
+ ListenerPort int
+ RuleList string
+}
+
+type Rule struct {
+ RuleId string
+ RuleName string
+ Domain string
+ Url string `json:",omitempty"`
+ VServerGroupId string
+}
+
+// Create forward rules
+//
+// You can read doc at https://help.aliyun.com/document_detail/35226.html?spm=5176.doc35226.6.671.625Omh
+func (client *Client) CreateRules(args *CreateRulesArgs) error {
+ response := CreateRulesResponse{}
+ err := client.Invoke("CreateRules", args, &response)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+type DeleteRulesArgs struct {
+ RegionId common.Region
+ RuleIds string
+}
+
+type DeleteRulesResponse struct {
+ common.Response
+}
+
+// Delete forward rules
+//
+// You can read doc at https://help.aliyun.com/document_detail/35227.html?spm=5176.doc35226.6.672.6iNBtR
+func (client *Client) DeleteRules(args *DeleteRulesArgs) error {
+ response := DeleteRulesResponse{}
+ err := client.Invoke("DeleteRules", args, &response)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+type SetRuleArgs struct {
+ RegionId common.Region
+ RuleId string
+ VServerGroupId string
+}
+
+type SetRuleResponse struct {
+ common.Response
+}
+
+// Modify forward rules
+//
+// You can read doc at https://help.aliyun.com/document_detail/35228.html?spm=5176.doc35227.6.673.rq40a9
+func (client *Client) SetRule(args *SetRuleArgs) error {
+ response := SetRuleResponse{}
+ err := client.Invoke("SetRule", args, &response)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+type DescribeRuleAttributeArgs struct {
+ RegionId common.Region
+ RuleId string
+}
+
+type DescribeRuleAttributeResponse struct {
+ common.Response
+ LoadBalancerId string
+ ListenerPort int
+ Rule
+}
+
+// Describe rule
+//
+// You can read doc at https://help.aliyun.com/document_detail/35229.html?spm=5176.doc35226.6.674.DRJeKJ
+func (client *Client) DescribeRuleAttribute(args *DescribeRuleAttributeArgs) (*DescribeRuleAttributeResponse, error) {
+ response := &DescribeRuleAttributeResponse{}
+ err := client.Invoke("DescribeRuleAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, nil
+}
+
+type DescribeRulesArgs struct {
+ RegionId common.Region
+ LoadBalancerId string
+ ListenerPort int
+}
+
+type DescribeRulesResponse struct {
+ common.Response
+ Rules struct {
+ Rule []Rule
+ }
+}
+
+// Describe rule
+//
+// You can read doc at https://help.aliyun.com/document_detail/35229.html?spm=5176.doc35226.6.674.DRJeKJ
+func (client *Client) DescribeRules(args *DescribeRulesArgs) (*DescribeRulesResponse, error) {
+ response := &DescribeRulesResponse{}
+ err := client.Invoke("DescribeRules", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/servers.go b/vendor/github.com/denverdino/aliyungo/slb/servers.go
new file mode 100644
index 000000000..18be45891
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/servers.go
@@ -0,0 +1,120 @@
+package slb
+
+import (
+ "encoding/json"
+
+ "github.com/denverdino/aliyungo/common"
+)
+
+type AddBackendServersArgs struct {
+ LoadBalancerId string
+ BackendServers string
+}
+
+type SetBackendServersArgs AddBackendServersArgs
+
+type AddBackendServersResponse struct {
+ common.Response
+ LoadBalancerId string
+ BackendServers struct {
+ BackendServer []BackendServerType
+ }
+}
+
+type SetBackendServersResponse AddBackendServersResponse
+
+// SetBackendServers set weight of backend servers
+
+func (client *Client) SetBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) {
+ bytes, _ := json.Marshal(backendServers)
+
+ args := &SetBackendServersArgs{
+ LoadBalancerId: loadBalancerId,
+ BackendServers: string(bytes),
+ }
+ response := &SetBackendServersResponse{}
+
+ err = client.Invoke("SetBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response.BackendServers.BackendServer, err
+}
+
+// AddBackendServers Add backend servers
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&AddBackendServers
+func (client *Client) AddBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) {
+
+ bytes, _ := json.Marshal(backendServers)
+
+ args := &AddBackendServersArgs{
+ LoadBalancerId: loadBalancerId,
+ BackendServers: string(bytes),
+ }
+ response := &AddBackendServersResponse{}
+
+ err = client.Invoke("AddBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response.BackendServers.BackendServer, err
+}
+
+type RemoveBackendServersArgs struct {
+ LoadBalancerId string
+ BackendServers []string
+}
+
+type RemoveBackendServersResponse struct {
+ common.Response
+ LoadBalancerId string
+ BackendServers struct {
+ BackendServer []BackendServerType
+ }
+}
+
+// RemoveBackendServers Remove backend servers
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&RemoveBackendServers
+func (client *Client) RemoveBackendServers(loadBalancerId string, backendServers []string) (result []BackendServerType, err error) {
+ args := &RemoveBackendServersArgs{
+ LoadBalancerId: loadBalancerId,
+ BackendServers: backendServers,
+ }
+ response := &RemoveBackendServersResponse{}
+ err = client.Invoke("RemoveBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response.BackendServers.BackendServer, err
+}
+
+type HealthStatusType struct {
+ ServerId string
+ ServerHealthStatus string
+}
+
+type DescribeHealthStatusArgs struct {
+ LoadBalancerId string
+ ListenerPort int
+}
+
+type DescribeHealthStatusResponse struct {
+ common.Response
+ BackendServers struct {
+ BackendServer []HealthStatusType
+ }
+}
+
+// DescribeHealthStatus Describe health status
+//
+// You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&DescribeHealthStatus
+func (client *Client) DescribeHealthStatus(args *DescribeHealthStatusArgs) (response *DescribeHealthStatusResponse, err error) {
+ response = &DescribeHealthStatusResponse{}
+ err = client.Invoke("DescribeHealthStatus", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/tags.go b/vendor/github.com/denverdino/aliyungo/slb/tags.go
new file mode 100644
index 000000000..4128559d4
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/tags.go
@@ -0,0 +1,85 @@
+package slb
+
+import "github.com/denverdino/aliyungo/common"
+
+type TagItem struct {
+ TagKey string
+ TagValue string
+}
+
+type AddTagsArgs struct {
+ RegionId common.Region
+ LoadBalancerID string
+ Tags string
+}
+
+type AddTagsResponse struct {
+ common.Response
+}
+
+// AddTags Add tags to resource
+//
+// You can read doc at https://help.aliyun.com/document_detail/42871.html
+func (client *Client) AddTags(args *AddTagsArgs) error {
+ response := AddTagsResponse{}
+ err := client.Invoke("AddTags", args, &response)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+type RemoveTagsArgs struct {
+ RegionId common.Region
+ LoadBalancerID string
+ Tags string
+}
+
+type RemoveTagsResponse struct {
+ common.Response
+}
+
+// RemoveTags remove tags to resource
+//
+// You can read doc at https://help.aliyun.com/document_detail/42872.html
+func (client *Client) RemoveTags(args *RemoveTagsArgs) error {
+ response := RemoveTagsResponse{}
+ err := client.Invoke("RemoveTags", args, &response)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+type TagItemType struct {
+ TagItem
+ InstanceCount int
+}
+
+type DescribeTagsArgs struct {
+ RegionId common.Region
+ LoadBalancerID string
+ Tags string
+ common.Pagination
+}
+
+type DescribeTagsResponse struct {
+ common.Response
+ common.PaginationResult
+ TagSets struct {
+ TagSet []TagItemType
+ }
+}
+
+// DescribeResourceByTags describe resource by tags
+//
+// You can read doc at https://help.aliyun.com/document_detail/42873.html?spm=5176.doc42872.6.267.CP1iWu
+func (client *Client) DescribeTags(args *DescribeTagsArgs) (tags []TagItemType, pagination *common.PaginationResult, err error) {
+ args.Validate()
+ response := DescribeTagsResponse{}
+ err = client.Invoke("DescribeTags", args, &response)
+ if err != nil {
+ return nil, nil, err
+ }
+ return response.TagSets.TagSet, &response.PaginationResult, nil
+}
diff --git a/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go
new file mode 100644
index 000000000..1fa83d09b
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/slb/vserver_group.go
@@ -0,0 +1,159 @@
+package slb
+
+import (
+ "github.com/denverdino/aliyungo/common"
+)
+
+type VBackendServerType struct {
+ ServerId string
+ Weight int
+ Port int
+}
+
+type VServerGroup struct {
+ VServerGroupName string
+ VServerGroupId string
+}
+
+type VBackendServers struct {
+ BackendServer []VBackendServerType
+}
+
+type CreateVServerGroupArgs struct {
+ LoadBalancerId string
+ RegionId common.Region
+ VServerGroupName string
+ VServerGroupId string
+ BackendServers string
+}
+
+type SetVServerGroupAttributeArgs struct {
+ LoadBalancerId string
+ RegionId common.Region
+ VServerGroupName string
+ VServerGroupId string
+ BackendServers string
+}
+
+type AddVServerGroupBackendServersArgs CreateVServerGroupArgs
+type RemoveVServerGroupBackendServersArgs CreateVServerGroupArgs
+type ModifyVServerGroupBackendServersArgs struct {
+ VServerGroupId string
+ RegionId common.Region
+ OldBackendServers string
+ NewBackendServers string
+}
+
+type DeleteVServerGroupArgs struct {
+ VServerGroupId string
+ RegionId common.Region
+}
+
+type DescribeVServerGroupsArgs struct {
+ LoadBalancerId string
+ RegionId common.Region
+}
+
+type DescribeVServerGroupAttributeArgs struct {
+ VServerGroupId string
+ RegionId common.Region
+}
+
+type CreateVServerGroupResponse struct {
+ common.Response
+ VServerGroupId string
+ VServerGroupName string
+ BackendServers VBackendServers
+}
+
+type SetVServerGroupAttributeResponse struct {
+ common.Response
+ VServerGroupId string
+ VServerGroupName string
+ BackendServers VBackendServers
+}
+
+type AddVServerGroupBackendServersResponse CreateVServerGroupResponse
+type RemoveVServerGroupBackendServersResponse CreateVServerGroupResponse
+type ModifyVServerGroupBackendServersResponse CreateVServerGroupResponse
+type DeleteVServerGroupResponse struct{ common.Response }
+type DescribeVServerGroupsResponse struct {
+ common.Response
+ VServerGroups struct {
+ VServerGroup []VServerGroup
+ }
+}
+type DescribeVServerGroupAttributeResponse CreateVServerGroupResponse
+
+
+func (client *Client) CreateVServerGroup(args *CreateVServerGroupArgs) (response *CreateVServerGroupResponse, err error) {
+ response = &CreateVServerGroupResponse{}
+ err = client.Invoke("CreateVServerGroup", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) SetVServerGroupAttribute(args *SetVServerGroupAttributeArgs) (response *SetVServerGroupAttributeResponse, err error) {
+ response = &SetVServerGroupAttributeResponse{}
+ err = client.Invoke("SetVServerGroupAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) AddVServerGroupBackendServers(args *AddVServerGroupBackendServersArgs) (response *AddVServerGroupBackendServersResponse, err error) {
+ response = &AddVServerGroupBackendServersResponse{}
+ err = client.Invoke("AddVServerGroupBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) RemoveVServerGroupBackendServers(args *RemoveVServerGroupBackendServersArgs) (response *RemoveVServerGroupBackendServersResponse, err error) {
+ response = &RemoveVServerGroupBackendServersResponse{}
+ err = client.Invoke("RemoveVServerGroupBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) ModifyVServerGroupBackendServers(args *ModifyVServerGroupBackendServersArgs) (response *ModifyVServerGroupBackendServersResponse, err error) {
+ response = &ModifyVServerGroupBackendServersResponse{}
+ err = client.Invoke("ModifyVServerGroupBackendServers", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) DeleteVServerGroup(args *DeleteVServerGroupArgs) (response *DeleteVServerGroupResponse, err error) {
+ response = &DeleteVServerGroupResponse{}
+ err = client.Invoke("DeleteVServerGroup", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) DescribeVServerGroups(args *DescribeVServerGroupsArgs) (response *DescribeVServerGroupsResponse, err error) {
+ response = &DescribeVServerGroupsResponse{}
+ err = client.Invoke("DescribeVServerGroups", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
+
+func (client *Client) DescribeVServerGroupAttribute(args *DescribeVServerGroupAttributeArgs) (response *DescribeVServerGroupAttributeResponse, err error) {
+ response = &DescribeVServerGroupAttributeResponse{}
+ err = client.Invoke("DescribeVServerGroupAttribute", args, response)
+ if err != nil {
+ return nil, err
+ }
+ return response, err
+}
diff --git a/vendor/github.com/denverdino/aliyungo/util/attempt.go b/vendor/github.com/denverdino/aliyungo/util/attempt.go
new file mode 100644
index 000000000..2d07f03a8
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/util/attempt.go
@@ -0,0 +1,76 @@
+package util
+
+import (
+ "time"
+)
+
+// AttemptStrategy is reused from the goamz package
+
+// AttemptStrategy represents a strategy for waiting for an action
+// to complete successfully. This is an internal type used by the
+// implementation of other packages.
+type AttemptStrategy struct {
+ Total time.Duration // total duration of attempt.
+ Delay time.Duration // interval between each try in the burst.
+ Min int // minimum number of retries; overrides Total
+}
+
+type Attempt struct {
+ strategy AttemptStrategy
+ last time.Time
+ end time.Time
+ force bool
+ count int
+}
+
+// Start begins a new sequence of attempts for the given strategy.
+func (s AttemptStrategy) Start() *Attempt {
+ now := time.Now()
+ return &Attempt{
+ strategy: s,
+ last: now,
+ end: now.Add(s.Total),
+ force: true,
+ }
+}
+
+// Next waits until it is time to perform the next attempt or returns
+// false if it is time to stop trying.
+func (a *Attempt) Next() bool {
+ now := time.Now()
+ sleep := a.nextSleep(now)
+ if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
+ return false
+ }
+ a.force = false
+ if sleep > 0 && a.count > 0 {
+ time.Sleep(sleep)
+ now = time.Now()
+ }
+ a.count++
+ a.last = now
+ return true
+}
+
+func (a *Attempt) nextSleep(now time.Time) time.Duration {
+ sleep := a.strategy.Delay - now.Sub(a.last)
+ if sleep < 0 {
+ return 0
+ }
+ return sleep
+}
+
+// HasNext returns whether another attempt will be made if the current
+// one fails. If it returns true, the following call to Next is
+// guaranteed to return true.
+func (a *Attempt) HasNext() bool {
+ if a.force || a.strategy.Min > a.count {
+ return true
+ }
+ now := time.Now()
+ if now.Add(a.nextSleep(now)).Before(a.end) {
+ a.force = true
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/denverdino/aliyungo/util/encoding.go b/vendor/github.com/denverdino/aliyungo/util/encoding.go
new file mode 100644
index 000000000..8cb588288
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/util/encoding.go
@@ -0,0 +1,314 @@
+package util
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// change instance=["a", "b"]
+// to instance.1="a" instance.2="b"
+func FlattenFn(fieldName string, field reflect.Value, values *url.Values) {
+ l := field.Len()
+ if l > 0 {
+ for i := 0; i < l; i++ {
+ str := field.Index(i).String()
+ values.Set(fieldName+"."+strconv.Itoa(i+1), str)
+ }
+ }
+}
+
+func Underline2Dot(name string) string {
+ return strings.Replace(name, "_", ".", -1)
+}
+
+//ConvertToQueryValues converts the struct to url.Values
+func ConvertToQueryValues(ifc interface{}) url.Values {
+ values := url.Values{}
+ SetQueryValues(ifc, &values)
+ return values
+}
+
+//SetQueryValues sets the struct to existing url.Values following ECS encoding rules
+func SetQueryValues(ifc interface{}, values *url.Values) {
+ setQueryValues(ifc, values, "")
+}
+
+func SetQueryValueByFlattenMethod(ifc interface{}, values *url.Values) {
+ setQueryValuesByFlattenMethod(ifc, values, "")
+}
+
+func setQueryValues(i interface{}, values *url.Values, prefix string) {
+ // add to support url.Values
+ mapValues, ok := i.(url.Values)
+ if ok {
+ for k, _ := range mapValues {
+ values.Set(k, mapValues.Get(k))
+ }
+ return
+ }
+
+ elem := reflect.ValueOf(i)
+ if elem.Kind() == reflect.Ptr {
+ elem = elem.Elem()
+ }
+ elemType := elem.Type()
+ for i := 0; i < elem.NumField(); i++ {
+
+ fieldName := elemType.Field(i).Name
+ anonymous := elemType.Field(i).Anonymous
+ field := elem.Field(i)
+ // TODO Use Tag for validation
+ // tag := typ.Field(i).Tag.Get("tagname")
+ kind := field.Kind()
+ if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() {
+ continue
+ }
+ if kind == reflect.Ptr {
+ field = field.Elem()
+ kind = field.Kind()
+ }
+ var value string
+ //switch field.Interface().(type) {
+ switch kind {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i := field.Int()
+ if i != 0 {
+ value = strconv.FormatInt(i, 10)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ i := field.Uint()
+ if i != 0 {
+ value = strconv.FormatUint(i, 10)
+ }
+ case reflect.Float32:
+ value = strconv.FormatFloat(field.Float(), 'f', 4, 32)
+ case reflect.Float64:
+ value = strconv.FormatFloat(field.Float(), 'f', 4, 64)
+ case reflect.Bool:
+ value = strconv.FormatBool(field.Bool())
+ case reflect.String:
+ value = field.String()
+ case reflect.Map:
+ ifc := field.Interface()
+ m := ifc.(map[string]string)
+ if m != nil {
+ j := 0
+ for k, v := range m {
+ j++
+ keyName := fmt.Sprintf("%s.%d.Key", fieldName, j)
+ values.Set(keyName, k)
+ valueName := fmt.Sprintf("%s.%d.Value", fieldName, j)
+ values.Set(valueName, v)
+ }
+ }
+ case reflect.Slice:
+ switch field.Type().Elem().Kind() {
+ case reflect.Uint8:
+ value = string(field.Bytes())
+ case reflect.String:
+ l := field.Len()
+ if l > 0 {
+ strArray := make([]string, l)
+ for i := 0; i < l; i++ {
+ strArray[i] = field.Index(i).String()
+ }
+ bytes, err := json.Marshal(strArray)
+ if err == nil {
+ value = string(bytes)
+ } else {
+ log.Printf("Failed to convert JSON: %v", err)
+ }
+ }
+ default:
+ l := field.Len()
+ for j := 0; j < l; j++ {
+ prefixName := fmt.Sprintf("%s.%d.", fieldName, (j + 1))
+ ifc := field.Index(j).Interface()
+ //log.Printf("%s : %v", prefixName, ifc)
+ if ifc != nil {
+ setQueryValues(ifc, values, prefixName)
+ }
+ }
+ continue
+ }
+
+ default:
+ switch field.Interface().(type) {
+ case ISO6801Time:
+ t := field.Interface().(ISO6801Time)
+ value = t.String()
+ case time.Time:
+ t := field.Interface().(time.Time)
+ value = GetISO8601TimeStamp(t)
+ default:
+ ifc := field.Interface()
+ if ifc != nil {
+ if anonymous {
+ SetQueryValues(ifc, values)
+ } else {
+ prefixName := fieldName + "."
+ setQueryValues(ifc, values, prefixName)
+ }
+ continue
+ }
+ }
+ }
+ if value != "" {
+ name := elemType.Field(i).Tag.Get("ArgName")
+ if name == "" {
+ name = fieldName
+ }
+ if prefix != "" {
+ name = prefix + name
+ }
+ values.Set(name, value)
+ }
+ }
+}
+
+func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix string) {
+ // add to support url.Values
+ mapValues, ok := i.(url.Values)
+ if ok {
+ for k, _ := range mapValues {
+ values.Set(k, mapValues.Get(k))
+ }
+ return
+ }
+
+ elem := reflect.ValueOf(i)
+ if elem.Kind() == reflect.Ptr {
+ elem = elem.Elem()
+ }
+ elemType := elem.Type()
+ for i := 0; i < elem.NumField(); i++ {
+
+ fieldName := elemType.Field(i).Name
+ anonymous := elemType.Field(i).Anonymous
+ field := elem.Field(i)
+
+ // TODO Use Tag for validation
+ // tag := typ.Field(i).Tag.Get("tagname")
+ kind := field.Kind()
+
+ if (kind == reflect.Ptr || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan) && field.IsNil() {
+ continue
+ }
+ if kind == reflect.Ptr {
+ field = field.Elem()
+ kind = field.Kind()
+ }
+
+ var value string
+ //switch field.Interface().(type) {
+ switch kind {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i := field.Int()
+ if i != 0 {
+ value = strconv.FormatInt(i, 10)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ i := field.Uint()
+ if i != 0 {
+ value = strconv.FormatUint(i, 10)
+ }
+ case reflect.Float32:
+ value = strconv.FormatFloat(field.Float(), 'f', 4, 32)
+ case reflect.Float64:
+ value = strconv.FormatFloat(field.Float(), 'f', 4, 64)
+ case reflect.Bool:
+ value = strconv.FormatBool(field.Bool())
+ case reflect.String:
+ value = field.String()
+ case reflect.Map:
+ ifc := field.Interface()
+ m := ifc.(map[string]string)
+ if m != nil {
+ j := 0
+ for k, v := range m {
+ j++
+ keyName := fmt.Sprintf("%s.%d.Key", fieldName, j)
+ values.Set(keyName, k)
+ valueName := fmt.Sprintf("%s.%d.Value", fieldName, j)
+ values.Set(valueName, v)
+ }
+ }
+ case reflect.Slice:
+ if field.Type().Name() == "FlattenArray" {
+ FlattenFn(fieldName, field, values)
+ } else {
+ switch field.Type().Elem().Kind() {
+ case reflect.Uint8:
+ value = string(field.Bytes())
+ case reflect.String:
+ l := field.Len()
+ if l > 0 {
+ strArray := make([]string, l)
+ for i := 0; i < l; i++ {
+ strArray[i] = field.Index(i).String()
+ }
+ bytes, err := json.Marshal(strArray)
+ if err == nil {
+ value = string(bytes)
+ } else {
+ log.Printf("Failed to convert JSON: %v", err)
+ }
+ }
+ default:
+ l := field.Len()
+ for j := 0; j < l; j++ {
+ prefixName := fmt.Sprintf("%s.%d.", fieldName, (j + 1))
+ ifc := field.Index(j).Interface()
+ //log.Printf("%s : %v", prefixName, ifc)
+ if ifc != nil {
+ setQueryValuesByFlattenMethod(ifc, values, prefixName)
+ }
+ }
+ continue
+ }
+ }
+
+ default:
+ switch field.Interface().(type) {
+ case ISO6801Time:
+ t := field.Interface().(ISO6801Time)
+ value = t.String()
+ case time.Time:
+ t := field.Interface().(time.Time)
+ value = GetISO8601TimeStamp(t)
+ default:
+
+ ifc := field.Interface()
+ if ifc != nil {
+ if anonymous {
+ SetQueryValues(ifc, values)
+ } else {
+ prefixName := fieldName + "."
+ setQueryValuesByFlattenMethod(ifc, values, prefixName)
+ }
+ continue
+ }
+ }
+ }
+ if value != "" {
+ name := elemType.Field(i).Tag.Get("ArgName")
+ if name == "" {
+ name = fieldName
+ }
+ if prefix != "" {
+ name = prefix + name
+ }
+ // NOTE: here we will change name to underline style when the type is UnderlineString
+ if field.Type().Name() == "UnderlineString" {
+ name = Underline2Dot(name)
+ }
+ values.Set(name, value)
+ }
+ }
+}
diff --git a/vendor/github.com/denverdino/aliyungo/util/iso6801.go b/vendor/github.com/denverdino/aliyungo/util/iso6801.go
new file mode 100644
index 000000000..9c25e8f68
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/util/iso6801.go
@@ -0,0 +1,80 @@
+package util
+
+import (
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// GetISO8601TimeStamp gets timestamp string in ISO8601 format
+func GetISO8601TimeStamp(ts time.Time) string {
+ t := ts.UTC()
+ return fmt.Sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
+}
+
+const formatISO8601 = "2006-01-02T15:04:05Z"
+const jsonFormatISO8601 = `"` + formatISO8601 + `"`
+const formatISO8601withoutSeconds = "2006-01-02T15:04Z"
+const jsonFormatISO8601withoutSeconds = `"` + formatISO8601withoutSeconds + `"`
+
+// A ISO6801Time represents a time in ISO8601 format
+type ISO6801Time time.Time
+
+// New constructs a new iso8601.Time instance from an existing
+// time.Time instance. This causes the nanosecond field to be set to
+// 0, and its time zone set to a fixed zone with no offset from UTC
+// (but it is *not* UTC itself).
+func NewISO6801Time(t time.Time) ISO6801Time {
+ return ISO6801Time(time.Date(
+ t.Year(),
+ t.Month(),
+ t.Day(),
+ t.Hour(),
+ t.Minute(),
+ t.Second(),
+ 0,
+ time.UTC,
+ ))
+}
+
+// IsDefault checks if the time is default
+func (it *ISO6801Time) IsDefault() bool {
+ return *it == ISO6801Time{}
+}
+
+// MarshalJSON serializes the ISO6801Time into JSON string
+func (it ISO6801Time) MarshalJSON() ([]byte, error) {
+ return []byte(time.Time(it).Format(jsonFormatISO8601)), nil
+}
+
+// UnmarshalJSON deserializes the ISO6801Time from JSON string
+func (it *ISO6801Time) UnmarshalJSON(data []byte) error {
+ str := string(data)
+
+ if str == "\"\"" || len(data) == 0 {
+ return nil
+ }
+ var t time.Time
+ var err error
+ if str[0] == '"' {
+ t, err = time.ParseInLocation(jsonFormatISO8601, str, time.UTC)
+ if err != nil {
+ t, err = time.ParseInLocation(jsonFormatISO8601withoutSeconds, str, time.UTC)
+ }
+ } else {
+ var i int64
+ i, err = strconv.ParseInt(str, 10, 64)
+ if err == nil {
+ t = time.Unix(i/1000, i%1000)
+ }
+ }
+ if err == nil {
+ *it = ISO6801Time(t)
+ }
+ return err
+}
+
+// String returns the time in ISO6801Time format
+func (it ISO6801Time) String() string {
+ return time.Time(it).Format(formatISO8601)
+}
diff --git a/vendor/github.com/denverdino/aliyungo/util/signature.go b/vendor/github.com/denverdino/aliyungo/util/signature.go
new file mode 100644
index 000000000..a00b27c19
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/util/signature.go
@@ -0,0 +1,40 @@
+package util
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base64"
+ "net/url"
+ "strings"
+)
+
+//CreateSignature creates signature for string following Aliyun rules
+func CreateSignature(stringToSignature, accessKeySecret string) string {
+ // Crypto by HMAC-SHA1
+ hmacSha1 := hmac.New(sha1.New, []byte(accessKeySecret))
+ hmacSha1.Write([]byte(stringToSignature))
+ sign := hmacSha1.Sum(nil)
+
+ // Encode to Base64
+ base64Sign := base64.StdEncoding.EncodeToString(sign)
+
+ return base64Sign
+}
+
+func percentReplace(str string) string {
+ str = strings.Replace(str, "+", "%20", -1)
+ str = strings.Replace(str, "*", "%2A", -1)
+ str = strings.Replace(str, "%7E", "~", -1)
+
+ return str
+}
+
+// CreateSignatureForRequest creates signature for query string values
+func CreateSignatureForRequest(method string, values *url.Values, accessKeySecret string) string {
+
+ canonicalizedQueryString := percentReplace(values.Encode())
+
+ stringToSign := method + "&%2F&" + url.QueryEscape(canonicalizedQueryString)
+
+ return CreateSignature(stringToSign, accessKeySecret)
+}
diff --git a/vendor/github.com/denverdino/aliyungo/util/util.go b/vendor/github.com/denverdino/aliyungo/util/util.go
new file mode 100644
index 000000000..dd68214e3
--- /dev/null
+++ b/vendor/github.com/denverdino/aliyungo/util/util.go
@@ -0,0 +1,147 @@
+package util
+
+import (
+ "bytes"
+ srand "crypto/rand"
+ "encoding/binary"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "sort"
+ "time"
+)
+
+const dictionary = "_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+//CreateRandomString create random string
+func CreateRandomString() string {
+ b := make([]byte, 32)
+ l := len(dictionary)
+
+ _, err := srand.Read(b)
+
+ if err != nil {
+ // fail back to insecure rand
+ rand.Seed(time.Now().UnixNano())
+ for i := range b {
+ b[i] = dictionary[rand.Int()%l]
+ }
+ } else {
+ for i, v := range b {
+ b[i] = dictionary[v%byte(l)]
+ }
+ }
+
+ return string(b)
+}
+
+// Encode encodes the values into ``URL encoded'' form
+// ("acl&bar=baz&foo=quux") sorted by key.
+func Encode(v url.Values) string {
+ if v == nil {
+ return ""
+ }
+ var buf bytes.Buffer
+ keys := make([]string, 0, len(v))
+ for k := range v {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ vs := v[k]
+ prefix := url.QueryEscape(k)
+ for _, v := range vs {
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(prefix)
+ if v != "" {
+ buf.WriteString("=")
+ buf.WriteString(url.QueryEscape(v))
+ }
+ }
+ }
+ return buf.String()
+}
+
+func GetGMTime() string {
+ return time.Now().UTC().Format(http.TimeFormat)
+}
+
+//
+
+func randUint32() uint32 {
+ return randUint32Slice(1)[0]
+}
+
+func randUint32Slice(c int) []uint32 {
+ b := make([]byte, c*4)
+
+ _, err := srand.Read(b)
+
+ if err != nil {
+ // fail back to insecure rand
+ rand.Seed(time.Now().UnixNano())
+ for i := range b {
+ b[i] = byte(rand.Int())
+ }
+ }
+
+ n := make([]uint32, c)
+
+ for i := range n {
+ n[i] = binary.BigEndian.Uint32(b[i*4 : i*4+4])
+ }
+
+ return n
+}
+
+func toByte(n uint32, st, ed byte) byte {
+ return byte(n%uint32(ed-st+1) + uint32(st))
+}
+
+func toDigit(n uint32) byte {
+ return toByte(n, '0', '9')
+}
+
+func toLowerLetter(n uint32) byte {
+ return toByte(n, 'a', 'z')
+}
+
+func toUpperLetter(n uint32) byte {
+ return toByte(n, 'A', 'Z')
+}
+
+type convFunc func(uint32) byte
+
+var convFuncs = []convFunc{toDigit, toLowerLetter, toUpperLetter}
+
+// tools for generating a random ECS instance password
+// from 8 to 30 char MUST contain digit upper, case letter and upper case letter
+// http://docs.aliyun.com/#/pub/ecs/open-api/instance&createinstance
+func GenerateRandomECSPassword() string {
+
+ // [8, 30]
+ l := int(randUint32()%23 + 8)
+
+ n := randUint32Slice(l)
+
+ b := make([]byte, l)
+
+ b[0] = toDigit(n[0])
+ b[1] = toLowerLetter(n[1])
+ b[2] = toUpperLetter(n[2])
+
+ for i := 3; i < l; i++ {
+ b[i] = convFuncs[n[i]%3](n[i])
+ }
+
+ s := make([]byte, l)
+ perm := rand.Perm(l)
+ for i, v := range perm {
+ s[v] = b[i]
+ }
+
+ return string(s)
+
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 6d9659245..be8d8fea5 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -73,6 +73,12 @@
"path": "github.com/Azure/go-ntlmssp",
"revision": "e0b63eb299a769ea4b04dadfe530f6074b277afb"
},
+ {
+ "checksumSHA1": "HttiPj314X1a0i2Jen1p6lRH/vE=",
+ "path": "github.com/aliyun/aliyun-oss-go-sdk/oss",
+ "revision": "e6dbea820a9f304b43d3b357dd48ced23b86df72",
+ "revisionTime": "2017-01-13T02:27:42Z"
+ },
{
"checksumSHA1": "gc98KRYAAiw4g1FrSTsuggSNv8k=",
"path": "github.com/approvals/go-approval-tests",
@@ -306,6 +312,36 @@
"path": "github.com/davecgh/go-spew/spew",
"revision": "6d212800a42e8ab5c146b8ace3490ee17e5225f9"
},
+ {
+ "checksumSHA1": "4YIveqfMA1MH8oX8YMG7rDSl+ms=",
+ "path": "github.com/denverdino/aliyungo/common",
+ "revision": "920561accef9a3e387f10bf8f93be73d6bb55054",
+ "revisionTime": "2017-05-25T09:08:54Z"
+ },
+ {
+ "checksumSHA1": "x4l/zVF/J4ibAH39pNrSvTnFsPI=",
+ "path": "github.com/denverdino/aliyungo/ecs",
+ "revision": "920561accef9a3e387f10bf8f93be73d6bb55054",
+ "revisionTime": "2017-05-25T09:08:54Z"
+ },
+ {
+ "checksumSHA1": "40PbgN4Emct6AIiJbhPoEdd+vIA=",
+ "path": "github.com/denverdino/aliyungo/ram",
+ "revision": "920561accef9a3e387f10bf8f93be73d6bb55054",
+ "revisionTime": "2017-05-25T09:08:54Z"
+ },
+ {
+ "checksumSHA1": "iZftKqx5jdcEaMJNJhrb5QwmMuc=",
+ "path": "github.com/denverdino/aliyungo/slb",
+ "revision": "920561accef9a3e387f10bf8f93be73d6bb55054",
+ "revisionTime": "2017-05-25T09:08:54Z"
+ },
+ {
+ "checksumSHA1": "piZlmhWPLGxYkXLysTrjcXllO4c=",
+ "path": "github.com/denverdino/aliyungo/util",
+ "revision": "920561accef9a3e387f10bf8f93be73d6bb55054",
+ "revisionTime": "2017-05-25T09:08:54Z"
+ },
{
"checksumSHA1": "D37uI+U+FYvTJIdG2TTozXe7i7U=",
"comment": "v3.0.0",
diff --git a/website/source/docs/builders/alicloud-ecs.html.md b/website/source/docs/builders/alicloud-ecs.html.md
new file mode 100644
index 000000000..ac1450ccc
--- /dev/null
+++ b/website/source/docs/builders/alicloud-ecs.html.md
@@ -0,0 +1,223 @@
+---
+description: |
+ The `alicloud-ecs` Packer builder plugin provide the capability to build
+ customized images based on an existing base images.
+layout: docs
+page_title: Alicloud Image Builder
+...
+
+# Alicloud Image Builder
+
+Type: `alicloud-ecs`
+
+The `alicloud-ecs` Packer builder plugin provide the capability to build
+customized images based on an existing base images.
+
+## Configuration Reference
+
+The following configuration options are available for building Alicloud images.
+In addition to the options listed here,
+a [communicator](/docs/templates/communicator.html) can be configured for this
+builder.
+
+### Required:
+
+- `access_key` (string) - This is the Alicloud access key. It must be provided,
+ but it can also be sourced from the `ALICLOUD_ACCESS_KEY` environment
+ variable.
+
+- `secret_key` (string) - This is the Alicloud secret key. It must be provided,
+ but it can also be sourced from the `ALICLOUD_SECRET_KEY` environment
+ variable.
+
+- `region` (string) - This is the Alicloud region. It must be provided, but it
+ can also be sourced from the `ALICLOUD_REGION` environment variables.
+
+- `instance_type` (string) - Type of the instance. For values, see [Instance
+ Type Table](). You can also obtain the latest instance type table by invoking
+ the [Querying Instance Type
+ Table](https://intl.aliyun.com/help/doc-detail/25620.htm?spm=a3c0i.o25499en.a3.6.Dr1bik)
+ interface.
+
+- `image_name` (string) - The name of the user-defined image, [2, 128] English
+ or Chinese characters. It must begin with an uppercase/lowercase letter or
+ a Chinese character, and may contain numbers, `_` or `-`. It cannot begin with
+ `http://` or `https://`.
+
+- `source_image` (string) - This is the base image id which you want to create
+ your customized images.
+
+
+
+### Optional:
+
+- `skip_region_validation` (bool) - The region validation can be skipped if this
+ value is true, the default value is false.
+
+- `image_description` (string) - The description of the image, with a length
+ limit of 0 to 256 characters. Leaving it blank means null, which is the
+ default value. It cannot begin with http:// or https://.
+
+- `image_version` (string) - The version number of the image, with a length limit
+ of 1 to 40 English characters.
+
+- `image_share_account` (array of string) - The IDs of to-be-added Aliyun
+ accounts to which the image is shared. The number of accounts is 1 to 10. If
+ number of accounts is greater than 10, this parameter is ignored.
+
+- `image_copy_regions` (array of string) - Copy to the destination regionIds.
+
+- `image_copy_names` (array of string) - The name of the destination image, [2,
+ 128] English or Chinese characters. It must begin with an uppercase/lowercase
+ letter or a Chinese character, and may contain numbers, `_` or `-`. It cannot
+ begin with `http://` or `https://`.
+
+- `image_force_delete` (bool) - If this value is true, when the target image name
+ is duplicated with an existing image, it will delete the existing image and
+ then create the target image, otherwise, the creation will fail. The default
+ value is false.
+
+- `image_force_delete_snapshots` (bool) - If this value is true, when delete the
+ duplicated existing image, the source snapshot of this image will be delete
+ either.
+
+- `disk_name` (string) - The value of disk name is blank by default. [2, 128]
+ English or Chinese characters, must begin with an uppercase/lowercase letter
+ or Chinese character. Can contain numbers, `.`, `_` and `-`. The disk name
+ will appear on the console. It cannot begin with http:// or https://.
+
+- `disk_category` (string) - Category of the data disk. Optional values are:
+ - cloud - general cloud disk
+ - cloud_efficiency - efficiency cloud disk
+ - cloud_ssd - cloud SSD
+
+ Default value: cloud.
+
+- `disk_size` (int) - Size of the system disk, in GB, values range:
+ - cloud - 5 ~ 2000
+ - cloud_efficiency - 20 ~ 2048
+ - cloud_ssd - 20 ~ 2048
+
+ The value should be equal to or greater than the size of the specific SnapshotId.
+
+- `disk_snapshot_id` (string) - Snapshots are used to create the data disk
+ After this parameter is specified, Size is ignored. The actual size of the
+ created disk is the size of the specified snapshot.
+
+ Snapshots from on or before July 15, 2013 cannot be used to create a disk.
+
+- `disk_description` (string) - The value of disk description is blank by default. [2, 256] characters. The disk description will appear on the console. It cannot begin with http:// or https://.
+
+- `disk_delete_with_instance` (string) - Whether or not the disk is released along with the instance:
+ - True indicates that when the instance is released, this disk will be released with it
+ - False indicates that when the instance is released, this disk will be retained.
+
+- `disk_device` (string) - Device information of the related instance: such as
+ `/dev/xvdb` It is null unless the Status is In_use.
+
+- `zone_id` (string) - ID of the zone to which the disk belongs.
+
+- `io_optimized` (string) - I/O optimized. Optional values are:
+ - none: none I/O Optimized
+ - optimized: I/O Optimized
+
+ Default value: none for Generation I instances; optimized for other instances.
+
+- `force_stop_instance` (bool) - Whether to force shutdown upon device restart.
+ The default value is `false`.
+
+ If it is set to `false`, the system is shut down normally; if it is set to
+ `true`, the system is forced to shut down.
+
+- `security_group_id` (string) - ID of the security group to which a newly
+ created instance belongs. Mutual access is allowed between instances in one
+ security group. If not specified, the newly created instance will be added to
+ the default security group. If the default group doesn’t exist, or the number
+ of instances in it has reached the maximum limit, a new security group will
+ be created automatically.
+
+- `security_group_name` (string) - The security group name. The default value is
+ blank. [2, 128] English or Chinese characters, must begin with an
+ uppercase/lowercase letter or Chinese character. Can contain numbers, `.`,
+ `_` or `-`. It cannot begin with `http://` or `https://`.
+
+- `user_data` (string) - The UserData of an instance must be encoded in `Base64`
+ format, and the maximum size of the raw data is `16 KB`.
+
+- `user_data_file` (string) - The file name of the userdata.
+
+- `vpc_id` (string) - VPC ID allocated by the system.
+
+- `vpc_name` (string) - The VPC name. The default value is blank. [2, 128]
+ English or Chinese characters, must begin with an uppercase/lowercase letter
+ or Chinese character. Can contain numbers, `_` and `-`. The disk description
+ will appear on the console. Cannot begin with `http://` or `https://`.
+
+- `vpc_cidr_block` (string) - Value options: `192.168.0.0/16` and `172.16.0.0/16`.
+ When not specified, the default value is `172.16.0.0/16`.
+
+- `vswitch_id` (string) - The ID of the VSwitch to be used.
+
+- `instance_name` (string) - Display name of the instance, which is a string of
+ 2 to 128 Chinese or English characters. It must begin with an
+ uppercase/lowercase letter or a Chinese character and can contain numerals,
+ `.`, `_`, or `-`. The instance name is displayed on the Alibaba Cloud
+ console. If this parameter is not specified, the default value is InstanceId
+ of the instance. It cannot begin with http:// or https://.
+
+- `internet_charge_type` (string) - Internet charge type, which can be
+ `PayByTraffic` or `PayByBandwidth`. Optional values:
+ - PayByBandwidth
+ - PayByTraffic
+
+ If this parameter is not specified, the default value is `PayByBandwidth`.
+
+
+- `internet_max_bandwith_out` (string) - Maximum outgoing bandwidth to the public
+ network, measured in Mbps (Mega bit per second).
+
+ Value range:
+ - PayByBandwidth: [0, 100]. If this parameter is not specified, API automatically sets it to 0 Mbps.
+ - PayByTraffic: [1, 100]. If this parameter is not specified, an error is returned.
+
+- `temporary_key_pair_name` (string) - The name of the temporary key pair to
+ generate. By default, Packer generates a name that looks like `packer_`,
+ where `` is a 36 character unique identifier.
+
+
+## Basic Example
+
+Here is a basic example for Alicloud.
+
+```json
+{
+ "variables": {
+ "access_key": "{{env `ALICLOUD_ACCESS_KEY`}}",
+ "secret_key": "{{env `ALICLOUD_SECRET_KEY`}}"
+ },
+ "builders": [{
+ "type":"alicloud-ecs",
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "region":"cn-beijing",
+ "image_name":"packer_test2",
+ "source_image":"centos_7_2_64_40G_base_20170222.vhd",
+ "ssh_username":"root",
+ "instance_type":"ecs.n1.tiny",
+ "io_optimized":"true",
+ "image_force_delete":"true"
+ }],
+ "provisioners": [{
+ "type": "shell",
+ "inline": [
+ "sleep 30",
+ "yum install redis.x86_64 -y"
+ ]
+ }]
+}
+```
+
+
+See the
+[examples/alicloud](https://github.com/hashicorp/packer/tree/master/examples/alicloud)
+folder in the packer project for more examples.
diff --git a/website/source/docs/post-processors/alicloud-import.html.md b/website/source/docs/post-processors/alicloud-import.html.md
new file mode 100644
index 000000000..5e0e645da
--- /dev/null
+++ b/website/source/docs/post-processors/alicloud-import.html.md
@@ -0,0 +1,114 @@
+---
+description: |
+ The Packer Alicloud Import post-processor takes a RAW or VHD artifact from
+ various builders and imports it to an Alicloud customized image list.
+layout: docs
+page_title: 'Alicloud Import Post-Processor'
+...
+
+# Aicloud Import Post-Processor
+
+Type: `alicloud-import`
+
+The Packer Alicloud Import post-processor takes a RAW or VHD artifact from
+various builders and imports it to an Alicloud ECS Image.
+
+## How Does it Work?
+
+The import process operates by making a temporary copy of the RAW or VHD to an OSS
+bucket, and calling an import task in ECS on the RAW or VHD file. Once
+completed, an Alicloud ECS Image is returned. The temporary RAW or VHD copy in
+OSS 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:
+
+- `access_key` (string) - This is the Alicloud access key. It must be provided,
+ but it can also be sourced from the `ALICLOUD_ACCESS_KEY` environment
+ variable.
+
+- `secret_key` (string) - This is the Alicloud secret key. It must be provided,
+ but it can also be sourced from the `ALICLOUD_SECRET_KEY` environment
+ variable.
+
+- `region` (string) - This is the Alicloud region. It must be provided, but it
+ can also be sourced from the `ALICLOUD_REGION` environment variables.
+
+- `image_name` (string) - The name of the user-defined image, [2, 128] English
+ or Chinese characters. It must begin with an uppercase/lowercase letter or
+ a Chinese character, and may contain numbers, `_` or `-`. It cannot begin
+ with http:// or https://.
+
+- `oss_bucket_name` (string) - The name of the OSS bucket where the RAW or VHD
+ file will be copied to for import. If the Bucket isn't exist, post-process
+ will create it for you.
+
+- `image_os_type` (string) - Type of the OS linux/windows
+
+- `image_platform` (string) - platform such `CentOS`
+
+- `image_architecture` (string) - Platform type of the image system:i386
+ | x86_64
+
+- `format` (string) - The format of the image for import, now alicloud only
+ support RAW and VHD.
+
+### Optional:
+
+- `oss_key_name` (string) - The name of the object key in `oss_bucket_name`
+ where the RAW or VHD file will be copied to for import.
+
+- `skip_clean` (boolean) - Whether we should skip removing the RAW or VHD file
+ uploaded to OSS after the import process has completed. `true` means that we
+ should leave it in the OSS bucket, `false` means to clean it out. Defaults to
+ `false`.
+
+- `image_description` (string) - The description of the image, with a length
+ limit of 0 to 256 characters. Leaving it blank means null, which is the
+ default value. It cannot begin with http:// or https://.
+
+- `image_force_delete` (bool) - If this value is true, when the target image
+ name is duplicated with an existing image, it will delete the existing image
+ and then create the target image, otherwise, the creation will fail. The
+ default value is false.
+
+- `image_system_size` (int) - Size of the system disk, in GB, values range:
+ - cloud - 5 ~ 2000
+ - cloud_efficiency - 20 ~ 2048
+ - cloud_ssd - 20 ~ 2048
+
+## Basic Example
+
+Here is a basic example. This assumes that the builder has produced a RAW
+artifact. The user must have the role `AliyunECSImageImportDefaultRole` with
+`AliyunECSImageImportRolePolicy`, post-process will automatically configure the
+role and policy for you if you have the privilege, otherwise, you have to ask
+the administrator configure for you in advance.
+
+```json
+"post-processors":[
+ {
+ "access_key":"{{user `access_key`}}",
+ "secret_key":"{{user `secret_key`}}",
+ "type":"alicloud-import",
+ "oss_bucket_name": "packer",
+ "image_name": "packer_import",
+ "image_os_type": "linux",
+ "image_platform": "CentOS",
+ "image_architecture": "x86_64",
+ "image_system_size": "40",
+ "region":"cn-beijing"
+ }
+ ]
+```
+
+This will take the RAW generated by a builder and upload it to OSS. In this
+case, an existing bucket called `packer` in the `cn-beijing` region will be
+where the copy is placed.
+
+Once uploaded, the import process will start, creating an Alicloud ECS image in
+the `cn-beijing` region with the name you specified in template file.
diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb
index 45c92ed98..bb8b6fa71 100644
--- a/website/source/layouts/docs.erb
+++ b/website/source/layouts/docs.erb
@@ -59,6 +59,9 @@
>
Builders