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