implement disk task for ALICloud and fix typos

This commit is contained in:
LilyFaFa 2018-05-13 22:29:16 +08:00
parent d344951f91
commit 9262d1de65
6 changed files with 411 additions and 45 deletions

View File

@ -3,6 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"disk.go",
"disk_fitask.go",
"vpc.go",
"vpc_fitask.go",
"vswitch.go",

View File

@ -0,0 +1,206 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package alitasks
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/golang/glog"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/aliup"
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
)
// Disk represents a ALI Cloud Disk
//go:generate fitask -type=Disk
const (
DiskResource = "disk"
DiskType = ecs.DiskTypeAllData
)
type Disk struct {
Lifecycle *fi.Lifecycle
Name *string
DiskId *string
ZoneId *string
DiskCategory *string
Encrypted *bool
SizeGB *int
Tags map[string]string
}
var _ fi.CompareWithID = &Disk{}
func (d *Disk) CompareWithID() *string {
return d.DiskId
}
func (d *Disk) Find(c *fi.Context) (*Disk, error) {
cloud := c.Cloud.(aliup.ALICloud)
clusterTags := cloud.GetClusterTags()
request := &ecs.DescribeDisksArgs{
DiskType: DiskType,
RegionId: common.Region(cloud.Region()),
ZoneId: fi.StringValue(d.ZoneId),
Tag: clusterTags,
DiskName: fi.StringValue(d.Name),
}
responseDisks, _, err := cloud.EcsClient().DescribeDisks(request)
if err != nil {
return nil, fmt.Errorf("error finding Disks: %v", err)
}
// Don't exist disk with specified ClusterTags or Name.
if len(responseDisks) == 0 {
return nil, nil
}
if len(responseDisks) > 1 {
glog.V(4).Info("The number of specified disk with the same name and ClusterTags exceeds 1, diskName:%q", *d.Name)
}
glog.V(2).Infof("found matching Disk with name: %q", *d.Name)
actual := &Disk{}
actual.Name = fi.String(responseDisks[0].DiskName)
actual.DiskCategory = fi.String(string(responseDisks[0].Category))
actual.ZoneId = fi.String(responseDisks[0].ZoneId)
actual.SizeGB = fi.Int(responseDisks[0].Size)
actual.DiskId = fi.String(responseDisks[0].DiskId)
tags, err := cloud.GetTags(fi.StringValue(actual.DiskId), DiskResource)
if err != nil {
glog.V(4).Info("Error getting tags on resourceId:%q", *actual.DiskId)
}
actual.Tags = tags
// Ignore "system" fields
actual.Lifecycle = d.Lifecycle
d.DiskId = actual.DiskId
return actual, nil
}
func (d *Disk) Run(c *fi.Context) error {
if d.Tags == nil {
d.Tags = make(map[string]string)
}
c.Cloud.(aliup.ALICloud).AddClusterTags(d.Tags)
return fi.DefaultDeltaRunMethod(d, c)
}
func (_ *Disk) CheckChanges(a, e, changes *Disk) error {
if a == nil {
if e.ZoneId == nil {
return fi.RequiredField("ZoneId")
}
if e.Name == nil {
return fi.RequiredField("Name")
}
} else {
if changes.DiskCategory != nil {
return fi.CannotChangeField("DiskCategory")
}
}
return nil
}
//Disk can only modify tags.
func (_ *Disk) RenderALI(t *aliup.ALIAPITarget, a, e, changes *Disk) error {
if a == nil {
glog.V(2).Infof("Creating Disk with Name:%q", fi.StringValue(e.Name))
request := &ecs.CreateDiskArgs{
DiskName: fi.StringValue(e.Name),
RegionId: common.Region(t.Cloud.Region()),
ZoneId: fi.StringValue(e.ZoneId),
Encrypted: fi.BoolValue(e.Encrypted),
DiskCategory: ecs.DiskCategory(fi.StringValue(e.DiskCategory)),
Size: fi.IntValue(e.SizeGB),
}
diskId, err := t.Cloud.EcsClient().CreateDisk(request)
if err != nil {
return fmt.Errorf("error creating disk: %v", err)
}
e.DiskId = fi.String(diskId)
}
if changes != nil && changes.Tags != nil {
glog.V(2).Infof("Modifing tags of disk with Name:%q", fi.StringValue(e.Name))
if err := t.Cloud.CreateTags(*e.DiskId, DiskResource, e.Tags); err != nil {
return fmt.Errorf("error adding Tags to ALI YunPan: %v", err)
}
}
if a != nil && (len(a.Tags) > 0) {
tagsToDelete := e.getDiskTagsToDelete(a.Tags)
if len(tagsToDelete) > 0 {
glog.V(2).Infof("Deleting tags of disk with Name:%q", fi.StringValue(e.Name))
if err := t.Cloud.RemoveTags(*e.DiskId, DiskResource, tagsToDelete); err != nil {
return fmt.Errorf("error removing Tags from ALI YunPan: %v", err)
}
}
}
return nil
}
// getDiskTagsToDelete loops through the currently set tags and builds a list of tags to be deleted from the specificated disk
func (d *Disk) getDiskTagsToDelete(currentTags map[string]string) map[string]string {
tagsToDelete := map[string]string{}
for k, v := range currentTags {
if _, ok := d.Tags[k]; !ok {
tagsToDelete[k] = v
}
}
return tagsToDelete
}
type terraformDiskTag struct {
Key *string `json:"key"`
Value *string `json:"value"`
}
type terraformDisk struct {
DiskName *string `json:"name,omitempty"`
DiskCategory *string `json:"category,omitempty"`
SizeGB *int `json:"size,omitempty"`
Zone *string `json:"availability_zone,omitempty"`
Tags []*terraformDiskTag `json:"tags,omitempty"`
}
func (_ *Disk) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Disk) error {
tf := &terraformDisk{
DiskName: e.Name,
DiskCategory: e.DiskCategory,
SizeGB: e.SizeGB,
Zone: e.ZoneId,
}
for key, value := range e.Tags {
tf.Tags = append(tf.Tags, &terraformDiskTag{
Key: &key,
Value: &value,
})
}
return t.RenderResource("alicloud_disk", *e.Name, tf)
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by ""fitask" -type=Disk"; DO NOT EDIT
package alitasks
import (
"encoding/json"
"k8s.io/kops/upup/pkg/fi"
)
// Disk
// JSON marshalling boilerplate
type realDisk Disk
// UnmarshalJSON implements conversion to JSON, supporitng an alternate specification of the object as a string
func (o *Disk) UnmarshalJSON(data []byte) error {
var jsonName string
if err := json.Unmarshal(data, &jsonName); err == nil {
o.Name = &jsonName
return nil
}
var r realDisk
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = Disk(r)
return nil
}
var _ fi.HasLifecycle = &Disk{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *Disk) GetLifecycle() *fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *Disk) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = &lifecycle
}
var _ fi.HasName = &Disk{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *Disk) GetName() *string {
return o.Name
}
// SetName sets the Name of the object, implementing fi.SetName
func (o *Disk) SetName(name string) {
o.Name = &name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *Disk) String() string {
return fi.TaskAsString(o)
}

