[WIP] support Infra huawei (#772)

* refactor(dev): huawei feature

* refactor(dev): huawei feature VPC feature

* refactor(dev): types consts to infra module

* refactor(dev): add deepCopy

* refactor(dev): huawei feature lic
This commit is contained in:
cuisongliu
2021-12-29 17:52:55 +08:00
committed by GitHub
parent 2b643e2537
commit 8314f1c5ba
21 changed files with 1536 additions and 88 deletions
+6 -2
View File
@@ -75,10 +75,14 @@ generator-contributors:
git log --format='%aN <%aE>' | sort -uf > CONTRIBUTORS
DEEPCOPY_BIN = $(shell pwd)/bin/deepcopy-gen
install-deepcopy: ## check license if not exist install go-lint tools
$(call go-get-tool,$(DEEPCOPY_BIN),k8s.io/code-generator/cmd/deepcopy-gen@latest)
HEAD_FILE := hack/template/boilerplate.go.txt
INPUT_DIR := github.com/fanux/sealos/pkg/types/v1beta1
deepcopy:
deepcopy-gen \
deepcopy:install-deepcopy
$(DEEPCOPY_BIN) \
--input-dirs="$(INPUT_DIR)" \
-O zz_generated.deepcopy \
--go-header-file "$(HEAD_FILE)" \
+2 -1
View File
@@ -8,6 +8,7 @@ require (
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.72
github.com/mitchellh/go-homedir v1.1.0
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.7.0
@@ -24,7 +25,7 @@ require (
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/ini.v1 v1.61.0 // indirect
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.18.0
k8s.io/apimachinery v0.18.0
k8s.io/client-go v0.18.0
+3
View File
@@ -223,6 +223,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.72 h1:gfQoGtygEg2ZX5fxHyIynxGqdZvb3dfIXK5BBN2+LHI=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.0.72/go.mod h1:Z+vVu7nV/6xqti0P2evPEqhzh86ArBELsXTOU9zsnoM=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@@ -383,6 +385,7 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
github.com/sealyun/lvscare v1.1.2-alpha.2 h1:SlnEAXOPn5gC6l8tgvbn9fApyJaQ0ecQHSV3cEfvQYY=
github.com/sealyun/lvscare v1.1.2-alpha.2/go.mod h1:FtOEdsXuYtw9Jwd/Jct25K+PcpUFSDemvF4VgNygjj0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+10 -20
View File
@@ -19,32 +19,22 @@ package aliyun
import (
"time"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/types/v1beta1"
)
const (
Scheme = "https"
IPProtocol = "tcp"
APIServerPortRange = "6443/6443"
SSHPortRange = "22/22"
SourceCidrIP = "0.0.0.0/0"
CidrBlock = "172.16.0.0/24"
Policy = "accept"
DestinationResource = "InstanceType"
InstanceChargeType = "PostPaid"
InternetChargeType = "PayByTraffic"
Product = "product"
Role = "role"
Arch = "arch"
Stopped = "Stopped"
AvailableTypeStatus = "WithStock"
Bandwidth = "100"
AliDomain = "www.sealyun.com/"
TryTimes = 10
TrySleepTime = time.Second
JustGetInstanceInfo = 0
)
const AliyunProvider v1beta1.Provider = "AliyunProvider"
type ResourceName string
const (
@@ -56,29 +46,29 @@ const (
ShouldBeDeleteInstancesIDs ResourceName = "ShouldBeDeleteInstancesIDs"
)
func (r ResourceName) ClusterValue(infra v2.InfraSpec) string {
func (r ResourceName) ClusterValue(infra v1beta1.InfraSpec) string {
return infra.Cluster.Annotations[string(r)]
}
func (r ResourceName) ClusterSetValue(infra v2.InfraSpec, val string) {
func (r ResourceName) ClusterSetValue(infra v1beta1.InfraSpec, val string) {
infra.Cluster.Annotations[string(r)] = val
}
func (r ResourceName) Value(status v2.InfraStatus) string {
func (r ResourceName) Value(status v1beta1.InfraStatus) string {
return status.Cluster.Annotations[string(r)]
}
func (r ResourceName) SetValue(status v2.InfraStatus, val string) {
func (r ResourceName) SetValue(status v1beta1.InfraStatus, val string) {
status.Cluster.Annotations[string(r)] = val
}
type ImageArch string
func ConvertImageArch(arch v2.Arch) ImageArch {
func ConvertImageArch(arch v1beta1.Arch) ImageArch {
switch arch {
case v2.ARM64:
case v1beta1.ARM64:
return "arm64"
case v2.AMD64:
case v1beta1.AMD64:
return "x86_64"
}
return ""
+6 -9
View File
@@ -35,9 +35,6 @@ type Instance struct {
PrimaryIPAddress string
}
type EcsManager struct {
}
func (a *AliProvider) InputIPlist(host *v1beta1.Host) (ipList []string, err error) {
if host == nil {
return nil, err
@@ -89,7 +86,7 @@ func (a *AliProvider) ChangeInstanceType(instanceID string, host *v1beta1.Host)
if err != nil {
return err
}
if instanceStatus != Stopped {
if instanceStatus != "Stopped" {
err = a.PowerOffInstance(instanceID)
if err != nil {
return err
@@ -314,14 +311,14 @@ func (a *AliProvider) RunInstances(host *v1beta1.Host, count int) error {
return nil
}
func (a *AliProvider) AuthorizeSecurityGroup(securityGroupID, portRange string) bool {
func (a *AliProvider) AuthorizeSecurityGroup(securityGroupID string, exportPort v1beta1.ExportPort) bool {
request := ecs.CreateAuthorizeSecurityGroupRequest()
request.Scheme = Scheme
request.SecurityGroupId = securityGroupID
request.IpProtocol = IPProtocol
request.PortRange = portRange
request.SourceCidrIp = SourceCidrIP
request.Policy = Policy
request.IpProtocol = string(exportPort.Protocol)
request.PortRange = exportPort.PortRange
request.SourceCidrIp = exportPort.CidrIP
request.Policy = "accept"
response := ecs.CreateAuthorizeSecurityGroupResponse()
err := a.RetryEcsRequest(request, response)
+3 -3
View File
@@ -180,8 +180,8 @@ func (a *AliProvider) GetAvailableResource(host *v1beta1.Host, systemCategory, d
request.Scheme = Scheme
request.RegionId = a.Infra.Status.Cluster.RegionID
request.ZoneId = a.Infra.Status.Cluster.ZoneID
request.DestinationResource = DestinationResource
request.InstanceChargeType = InstanceChargeType
request.DestinationResource = "InstanceType"
request.InstanceChargeType = "PostPaid"
request.SpotStrategy = a.Infra.Status.Cluster.SpotStrategy
request.SystemDiskCategory = systemCategory
request.DataDiskCategory = dataCategory
@@ -198,7 +198,7 @@ func (a *AliProvider) GetAvailableResource(host *v1beta1.Host, systemCategory, d
for _, i := range response.AvailableZones.AvailableZone {
for _, f := range i.AvailableResources.AvailableResource {
for _, r := range f.SupportedResources.SupportedResource {
if r.StatusCategory == AvailableTypeStatus {
if r.StatusCategory == "WithStock" {
instanceType = append(instanceType, r.Value)
}
}
+11 -12
View File
@@ -18,15 +18,15 @@ import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/util/validation/field"
"github.com/fanux/sealos/pkg/utils"
"k8s.io/apimachinery/pkg/util/sets"
"github.com/pkg/errors"
"github.com/fanux/sealos/pkg/types/validation"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
@@ -52,7 +52,7 @@ const (
type AliProvider struct {
EcsClient ecs.Client
VpcClient vpc.Client
Infra *v2.Infra
Infra *v1beta1.Infra
}
type AliFunc func() error
@@ -237,21 +237,20 @@ func (a *AliProvider) Reconcile() error {
}
func (a *AliProvider) Apply() error {
if err := v2.Default(a.Infra, defaultInfra); err != nil {
return err
}
if err := validation.ValidateInfra(a.Infra); len(err) != 0 {
return err.ToAggregate()
}
return a.Reconcile()
}
func defaultInfra(infra *v2.Infra) error {
func DefaultInfra(infra *v1beta1.Infra) error {
//https://help.aliyun.com/document_detail/63440.htm?spm=a2c4g.11186623.0.0.f5952752gkxpB7#t9856.html
if infra.Spec.Cluster.IsSeize {
if infra.Spec.Cluster.Metadata.IsSeize {
infra.Status.Cluster.SpotStrategy = "SpotAsPriceGo"
} else {
infra.Status.Cluster.SpotStrategy = "NoSpot"
}
return nil
}
func DefaultValidate(infra *v1beta1.Infra) field.ErrorList {
allErrors := field.ErrorList{}
return allErrors
}
+7 -9
View File
@@ -68,10 +68,9 @@ func (a *AliProvider) CreateVSwitch() error {
request := vpc.CreateCreateVSwitchRequest()
request.Scheme = Scheme
request.ZoneId = a.Infra.Status.Cluster.ZoneID
request.CidrBlock = CidrBlock
request.CidrBlock = a.Infra.Spec.Cluster.Metadata.Network.PrivateCidrIP
request.VpcId = VpcID.Value(a.Infra.Status)
request.RegionId = a.Infra.Status.Cluster.RegionID
//response, err := d.Client.CreateVSwitch(request)
response := vpc.CreateCreateVSwitchResponse()
err := a.RetryVpcRequest(request, response)
if err != nil {
@@ -110,11 +109,10 @@ func (a *AliProvider) CreateSecurityGroup() error {
return err
}
if !a.AuthorizeSecurityGroup(response.SecurityGroupId, SSHPortRange) {
return fmt.Errorf("authorize securitygroup ssh port failed")
}
if !a.AuthorizeSecurityGroup(response.SecurityGroupId, APIServerPortRange) {
return fmt.Errorf("authorize securitygroup apiserver port failed")
for _, port := range a.Infra.Spec.Cluster.Metadata.Network.ExportPorts {
if !a.AuthorizeSecurityGroup(response.SecurityGroupId, port) {
return fmt.Errorf("authorize securitygroup port: %v failed", port)
}
}
SecurityGroupID.SetValue(a.Infra.Status, response.SecurityGroupId)
return nil
@@ -197,8 +195,8 @@ func (a *AliProvider) BindEipForMaster0() error {
func (a *AliProvider) allocateEipAddress() (eIP, eIPID string, err error) {
request := vpc.CreateAllocateEipAddressRequest()
request.Scheme = Scheme
request.Bandwidth = Bandwidth
request.InternetChargeType = InternetChargeType
request.Bandwidth = a.Infra.Spec.Cluster.Metadata.Network.Bandwidth
request.InternetChargeType = "PayByTraffic"
response := vpc.CreateAllocateEipAddressResponse()
err = a.RetryVpcRequest(request, response)
if err != nil {
+85
View File
@@ -0,0 +1,85 @@
// Copyright © 2021 sealos.
//
// 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 huawei
import (
"time"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
)
const (
Scheme = "https"
IPProtocol = "tcp"
APIServerPortRange = "6443/6443"
SSHPortRange = "22/22"
SourceCidrIP = "0.0.0.0/0"
CidrBlock = "172.16.0.0/24"
Policy = "accept"
DestinationResource = "InstanceType"
InstanceChargeType = "PostPaid"
InternetChargeType = "PayByTraffic"
Product = "product"
Role = "role"
Arch = "arch"
Stopped = "Stopped"
AvailableTypeStatus = "WithStock"
Bandwidth = "100"
HwDomain = "www.sealyun.com/"
TryTimes = 10
TrySleepTime = time.Second
JustGetInstanceInfo = 0
)
const HuaweiProvider v2.Provider = "HuaweiProvider"
type ResourceName string
const (
EipID ResourceName = HwDomain + "EipID"
VpcID ResourceName = HwDomain + "VpcID"
VSwitchID ResourceName = HwDomain + "VSwitchID"
SecurityGroupID ResourceName = HwDomain + "SecurityGroupID"
ZoneID ResourceName = HwDomain + "ZoneID"
ShouldBeDeleteInstancesIDs ResourceName = "ShouldBeDeleteInstancesIDs"
)
func (r ResourceName) ClusterValue(infra v2.InfraSpec) string {
return infra.Cluster.Annotations[string(r)]
}
func (r ResourceName) ClusterSetValue(infra v2.InfraSpec, val string) {
infra.Cluster.Annotations[string(r)] = val
}
func (r ResourceName) Value(status v2.InfraStatus) string {
return status.Cluster.Annotations[string(r)]
}
func (r ResourceName) SetValue(status v2.InfraStatus, val string) {
status.Cluster.Annotations[string(r)] = val
}
type ImageArch string
func ConvertImageArch(arch v2.Arch) ImageArch {
switch arch {
case v2.ARM64:
return "arm64"
case v2.AMD64:
return "x86_64"
}
return ""
}
+338
View File
@@ -0,0 +1,338 @@
// Copyright © 2021 sealos.
//
// 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 huawei
import (
"errors"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
v2 "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2/model"
)
//type Instance struct {
// CPU int
// Memory int
// InstanceID string
// PrimaryIPAddress string
//}
//
func (a *HwProvider) GetAvailableZoneID() error {
if a.Infra.Status.Cluster.ZoneID != "" {
logger.Debug("zoneID using status value")
return nil
}
defer func() {
logger.Info("create resource success %s: %s", "GetAvailableZoneID", a.Infra.Status.Cluster.ZoneID)
}()
if len(a.Infra.Spec.Cluster.ZoneIDs) != 0 {
a.Infra.Status.Cluster.ZoneID = a.Infra.Spec.Cluster.ZoneIDs[utils.Rand(len(a.Infra.Spec.Cluster.ZoneIDs))]
return nil
}
resp, err := a.RetryEcsRequest(&model.NovaListAvailabilityZonesRequest{}, v2.GenReqDefForNovaListAvailabilityZones())
if err != nil {
return err
}
if zones, ok := resp.(*model.NovaListAvailabilityZonesResponse); ok {
if zones.HttpStatusCode != 200 || zones.AvailabilityZoneInfo == nil || len(*zones.AvailabilityZoneInfo) == 0 {
return errors.New("not available ZoneID ")
}
if zones.AvailabilityZoneInfo != nil {
zoneID := (*zones.AvailabilityZoneInfo)[utils.Rand(len(*zones.AvailabilityZoneInfo))].ZoneName
a.Infra.Status.Cluster.ZoneID = zoneID
}
}
return nil
}
//func (a *HwProvider) InputIPlist(host *v1beta1.Host) (ipList []string, err error) {
// if host == nil {
// return nil, err
// }
// instances, err := a.GetInstancesInfo(host, host.Count)
// if err != nil {
// return nil, err
// }
// for _, instance := range instances {
// ipList = append(ipList, instance.PrimaryIPAddress)
// }
// return ipList, nil
//}
//
//func (a *HwProvider) GetInstanceStatus(instanceID string) (instanceStatus string, err error) {
// request := ecs.CreateDescribeInstanceStatusRequest()
// request.Scheme = Scheme
// request.InstanceId = &[]string{instanceID}
// response := ecs.CreateDescribeInstanceStatusResponse()
// err = a.RetryEcsRequest(request, response)
// if err != nil {
// return "", fmt.Errorf("get GetAvailableZoneID status failed %v , error :%v", instanceID, err)
// }
// if len(response.InstanceStatuses.InstanceStatus) == 0 {
// return "", fmt.Errorf("GetAvailableZoneID list is empty")
// }
// return response.InstanceStatuses.InstanceStatus[0].Status, nil
//}
//
//func (a *HwProvider) PowerOffInstance(instanceID string) error {
// request := ecs.CreateStopInstancesRequest()
// request.Scheme = Scheme
// request.InstanceId = &[]string{instanceID}
//
// response := ecs.CreateStopInstancesResponse()
// return a.RetryEcsRequest(request, response)
//}
//
//func (a *HwProvider) StartInstance(instanceID string) error {
// request := ecs.CreateStartInstanceRequest()
// request.Scheme = Scheme
// request.InstanceId = instanceID
// response := ecs.CreateStartInstanceResponse()
// return a.RetryEcsRequest(request, response)
//}
//
//func (a *HwProvider) ChangeInstanceType(instanceID string, host *v1beta1.Host) error {
// instanceStatus, err := a.GetInstanceStatus(instanceID)
// if err != nil {
// return err
// }
// if instanceStatus != Stopped {
// err = a.PowerOffInstance(instanceID)
// if err != nil {
// return err
// }
// }
// expectInstanceType, err := a.GetAvailableInstanceType(host)
// if err != nil {
// return err
// }
//
// request := ecs.CreateModifyInstanceSpecRequest()
// request.Scheme = Scheme
// request.InstanceId = instanceID
// response := ecs.CreateModifyInstanceSpecResponse()
// err = a.RetryEcsInstanceType(request, response, expectInstanceType, host.Roles)
// if err != nil {
// return err
// }
// return a.StartInstance(instanceID)
//}
//
//func (a *HwProvider) GetInstancesInfo(host *v1beta1.Host, expectCount int) (instances []Instance, err error) {
// var count int
// tag := make(map[string]string)
// tag[Product] = a.Infra.Name
// tag[Role] = strings.Join(host.Roles, ",")
// tag[Arch] = string(host.Arch)
// if expectCount == 0 {
// count = -1
// } else {
// count = expectCount
// }
// instancesTags := CreateDescribeInstancesTag(tag)
// request := ecs.CreateDescribeInstancesRequest()
// request.Scheme = Scheme
// request.RegionId = a.Infra.Status.Cluster.RegionID
// request.VSwitchId = VSwitchID.Value(a.Infra.Status)
// request.SecurityGroupId = SecurityGroupID.Value(a.Infra.Status)
// request.Tag = &instancesTags
// response := ecs.CreateDescribeInstancesResponse()
// err = a.TryGetInstance(request, response, count)
// if err != nil {
// return nil, err
// }
//
// for _, i := range response.Instances.Instance {
// instances = append(instances,
// Instance{
// CPU: i.Cpu,
// Memory: i.Memory / 1024,
// InstanceID: i.InstanceId,
// PrimaryIPAddress: i.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress})
// }
// return
//}
//
//func (a *HwProvider) ReconcileInstances(host *v1beta1.Host, status *v1beta1.HostStatus) error {
// var instances []Instance
// var instancesIDs string
// var IPList []string
// instancesIDs = status.IDs
// switch host.ToRole() {
// case v1beta1.Master:
// if host.Count == 0 {
// return errors.New("master count not set")
// }
// case v1beta1.Node:
// if host == nil {
// return nil
// }
// }
// if host == nil {
// return errors.New("hosts not set")
// }
// var err error
// if instancesIDs != "" {
// instances, err = a.GetInstancesInfo(host, JustGetInstanceInfo)
// }
// if err != nil {
// return err
// }
// if i := host.Count; len(instances) < i {
// err = a.RunInstances(host, i-len(instances))
// if err != nil {
// return err
// }
// ipList, err := a.InputIPlist(host)
// if err != nil {
// return err
// }
// IPList = utils.AppendIPList(IPList, ipList)
// logger.Info("get scale up IP list %v, append iplist %v, host count %d", ipList, IPList, host.Count)
// }
//
// for _, instance := range instances {
// if instance.CPU != host.CPU || instance.Memory != host.Memory {
// err = a.ChangeInstanceType(instance.InstanceID, host)
// if err != nil {
// return err
// }
// }
// }
// status.IPs = IPList
// logger.Info("reconcile %s instances success %v ", host.String(), IPList)
// return nil
//}
//
//func (a *HwProvider) DeleteInstances() error {
// instanceIDs := strings.Split(ShouldBeDeleteInstancesIDs.Value(a.Infra.Status), ",")
// if len(instanceIDs) == 0 {
// return nil
// }
// request := ecs.CreateDeleteInstancesRequest()
// request.Scheme = Scheme
// request.InstanceId = &instanceIDs
// request.Force = requests.NewBoolean(true)
// response := ecs.CreateDeleteInstancesResponse()
// err := a.RetryEcsRequest(request, response)
// if err != nil {
// return err
// }
// ShouldBeDeleteInstancesIDs.SetValue(a.Infra.Status, "")
// if v1beta1.In(a.Infra.Status.Cluster.Master0ID, instanceIDs) {
// logger.Debug("delete instance success,need delete about instance info[master0id,master0InternalIP,eip,eipID]")
// a.Infra.Status.Cluster.Master0ID = ""
// a.Infra.Status.Cluster.Master0InternalIP = ""
// a.Infra.Status.Cluster.EIP = ""
// delete(a.Infra.Status.Cluster.Annotations, string(EipID))
// }
// return nil
//}
//
//func CreateDescribeInstancesTag(tags map[string]string) (instanceTags []ecs.DescribeInstancesTag) {
// for k, v := range tags {
// instanceTags = append(instanceTags, ecs.DescribeInstancesTag{Key: k, Value: v})
// }
// return
//}
//
//func CreateInstanceDataDisk(dataDisks []v1beta1.Disk, category string) (instanceDisks []ecs.RunInstancesDataDisk) {
// for _, v := range dataDisks {
// instanceDisks = append(instanceDisks,
// ecs.RunInstancesDataDisk{Size: strconv.Itoa(v.Capacity), Category: category})
// }
// return
//}
//
//func (a *HwProvider) RunInstances(host *v1beta1.Host, count int) error {
// if host == nil {
// return errors.New("host not set")
// }
// j := a.Infra.Status.FindHostsByRoles(host.Roles)
// if j == -1 {
// return fmt.Errorf("failed to get status, %v", "not find host status,pelase retry")
// }
// systemDiskSize := host.Disks[0]
// var instanceType []string
// var err error
// var imageID string
// if imageID, err = a.GetAvailableImageID(host); err != nil {
// return err
// }
// a.Infra.Status.Hosts[j].ImageID = imageID
// a.Infra.Status.Hosts[j].Arch = host.Arch
// instanceType, err = a.GetAvailableInstanceType(host)
// if err != nil {
// return err
// }
// tag := make(map[string]string)
// tag[Product] = a.Infra.Name
// tag[Role] = strings.Join(host.Roles, ",")
// tag[Arch] = string(host.Arch)
// instancesTag := CreateInstanceTag(tag)
//
// dataDisks := host.Disks[1:]
// datadisk := CreateInstanceDataDisk(dataDisks, a.Infra.Status.Hosts[j].DataCategory)
//
// request := ecs.CreateRunInstancesRequest()
// request.Scheme = Scheme
// request.ImageId = imageID
//
// request.Password = a.Infra.Spec.Cluster.AccessChannels.SSH.Passwd
// request.SecurityGroupId = SecurityGroupID.Value(a.Infra.Status)
// request.VSwitchId = VSwitchID.Value(a.Infra.Status)
// request.SystemDiskSize = strconv.Itoa(systemDiskSize.Capacity)
// request.SystemDiskCategory = a.Infra.Status.Hosts[j].SystemCategory
// request.DataDisk = &datadisk
// request.SpotStrategy = a.Infra.Status.Cluster.SpotStrategy
// request.Amount = requests.NewInteger(count)
// request.Tag = &instancesTag
// response := ecs.CreateRunInstancesResponse()
// err = a.RetryEcsInstanceType(request, response, instanceType, host.Roles)
// if err != nil {
// return err
// }
//
// instancesIDs := strings.Join(response.InstanceIdSets.InstanceIdSet, ",")
// a.Infra.Status.Hosts[j].IDs += instancesIDs
// return nil
//}
//
//func (a *HwProvider) AuthorizeSecurityGroup(securityGroupID, portRange string) bool {
// request := ecs.CreateAuthorizeSecurityGroupRequest()
// request.Scheme = Scheme
// request.SecurityGroupId = securityGroupID
// request.IpProtocol = IPProtocol
// request.PortRange = portRange
// request.SourceCidrIp = SourceCidrIP
// request.Policy = Policy
//
// response := ecs.CreateAuthorizeSecurityGroupResponse()
// err := a.RetryEcsRequest(request, response)
// if err != nil {
// logger.Error("%v", err)
// return false
// }
// return response.BaseResponse.IsSuccess()
//}
//func CreateInstanceTag(tags map[string]string) (instanceTags []ecs.RunInstancesTag) {
// for k, v := range tags {
// instanceTags = append(instanceTags, ecs.RunInstancesTag{Key: k, Value: v})
// }
// return
//}
+206
View File
@@ -0,0 +1,206 @@
// Copyright © 2021 sealos.
//
// 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 huawei
//func (a *HwProvider) GetAvailableImageID(host *v1beta1.Host) (string, error) {
// if host.OS.ID != "" {
// logger.Info("host tags is %v,using imageID is %s", host.Roles, host.OS.ID)
// return host.OS.ID, nil
// }
// logger.Warn("tips: imageID not set,so imageID is random.that will maybe let instanceType not find.")
// request := ecs.CreateDescribeImagesRequest()
// request.ImageOwnerAlias = "system"
// request.PageSize = "100"
// request.Architecture = string(ConvertImageArch(host.Arch))
// request.OSType = "linux"
// response := ecs.CreateDescribeImagesResponse()
// err := a.RetryEcsRequest(request, response)
// if err != nil {
// return "", fmt.Errorf("get ImageID failed , error :%v", err)
// }
// if response.TotalCount == 0 {
// return "", fmt.Errorf("ImageID list is empty")
// }
// var images []string
//
// for _, image := range response.Images.Image {
// flag := true
// imagesName := strings.ToLower(image.Platform)
// imagesName = strings.ReplaceAll(imagesName, " ", "_")
// if strings.ToLower(host.OS.Name) != "" {
// if imagesName != strings.ToLower(host.OS.Name) {
// flag = false
// }
// }
// if strings.ToLower(host.OS.Version) != "" {
// imageID := strings.ReplaceAll(image.ImageName, imagesName, "")
// imageIDArr := strings.Split(imageID, "_")
// var versions []string
// for _, v := range imageIDArr {
// if v == string(ConvertImageArch(host.Arch)) {
// break
// } else {
// versions = append(versions, v)
// }
// }
// version := strings.Join(versions, ".")
// //7.1,7,7.3,8.7
// if !strings.HasPrefix(version, strings.ToLower(host.OS.Version)) {
// flag = false
// }
// }
// if flag {
// logger.Debug("host tags is %v,search imageID is %s", host.Roles, image.ImageId)
// images = append(images, image.ImageId)
// }
// }
// if len(images) == 0 {
// return "", fmt.Errorf("search ImageID list is empty")
// }
// logger.Info("host tags is %v,using first imageID is %s", host.Roles, images[0])
// return images[utils.Rand(len(images))], nil
//}
//
//func (a *HwProvider) GetDefaultDiskCategories(host *v1beta1.Host) (system []string, data []string) {
// categories := []string{"cloud", "cloud_efficiency", "cloud_ssd", "cloud_essd"}
// if host.Disks[0].Category != "" {
// system = []string{host.Disks[0].Category}
// } else {
// logger.Warn("host tags is %v,system category not set", host.Roles)
// system = categories
// }
// if len(host.Disks) > 1 {
// if host.Disks[1].Category != "" {
// data = []string{host.Disks[1].Category}
// } else {
// logger.Warn("host tags is %v,data category not set", host.Roles)
// data = categories
// }
// }
// return
//}
//
//func (a *HwProvider) GetAvailableInstanceType(host *v1beta1.Host) ([]string, error) {
// if host.EcsType != "" {
// return []string{host.EcsType}, nil
// }
// var systemInstanceTypes []string
// j := a.Infra.Status.FindHostsByRoles(host.Roles)
// if j == -1 {
// return nil, fmt.Errorf("failed to get host, %v", "not find host status,pelase retry")
// }
// var err error
// systemDisk, dataDisk := a.GetDefaultDiskCategories(host)
//
// for _, sys := range systemDisk {
// if len(dataDisk) > 0 {
// for _, data := range dataDisk {
// logger.Debug("host tags is %v,search systemDiskCategory=%s,dataDiskCategory=%s", host.Roles, sys, data)
// systemInstanceTypes, err = a.GetAvailableResource(host, sys, data)
// if err == nil {
// for i := 0; i < len(host.Disks); i++ {
// host.Disks[i].Category = data
// }
// host.Disks[0].Category = sys
// a.Infra.Status.Hosts[j].DataCategory = data
// a.Infra.Status.Hosts[j].SystemCategory = sys
// break
// }
// }
// } else {
// logger.Debug("host tags is %v,search systemDiskCategory=%s", host.Roles, sys)
// systemInstanceTypes, err = a.GetAvailableResource(host, sys, "")
// if err == nil {
// host.Disks[0].Category = sys
// a.Infra.Status.Hosts[j].SystemCategory = sys
// break
// }
// }
// }
//
// if len(systemInstanceTypes) < 1 {
// return nil, fmt.Errorf("host tags is %v,systemInstanceType not find", host.Roles)
// }
//
// var instanceTypes []string
// if err != nil {
// return nil, err
// }
//
// request := ecs.CreateDescribeImageSupportInstanceTypesRequest()
// request.Scheme = Scheme
// request.RegionId = a.Infra.Status.Cluster.RegionID
// request.ImageId = a.Infra.Status.Hosts[j].ImageID
//
// //response := ecs.CreateDescribeImageSupportInstanceTypesResponse()
// //err = a.EcsClient.DoAction(request, response)
// //if err != nil {
// // return nil, err
// //}
// //for _, i := range response.InstanceTypes.InstanceType {
// // if i.CpuCoreCount == host.CPU && int(i.MemorySize) == host.Memory {
// // if v1beta1.In(i.InstanceTypeId, systemInstanceTypes) {
// // logger.Debug("host tags is %v,append InstanceType is %s", host.Roles, i.InstanceTypeId)
// // instanceTypes = append(instanceTypes, i.InstanceTypeId)
// // }
// // }
// //}
// if len(instanceTypes) < 1 {
// return nil, fmt.Errorf("host tags is %v,instanceType not find", host.Roles)
// }
// return instanceTypes, nil
//}
//
//func (a *HwProvider) GetAvailableResource(host *v1beta1.Host, systemCategory, dataCategory string) (instanceType []string, err error) {
// request := ecs.CreateDescribeAvailableResourceRequest()
// request.Scheme = Scheme
// request.RegionId = a.Infra.Status.Cluster.RegionID
// request.ZoneId = a.Infra.Status.Cluster.ZoneID
// request.DestinationResource = DestinationResource
// request.InstanceChargeType = InstanceChargeType
// request.SpotStrategy = a.Infra.Status.Cluster.SpotStrategy
// request.SystemDiskCategory = systemCategory
// request.DataDiskCategory = dataCategory
// request.Cores = requests.NewInteger(host.CPU)
// request.Memory = requests.NewFloat(float64(host.Memory))
// //response := ecs.CreateDescribeAvailableResourceResponse()
// //err = a.EcsClient.DoAction(request, response)
// //if err != nil {
// // return nil, err
// //}
// //if len(response.AvailableZones.AvailableZone) < 1 {
// // return nil, fmt.Errorf("available zone not find")
// //}
// //for _, i := range response.AvailableZones.AvailableZone {
// // for _, f := range i.AvailableResources.AvailableResource {
// // for _, r := range f.SupportedResources.SupportedResource {
// // if r.StatusCategory == AvailableTypeStatus {
// // instanceType = append(instanceType, r.Value)
// // }
// // }
// // }
// //}
// j := a.Infra.Status.FindHostsByRoles(host.Roles)
// if j == -1 {
// return nil, fmt.Errorf("failed to get ecs instance type, %v", "not find host status,pelase retry")
// }
//
// if a.Infra.Status.Hosts[j].InstanceType != "" {
// defaultInstanceType := []string{a.Infra.Status.Hosts[j].InstanceType}
// instanceType = append(defaultInstanceType, instanceType...)
// }
//
// return
//}
+279
View File
@@ -0,0 +1,279 @@
// Copyright © 2021 sealos.
//
// 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 huawei
import (
"fmt"
"k8s.io/apimachinery/pkg/util/validation/field"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
"github.com/fanux/sealos/pkg/utils"
"github.com/fanux/sealos/pkg/utils/logger"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
ecs "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2"
vpc "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vpc/v2"
)
type ActionName string
const (
CreateVPC ActionName = "CreateVPC"
CreateVSwitch ActionName = "CreateVSwitch"
CreateSecurityGroup ActionName = "CreateSecurityGroup"
ReconcileInstance ActionName = "ReconcileInstance"
BindEIP ActionName = "BindEIP"
ReleaseEIP ActionName = "ReleaseEIP"
ClearInstances ActionName = "ClearInstances"
DeleteVSwitch ActionName = "DeleteVSwitch"
DeleteSecurityGroup ActionName = "DeleteSecurityGroup"
DeleteVPC ActionName = "DeleteVPC"
GetZoneID ActionName = "GetAvailableZoneID"
)
type HwProvider struct {
EcsClient ecs.EcsClient
VpcClient vpc.VpcClient
Infra *v2.Infra
}
type HwFunc func() error
func (a *HwProvider) ReconcileResource(resourceKey ResourceName, action HwFunc) error {
if resourceKey.Value(a.Infra.Status) != "" {
logger.Debug("using resource status value %s: %s", resourceKey, resourceKey.Value(a.Infra.Status))
return nil
}
if err := action(); err != nil {
logger.Error("reconcile resource %s failed err: %s", resourceKey, err)
return err
}
if resourceKey.Value(a.Infra.Status) != "" {
logger.Info("create resource success %s: %s", resourceKey, resourceKey.Value(a.Infra.Status))
}
return nil
}
func (a *HwProvider) DeleteResource(resourceKey ResourceName, action HwFunc) {
val := resourceKey.Value(a.Infra.Status)
if val == "" {
logger.Warn("delete resource not exists %s", resourceKey)
return
}
if err := action(); err != nil {
logger.Error("delete resource %s failed err: %s", resourceKey, err)
} else {
logger.Info("delete resource Success %s: %s", resourceKey, val)
}
}
var RecocileFuncMap = map[ActionName]func(provider *HwProvider) error{
CreateVPC: func(hwProvider *HwProvider) error {
return hwProvider.ReconcileResource(VpcID, hwProvider.CreateVPC)
},
//CreateVSwitch: func(hwProvider *HwProvider) error {
// return hwProvider.ReconcileResource(VSwitchID, hwProvider.CreateVSwitch)
//},
//CreateSecurityGroup: func(hwProvider *HwProvider) error {
// return hwProvider.ReconcileResource(SecurityGroupID, hwProvider.CreateSecurityGroup)
//},
//ReconcileInstance: func(hwProvider *HwProvider) error {
// var errorMsg []string
// current := sets.NewString()
// spec := sets.NewString()
// for _, h := range hwProvider.Infra.Status.Hosts {
// current.Insert(strings.Join(h.Roles, ","))
// }
// for _, h := range hwProvider.Infra.Spec.Hosts {
// spec.Insert(strings.Join(h.Roles, ","))
// host := &h
// statusIndex := hwProvider.Infra.Status.FindHostsByRoles(h.Roles)
// if statusIndex < 0 {
// errorMsg = append(errorMsg, fmt.Sprintf("infra status not fount in role tag: %v", h.Roles))
// continue
// }
// status := &hwProvider.Infra.Status.Hosts[statusIndex]
// err := hwProvider.ReconcileInstances(host, status)
// if err != nil {
// errorMsg = append(errorMsg, err.Error())
// status.Ready = false
// } else {
// status.Ready = true
// }
// }
// deleteData := current.Difference(spec)
// var instanceIDs []string
// finalStatus := hwProvider.Infra.Status.Hosts
// for _, roles := range deleteData.List() {
// statusIndex := hwProvider.Infra.Status.FindHostsByRolesString(roles)
// ids := hwProvider.Infra.Status.Hosts[statusIndex].IDs
// instanceIDs = append(instanceIDs, ids)
// finalStatus = append(finalStatus[:statusIndex], finalStatus[statusIndex+1:]...)
// }
// if len(instanceIDs) != 0 {
// ShouldBeDeleteInstancesIDs.SetValue(hwProvider.Infra.Status, strings.Join(instanceIDs, ","))
// hwProvider.DeleteResource(ShouldBeDeleteInstancesIDs, hwProvider.DeleteInstances)
// hwProvider.Infra.Status.Hosts = finalStatus
// }
//
// if len(errorMsg) == 0 {
// return nil
// }
// return errors.New(strings.Join(errorMsg, " && "))
//},
GetZoneID: func(hwProvider *HwProvider) error {
return hwProvider.ReconcileResource(ZoneID, hwProvider.GetAvailableZoneID)
},
//BindEIP: func(hwProvider *HwProvider) error {
// return hwProvider.ReconcileResource(EipID, hwProvider.BindEipForMaster0)
//},
}
var DeleteFuncMap = map[ActionName]func(provider *HwProvider){
//ReleaseEIP: func(hwProvider *HwProvider) {
// hwProvider.DeleteResource(EipID, hwProvider.ReleaseEipAddress)
//},
//ClearInstances: func(hwProvider *HwProvider) {
// var instanceIDs []string
// for _, h := range hwProvider.Infra.Status.Hosts {
// instances, err := hwProvider.GetInstancesInfo(h.ToHost(), JustGetInstanceInfo)
// if err != nil {
// logger.Error("get %s instanceInfo failed %v", strings.Join(h.Roles, ","), err)
// }
// for _, instance := range instances {
// instanceIDs = append(instanceIDs, instance.InstanceID)
// }
// }
//
// if len(instanceIDs) != 0 {
// ShouldBeDeleteInstancesIDs.SetValue(hwProvider.Infra.Status, strings.Join(instanceIDs, ","))
// }
// hwProvider.DeleteResource(ShouldBeDeleteInstancesIDs, hwProvider.DeleteInstances)
//},
//DeleteVSwitch: func(hwProvider *HwProvider) {
// hwProvider.DeleteResource(VSwitchID, hwProvider.DeleteVSwitch)
//},
//DeleteSecurityGroup: func(hwProvider *HwProvider) {
// hwProvider.DeleteResource(SecurityGroupID, hwProvider.DeleteSecurityGroup)
//},
DeleteVPC: func(hwProvider *HwProvider) {
hwProvider.DeleteResource(VpcID, hwProvider.DeleteVPC)
},
}
func (a *HwProvider) basicAuth() basic.Credentials {
//https://developer.huaweicloud.com/endpoint?ECS
core.NewHcHttpClientBuilder()
return basic.NewCredentialsBuilder().
WithAk(a.Infra.Spec.Credential.AccessKey).
WithSk(a.Infra.Spec.Credential.AccessSecret).
WithProjectId(a.Infra.Spec.Credential.ProjectID).
Build()
}
func (a *HwProvider) NewClient() (err error) {
regionID := a.Infra.Spec.Cluster.RegionIDs[utils.Rand(len(a.Infra.Spec.Cluster.RegionIDs))]
a.Infra.Status.Cluster.RegionID = regionID
logger.Info("using regionID is %s", regionID)
ecsEndpoint := fmt.Sprintf("https://ecs.%s.myhuaweicloud.com", regionID)
vpcEndpoint := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com", regionID)
logger.Info("using ecs endpoint is %s", ecsEndpoint)
logger.Info("using vpc endpoint is %s", vpcEndpoint)
//var err error
defer func() {
if info := recover(); info != nil {
err = fmt.Errorf("%v", info)
}
}()
ecsClient := ecs.NewEcsClient(
ecs.EcsClientBuilder().
WithEndpoint(ecsEndpoint).
WithCredential(a.basicAuth()).
Build())
vpcClient := vpc.NewVpcClient(
vpc.VpcClientBuilder().
WithEndpoint(vpcEndpoint).
WithCredential(a.basicAuth()).
Build())
a.EcsClient = *ecsClient
a.VpcClient = *vpcClient
return
}
func (a *HwProvider) ClearCluster() {
todolist := []ActionName{
//ReleaseEIP,
//ClearInstances,
//DeleteVSwitch,
//DeleteSecurityGroup,
DeleteVPC,
}
for _, name := range todolist {
DeleteFuncMap[name](a)
}
}
func (a *HwProvider) Reconcile() error {
if a.Infra.DeletionTimestamp != nil {
logger.Info("deletionTimestamp not nil will clear infra")
a.ClearCluster()
return nil
}
todolist := []ActionName{
GetZoneID,
CreateVPC,
//CreateVSwitch,
//CreateSecurityGroup,
//ReconcileInstance,
//BindEIP,
}
for _, actionName := range todolist {
err := RecocileFuncMap[actionName](a)
if err != nil {
logger.Warn("actionName: %s,err: %v ,skip it", actionName, err)
}
}
return nil
}
func (a *HwProvider) Apply() error {
return a.Reconcile()
}
func DefaultInfra(infra *v2.Infra) error {
//if infra.Spec.Cluster.IsSeize {
// infra.Status.Cluster.SpotStrategy = "SpotAsPriceGo"
//} else {
// infra.Status.Cluster.SpotStrategy = "NoSpot"
//}
return nil
}
func DefaultValidate(infra *v2.Infra) field.ErrorList {
allErrors := field.ErrorList{}
//if provide == v1beta1.HuaweiProvider {
// if len(credential.ProjectID) == 0 {
// logger.Warn("in huawei cloud , you need fetch projectID in you iam, please visit https://support.huaweicloud.com/apm_faq/apm_03_0001.html")
// allErrors = append(allErrors, field.Invalid(fldPath.Key("projectID"), credential.ProjectID,
// "projectID not empty"))
// }
//}
return allErrors
}
+218
View File
@@ -0,0 +1,218 @@
// Copyright © 2021 sealos.
//
// 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 huawei
import (
"errors"
"github.com/fanux/sealos/pkg/utils/logger"
v2 "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vpc/v2"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vpc/v2/model"
)
func (a *HwProvider) CreateVPC() error {
if vpcID := VpcID.ClusterValue(a.Infra.Spec); vpcID != "" {
VpcID.SetValue(a.Infra.Status, vpcID)
logger.Debug("VpcID using default value")
return nil
}
request := &model.CreateVpcRequest{}
request.Body = &model.CreateVpcRequestBody{
Vpc: &model.CreateVpcOption{
Cidr: &a.Infra.Spec.Cluster.Metadata.Network.PrivateCidrIP,
},
}
resp, err := a.RetryVpcRequest(request, v2.GenReqDefForCreateVpc())
if err != nil {
return err
}
if vpc, ok := resp.(*model.CreateVpcResponse); ok {
if vpc.HttpStatusCode != 200 || vpc.Vpc == nil {
return errors.New("not available vpcID ")
}
VpcID.SetValue(a.Infra.Status, vpc.Vpc.Id)
}
return nil
}
func (a *HwProvider) DeleteVPC() error {
if VpcID.ClusterValue(a.Infra.Spec) != "" && VpcID.Value(a.Infra.Status) != "" {
return nil
}
request := &model.DeleteVpcRequest{}
resp, err := a.RetryVpcRequest(request, v2.GenReqDefForDeleteVpc())
if err != nil {
return err
}
if vpcResp, ok := resp.(*model.DeleteVpcResponse); ok {
if vpcResp.HttpStatusCode != 200 {
return errors.New("delete vpc field,return httpCode is not 200")
}
}
return nil
}
//
//func (a *HwProvider) CreateVSwitch() error {
// if vSwitchID := VSwitchID.ClusterValue(a.Infra.Spec); vSwitchID != "" {
// logger.Debug("VSwitchID using default value")
// VSwitchID.SetValue(a.Infra.Status, vSwitchID)
// return nil
// }
// request := vpc.CreateCreateVSwitchRequest()
// request.Scheme = Scheme
// request.ZoneId = a.Infra.Status.Cluster.ZoneID
// request.CidrBlock = CidrBlock
// request.VpcId = VpcID.Value(a.Infra.Status)
// request.RegionId = a.Infra.Status.Cluster.RegionID
// //response, err := d.Client.CreateVSwitch(request)
// response := vpc.CreateCreateVSwitchResponse()
// err := a.RetryVpcRequest(request, response)
// if err != nil {
// return err
// }
// VSwitchID.SetValue(a.Infra.Status, response.VSwitchId)
//
// return nil
//}
//
//func (a *HwProvider) DeleteVSwitch() error {
// if VSwitchID.ClusterValue(a.Infra.Spec) != "" && VSwitchID.Value(a.Infra.Status) != "" {
// return nil
// }
// request := vpc.CreateDeleteVSwitchRequest()
// request.Scheme = Scheme
// request.VSwitchId = VSwitchID.Value(a.Infra.Status)
//
// response := vpc.CreateDeleteVSwitchResponse()
// return a.RetryVpcRequest(request, response)
//}
//
//func (a *HwProvider) CreateSecurityGroup() error {
// if securityGroupID := SecurityGroupID.ClusterValue(a.Infra.Spec); securityGroupID != "" {
// logger.Debug("securityGroupID using default value")
// SecurityGroupID.SetValue(a.Infra.Status, securityGroupID)
// return nil
// }
// request := ecs.CreateCreateSecurityGroupRequest()
// request.Scheme = Scheme
// request.RegionId = a.Infra.Status.Cluster.RegionID
// request.VpcId = VpcID.Value(a.Infra.Status)
// response := ecs.CreateCreateSecurityGroupResponse()
// err := a.RetryEcsRequest(request, response)
// if err != nil {
// return err
// }
//
// if !a.AuthorizeSecurityGroup(response.SecurityGroupId, SSHPortRange) {
// return fmt.Errorf("authorize securitygroup ssh port failed")
// }
// if !a.AuthorizeSecurityGroup(response.SecurityGroupId, APIServerPortRange) {
// return fmt.Errorf("authorize securitygroup apiserver port failed")
// }
// SecurityGroupID.SetValue(a.Infra.Status, response.SecurityGroupId)
// return nil
//}
//
//func (a *HwProvider) DeleteSecurityGroup() error {
// if SecurityGroupID.ClusterValue(a.Infra.Spec) != "" && SecurityGroupID.Value(a.Infra.Status) != "" {
// return nil
// }
// request := ecs.CreateDeleteSecurityGroupRequest()
// request.Scheme = Scheme
// request.SecurityGroupId = SecurityGroupID.Value(a.Infra.Status)
//
// response := ecs.CreateDeleteSecurityGroupResponse()
// return a.RetryEcsRequest(request, response)
//}
//
//func (a *HwProvider) BindEipForMaster0() error {
// var host *v1beta1.Host
// for i, h := range a.Infra.Status.Hosts {
// if v1beta1.In(v1beta1.Master, h.Roles) && h.Ready {
// host = &a.Infra.Spec.Hosts[i]
// break
// }
// }
// if host == nil {
// return fmt.Errorf("bind eip for master error: ready master host not fount")
// }
// instances, err := a.GetInstancesInfo(host, JustGetInstanceInfo)
// if err != nil {
// return err
// }
// if len(instances) == 0 {
// return errors.New("can not find master0 ")
// }
// master0 := instances[0]
// eIP, eIPID, err := a.allocateEipAddress()
// if err != nil {
// return err
// }
// err = a.associateEipAddress(master0.InstanceID, eIPID)
// if err != nil {
// return err
// }
// a.Infra.Status.Cluster.EIP = eIP
// EipID.SetValue(a.Infra.Status, eIPID)
// a.Infra.Status.Cluster.Master0ID = master0.InstanceID
// a.Infra.Status.Cluster.Master0InternalIP = master0.PrimaryIPAddress
// return nil
//}
//
//func (a *HwProvider) allocateEipAddress() (eIP, eIPID string, err error) {
// request := vpc.CreateAllocateEipAddressRequest()
// request.Scheme = Scheme
// request.Bandwidth = Bandwidth
// request.InternetChargeType = InternetChargeType
// response := vpc.CreateAllocateEipAddressResponse()
// err = a.RetryVpcRequest(request, response)
// if err != nil {
// return "", "", err
// }
// return response.EipAddress, response.AllocationId, nil
//}
//
//func (a *HwProvider) associateEipAddress(instanceID, eipID string) error {
// request := vpc.CreateAssociateEipAddressRequest()
// request.Scheme = Scheme
// request.InstanceId = instanceID
// request.AllocationId = eipID
//
// response := vpc.CreateAssociateEipAddressResponse()
// return a.RetryVpcRequest(request, response)
//}
//
//func (a *HwProvider) unAssociateEipAddress() error {
// request := vpc.CreateUnassociateEipAddressRequest()
// request.Scheme = Scheme
// request.AllocationId = EipID.Value(a.Infra.Status)
// request.Force = requests.NewBoolean(true)
// response := vpc.CreateUnassociateEipAddressResponse()
// return a.RetryVpcRequest(request, response)
//}
//
//func (a *HwProvider) ReleaseEipAddress() error {
// err := a.unAssociateEipAddress()
// if err != nil {
// return err
// }
// request := vpc.CreateReleaseEipAddressRequest()
// request.Scheme = Scheme
// request.AllocationId = EipID.Value(a.Infra.Status)
// response := vpc.CreateReleaseEipAddressResponse()
// return a.RetryVpcRequest(request, response)
//}
+111
View File
@@ -0,0 +1,111 @@
// Copyright © 2021 sealos.
//
// 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 huawei
import (
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/def"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/fanux/sealos/pkg/utils"
)
func (a *HwProvider) RetryVpcRequest(request interface{}, def *def.HttpRequestDef) (interface{}, error) {
var respInterface interface{}
var err error
allErr := utils.Retry(TryTimes, TrySleepTime, func() error {
respInterface, err = a.VpcClient.HcClient.Sync(request, def)
if err != nil {
return err
}
return nil
})
return respInterface, allErr
}
func (a *HwProvider) RetryEcsRequest(request interface{}, def *def.HttpRequestDef) (interface{}, error) {
return a.RetryEcsAction(request, def, TryTimes)
}
func (a *HwProvider) RetryEcsAction(request interface{}, def *def.HttpRequestDef, tryTimes int) (interface{}, error) {
var respInterface interface{}
var err error
allErr := utils.Retry(tryTimes, TrySleepTime, func() error {
respInterface, err = a.EcsClient.HcClient.Sync(request, def)
if err != nil {
return err
}
return nil
})
return respInterface, allErr
}
func (a *HwProvider) RetryEcsInstanceType(request requests.AcsRequest, response responses.AcsResponse, instances, instanceRole []string) error {
//for i := 0; i < len(instances); i++ {
// switch req := request.(type) {
// case *ecs.ModifyInstanceSpecRequest:
// req.InstanceType = instances[i]
// case *ecs.RunInstancesRequest:
// req.InstanceType = instances[i]
// }
// err := a.RetryEcsAction(request, response, 4)
// if err == nil {
// logger.Info("use instance type: %s", instances[i])
// j := a.Infra.Status.FindHostsByRoles(instanceRole)
// if j == -1 {
// return fmt.Errorf("failed to get ecs instance type, %v", "not find host status,pelase retry")
// }
// a.Infra.Status.Hosts[j].InstanceType = instances[i]
// break
// } else {
// logger.Warn("failed to get ecs instance type: %s", instances[i])
// if i == len(instances)-1 {
// return fmt.Errorf("failed to get ecs instance type, %v", err)
// }
// }
//}
return nil
}
func (a *HwProvider) TryGetInstance(request *ecs.DescribeInstancesRequest, response *ecs.DescribeInstancesResponse, expectCount int) error {
return utils.Retry(TryTimes, TrySleepTime, func() error {
//err := a.EcsClient.DoAction(request, response)
//var ipList []string
//if err != nil {
// return err
//}
//instances := response.Instances.Instance
//if expectCount == -1 {
// return nil
//}
//
//if len(instances) != expectCount {
// return errors.New("the number of instances is not as expected")
//}
//for _, instance := range instances {
// if instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress == "" {
// return errors.New("PrimaryIpAddress cannt nob be nil")
// }
// if len(ipList) != 0 && !utils.NotIn(instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress, ipList) {
// return errors.New("PrimaryIpAddress cannt nob be same")
// }
//
// ipList = append(ipList, instance.NetworkInterfaces.NetworkInterface[0].PrimaryIpAddress)
//}
return nil
})
}
+27 -1
View File
@@ -18,6 +18,9 @@ import (
"fmt"
"os"
"github.com/fanux/sealos/pkg/infra/huawei"
"github.com/fanux/sealos/pkg/types/validation"
"github.com/fanux/sealos/pkg/infra/aliyun"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
)
@@ -45,17 +48,40 @@ func loadConfig(infra *v2.Infra) {
func newAliProvider(infra *v2.Infra) (Interface, error) {
aliProvider := new(aliyun.AliProvider)
aliProvider.Infra = infra
if err := v2.Default(aliProvider.Infra, aliyun.DefaultInfra); err != nil {
return nil, err
}
if err := validation.ValidateInfra(aliProvider.Infra, aliyun.DefaultValidate); len(err) != 0 {
return nil, err.ToAggregate()
}
if err := aliProvider.NewClient(); err != nil {
return nil, err
}
return aliProvider, nil
}
func newHwProvider(infra *v2.Infra) (Interface, error) {
hwProvider := new(huawei.HwProvider)
hwProvider.Infra = infra
if err := v2.Default(hwProvider.Infra, huawei.DefaultInfra); err != nil {
return nil, err
}
if err := validation.ValidateInfra(hwProvider.Infra, huawei.DefaultValidate); len(err) != 0 {
return nil, err.ToAggregate()
}
if err := hwProvider.NewClient(); err != nil {
return nil, err
}
return hwProvider, nil
}
func NewDefaultProvider(infra *v2.Infra) (Interface, error) {
loadConfig(infra)
switch infra.Spec.Provider {
case v2.AliyunProvider:
case aliyun.AliyunProvider:
return newAliProvider(infra)
case huawei.HuaweiProvider:
return newHwProvider(infra)
default:
return nil, fmt.Errorf("the provider is invalid, please set the provider correctly")
}
+64 -24
View File
@@ -19,6 +19,9 @@ import (
"testing"
"time"
"github.com/fanux/sealos/pkg/infra/aliyun"
"github.com/fanux/sealos/pkg/infra/huawei"
"sigs.k8s.io/yaml"
v2 "github.com/fanux/sealos/pkg/types/v1beta1"
@@ -26,7 +29,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestApply(t *testing.T) {
func TestAliApply(t *testing.T) {
//setup infra
infra := v2.Infra{
TypeMeta: metav1.TypeMeta{
@@ -46,30 +49,11 @@ func TestApply(t *testing.T) {
Port: 22,
},
},
IsSeize: true,
Metadata: v2.ClusterMeta{
IsSeize: true,
},
},
Hosts: []v2.Host{
//{
// Roles: []string{"master", "ssd"},
// CPU: 2,
// Memory: 4,
// Count: 1,
// Disks: []v2.Disk{},
// OS: v2.OS{
// Name: "centos",
// },
//},
//{
// Roles: []string{"node", "ssd"},
// CPU: 2,
// Memory: 4,
// Count: 1,
// Disks: []v2.Disk{},
// Arch: v2.ARM64,
// OS: v2.OS{
// Name: "centos",
// },
//},
{
Roles: []string{"master", "ssdxxx"},
CPU: 2,
@@ -81,7 +65,7 @@ func TestApply(t *testing.T) {
},
},
},
Provider: v2.AliyunProvider,
Provider: aliyun.AliyunProvider,
},
}
@@ -143,3 +127,59 @@ func TestApply(t *testing.T) {
infra.ObjectMeta.DeletionTimestamp = &now
t.Log(fmt.Sprintf("%v", aliProvider.Apply()))
}
func TestHuaweiApply(t *testing.T) {
//setup infra
infra := v2.Infra{
TypeMeta: metav1.TypeMeta{
Kind: "Infra",
APIVersion: v2.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-infra",
},
Spec: v2.InfraSpec{
Credential: v2.Credential{ProjectID: "06b264130780105b2f50c0145ba32d41"},
Cluster: v2.Cluster{
RegionIDs: []string{"cn-north-4"},
ZoneIDs: []string{""},
AccessChannels: v2.AccessChannels{
SSH: v2.SSH{
Passwd: "Fanux#123",
Port: 22,
},
},
Metadata: v2.ClusterMeta{IsSeize: true},
},
Hosts: []v2.Host{
{
Roles: []string{"master", "ssdxxx"},
CPU: 2,
Memory: 4,
Count: 1,
Disks: []v2.Disk{},
OS: v2.OS{
Name: "ubuntu",
},
},
},
Provider: huawei.HuaweiProvider,
},
}
hwProvider, err := NewDefaultProvider(&infra)
if err != nil {
fmt.Printf("%v", err)
return
}
fmt.Printf("%v", hwProvider.Apply())
// todo
t.Run("default", func(t *testing.T) {
})
//teardown
time.Sleep(20 * time.Second)
now := metav1.Now()
infra.ObjectMeta.DeletionTimestamp = &now
t.Log(fmt.Sprintf("%v", hwProvider.Apply()))
}
+52
View File
@@ -19,6 +19,28 @@ package v1beta1
import (
"math/rand"
"time"
"github.com/fanux/sealos/pkg/utils"
"k8s.io/apimachinery/pkg/util/sets"
)
const (
SourceCidrIP = "0.0.0.0/0"
SSHPort = "22/22"
APIServerPort = "6443/6443"
)
var (
sshExportPort = ExportPort{
Protocol: ProtocolTCP,
CidrIP: SourceCidrIP,
PortRange: SSHPort,
}
apiserverExportPort = ExportPort{
Protocol: ProtocolTCP,
CidrIP: SourceCidrIP,
PortRange: APIServerPort,
}
)
func Default(infra *Infra, fn func(infra *Infra) error) error {
@@ -36,6 +58,36 @@ func defaultCluster(infra *Infra) {
if infra.Spec.Cluster.Annotations == nil {
infra.Spec.Cluster.Annotations = make(map[string]string)
}
if infra.Spec.Cluster.RegionIDs != nil {
infra.Spec.Cluster.RegionIDs = utils.RemoveSliceEmpty(infra.Spec.Cluster.RegionIDs)
}
if infra.Spec.Cluster.ZoneIDs != nil {
infra.Spec.Cluster.ZoneIDs = utils.RemoveSliceEmpty(infra.Spec.Cluster.ZoneIDs)
}
if infra.Spec.Cluster.Metadata.Network.Bandwidth == "" {
infra.Spec.Cluster.Metadata.Network.Bandwidth = "100"
}
if len(infra.Spec.Cluster.Metadata.Network.ExportPorts) == 0 {
infra.Spec.Cluster.Metadata.Network.ExportPorts = []ExportPort{
sshExportPort,
apiserverExportPort,
}
} else {
ports := sets.NewString()
for _, port := range infra.Spec.Cluster.Metadata.Network.ExportPorts {
ports.Insert(port.PortRange)
}
if !ports.Has(SSHPort) {
infra.Spec.Cluster.Metadata.Network.ExportPorts = append(infra.Spec.Cluster.Metadata.Network.ExportPorts, sshExportPort)
}
if !ports.Has(APIServerPort) {
infra.Spec.Cluster.Metadata.Network.ExportPorts = append(infra.Spec.Cluster.Metadata.Network.ExportPorts, apiserverExportPort)
}
}
if infra.Spec.Cluster.Metadata.Network.PrivateCidrIP == "" {
infra.Spec.Cluster.Metadata.Network.PrivateCidrIP = "172.16.0.0/24"
}
}
func defaultHosts(infra *Infra) {
+27 -5
View File
@@ -26,10 +26,6 @@ import (
type Provider string
const (
AliyunProvider Provider = "AliyunProvider"
)
type Arch string
const (
@@ -80,18 +76,44 @@ type Disk struct {
type Credential struct {
AccessKey string `json:"accessKey"`
AccessSecret string `json:"accessSecret"`
ProjectID string `json:"ProjectID,omitempty"`
}
type AccessChannels struct {
SSH SSH `json:"ssh,omitempty"`
}
type ClusterMeta struct {
IsSeize bool `json:"isSeize,omitempty"`
Network ClusterNetworkMeta `json:"network"`
}
type ClusterNetworkMeta struct {
Bandwidth string `json:"bandwidth"`
ExportPorts []ExportPort `json:"exportPorts,omitempty"`
PrivateCidrIP string `json:"privateCidrIP,omitempty"`
}
type Protocol string
const (
ProtocolTCP Protocol = "tcp"
ProtocolUDP Protocol = "udp"
)
type ExportPort struct {
Protocol Protocol `json:"protocol"`
CidrIP string `json:"cidrIP"`
PortRange string `json:"portRange"`
}
type Cluster struct {
RegionIDs []string `json:"regionIDs,omitempty"`
ZoneIDs []string `json:"zoneIDs,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
AccessChannels AccessChannels `json:"accessChannels"`
IsSeize bool `json:"isSeize,omitempty"`
Metadata ClusterMeta `json:"metadata,omitempty"`
}
// InfraSpec defines the desired state of Infra
@@ -60,6 +60,7 @@ func (in *Cluster) DeepCopyInto(out *Cluster) {
}
}
out.AccessChannels = in.AccessChannels
in.Metadata.DeepCopyInto(&out.Metadata)
return
}
@@ -73,6 +74,44 @@ func (in *Cluster) DeepCopy() *Cluster {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterMeta) DeepCopyInto(out *ClusterMeta) {
*out = *in
in.Network.DeepCopyInto(&out.Network)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterMeta.
func (in *ClusterMeta) DeepCopy() *ClusterMeta {
if in == nil {
return nil
}
out := new(ClusterMeta)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterNetworkMeta) DeepCopyInto(out *ClusterNetworkMeta) {
*out = *in
if in.ExportPorts != nil {
in, out := &in.ExportPorts, &out.ExportPorts
*out = make([]ExportPort, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterNetworkMeta.
func (in *ClusterNetworkMeta) DeepCopy() *ClusterNetworkMeta {
if in == nil {
return nil
}
out := new(ClusterNetworkMeta)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) {
*out = *in
@@ -128,6 +167,22 @@ func (in *Disk) DeepCopy() *Disk {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExportPort) DeepCopyInto(out *ExportPort) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExportPort.
func (in *ExportPort) DeepCopy() *ExportPort {
if in == nil {
return nil
}
out := new(ExportPort)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Host) DeepCopyInto(out *Host) {
*out = *in
+17 -2
View File
@@ -72,14 +72,29 @@ func validateCluster(cluster *v1beta1.Cluster, fldPath *field.Path) field.ErrorL
return allErrors
}
func ValidateInfra(infra *v1beta1.Infra) field.ErrorList {
func validateCredential(credential *v1beta1.Credential, fldPath *field.Path) field.ErrorList {
allErrors := field.ErrorList{}
if len(credential.AccessKey) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Key("accessKey"), credential.AccessKey,
"accessKey not empty"))
}
if len(credential.AccessSecret) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Key("accessSecret"), credential.AccessSecret,
"accessSecret not empty"))
}
return allErrors
}
func ValidateInfra(infra *v1beta1.Infra, fun func(infra *v1beta1.Infra) field.ErrorList) field.ErrorList {
allErrors := apimachineryvalidation.ValidateObjectMeta(&infra.ObjectMeta, false, ValidateInfraName, field.NewPath("metadata"))
allErrors = append(allErrors, validateInfraSpec(&infra.Spec, field.NewPath("spec"))...)
allErrors = append(allErrors, fun(infra)...)
return allErrors
}
func validateInfraSpec(spec *v1beta1.InfraSpec, fldPath *field.Path) field.ErrorList {
allErrors := field.ErrorList{}
allErrors = append(allErrors, validateCredential(&spec.Credential, fldPath.Child("credential"))...)
allErrors = append(allErrors, validateCluster(&spec.Cluster, fldPath.Child("cluster"))...)
var roles []string
roleSet := sets.NewString()
+9
View File
@@ -129,3 +129,12 @@ func IsEmptyLine(str string) bool {
func TrimWS(str string) string {
return strings.Trim(str, " \n\t")
}
func RemoveSliceEmpty(list []string) (fList []string) {
for i := range list {
if strings.TrimSpace(list[i]) != "" {
fList = append(fList, list[i])
}
}
return
}