Skip to content

Commit

Permalink
Merge pull request openpitrix#437 from chilianyi/create
Browse files Browse the repository at this point in the history
 Check resource quota before generate job
  • Loading branch information
chilianyi authored Jun 22, 2018
2 parents af1275b + fa349d8 commit 39455ed
Show file tree
Hide file tree
Showing 18 changed files with 488 additions and 238 deletions.
57 changes: 57 additions & 0 deletions pkg/models/quota.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2018 The OpenPitrix Authors. All rights reserved.
// Use of this source code is governed by a Apache license
// that can be found in the LICENSE file.

package models

import (
"fmt"
)

type Quota struct {
Name string
Count int
}

type Quotas struct {
Instance *Quota
Cpu *Quota
Gpu *Quota
Memory *Quota
Volume *Quota
VolumeSize *Quota
}

func NewQuotas() *Quotas {
quotas := &Quotas{
Instance: new(Quota),
Cpu: new(Quota),
Gpu: new(Quota),
Memory: new(Quota),
Volume: new(Quota),
VolumeSize: new(Quota),
}
return quotas
}

func (p *Quotas) LessThan(quotas *Quotas) error {
if p.Instance.Count > quotas.Instance.Count {
return fmt.Errorf("need %d more %s quota", p.Instance.Count-quotas.Instance.Count, p.Instance.Name)
}
if p.Cpu.Count > quotas.Cpu.Count {
return fmt.Errorf("need %d more %s quota", p.Cpu.Count-quotas.Cpu.Count, p.Cpu.Name)
}
if p.Gpu.Count > quotas.Gpu.Count {
return fmt.Errorf("need %d more %s quota", p.Gpu.Count-quotas.Gpu.Count, p.Gpu.Name)
}
if p.Memory.Count > quotas.Memory.Count {
return fmt.Errorf("need %d more %s quota", p.Memory.Count-quotas.Memory.Count, p.Memory.Name)
}
if p.Volume.Count > quotas.Volume.Count {
return fmt.Errorf("need %d more %s quota", p.Volume.Count-quotas.Volume.Count, p.Volume.Name)
}
if p.VolumeSize.Count > quotas.VolumeSize.Count {
return fmt.Errorf("need %d more %s quota", p.VolumeSize.Count-quotas.VolumeSize.Count, p.VolumeSize.Name)
}
return nil
}
4 changes: 2 additions & 2 deletions pkg/plugins/helm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,8 @@ func (p *Provider) DescribeSubnets(ctx context.Context, req *pb.DescribeSubnetsR
return nil, nil
}

func (p *Provider) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) (string, error) {
return "", nil
func (p *Provider) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) error {
return nil
}

func (p *Provider) DescribeVpc(runtimeId, vpcId string) (*models.Vpc, error) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/plugins/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type ProviderInterface interface {
HandleSubtask(task *models.Task) error
WaitSubtask(task *models.Task, timeout time.Duration, waitInterval time.Duration) error
DescribeSubnets(ctx context.Context, req *pb.DescribeSubnetsRequest) (*pb.DescribeSubnetsResponse, error)
CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) (string, error)
CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) error
DescribeVpc(runtimeId, vpcId string) (*models.Vpc, error)
ValidateCredential(url, credential, zone string) error
DescribeRuntimeProviderZones(url, credential string) ([]string, error)
Expand Down
7 changes: 7 additions & 0 deletions pkg/plugins/qingcloud/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ const (
DefaultLoginPassword = "p12cHANgepwD"

DefaultUserDataType = "exec"

ResourceTypeInstance = "hp_instance"
ResourceTypeCpu = "hp_cpu"
ResourceTypeGpu = "gpu_passthrough"
ResourceTypeMemory = "hp_memory"
ResourceTypeVolume = "hpp_volume"
ResourceTypeVolumeSize = "hpp_volume_size"
)
2 changes: 1 addition & 1 deletion pkg/plugins/qingcloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func (p *Provider) DescribeSubnets(ctx context.Context, req *pb.DescribeSubnetsR
return handler.DescribeSubnets(ctx, req)
}

