Merge pull request #4619 from zhuzhih2017/master

Add packer plugin which support to create alicloud ecs image
This commit is contained in:
Matthew Hooker 2017-06-02 13:33:10 -07:00 committed by GitHub
commit b1bcf62ae1
114 changed files with 17572 additions and 0 deletions

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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}}"
}]
}`

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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))
}
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}
}
}

View File

@ -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))
}

View File

@ -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) {}

View File

@ -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."))
}
}

View File

@ -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))
}
}
}

View File

@ -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) {
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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) {
}

View File

@ -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 ""
}

View File

@ -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) {}

View File

@ -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))
}
}
}
}

View File

@ -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))
}
}
}
}

View File

@ -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))
}
}
}
}

View File

@ -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...
}

View File

@ -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),

View File

@ -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"
]
}]
}

View File

@ -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:\\"]
}]
}

View File

@ -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"
]
}]
}

View File

@ -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"
]
}]
}

View File

@ -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

View File

@ -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

View File

@ -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
}

92
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/auth.go generated vendored Normal file
View File

@ -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]
}

View File

@ -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个objectmy-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存在三个objectfun/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 对象的metaerror为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 对象的metaerror为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的读操作包括GetObjectHeadObjectCopyObject和UploadPartCopy中的对source object的读
// Object的写操作包括PutObjectPostObjectAppendObjectDeleteObject
// DeleteMultipleObjectsCompleteMultipartUpload以及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
}

View File

@ -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)
}

67
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conf.go generated vendored Normal file
View File

@ -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
}

437
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/conn.go generated vendored Normal file
View File

@ -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)
}

View File

@ -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版本
)

44
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/crc.go generated vendored Normal file
View File

@ -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))
}

View File

@ -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)
}

View File

@ -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)}
}

245
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/mime.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

442
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/type.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

165
vendor/github.com/aliyun/aliyun-oss-go-sdk/oss/utils.go generated vendored Normal file
View File

@ -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)
}

191
vendor/github.com/denverdino/aliyungo/LICENSE.txt generated vendored Normal file
View File

@ -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.

345
vendor/github.com/denverdino/aliyungo/common/client.go generated vendored Executable file
View File

@ -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())
}

View File

@ -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 ""
}

File diff suppressed because it is too large Load Diff

View File

@ -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,
}

101
vendor/github.com/denverdino/aliyungo/common/request.go generated vendored Normal file
View File

@ -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}
}

89
vendor/github.com/denverdino/aliyungo/common/types.go generated vendored Normal file
View File

@ -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"`
}

View File

@ -0,0 +1,3 @@
package common
const Version = "0.1"

74
vendor/github.com/denverdino/aliyungo/ecs/client.go generated vendored Normal file
View File

@ -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
}

330
vendor/github.com/denverdino/aliyungo/ecs/disks.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

317
vendor/github.com/denverdino/aliyungo/ecs/images.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

607
vendor/github.com/denverdino/aliyungo/ecs/instances.go generated vendored Normal file
View File

@ -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
}

136
vendor/github.com/denverdino/aliyungo/ecs/monitoring.go generated vendored Normal file
View File

@ -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
}

View File

@ -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")
)

249
vendor/github.com/denverdino/aliyungo/ecs/networks.go generated vendored Normal file
View File

@ -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
}

34
vendor/github.com/denverdino/aliyungo/ecs/regions.go generated vendored Normal file
View File

@ -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&regiontype
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
}

View File

@ -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
}

View File

@ -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
}

131
vendor/github.com/denverdino/aliyungo/ecs/snapshots.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

120
vendor/github.com/denverdino/aliyungo/ecs/tags.go generated vendored Normal file
View File

@ -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
}

152
vendor/github.com/denverdino/aliyungo/ecs/vpcs.go generated vendored Normal file
View File

@ -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
}

68
vendor/github.com/denverdino/aliyungo/ecs/vrouters.go generated vendored Normal file
View File

@ -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)
}

153
vendor/github.com/denverdino/aliyungo/ecs/vswitches.go generated vendored Normal file
View File

@ -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
}

65
vendor/github.com/denverdino/aliyungo/ecs/zones.go generated vendored Normal file
View File

@ -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
}

78
vendor/github.com/denverdino/aliyungo/ram/account.go generated vendored Normal file
View File

@ -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
}

63
vendor/github.com/denverdino/aliyungo/ram/ak.go generated vendored Normal file
View File

@ -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
}

79
vendor/github.com/denverdino/aliyungo/ram/api.go generated vendored Normal file
View File

@ -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)
}

30
vendor/github.com/denverdino/aliyungo/ram/client.go generated vendored Normal file
View File

@ -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
}

4
vendor/github.com/denverdino/aliyungo/ram/error.go generated vendored Normal file
View File

@ -0,0 +1,4 @@
package ram
//common errors
var ()

11
vendor/github.com/denverdino/aliyungo/ram/group.go generated vendored Normal file
View File

@ -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() {}

11
vendor/github.com/denverdino/aliyungo/ram/mfa.go generated vendored Normal file
View File

@ -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() {}

195
vendor/github.com/denverdino/aliyungo/ram/policy.go generated vendored Normal file
View File

@ -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() {}

24
vendor/github.com/denverdino/aliyungo/ram/profile.go generated vendored Normal file
View File

@ -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() {
}

73
vendor/github.com/denverdino/aliyungo/ram/role.go generated vendored Normal file
View File

@ -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
}

40
vendor/github.com/denverdino/aliyungo/ram/security.go generated vendored Normal file
View File

@ -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
}

126
vendor/github.com/denverdino/aliyungo/ram/types.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

49
vendor/github.com/denverdino/aliyungo/slb/client.go generated vendored Normal file
View File

@ -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
}

531
vendor/github.com/denverdino/aliyungo/slb/listeners.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

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