View File

@ -17,6 +17,7 @@ go_library(
"//upup/pkg/fi:go_default_library",
"//vendor/github.com/denverdino/aliyungo/common:go_default_library",
"//vendor/github.com/denverdino/aliyungo/ecs:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
],
)

View File

@ -20,10 +20,11 @@ import (
"errors"
"fmt"
"os"
"strings"
common "github.com/denverdino/aliyungo/common"
ecs "github.com/denverdino/aliyungo/ecs"
"github.com/golang/glog"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"k8s.io/api/core/v1"
prj "k8s.io/kops"
@ -43,6 +44,11 @@ type ALICloud interface {
EcsClient() *ecs.Client
Region() string
AddClusterTags(tags map[string]string)
GetTags(resourceId string, resourceType string) (map[string]string, error)
CreateTags(resourceId string, resourceType string, tags map[string]string) error
RemoveTags(resourceId string, resourceType string, tags map[string]string) error
GetClusterTags() map[string]string
}
type aliCloudImplementation struct {
@ -121,12 +127,12 @@ func (c *aliCloudImplementation) FindVPCInfo(id string) (*fi.VPCInfo, error) {
VpcId: id,
RegionId: common.Region(c.Region()),
}
vswitcheList, _, err := c.EcsClient().DescribeVSwitches(describeVSwitchesArgs)
vswitchList, _, err := c.EcsClient().DescribeVSwitches(describeVSwitchesArgs)
if err != nil {
return nil, fmt.Errorf("error listing VSwitchs: %v", err)
}
for _, vswitch := range vswitcheList {
for _, vswitch := range vswitchList {
s := &fi.SubnetInfo{
ID: vswitch.VSwitchId,
Zone: vswitch.ZoneId,
@ -140,7 +146,100 @@ func (c *aliCloudImplementation) FindVPCInfo(id string) (*fi.VPCInfo, error) {
}
func (c *aliCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) {
return nil, fmt.Errorf("GetCloudGroups not implemented on aliCloud")
return nil, errors.New("GetCloudGroups not implemented on aliCloud")
}
// GetTags will get the specified resource's tags.
func (c *aliCloudImplementation) GetTags(resourceId string, resourceType string) (map[string]string, error) {
if resourceId == "" {
return nil, errors.New("resourceId not provided to GetTags")
}
tags := map[string]string{}
request := &ecs.DescribeTagsArgs{
RegionId: common.Region(c.Region()),
ResourceType: ecs.TagResourceType(resourceType), //image, instance, snapshot or disk
ResourceId: resourceId,
}
responseTags, _, err := c.EcsClient().DescribeTags(request)
if err != nil {
return tags, fmt.Errorf("error getting tags on %v: %v", resourceId, err)
}
for _, tag := range responseTags {
tags[tag.TagKey] = tag.TagValue
}
return tags, nil
}
// AddClusterTags will add ClusterTags to resources (in ALI, only disk, instance, snapshot or image can be tagged )
func (c *aliCloudImplementation) AddClusterTags(tags map[string]string) {
if c.tags != nil && len(c.tags) != 0 && tags != nil {
for k, v := range c.tags {
tags[k] = v
}
}
}
// CreateTags will add tags to the specified resource.
func (c *aliCloudImplementation) CreateTags(resourceId string, resourceType string, tags map[string]string) error {
if len(tags) == 0 {
return nil
} else if len(tags) > 10 {
glog.V(4).Info("The number of specified resource's tags exceeds 10, resourceId:%q", resourceId)
}
if resourceId == "" {
return errors.New("resourceId not provided to CreateTags")
}
if resourceType == "" {
return errors.New("resourceType not provided to CreateTags")
}
request := &ecs.AddTagsArgs{
ResourceId: resourceId,
ResourceType: ecs.TagResourceType(resourceType), //image, instance, snapshot or disk
RegionId: common.Region(c.Region()),
Tag: tags,
}
err := c.EcsClient().AddTags(request)
if err != nil {
return fmt.Errorf("error creating tags on %v: %v", resourceId, err)
}
return nil
}
// RemoveTags will remove tags from the specified resource.
func (c *aliCloudImplementation) RemoveTags(resourceId string, resourceType string, tags map[string]string) error {
if len(tags) == 0 {
return nil
}
if resourceId == "" {
return errors.New("resourceId not provided to RemoveTags")
}
if resourceType == "" {
return errors.New("resourceType not provided to RemoveTags")
}
request := &ecs.RemoveTagsArgs{
ResourceId: resourceId,
ResourceType: ecs.TagResourceType(resourceType), //image, instance, snapshot or disk
RegionId: common.Region(c.Region()),
Tag: tags,
}
err := c.EcsClient().RemoveTags(request)
if err != nil {
return fmt.Errorf("error removing tags on %v: %v", resourceId, err)
}
return nil
}
// GetClusterTags will get the ClusterTags
func (c *aliCloudImplementation) GetClusterTags() map[string]string {
return c.tags
}
func ZoneToVSwitchID(VPCID string, zones []string, vswitchIDs []string) (map[string]string, error) {
@ -190,52 +289,25 @@ func ZoneToVSwitchID(VPCID string, zones []string, vswitchIDs []string) (map[str
VSwitchId: VSwitchId,
}
vswitcheList, _, err := aliCloud.EcsClient().DescribeVSwitches(describeVSwitchesArgs)
vswitchList, _, err := aliCloud.EcsClient().DescribeVSwitches(describeVSwitchesArgs)
if err != nil {
return nil, fmt.Errorf("error listing VSwitchs: %v", err)
}
if len(vswitcheList) == 0 {
if len(vswitchList) == 0 {
return nil, fmt.Errorf("VSwitch %q not found", VSwitchId)
}
if len(vswitcheList) != 1 {
if len(vswitchList) != 1 {
return nil, fmt.Errorf("found multiple VSwitchs for %q", VSwitchId)
}
zone := vswitcheList[0].ZoneId
zone := vswitchList[0].ZoneId
if res[zone] != "" {
return res, fmt.Errorf("vswitch %s and %s have the same zone", vswitcheList[0].VSwitchId, zone)
return res, fmt.Errorf("vswitch %s and %s have the same zone", vswitchList[0].VSwitchId, zone)
}
res[zone] = vswitcheList[0].VSwitchId
res[zone] = vswitchList[0].VSwitchId
}
return res, nil
}
func getRegionByZones(zones []string) (string, error) {
region := ""
for _, zone := range zones {
zoneSplit := strings.Split(zone, "-")
zoneRegion := ""
if len(zoneSplit) != 3 {
return "", fmt.Errorf("invalid ALI zone: %q ", zone)
}
if len(zoneSplit[2]) == 1 {
zoneRegion = zoneSplit[0] + "-" + zoneSplit[1]
} else if len(zoneSplit[2]) == 2 {
zoneRegion = zone[:len(zone)-1]
} else {
return "", fmt.Errorf("invalid ALI zone: %q ", zone)
}
if region != "" && zoneRegion != region {
return "", fmt.Errorf("clusters cannot span multiple regions (found zone %q, but region is %q)", zone, region)
}
region = zoneRegion
}
return region, nil
}

View File

@ -26,24 +26,34 @@ import (
// FindRegion determines the region from the zones specified in the cluster
func FindRegion(cluster *kops.Cluster) (string, error) {
region := ""
zones := []string{}
for _, subnet := range cluster.Spec.Subnets {
zoneSplit := strings.Split(subnet.Zone, "-")
zones = append(zones, subnet.Zone)
}
return getRegionByZones(zones)
}
func getRegionByZones(zones []string) (string, error) {
region := ""
for _, zone := range zones {
zoneSplit := strings.Split(zone, "-")
zoneRegion := ""
if len(zoneSplit) != 3 {
return "", fmt.Errorf("invalid ALI zone: %q in subnet %q", subnet.Zone, subnet.Name)
return "", fmt.Errorf("invalid ALI zone: %q ", zone)
}
if len(zoneSplit[2]) == 1 {
zoneRegion = zoneSplit[0] + "-" + zoneSplit[1]
} else if len(zoneSplit[2]) == 2 {
zoneRegion = subnet.Zone[:len(subnet.Zone)-1]
zoneRegion = zone[:len(zone)-1]
} else {
return "", fmt.Errorf("invalid ALI zone: %q in subnet %q", subnet.Zone, subnet.Name)
return "", fmt.Errorf("invalid ALI zone: %q ", zone)
}
if region != "" && zoneRegion != region {
return "", fmt.Errorf("Clusters cannot span multiple regions (found zone %q, but region is %q)", subnet.Zone, region)
return "", fmt.Errorf("clusters cannot span multiple regions (found zone %q, but region is %q)", zone, region)
}
region = zoneRegion
}