func (p *Provider) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) (string, error) {
func (p *Provider) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) error {
handler := GetProviderHandler(p.Logger)
return handler.CheckResourceQuotas(ctx, clusterWrapper)
}
Expand Down
92 changes: 88 additions & 4 deletions pkg/plugins/qingcloud/provider_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (p *ProviderHandler) RunInstances(task *models.Task) error {
LoginPasswd: qcservice.String(DefaultLoginPassword),
NeedUserdata: qcservice.Int(instance.NeedUserData),
Hostname: qcservice.String(instance.Hostname),
// GPU: qcservice.Int(instance.Gpu),
Gpu: qcservice.Int(instance.Gpu),
}
if instance.VolumeId != "" {
input.Volumes = qcservice.StringSlice([]string{instance.VolumeId})
Expand Down Expand Up @@ -886,7 +886,7 @@ func (p *ProviderHandler) DescribeSubnets(ctx context.Context, req *pb.DescribeS
return response, nil
}

func (p *ProviderHandler) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) (string, error) {
func (p *ProviderHandler) CheckResourceQuotas(ctx context.Context, clusterWrapper *models.ClusterWrapper) error {
roleCount := make(map[string]int)
for _, clusterNode := range clusterWrapper.ClusterNodes {
role := clusterNode.Role
Expand All @@ -898,8 +898,92 @@ func (p *ProviderHandler) CheckResourceQuotas(ctx context.Context, clusterWrappe
}
}

//TODO: need send request to qingcloud to validate quota, https://github.com/yunify/qingcloud-sdk-go/issues/99
return "", nil
needQuotas := models.NewQuotas()
needQuotas.Instance.Name = ResourceTypeInstance
needQuotas.Cpu.Name = ResourceTypeCpu
needQuotas.Gpu.Name = ResourceTypeGpu
needQuotas.Memory.Name = ResourceTypeMemory
needQuotas.Volume.Name = ResourceTypeVolume
needQuotas.VolumeSize.Name = ResourceTypeVolumeSize
for role, count := range roleCount {
clusterRole := clusterWrapper.ClusterRoles[role]
needQuotas.Instance.Count += count
needQuotas.Cpu.Count += int(clusterRole.Cpu) * count
needQuotas.Gpu.Count += int(clusterRole.Gpu) * count
needQuotas.Memory.Count += int(clusterRole.Memory) * count
needQuotas.Volume.Count += count
needQuotas.VolumeSize.Count += int(clusterRole.StorageSize) * count
}

qingcloudService, err := p.initService(clusterWrapper.Cluster.RuntimeId)
if err != nil {
p.Logger.Error("Init %s api service failed: %+v", MyProvider, err)
return err
}

resourceTypes := []string{ResourceTypeInstance, ResourceTypeCpu, ResourceTypeGpu, ResourceTypeMemory,
ResourceTypeVolume, ResourceTypeVolumeSize}
var qcResourceTypes []*string

for _, resourceType := range resourceTypes {
qcResourceTypes = append(qcResourceTypes, qcservice.String(resourceType))
}

miscService, err := qingcloudService.Misc()
if err != nil {
p.Logger.Error("Init %s misc api service failed: %+v", MyProvider, err)
return err
}
output, err := miscService.GetQuotaLeft(&qcservice.GetQuotaLeftInput{
ResourceTypes: qcResourceTypes,
Zone: qcservice.String(qingcloudService.Config.Zone),
})
if err != nil {
p.Logger.Error("GetQuotaLeft to %s failed: %+v", MyProvider, err)
return err
}

retCode := qcservice.IntValue(output.RetCode)
if retCode != 0 {
message := qcservice.StringValue(output.Message)
p.Logger.Error("Send GetQuotaLeft to %s failed with return code [%d], message [%s]",
MyProvider, retCode, message)
return fmt.Errorf("send GetQuotaLeft to %s failed: %s", MyProvider, message)
}

leftQuotas := models.NewQuotas()
for _, quotaLeftSet := range output.QuotaLeftSet {
switch qcservice.StringValue(quotaLeftSet.ResourceType) {
case ResourceTypeInstance:
leftQuotas.Instance.Name = ResourceTypeInstance
leftQuotas.Instance.Count = qcservice.IntValue(quotaLeftSet.Left)
case ResourceTypeCpu:
leftQuotas.Cpu.Name = ResourceTypeCpu
leftQuotas.Cpu.Count = qcservice.IntValue(quotaLeftSet.Left)
case ResourceTypeGpu:
leftQuotas.Gpu.Name = ResourceTypeGpu
leftQuotas.Gpu.Count = qcservice.IntValue(quotaLeftSet.Left)
case ResourceTypeMemory:
leftQuotas.Memory.Name = ResourceTypeMemory
leftQuotas.Memory.Count = qcservice.IntValue(quotaLeftSet.Left)
case ResourceTypeVolume:
leftQuotas.Volume.Name = ResourceTypeVolume
leftQuotas.Volume.Count = qcservice.IntValue(quotaLeftSet.Left)
case ResourceTypeVolumeSize:
leftQuotas.VolumeSize.Name = ResourceTypeVolumeSize
leftQuotas.VolumeSize.Count = qcservice.IntValue(quotaLeftSet.Left)
default:
p.Logger.Error("Unknown quota type: %s", qcservice.StringValue(quotaLeftSet.ResourceType))
}
}

err = needQuotas.LessThan(leftQuotas)
if err != nil {
p.Logger.Error("[%s] quota not enough: %+v", MyProvider, err)
return err
}

return nil
}

func (p *ProviderHandler) DescribeVpc(runtimeId, vpcId string) (*models.Vpc, error) {
Expand Down
14 changes: 7 additions & 7 deletions pkg/service/app/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func (p *Server) DeleteAppVersions(ctx context.Context, req *pb.DeleteAppVersion
Where(db.Eq(models.ColumnVersionId, versionIds)).
Exec()
if err != nil {
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDeleteResourcesFailed)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDeleteResourceFailed, strings.Join(versionIds, ","))
}

return &pb.DeleteAppVersionsResponse{
Expand All @@ -296,19 +296,19 @@ func (p *Server) GetAppVersionPackage(ctx context.Context, req *pb.GetAppVersion
versionId := req.GetVersionId().GetValue()
version, err := p.getAppVersion(versionId)
if err != nil {
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.NotFound, err, gerr.ErrorResourceNotFound, versionId)
}
logger.Debug("Got app version: [%+v]", version)
packageUrl := version.PackageName
resp, err := httputil.HttpGet(packageUrl)
if err != nil {
logger.Error("Failed to http get [%s], error: %+v", packageUrl, err)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourceFailed, versionId)
}
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Error("Failed to read http response [%s], error: %+v", packageUrl, err)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourceFailed, versionId)
}
return &pb.GetAppVersionPackageResponse{
Package: content,
Expand All @@ -322,18 +322,18 @@ func (p *Server) GetAppVersionPackageFiles(ctx context.Context, req *pb.GetAppVe
includeFiles := req.Files
version, err := p.getAppVersion(versionId)
if err != nil {
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.NotFound, err, gerr.ErrorResourceNotFound, versionId)
}
packageUrl := version.PackageName
resp, err := httputil.HttpGet(packageUrl)
if err != nil {
logger.Error("Failed to http get [%s], error: %+v", packageUrl, err)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourceFailed, versionId)
}
archiveFiles, err := gziputil.LoadArchive(resp.Body, includeFiles...)
if err != nil {
logger.Error("Failed to load package [%s] archive, error: %+v", packageUrl, err)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourcesFailed)
return nil, gerr.NewWithDetail(gerr.Internal, err, gerr.ErrorDescribeResourceFailed, versionId)
}
return &pb.GetAppVersionPackageFilesResponse{
Files: archiveFiles,
Expand Down
4 changes: 2 additions & 2 deletions pkg/service/cluster/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ func CheckVmBasedProvider(ctx context.Context, runtime *runtimeclient.Runtime, p
clusterWrapper.Cluster.VpcId = vpcId

// check resource quota
message, err := providerInterface.CheckResourceQuotas(ctx, clusterWrapper)
err = providerInterface.CheckResourceQuotas(ctx, clusterWrapper)
if err != nil {
return gerr.NewWithDetail(gerr.PermissionDenied, err, gerr.ErrorResourceQuotaNotEnough, message)
return gerr.NewWithDetail(gerr.PermissionDenied, err, gerr.ErrorResourceQuotaNotEnough, err.Error())
}

fg := &Frontgate{
Expand Down
1 change: 1 addition & 0 deletions pkg/service/cluster/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ func (p *Server) CreateCluster(ctx context.Context, req *pb.CreateClusterRequest
return nil, gerr.NewWithDetail(gerr.InvalidArgument, err, gerr.ErrorValidateFailed)
}

clusterWrapper.Cluster.RuntimeId = runtimeId
clusterWrapper.Cluster.Owner = s.UserId
clusterWrapper.Cluster.ClusterId = clusterId
clusterWrapper.Cluster.ClusterType = constants.NormalClusterType
Expand Down
89 changes: 0 additions & 89 deletions vendor/github.com/yunify/qingcloud-sdk-go/service/eip.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 39455ed

Please sign in to comment.