diff --git a/go.mod b/go.mod index c9b63f0bed..34bd68e60d 100644 --- a/go.mod +++ b/go.mod @@ -56,6 +56,7 @@ require ( github.com/golang-plus/errors v1.0.0 github.com/golang-plus/testing v1.0.0 // indirect github.com/golang-plus/uuid v1.0.0 + github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 github.com/google/gopacket v1.1.17 github.com/googleapis/gnostic v0.2.0 // indirect diff --git a/go.sum b/go.sum index 23e26b0e13..b4b6a23af3 100644 --- a/go.sum +++ b/go.sum @@ -361,6 +361,7 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/pkg/scheduler/api/sched.go b/pkg/scheduler/api/sched.go index 8c06d224c6..9307468f08 100644 --- a/pkg/scheduler/api/sched.go +++ b/pkg/scheduler/api/sched.go @@ -258,14 +258,17 @@ type ForecastFilter struct { Count int64 `json:"count"` } -type ForecastResult struct { - Candidate string `json:"candidate"` - Count int64 `json:"count"` - Capacity int64 `json:"capacity"` +type FilteredCandidate struct { + ID string `json:"id"` + Name string `json:"name"` + FilterName string `json:"filter_name"` + Reasons []string `json:"reasons"` } type SchedForecastResult struct { - CanCreate bool `json:"can_create"` - Filters []*ForecastFilter `json:"filters"` - Results []*api.CandidateResource `json:"results"` + CanCreate bool `json:"can_create"` + ReqCount int64 `json:"req_count"` + AllowCount int64 `json:"allow_count"` + NotAllowReasons []string `json:"not_allow_reasons"` + FilteredCandidates []FilteredCandidate `json:"filtered_candidates"` } diff --git a/pkg/scheduler/cache/candidate/base.go b/pkg/scheduler/cache/candidate/base.go index 5619f87b4f..40ab5f2e2f 100644 --- a/pkg/scheduler/cache/candidate/base.go +++ b/pkg/scheduler/cache/candidate/base.go @@ -30,11 +30,10 @@ import ( computemodels "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/scheduler/api" "yunion.io/x/onecloud/pkg/scheduler/core" + "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" schedmodels "yunion.io/x/onecloud/pkg/scheduler/models" ) -var ErrInstanceGroupNotFound = errors.Error("InstanceGroupNotFound") - type BaseHostDesc struct { *computemodels.SHost Region *computemodels.SCloudregion `json:"region"` @@ -126,6 +125,11 @@ func (b baseHostGetter) HostSchedtags() []computemodels.SSchedtag { return b.h.HostSchedtags } +func (b baseHostGetter) Sku(instanceType string) *sku.ServerSku { + zone := b.Zone() + return sku.GetByZone(instanceType, zone.GetId()) +} + func (b baseHostGetter) Storages() []*api.CandidateStorage { return b.h.Storages } @@ -138,7 +142,7 @@ func (b baseHostGetter) GetFreeGroupCount(groupId string) (int, error) { // Must Be scg, ok := b.h.InstanceGroups[groupId] if !ok { - return 0, errors.Wrap(ErrInstanceGroupNotFound, groupId) + return 0, errors.Wrap(core.ErrInstanceGroupNotFound, groupId) } free := scg.Granularity - scg.ReferCount if free < 1 { diff --git a/pkg/scheduler/core/generic_scheduler.go b/pkg/scheduler/core/generic_scheduler.go index db72c0a9b1..e3d90498d9 100644 --- a/pkg/scheduler/core/generic_scheduler.go +++ b/pkg/scheduler/core/generic_scheduler.go @@ -15,7 +15,6 @@ package core import ( - "encoding/json" "fmt" "sort" "strings" @@ -29,9 +28,6 @@ import ( utiltrace "yunion.io/x/pkg/util/trace" "yunion.io/x/pkg/util/workqueue" - "yunion.io/x/onecloud/pkg/apis/compute" - schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" - "yunion.io/x/onecloud/pkg/scheduler/api" o "yunion.io/x/onecloud/pkg/scheduler/options" ) @@ -104,7 +100,7 @@ func NewGenericScheduler(s Scheduler) (*GenericScheduler, error) { return g, nil } -func (g *GenericScheduler) Schedule(unit *Unit, candidates []Candidater) (*SchedResultItemList, error) { +func (g *GenericScheduler) Schedule(unit *Unit, candidates []Candidater, helper IResultHelper) (*ScheduleResult, error) { startTime := time.Now() defer func() { log.V(4).Infof("Schedule cost time: %v", time.Since(startTime)) @@ -171,7 +167,8 @@ func (g *GenericScheduler) Schedule(unit *Unit, candidates []Candidater) (*Sched return nil, err } - return &SchedResultItemList{Unit: unit, Data: resultItems}, nil + itemList := &SchedResultItemList{Unit: unit, Data: resultItems} + return helper.ResultHelp(itemList, unit.SchedInfo), nil } func newSchedResultByCtx(u *Unit, count int64, c Candidater) *SchedResultItem { @@ -238,24 +235,6 @@ func generateScheduleResult(u *Unit, scs []*SelectedCandidate, cs []Candidater) return results, nil } -type SchedResultItem struct { - ID string `json:"id"` - Name string `json:"name"` - Count int64 `json:"count"` - Data map[string]interface{} `json:"data"` - Capacity int64 `json:"capacity"` - Score Score `json:"score"` - - CapacityDetails map[string]int64 `json:"capacity_details"` - ScoreDetails string `json:"score_details"` - - Candidater Candidater `json:"-"` - - *AllocatedResource - - SchedData *api.SchedInfo -} - type StorageUsed struct { used map[string]int64 } @@ -285,27 +264,6 @@ func (s *StorageUsed) Add(storageId string, used int64) { } } -func (item *SchedResultItem) ToCandidateResource(storageUsed *StorageUsed) *schedapi.CandidateResource { - return &schedapi.CandidateResource{ - HostId: item.ID, - Name: item.Name, - Disks: item.getDisks(storageUsed), - Nets: item.Nets, - } -} - -func (item *SchedResultItem) getDisks(used *StorageUsed) []*schedapi.CandidateDisk { - inputs := item.SchedData.Disks - ret := make([]*schedapi.CandidateDisk, 0) - for idx, disk := range item.Disks { - ret = append(ret, &schedapi.CandidateDisk{ - Index: idx, - StorageIds: item.getSortStorageIds(used, inputs[idx], disk.Storages), - }) - } - return ret -} - type sortStorage struct { Id string FeeSize int64 @@ -335,31 +293,6 @@ func (s sortStorages) getIds() []string { return ret } -func (item *SchedResultItem) getSortStorageIds( - used *StorageUsed, - disk *compute.DiskConfig, - storages []*schedapi.CandidateStorage) []string { - reqSize := disk.SizeMb - ss := make([]sortStorage, 0) - for _, s := range storages { - ss = append(ss, sortStorage{ - Id: s.Id, - FeeSize: s.FreeCapacity - used.Get(s.Id), - }) - } - toSort := sortStorages(ss) - sort.Sort(toSort) - sortedStorages := toSort.getIds() - ret := make([]string, 0) - for idx, id := range sortedStorages { - if idx == 0 { - used.Add(id, int64(reqSize)) - } - ret = append(ret, id) - } - return ret -} - func GetCapacities(u *Unit, id string) (res map[string]int64) { res = make(map[string]int64) capacities := u.GetCapacities(id) @@ -371,51 +304,6 @@ func GetCapacities(u *Unit, id string) (res map[string]int64) { return } -type SchedResultItems []*SchedResultItem - -func (its SchedResultItems) Len() int { - return len(its) -} - -func (its SchedResultItems) Swap(i, j int) { - its[i], its[j] = its[j], its[i] -} - -func (its SchedResultItems) Less(i, j int) bool { - it1, it2 := its[i], its[j] - return it1.Capacity < it2.Capacity - /* - ctx := its.Unit - - m := func(c int64) int64 { - if c > 0 { - return 1 - } - return 0 - } - - v := func(count, capacity, score int64) int64 { - return (m(count) << 42) | (m(capacity) << 21) | score - } - - count1, count2 := it1.Count, it2.Count - capacity1, capacity2 := ctx.GetCapacity(it1.ID), ctx.GetCapacity(it2.ID) - score1, score2 := int64(ctx.GetScore(it1.ID)), int64(ctx.GetScore(it2.ID)) - - return v(count1, capacity1, score1) < v(count2, capacity2, score2) - */ -} - -type SchedResultItemList struct { - Unit *Unit - Data SchedResultItems -} - -func (its SchedResultItemList) String() string { - bytes, _ := json.Marshal(its.Data) - return string(bytes) -} - type SelectedCandidate struct { Count int64 Candidate Candidater @@ -536,7 +424,11 @@ func findCandidatesThatFit(unit *Unit, candidates []Candidater, predicates map[s unit.AppendFailedCandidates(fcs) } } - workqueue.Parallelize(o.GetOptions().PredicateParallelizeSize, len(candidates), checkUnit) + workerSize := o.GetOptions().PredicateParallelizeSize + if workerSize == 0 { + workerSize = 1 + } + workqueue.Parallelize(workerSize, len(candidates), checkUnit) filtered = filtered[:filteredLen] if len(errsChannel) > 0 { errs := make([]error, 0) diff --git a/pkg/scheduler/manager/instancegroup_select.go b/pkg/scheduler/core/instancegroup_select.go similarity index 94% rename from pkg/scheduler/manager/instancegroup_select.go rename to pkg/scheduler/core/instancegroup_select.go index 969d71db24..242c272c02 100644 --- a/pkg/scheduler/manager/instancegroup_select.go +++ b/pkg/scheduler/core/instancegroup_select.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package manager +package core import ( "fmt" @@ -26,12 +26,10 @@ import ( schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/scheduler/api" - "yunion.io/x/onecloud/pkg/scheduler/cache/candidate" - "yunion.io/x/onecloud/pkg/scheduler/core" "yunion.io/x/onecloud/pkg/scheduler/core/score" ) -func transToInstanceGroupSchedResult(result *core.SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput { +func transToInstanceGroupSchedResult(result *SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput { for _, item := range result.Data { item.Count = 0 } @@ -50,7 +48,7 @@ type sGuestInfo struct { } type sSchedResultItem struct { - *core.SchedResultItem + *SchedResultItem instanceGroupCapacity map[string]int64 masterCount int64 backupCount int64 @@ -69,7 +67,7 @@ func (item *sSchedResultItem) minInstanceGroupCapacity(groupSet map[string]*mode return mincapa } -func buildHosts(result *core.SchedResultItemList, groups map[string]*models.SGroup) []*sSchedResultItem { +func buildHosts(result *SchedResultItemList, groups map[string]*models.SGroup) []*sSchedResultItem { hosts := make([]*sSchedResultItem, result.Data.Len()) for i := 0; i < len(result.Data); i++ { getter := result.Data[i].Candidater.Getter() @@ -77,7 +75,7 @@ func buildHosts(result *core.SchedResultItemList, groups map[string]*models.SGro for id, group := range groups { c, err := getter.GetFreeGroupCount(id) if err != nil { - if errors.Cause(err) == candidate.ErrInstanceGroupNotFound { + if errors.Cause(err) == ErrInstanceGroupNotFound { igCapacity[id] = int64(group.Granularity) } else { igCapacity[id] = 0 @@ -124,7 +122,7 @@ func sortHosts(hosts []*sSchedResultItem, guestInfo *sGuestInfo, isBackup *bool) // scoreNormalization compare the value of s1 and s2. // If s1 is less than s2, return 1, 0 which means s2 is better than s1. -func scoreNormalization(s1, s2 core.Score) (int64, int64) { +func scoreNormalization(s1, s2 Score) (int64, int64) { sb1, sb2 := s1.ScoreBucket, s2.ScoreBucket preferLess := score.PreferLess(sb1, sb2) avoidLess := score.AvoidLess(sb1, sb2) @@ -213,7 +211,8 @@ func hostsIndex(hostId string, hosts []*sSchedResultItem) int { // getBackupSchedResult return the ScheduleOutput for guest without backup func getSchedResult(hosts []*sSchedResultItem, guestInfos []sGuestInfo, sid string) *schedapi.ScheduleOutput { apiResults := make([]*schedapi.CandidateResource, 0) - storageUsed := core.NewStorageUsed() + storageUsed := + NewStorageUsed() var i int = 0 for ; i < len(guestInfos); i++ { host := selectHost(hosts, guestInfos[i], nil, true) @@ -244,7 +243,7 @@ func getBackupSchedResult(hosts []*sSchedResultItem, guestInfos, backGuestInfos wireHostMap := buildWireHosts(hosts) apiResults := make([]*schedapi.CandidateResource, 0, len(guestInfos)) nowireIds := sets.NewString() - storageUsed := core.NewStorageUsed() + storageUsed := NewStorageUsed() isBackup := true isMaster := false for i := 0; i < len(guestInfos); i++ { diff --git a/pkg/scheduler/core/result.go b/pkg/scheduler/core/result.go new file mode 100644 index 0000000000..e7a0c0eac6 --- /dev/null +++ b/pkg/scheduler/core/result.go @@ -0,0 +1,122 @@ +// Copyright 2019 Yunion +// +// 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 core + +import ( + "encoding/json" + "sort" + + "yunion.io/x/onecloud/pkg/apis/compute" + schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" + "yunion.io/x/onecloud/pkg/scheduler/api" +) + +type ScheduleResult struct { + // Result is sync schedule result + Result *schedapi.ScheduleOutput + // ForecastResult is forecast schedule result + ForecastResult *api.SchedForecastResult + // TestResult is test schedule result + TestResult interface{} +} + +type SchedResultItem struct { + ID string `json:"id"` + Name string `json:"name"` + Count int64 `json:"count"` + Data map[string]interface{} `json:"data"` + Capacity int64 `json:"capacity"` + Score Score `json:"score"` + + CapacityDetails map[string]int64 `json:"capacity_details"` + ScoreDetails string `json:"score_details"` + + Candidater Candidater `json:"-"` + + *AllocatedResource + + SchedData *api.SchedInfo +} + +type SchedResultItemList struct { + Unit *Unit + Data SchedResultItems +} + +func (its SchedResultItemList) String() string { + bytes, _ := json.Marshal(its.Data) + return string(bytes) +} + +type SchedResultItems []*SchedResultItem + +func (its SchedResultItems) Len() int { + return len(its) +} + +func (its SchedResultItems) Swap(i, j int) { + its[i], its[j] = its[j], its[i] +} + +func (its SchedResultItems) Less(i, j int) bool { + it1, it2 := its[i], its[j] + return it1.Capacity < it2.Capacity +} + +func (item *SchedResultItem) ToCandidateResource(storageUsed *StorageUsed) *schedapi.CandidateResource { + return &schedapi.CandidateResource{ + HostId: item.ID, + Name: item.Name, + Disks: item.getDisks(storageUsed), + Nets: item.Nets, + } +} + +func (item *SchedResultItem) getDisks(used *StorageUsed) []*schedapi.CandidateDisk { + inputs := item.SchedData.Disks + ret := make([]*schedapi.CandidateDisk, 0) + for idx, disk := range item.Disks { + ret = append(ret, &schedapi.CandidateDisk{ + Index: idx, + StorageIds: item.getSortStorageIds(used, inputs[idx], disk.Storages), + }) + } + return ret +} + +func (item *SchedResultItem) getSortStorageIds( + used *StorageUsed, + disk *compute.DiskConfig, + storages []*schedapi.CandidateStorage) []string { + reqSize := disk.SizeMb + ss := make([]sortStorage, 0) + for _, s := range storages { + ss = append(ss, sortStorage{ + Id: s.Id, + FeeSize: s.FreeCapacity - used.Get(s.Id), + }) + } + toSort := sortStorages(ss) + sort.Sort(toSort) + sortedStorages := toSort.getIds() + ret := make([]string, 0) + for idx, id := range sortedStorages { + if idx == 0 { + used.Add(id, int64(reqSize)) + } + ret = append(ret, id) + } + return ret +} diff --git a/pkg/scheduler/core/result_helper.go b/pkg/scheduler/core/result_helper.go new file mode 100644 index 0000000000..cc0527610d --- /dev/null +++ b/pkg/scheduler/core/result_helper.go @@ -0,0 +1,175 @@ +// Copyright 2019 Yunion +// +// 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 core + +import ( + "fmt" + + "yunion.io/x/log" + + schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" + "yunion.io/x/onecloud/pkg/scheduler/api" +) + +func ResultHelp(result *SchedResultItemList, schedInfo *api.SchedInfo) *ScheduleResult { + out := new(ScheduleResult) + out.Result = transToSchedResult(result, schedInfo) + return out +} + +func ResultHelpForForcast(result *SchedResultItemList, _ *api.SchedInfo) *ScheduleResult { + out := new(ScheduleResult) + out.ForecastResult = transToSchedForecastResult(result) + return out +} + +func ResultHelpForTest(result *SchedResultItemList, schedInfo *api.SchedInfo) *ScheduleResult { + out := new(ScheduleResult) + out.TestResult = transToSchedTestResult(result, schedInfo.SuggestionLimit) + return out +} + +type IResultHelper interface { + ResultHelp(result *SchedResultItemList, schedInfo *api.SchedInfo) *ScheduleResult +} + +// ResultHelperFunc type is an adapter to allwo the use of ordinary functions as a ResultHelper. +// If f is a function with the appropriate signature, ResultHelperFunc(f) is a ResultHelper that calls f. +type SResultHelperFunc func(result *SchedResultItemList, schedInfo *api.SchedInfo) *ScheduleResult + +func (r SResultHelperFunc) ResultHelp(result *SchedResultItemList, schedInfo *api.SchedInfo) *ScheduleResult { + return r(result, schedInfo) +} + +func transToSchedResult(result *SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput { + if schedInfo.Backup || len(schedInfo.InstanceGroupsDetail) > 0 { + return transToInstanceGroupSchedResult(result, schedInfo) + } else { + return transToRegionSchedResult(result.Data, int64(schedInfo.Count), schedInfo.SessionId) + } +} + +func transToRegionSchedResult(result SchedResultItems, count int64, sid string) *schedapi.ScheduleOutput { + apiResults := make([]*schedapi.CandidateResource, 0) + succCount := 0 + storageUsed := NewStorageUsed() + for _, nr := range result { + for { + if nr.Count <= 0 { + break + } + tr := nr.ToCandidateResource(storageUsed) + tr.SessionId = sid + apiResults = append(apiResults, tr) + nr.Count-- + succCount++ + } + } + + for { + if int64(succCount) >= count { + break + } + er := &schedapi.CandidateResource{Error: "Out of resource"} + apiResults = append(apiResults, er) + succCount++ + } + + return &schedapi.ScheduleOutput{ + Candidates: apiResults, + } +} + +func hostInResultItemsIndex(hostId string, hosts SchedResultItems) int { + for i := 0; i < len(hosts); i++ { + if hosts[i].ID == hostId { + return i + } + } + return -1 +} + +func transToSchedTestResult(result *SchedResultItemList, limit int64) interface{} { + return &api.SchedTestResult{ + Data: result.Data, + Total: int64(result.Data.Len()), + Limit: limit, + Offset: 0, + } +} + +func transToSchedForecastResult(result *SchedResultItemList) *api.SchedForecastResult { + unit := result.Unit + schedData := unit.SchedData() + filteredCandidates := make([]api.FilteredCandidate, 0) + ret := &api.SchedForecastResult{ + ReqCount: int64(schedData.Count), + } + + // build filteredCandidates + failedLogs := unit.LogManager.FailedLogs() + logIndex := func(item *SchedResultItem) string { + getter := item.Candidater.Getter() + name := getter.Name() + id := getter.Id() + return fmt.Sprintf("%s:%s", name, id) + } + for _, item := range result.Data { + if item.Capacity > 0 { + continue + } + filteredCandidate := api.FilteredCandidate{ + ID: item.ID, + Name: item.Name, + } + for preName, capa := range item.CapacityDetails { + if capa > 0 { + continue + } + filteredCandidate.FilterName = preName + } + failedLog := failedLogs.Get(logIndex(item)) + if failedLog == nil { + log.Errorf("candidate %q capacity is 0 but no failedLog found", item.Name) + continue + } + for _, msg := range failedLog.Messages { + filteredCandidate.Reasons = append(filteredCandidate.Reasons, msg.Info) + } + filteredCandidates = append(filteredCandidates, filteredCandidate) + } + ret.FilteredCandidates = filteredCandidates + + var ( + output = transToSchedResult(result, schedData) + readyCount int64 + ) + for _, candi := range output.Candidates { + if len(candi.Error) == 0 { + readyCount++ + continue + } + ret.NotAllowReasons = append(ret.NotAllowReasons, candi.Error) + } + + ret.AllowCount = readyCount + if ret.AllowCount < ret.ReqCount { + ret.CanCreate = false + } else { + ret.CanCreate = true + } + + return ret +} diff --git a/pkg/scheduler/core/types.go b/pkg/scheduler/core/types.go index 841e081cc1..d4edd9004a 100644 --- a/pkg/scheduler/core/types.go +++ b/pkg/scheduler/core/types.go @@ -18,6 +18,7 @@ import ( "strings" "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" computeapi "yunion.io/x/onecloud/pkg/apis/compute" schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" @@ -27,6 +28,7 @@ import ( computemodels "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/scheduler/api" "yunion.io/x/onecloud/pkg/scheduler/core/score" + "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" schedmodels "yunion.io/x/onecloud/pkg/scheduler/models" ) @@ -34,6 +36,8 @@ const ( PriorityStep int = 1 ) +var ErrInstanceGroupNotFound = errors.Error("InstanceGroupNotFound") + type FailedCandidate struct { Stage string Candidate Candidater @@ -71,6 +75,7 @@ type CandidatePropertyGetter interface { Region() *computemodels.SCloudregion HostType() string HostSchedtags() []computemodels.SSchedtag + Sku(string) *sku.ServerSku Storages() []*api.CandidateStorage Networks() []*api.CandidateNetwork OvnCapable() bool diff --git a/pkg/scheduler/manager/manager.go b/pkg/scheduler/manager/manager.go index 80469a9c78..370c3360ad 100644 --- a/pkg/scheduler/manager/manager.go +++ b/pkg/scheduler/manager/manager.go @@ -96,7 +96,7 @@ func (sm *SchedulerManager) start() { } } -func (sm *SchedulerManager) schedule(info *api.SchedInfo) (*ScheduleResult, error) { +func (sm *SchedulerManager) schedule(info *api.SchedInfo) (*core.ScheduleResult, error) { log.V(10).Infof("SchedulerManager do schedule, input: %#v", info) task, err := sm.TaskManager.AddTask(sm, info) if err != nil { @@ -121,7 +121,7 @@ func NewSessionID() string { // Schedule process the request data that is scheduled for dispatch and complements // the session information. -func Schedule(info *api.SchedInfo) (*ScheduleResult, error) { +func Schedule(info *api.SchedInfo) (*core.ScheduleResult, error) { if len(info.SessionId) == 0 { info.SessionId = NewSessionID() } diff --git a/pkg/scheduler/manager/result_helper.go b/pkg/scheduler/manager/result_helper.go deleted file mode 100644 index 8ded1924ff..0000000000 --- a/pkg/scheduler/manager/result_helper.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2019 Yunion -// -// 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 manager - -import ( - "fmt" - - "yunion.io/x/log" - - schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" - computemodels "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/onecloud/pkg/scheduler/api" - "yunion.io/x/onecloud/pkg/scheduler/core" - schedmodels "yunion.io/x/onecloud/pkg/scheduler/models" -) - -func transToSchedResult(result *core.SchedResultItemList, schedInfo *api.SchedInfo) *schedapi.ScheduleOutput { - if schedInfo.Backup || len(schedInfo.InstanceGroupsDetail) > 0 { - return transToInstanceGroupSchedResult(result, schedInfo) - } else { - return transToRegionSchedResult(result.Data, int64(schedInfo.Count), schedInfo.SessionId) - } -} - -func setSchedPendingUsage(driver computemodels.IGuestDriver, req *api.SchedInfo, resp *schedapi.ScheduleOutput) error { - if req.IsSuggestion || IsDriverSkipScheduleDirtyMark(driver) || req.SkipDirtyMarkHost() { - return nil - } - for _, item := range resp.Candidates { - schedmodels.HostPendingUsageManager.AddPendingUsage(req, item) - } - return nil -} - -func IsDriverSkipScheduleDirtyMark(driver computemodels.IGuestDriver) bool { - return !(driver.DoScheduleCPUFilter() && driver.DoScheduleMemoryFilter() && driver.DoScheduleStorageFilter()) -} - -func transToRegionSchedResult(result core.SchedResultItems, count int64, sid string) *schedapi.ScheduleOutput { - apiResults := make([]*schedapi.CandidateResource, 0) - succCount := 0 - storageUsed := core.NewStorageUsed() - for _, nr := range result { - for { - if nr.Count <= 0 { - break - } - tr := nr.ToCandidateResource(storageUsed) - tr.SessionId = sid - apiResults = append(apiResults, tr) - nr.Count-- - succCount++ - } - } - - for { - if int64(succCount) >= count { - break - } - er := &schedapi.CandidateResource{Error: "Out of resource"} - apiResults = append(apiResults, er) - succCount++ - } - - return &schedapi.ScheduleOutput{ - Candidates: apiResults, - } -} - -func hostInResultItemsIndex(hostId string, hosts core.SchedResultItems) int { - for i := 0; i < len(hosts); i++ { - if hosts[i].ID == hostId { - return i - } - } - return -1 -} - -func transToSchedTestResult(result *core.SchedResultItemList, limit int64) interface{} { - return &api.SchedTestResult{ - Data: result.Data, - Total: int64(result.Data.Len()), - Limit: limit, - Offset: 0, - } -} - -func transToSchedForecastResult(result *core.SchedResultItemList) interface{} { - unit := result.Unit - schedData := unit.SchedData() - reqCount := int64(schedData.Count) - filters := make([]*api.ForecastFilter, 0) - - filtersMap := make(map[string]*api.ForecastFilter) - getOrNewFilter := func(preName string) (*api.ForecastFilter, bool) { - if info, ok := filtersMap[preName]; !ok { - i := &api.ForecastFilter{ - Filter: preName, - Count: 0, - Messages: make([]string, 0), - } - filtersMap[preName] = i - return i, false - } else { - return info, true - } - } - - logIndex := func(item *core.SchedResultItem) string { - getter := item.Candidater.Getter() - name := getter.Name() - id := getter.Id() - return fmt.Sprintf("%s:%s", name, id) - } - addInfos := func(logs core.SchedLogList, item *core.SchedResultItem) { - for preName, cnt := range item.CapacityDetails { - if cnt > 0 { - continue - } - failedLog := logs.Get(logIndex(item)) - if failedLog == nil { - log.Errorf("predicate %q count is 0, but not found failed log", preName) - continue - } - for _, msg := range failedLog.Messages { - info, exist := getOrNewFilter(msg.Type) - info.Count++ - info.Messages = append(info.Messages, msg.Info) - if !exist { - filters = append(filters, info) - } - } - } - } - - items := make(core.SchedResultItems, 0) - for _, item := range result.Data { - hostType := item.Candidater.Getter().HostType() - if schedData.Hypervisor == hostType { - items = append(items, item) - } - } - - for _, item := range items { - addInfos(result.Unit.LogManager.FailedLogs(), item) - } - - var ( - output = transToSchedResult(result, schedData) - readyCount int64 - ) - - for _, candi := range output.Candidates { - if len(candi.Error) != 0 { - info, exist := getOrNewFilter("select_candidate") - msg := candi.Error - info.Messages = append(info.Messages, msg) - if !exist { - filters = append(filters, info) - } - } else { - readyCount++ - } - } - - canCreate := true - if readyCount < reqCount { - canCreate = false - filters = append(filters, &api.ForecastFilter{ - Messages: []string{ - fmt.Sprintf("No enough resources: %d/%d(free/request)", readyCount, reqCount), - }, - }) - } - return &api.SchedForecastResult{ - CanCreate: canCreate, - Filters: filters, - Results: output.Candidates, - } -} diff --git a/pkg/scheduler/manager/task_queue.go b/pkg/scheduler/manager/task_queue.go index 2f9dc9b502..57072d93e4 100644 --- a/pkg/scheduler/manager/task_queue.go +++ b/pkg/scheduler/manager/task_queue.go @@ -24,8 +24,10 @@ import ( "yunion.io/x/pkg/errors" schedapi "yunion.io/x/onecloud/pkg/apis/scheduler" + computemodels "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/scheduler/api" "yunion.io/x/onecloud/pkg/scheduler/core" + schedmodels "yunion.io/x/onecloud/pkg/scheduler/models" ) const ( @@ -47,7 +49,7 @@ type TaskExecutor struct { callback TaskExecuteCallback unit *core.Unit - resultItems *ScheduleResult + resultItems *core.ScheduleResult resultError error logs []string capacityMap interface{} @@ -81,16 +83,7 @@ func (te *TaskExecutor) Execute() { } } -type ScheduleResult struct { - // Result is sync schedule result - Result *schedapi.ScheduleOutput - // ForecastResult is forecast schedule result - ForecastResult interface{} - // TestResult is test schedule result - TestResult interface{} -} - -func (te *TaskExecutor) execute() (*ScheduleResult, error) { +func (te *TaskExecutor) execute() (*core.ScheduleResult, error) { scheduler := te.scheduler genericScheduler, err := core.NewGenericScheduler(scheduler.(core.Scheduler)) if err != nil { @@ -105,25 +98,43 @@ func (te *TaskExecutor) execute() (*ScheduleResult, error) { te.unit = scheduler.Unit() schedInfo := te.unit.SchedInfo - result, err := genericScheduler.Schedule(te.unit, candidates) + helper := GenerateResultHelper(schedInfo) + result, err := genericScheduler.Schedule(te.unit, candidates, helper) if err != nil { return nil, errors.Wrap(err, "genericScheduler.Schedule") } - out := new(ScheduleResult) if schedInfo.IsSuggestion { - if schedInfo.ShowSuggestionDetails && schedInfo.SuggestionAll { - out.ForecastResult = transToSchedForecastResult(result) - } else { - out.TestResult = transToSchedTestResult(result, schedInfo.SuggestionLimit) - } - } else { - out.Result = transToSchedResult(result, schedInfo) - driver := te.unit.GetHypervisorDriver() - if err := setSchedPendingUsage(driver, schedInfo, out.Result); err != nil { - return nil, errors.Wrap(err, "setSchedPendingUsage") - } + return result, nil } - return out, nil + driver := te.unit.GetHypervisorDriver() + if err := setSchedPendingUsage(driver, schedInfo, result.Result); err != nil { + return nil, errors.Wrap(err, "setSchedPendingUsage") + } + return result, nil +} + +func GenerateResultHelper(schedInfo *api.SchedInfo) core.IResultHelper { + if !schedInfo.IsSuggestion { + return core.SResultHelperFunc(core.ResultHelp) + } + if schedInfo.ShowSuggestionDetails && schedInfo.SuggestionAll { + return core.SResultHelperFunc(core.ResultHelpForForcast) + } + return core.SResultHelperFunc(core.ResultHelpForTest) +} + +func setSchedPendingUsage(driver computemodels.IGuestDriver, req *api.SchedInfo, resp *schedapi.ScheduleOutput) error { + if req.IsSuggestion || IsDriverSkipScheduleDirtyMark(driver) || req.SkipDirtyMarkHost() { + return nil + } + for _, item := range resp.Candidates { + schedmodels.HostPendingUsageManager.AddPendingUsage(req, item) + } + return nil +} + +func IsDriverSkipScheduleDirtyMark(driver computemodels.IGuestDriver) bool { + return !(driver.DoScheduleCPUFilter() && driver.DoScheduleMemoryFilter() && driver.DoScheduleStorageFilter()) } func (te *TaskExecutor) cleanup() { @@ -138,7 +149,7 @@ func (te *TaskExecutor) Kill() { } } -func (te *TaskExecutor) GetResult() (*ScheduleResult, error) { +func (te *TaskExecutor) GetResult() (*core.ScheduleResult, error) { return te.resultItems, te.resultError } @@ -299,7 +310,7 @@ type Task struct { waitCh chan struct{} completedCount int - resultItems *ScheduleResult + resultItems *core.ScheduleResult resultError error } @@ -393,12 +404,12 @@ func (t *Task) onCompleted() { close(t.waitCh) } -func (t *Task) Wait() (*ScheduleResult, error) { +func (t *Task) Wait() (*core.ScheduleResult, error) { log.V(10).Infof("Task wait...") <-t.waitCh return t.GetResult() } -func (t *Task) GetResult() (*ScheduleResult, error) { +func (t *Task) GetResult() (*core.ScheduleResult, error) { return t.resultItems, t.resultError } diff --git a/pkg/scheduler/test/doc.go b/pkg/scheduler/test/doc.go new file mode 100644 index 0000000000..2f9f6c11b1 --- /dev/null +++ b/pkg/scheduler/test/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// 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 test // import "yunion.io/x/onecloud/pkg/scheduler/test" diff --git a/pkg/scheduler/test/generic_scheduler_test.go b/pkg/scheduler/test/generic_scheduler_test.go new file mode 100644 index 0000000000..87fda02e6b --- /dev/null +++ b/pkg/scheduler/test/generic_scheduler_test.go @@ -0,0 +1,191 @@ +// Copyright 2019 Yunion +// +// 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 test + +import ( + "reflect" + "testing" + + "github.com/golang/mock/gomock" + + "yunion.io/x/onecloud/pkg/apis/compute" + apisdu "yunion.io/x/onecloud/pkg/apis/scheduler" + _ "yunion.io/x/onecloud/pkg/compute/guestdrivers" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/scheduler/api" + "yunion.io/x/onecloud/pkg/scheduler/core" +) + +func TestGenericSchedulerSchedule(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + scheduler, err := core.NewGenericScheduler(buildScheduler(ctrl, basePredicateNames...)) + if err != nil { + t.Errorf("NewGenericScheduler: %s", err.Error()) + return + } + commonInfo := &api.SchedInfo{ + ScheduleInput: &apisdu.ScheduleInput{ + ServerConfig: apisdu.ServerConfig{ + ServerConfigs: &compute.ServerConfigs{ + PreferRegion: GlobalCloudregion.GetId(), + PreferZone: GlobalZone.GetId(), + PreferHost: "host01", + Hypervisor: "hypervisor", + ResourceType: "shared", + InstanceType: "ecs.g1.c1m1", + Sku: "ecs.g1.c1m1", + Backup: false, + Count: 1, + Disks: []*compute.DiskConfig{ + { + Backend: "local", + DiskType: "sys", + ImageId: "CentOS7.6", + Index: 0, + SizeMb: 30720, + }, + { + Backend: "local", + DiskType: "data", + Index: 1, + SizeMb: 10240, + }, + }, + Networks: []*compute.NetworkConfig{ + { + Index: 0, + Network: "network01", + Domain: GlobalDoamin, + }, + }, + BaremetalDiskConfigs: []*compute.BaremetalDiskConfig{ + { + Type: "hybrid", + Conf: "none", + Count: 0, + }, + }, + InstanceGroupIds: []string{ + "instancegroup01", + }, + }, + Memory: 1024, + Ncpu: 1, + Project: GlobalProject, + Domain: GlobalDoamin, + }, + }, + PreferCandidates: []string{ + "host01", + }, + RequiredCandidates: 1, + InstanceGroupsDetail: map[string]*models.SGroup{ + "instancegroup01": buildInstanceGroup("instancegroup01", 1, true), + }, + } + + t.Run("Base test", func(t *testing.T) { + info := deepCopy(commonInfo) + getterParam := sGetterParams{ + HostId: "host01", + HostName: "host01name", + Domain: "default", + PublicScope: "system", + Zone: buildZone("zone01", ""), + CloudRegion: buildCloudregion("default", "", ""), + HostType: api.HostHypervisorForKvm, + Storages: []*api.CandidateStorage{buildStorage("storage01", "", 201330)}, + Networks: []*api.CandidateNetwork{buildNetwork("network01", "", "192.168.1.0/24")}, + TotalCPUCount: 8, + FreeCPUCount: 8, + TotalMemorySize: 10240, + FreeMemorySize: 10240, + FreeStorageSizeAnyType: 201330, + FreePort: 10, + FreeGroupCount: 1, + Skus: []string{"ecs.g1.c1m1"}, + } + candidate := buildCandidate(ctrl, getterParam) + _, err := scheduler.Schedule(preSchedule(info, []core.Candidater{candidate}, false)) + if err != nil { + t.Errorf("genericScheduler.Schedule error: %s", err.Error()) + } + }) + t.Run("Forcast schedule: no match specified network", func(t *testing.T) { + info := deepCopy(commonInfo) + info.PreferHost = "" + info.PreferCandidates = []string{} + info.InstanceGroupIds = []string{} + info.InstanceGroupsDetail = make(map[string]*models.SGroup) + info.Count = 2 + getterParam1 := sGetterParams{ + HostId: "host01", + HostName: "host01name", + Domain: "default", + PublicScope: "system", + Zone: buildZone("zone01", ""), + CloudRegion: buildCloudregion("default", "", ""), + HostType: api.HostHypervisorForKvm, + Storages: []*api.CandidateStorage{buildStorage("storage01", "", 201330)}, + Networks: []*api.CandidateNetwork{ + buildNetwork("network01", "nework01name", "192.168.1.0/24"), + buildNetwork("network02", "nework02name", "192.168.2.0/24"), + }, + TotalCPUCount: 8, + FreeCPUCount: 8, + TotalMemorySize: 10240, + FreeMemorySize: 10240, + FreeStorageSizeAnyType: 201330, + FreePort: 1, + Skus: []string{"ecs.g1.c1m1"}, + } + getterParam2 := getterParam1 + getterParam2.HostId = "host02" + getterParam2.HostName = "host02name" + getterParam2.Networks = []*api.CandidateNetwork{ + buildNetwork("network03", "nework03name", "192.168.3.0/24"), + buildNetwork("network04", "nework04name", "192.168.4.0/24"), + } + candidates := []core.Candidater{ + buildCandidate(ctrl, getterParam1), + buildCandidate(ctrl, getterParam2), + } + res, err := scheduler.Schedule(preSchedule(info, candidates, true)) + if err != nil { + t.Errorf("genericScheduler.Schedule error: %s", err.Error()) + } + forcastResult := &api.SchedForecastResult{ + CanCreate: false, + AllowCount: 1, + ReqCount: 2, + NotAllowReasons: []string{"Out of resource"}, + FilteredCandidates: []api.FilteredCandidate{ + { + FilterName: "host_network", + ID: "host02", + Name: "host02name", + Reasons: []string{ + "nework03name(network03): id/name not matched", + "nework04name(network04): id/name not matched", + }, + }, + }, + } + if !reflect.DeepEqual(res.ForecastResult, forcastResult) { + t.Errorf("want: %v, real: %v", forcastResult, res.ForecastResult) + } + }) +} diff --git a/pkg/scheduler/test/mock/core.go b/pkg/scheduler/test/mock/core.go new file mode 100644 index 0000000000..61b7788645 --- /dev/null +++ b/pkg/scheduler/test/mock/core.go @@ -0,0 +1,871 @@ +// 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 mock + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + + jsonutils "yunion.io/x/jsonutils" + + types "yunion.io/x/onecloud/pkg/cloudcommon/types" + baremetal "yunion.io/x/onecloud/pkg/compute/baremetal" + models "yunion.io/x/onecloud/pkg/compute/models" + api "yunion.io/x/onecloud/pkg/scheduler/api" + core "yunion.io/x/onecloud/pkg/scheduler/core" + sku "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" + models0 "yunion.io/x/onecloud/pkg/scheduler/models" +) + +// MockCandidatePropertyGetter is a mock of CandidatePropertyGetter interface +type MockCandidatePropertyGetter struct { + ctrl *gomock.Controller + recorder *MockCandidatePropertyGetterMockRecorder +} + +// MockCandidatePropertyGetterMockRecorder is the mock recorder for MockCandidatePropertyGetter +type MockCandidatePropertyGetterMockRecorder struct { + mock *MockCandidatePropertyGetter +} + +// NewMockCandidatePropertyGetter creates a new mock instance +func NewMockCandidatePropertyGetter(ctrl *gomock.Controller) *MockCandidatePropertyGetter { + mock := &MockCandidatePropertyGetter{ctrl: ctrl} + mock.recorder = &MockCandidatePropertyGetterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockCandidatePropertyGetter) EXPECT() *MockCandidatePropertyGetterMockRecorder { + return m.recorder +} + +// Cloudprovider mocks base method +func (m *MockCandidatePropertyGetter) Cloudprovider() *models.SCloudprovider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Cloudprovider") + ret0, _ := ret[0].(*models.SCloudprovider) + return ret0 +} + +// Cloudprovider indicates an expected call of Cloudprovider +func (mr *MockCandidatePropertyGetterMockRecorder) Cloudprovider() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cloudprovider", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Cloudprovider)) +} + +// CreatingGuestCount mocks base method +func (m *MockCandidatePropertyGetter) CreatingGuestCount() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreatingGuestCount") + ret0, _ := ret[0].(int) + return ret0 +} + +// CreatingGuestCount indicates an expected call of CreatingGuestCount +func (mr *MockCandidatePropertyGetterMockRecorder) CreatingGuestCount() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatingGuestCount", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).CreatingGuestCount)) +} + +// DomainId mocks base method +func (m *MockCandidatePropertyGetter) DomainId() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DomainId") + ret0, _ := ret[0].(string) + return ret0 +} + +// DomainId indicates an expected call of DomainId +func (mr *MockCandidatePropertyGetterMockRecorder) DomainId() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainId", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).DomainId)) +} + +// Enabled mocks base method +func (m *MockCandidatePropertyGetter) Enabled() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Enabled") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Enabled indicates an expected call of Enabled +func (mr *MockCandidatePropertyGetterMockRecorder) Enabled() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Enabled)) +} + +// FreeCPUCount mocks base method +func (m *MockCandidatePropertyGetter) FreeCPUCount(arg0 bool) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FreeCPUCount", arg0) + ret0, _ := ret[0].(int64) + return ret0 +} + +// FreeCPUCount indicates an expected call of FreeCPUCount +func (mr *MockCandidatePropertyGetterMockRecorder) FreeCPUCount(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FreeCPUCount", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).FreeCPUCount), arg0) +} + +// FreeMemorySize mocks base method +func (m *MockCandidatePropertyGetter) FreeMemorySize(arg0 bool) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FreeMemorySize", arg0) + ret0, _ := ret[0].(int64) + return ret0 +} + +// FreeMemorySize indicates an expected call of FreeMemorySize +func (mr *MockCandidatePropertyGetterMockRecorder) FreeMemorySize(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FreeMemorySize", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).FreeMemorySize), arg0) +} + +// GetFreeGroupCount mocks base method +func (m *MockCandidatePropertyGetter) GetFreeGroupCount(arg0 string) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFreeGroupCount", arg0) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFreeGroupCount indicates an expected call of GetFreeGroupCount +func (mr *MockCandidatePropertyGetterMockRecorder) GetFreeGroupCount(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFreeGroupCount", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetFreeGroupCount), arg0) +} + +// GetFreePort mocks base method +func (m *MockCandidatePropertyGetter) GetFreePort(arg0 string) int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFreePort", arg0) + ret0, _ := ret[0].(int) + return ret0 +} + +// GetFreePort indicates an expected call of GetFreePort +func (mr *MockCandidatePropertyGetterMockRecorder) GetFreePort(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFreePort", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetFreePort), arg0) +} + +// GetFreeStorageSizeOfType mocks base method +func (m *MockCandidatePropertyGetter) GetFreeStorageSizeOfType(arg0 string, arg1 bool) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFreeStorageSizeOfType", arg0, arg1) + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetFreeStorageSizeOfType indicates an expected call of GetFreeStorageSizeOfType +func (mr *MockCandidatePropertyGetterMockRecorder) GetFreeStorageSizeOfType(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFreeStorageSizeOfType", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetFreeStorageSizeOfType), arg0, arg1) +} + +// GetIpmiInfo mocks base method +func (m *MockCandidatePropertyGetter) GetIpmiInfo() types.SIPMIInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIpmiInfo") + ret0, _ := ret[0].(types.SIPMIInfo) + return ret0 +} + +// GetIpmiInfo indicates an expected call of GetIpmiInfo +func (mr *MockCandidatePropertyGetterMockRecorder) GetIpmiInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIpmiInfo", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetIpmiInfo)) +} + +// GetIsolatedDevice mocks base method +func (m *MockCandidatePropertyGetter) GetIsolatedDevice(arg0 string) *core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIsolatedDevice", arg0) + ret0, _ := ret[0].(*core.IsolatedDeviceDesc) + return ret0 +} + +// GetIsolatedDevice indicates an expected call of GetIsolatedDevice +func (mr *MockCandidatePropertyGetterMockRecorder) GetIsolatedDevice(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolatedDevice", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetIsolatedDevice), arg0) +} + +// GetIsolatedDevices mocks base method +func (m *MockCandidatePropertyGetter) GetIsolatedDevices() []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetIsolatedDevices") + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// GetIsolatedDevices indicates an expected call of GetIsolatedDevices +func (mr *MockCandidatePropertyGetterMockRecorder) GetIsolatedDevices() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIsolatedDevices", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetIsolatedDevices)) +} + +// GetPendingUsage mocks base method +func (m *MockCandidatePropertyGetter) GetPendingUsage() *models0.SPendingUsage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPendingUsage") + ret0, _ := ret[0].(*models0.SPendingUsage) + return ret0 +} + +// GetPendingUsage indicates an expected call of GetPendingUsage +func (mr *MockCandidatePropertyGetterMockRecorder) GetPendingUsage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingUsage", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetPendingUsage)) +} + +// GetQuotaKeys mocks base method +func (m *MockCandidatePropertyGetter) GetQuotaKeys(arg0 *api.SchedInfo) models.SComputeResourceKeys { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQuotaKeys", arg0) + ret0, _ := ret[0].(models.SComputeResourceKeys) + return ret0 +} + +// GetQuotaKeys indicates an expected call of GetQuotaKeys +func (mr *MockCandidatePropertyGetterMockRecorder) GetQuotaKeys(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQuotaKeys", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).GetQuotaKeys), arg0) +} + +// Host mocks base method +func (m *MockCandidatePropertyGetter) Host() *models.SHost { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Host") + ret0, _ := ret[0].(*models.SHost) + return ret0 +} + +// Host indicates an expected call of Host +func (mr *MockCandidatePropertyGetterMockRecorder) Host() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Host", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Host)) +} + +// HostSchedtags mocks base method +func (m *MockCandidatePropertyGetter) HostSchedtags() []models.SSchedtag { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HostSchedtags") + ret0, _ := ret[0].([]models.SSchedtag) + return ret0 +} + +// HostSchedtags indicates an expected call of HostSchedtags +func (mr *MockCandidatePropertyGetterMockRecorder) HostSchedtags() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HostSchedtags", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).HostSchedtags)) +} + +// HostStatus mocks base method +func (m *MockCandidatePropertyGetter) HostStatus() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HostStatus") + ret0, _ := ret[0].(string) + return ret0 +} + +// HostStatus indicates an expected call of HostStatus +func (mr *MockCandidatePropertyGetterMockRecorder) HostStatus() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HostStatus", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).HostStatus)) +} + +// HostType mocks base method +func (m *MockCandidatePropertyGetter) HostType() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HostType") + ret0, _ := ret[0].(string) + return ret0 +} + +// HostType indicates an expected call of HostType +func (mr *MockCandidatePropertyGetterMockRecorder) HostType() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HostType", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).HostType)) +} + +// Id mocks base method +func (m *MockCandidatePropertyGetter) Id() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Id") + ret0, _ := ret[0].(string) + return ret0 +} + +// Id indicates an expected call of Id +func (mr *MockCandidatePropertyGetterMockRecorder) Id() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Id)) +} + +// InstanceGroups mocks base method +func (m *MockCandidatePropertyGetter) InstanceGroups() map[string]*api.CandidateGroup { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InstanceGroups") + ret0, _ := ret[0].(map[string]*api.CandidateGroup) + return ret0 +} + +// InstanceGroups indicates an expected call of InstanceGroups +func (mr *MockCandidatePropertyGetterMockRecorder) InstanceGroups() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceGroups", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).InstanceGroups)) +} + +// IsEmpty mocks base method +func (m *MockCandidatePropertyGetter) IsEmpty() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsEmpty") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsEmpty indicates an expected call of IsEmpty +func (mr *MockCandidatePropertyGetterMockRecorder) IsEmpty() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEmpty", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).IsEmpty)) +} + +// IsPublic mocks base method +func (m *MockCandidatePropertyGetter) IsPublic() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsPublic") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsPublic indicates an expected call of IsPublic +func (mr *MockCandidatePropertyGetterMockRecorder) IsPublic() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPublic", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).IsPublic)) +} + +// KeywordPlural mocks base method +func (m *MockCandidatePropertyGetter) KeywordPlural() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KeywordPlural") + ret0, _ := ret[0].(string) + return ret0 +} + +// KeywordPlural indicates an expected call of KeywordPlural +func (mr *MockCandidatePropertyGetterMockRecorder) KeywordPlural() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeywordPlural", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).KeywordPlural)) +} + +// Name mocks base method +func (m *MockCandidatePropertyGetter) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name +func (mr *MockCandidatePropertyGetterMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Name)) +} + +// NetInterfaces mocks base method +func (m *MockCandidatePropertyGetter) NetInterfaces() map[string][]models.SNetInterface { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NetInterfaces") + ret0, _ := ret[0].(map[string][]models.SNetInterface) + return ret0 +} + +// NetInterfaces indicates an expected call of NetInterfaces +func (mr *MockCandidatePropertyGetterMockRecorder) NetInterfaces() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetInterfaces", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).NetInterfaces)) +} + +// Networks mocks base method +func (m *MockCandidatePropertyGetter) Networks() []*api.CandidateNetwork { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Networks") + ret0, _ := ret[0].([]*api.CandidateNetwork) + return ret0 +} + +// Networks indicates an expected call of Networks +func (mr *MockCandidatePropertyGetterMockRecorder) Networks() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Networks", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Networks)) +} + +// OvnCapable mocks base method +func (m *MockCandidatePropertyGetter) OvnCapable() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OvnCapable") + ret0, _ := ret[0].(bool) + return ret0 +} + +// OvnCapable indicates an expected call of OvnCapable +func (mr *MockCandidatePropertyGetterMockRecorder) OvnCapable() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OvnCapable", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).OvnCapable)) +} + +// ProjectGuests mocks base method +func (m *MockCandidatePropertyGetter) ProjectGuests() map[string]int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ProjectGuests") + ret0, _ := ret[0].(map[string]int64) + return ret0 +} + +// ProjectGuests indicates an expected call of ProjectGuests +func (mr *MockCandidatePropertyGetterMockRecorder) ProjectGuests() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProjectGuests", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).ProjectGuests)) +} + +// PublicScope mocks base method +func (m *MockCandidatePropertyGetter) PublicScope() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PublicScope") + ret0, _ := ret[0].(string) + return ret0 +} + +// PublicScope indicates an expected call of PublicScope +func (mr *MockCandidatePropertyGetterMockRecorder) PublicScope() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicScope", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).PublicScope)) +} + +// Region mocks base method +func (m *MockCandidatePropertyGetter) Region() *models.SCloudregion { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Region") + ret0, _ := ret[0].(*models.SCloudregion) + return ret0 +} + +// Region indicates an expected call of Region +func (mr *MockCandidatePropertyGetterMockRecorder) Region() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Region", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Region)) +} + +// ResourceType mocks base method +func (m *MockCandidatePropertyGetter) ResourceType() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ResourceType") + ret0, _ := ret[0].(string) + return ret0 +} + +// ResourceType indicates an expected call of ResourceType +func (mr *MockCandidatePropertyGetterMockRecorder) ResourceType() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceType", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).ResourceType)) +} + +// RunningCPUCount mocks base method +func (m *MockCandidatePropertyGetter) RunningCPUCount() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunningCPUCount") + ret0, _ := ret[0].(int64) + return ret0 +} + +// RunningCPUCount indicates an expected call of RunningCPUCount +func (mr *MockCandidatePropertyGetterMockRecorder) RunningCPUCount() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunningCPUCount", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).RunningCPUCount)) +} + +// RunningMemorySize mocks base method +func (m *MockCandidatePropertyGetter) RunningMemorySize() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunningMemorySize") + ret0, _ := ret[0].(int64) + return ret0 +} + +// RunningMemorySize indicates an expected call of RunningMemorySize +func (mr *MockCandidatePropertyGetterMockRecorder) RunningMemorySize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunningMemorySize", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).RunningMemorySize)) +} + +// SharedDomains mocks base method +func (m *MockCandidatePropertyGetter) SharedDomains() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SharedDomains") + ret0, _ := ret[0].([]string) + return ret0 +} + +// SharedDomains indicates an expected call of SharedDomains +func (mr *MockCandidatePropertyGetterMockRecorder) SharedDomains() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SharedDomains", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).SharedDomains)) +} + +// Sku mocks base method +func (m *MockCandidatePropertyGetter) Sku(arg0 string) *sku.ServerSku { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sku", arg0) + ret0, _ := ret[0].(*sku.ServerSku) + return ret0 +} + +// Sku indicates an expected call of Sku +func (mr *MockCandidatePropertyGetterMockRecorder) Sku(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sku", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Sku), arg0) +} + +// Status mocks base method +func (m *MockCandidatePropertyGetter) Status() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Status") + ret0, _ := ret[0].(string) + return ret0 +} + +// Status indicates an expected call of Status +func (mr *MockCandidatePropertyGetterMockRecorder) Status() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Status)) +} + +// StorageInfo mocks base method +func (m *MockCandidatePropertyGetter) StorageInfo() []*baremetal.BaremetalStorage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StorageInfo") + ret0, _ := ret[0].([]*baremetal.BaremetalStorage) + return ret0 +} + +// StorageInfo indicates an expected call of StorageInfo +func (mr *MockCandidatePropertyGetterMockRecorder) StorageInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageInfo", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).StorageInfo)) +} + +// Storages mocks base method +func (m *MockCandidatePropertyGetter) Storages() []*api.CandidateStorage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Storages") + ret0, _ := ret[0].([]*api.CandidateStorage) + return ret0 +} + +// Storages indicates an expected call of Storages +func (mr *MockCandidatePropertyGetterMockRecorder) Storages() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Storages", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Storages)) +} + +// TotalCPUCount mocks base method +func (m *MockCandidatePropertyGetter) TotalCPUCount(arg0 bool) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalCPUCount", arg0) + ret0, _ := ret[0].(int64) + return ret0 +} + +// TotalCPUCount indicates an expected call of TotalCPUCount +func (mr *MockCandidatePropertyGetterMockRecorder) TotalCPUCount(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalCPUCount", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).TotalCPUCount), arg0) +} + +// TotalMemorySize mocks base method +func (m *MockCandidatePropertyGetter) TotalMemorySize(arg0 bool) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TotalMemorySize", arg0) + ret0, _ := ret[0].(int64) + return ret0 +} + +// TotalMemorySize indicates an expected call of TotalMemorySize +func (mr *MockCandidatePropertyGetterMockRecorder) TotalMemorySize(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TotalMemorySize", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).TotalMemorySize), arg0) +} + +// UnusedGpuDevices mocks base method +func (m *MockCandidatePropertyGetter) UnusedGpuDevices() []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnusedGpuDevices") + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// UnusedGpuDevices indicates an expected call of UnusedGpuDevices +func (mr *MockCandidatePropertyGetterMockRecorder) UnusedGpuDevices() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnusedGpuDevices", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).UnusedGpuDevices)) +} + +// UnusedIsolatedDevices mocks base method +func (m *MockCandidatePropertyGetter) UnusedIsolatedDevices() []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnusedIsolatedDevices") + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// UnusedIsolatedDevices indicates an expected call of UnusedIsolatedDevices +func (mr *MockCandidatePropertyGetterMockRecorder) UnusedIsolatedDevices() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnusedIsolatedDevices", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).UnusedIsolatedDevices)) +} + +// UnusedIsolatedDevicesByModel mocks base method +func (m *MockCandidatePropertyGetter) UnusedIsolatedDevicesByModel(arg0 string) []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnusedIsolatedDevicesByModel", arg0) + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// UnusedIsolatedDevicesByModel indicates an expected call of UnusedIsolatedDevicesByModel +func (mr *MockCandidatePropertyGetterMockRecorder) UnusedIsolatedDevicesByModel(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnusedIsolatedDevicesByModel", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).UnusedIsolatedDevicesByModel), arg0) +} + +// UnusedIsolatedDevicesByType mocks base method +func (m *MockCandidatePropertyGetter) UnusedIsolatedDevicesByType(arg0 string) []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnusedIsolatedDevicesByType", arg0) + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// UnusedIsolatedDevicesByType indicates an expected call of UnusedIsolatedDevicesByType +func (mr *MockCandidatePropertyGetterMockRecorder) UnusedIsolatedDevicesByType(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnusedIsolatedDevicesByType", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).UnusedIsolatedDevicesByType), arg0) +} + +// UnusedIsolatedDevicesByVendorModel mocks base method +func (m *MockCandidatePropertyGetter) UnusedIsolatedDevicesByVendorModel(arg0 string) []*core.IsolatedDeviceDesc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnusedIsolatedDevicesByVendorModel", arg0) + ret0, _ := ret[0].([]*core.IsolatedDeviceDesc) + return ret0 +} + +// UnusedIsolatedDevicesByVendorModel indicates an expected call of UnusedIsolatedDevicesByVendorModel +func (mr *MockCandidatePropertyGetterMockRecorder) UnusedIsolatedDevicesByVendorModel(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnusedIsolatedDevicesByVendorModel", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).UnusedIsolatedDevicesByVendorModel), arg0) +} + +// Zone mocks base method +func (m *MockCandidatePropertyGetter) Zone() *models.SZone { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Zone") + ret0, _ := ret[0].(*models.SZone) + return ret0 +} + +// Zone indicates an expected call of Zone +func (mr *MockCandidatePropertyGetterMockRecorder) Zone() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Zone", reflect.TypeOf((*MockCandidatePropertyGetter)(nil).Zone)) +} + +// MockCandidater is a mock of Candidater interface +type MockCandidater struct { + ctrl *gomock.Controller + recorder *MockCandidaterMockRecorder +} + +// MockCandidaterMockRecorder is the mock recorder for MockCandidater +type MockCandidaterMockRecorder struct { + mock *MockCandidater +} + +// NewMockCandidater creates a new mock instance +func NewMockCandidater(ctrl *gomock.Controller) *MockCandidater { + mock := &MockCandidater{ctrl: ctrl} + mock.recorder = &MockCandidaterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockCandidater) EXPECT() *MockCandidaterMockRecorder { + return m.recorder +} + +// GetGuestCount mocks base method +func (m *MockCandidater) GetGuestCount() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetGuestCount") + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetGuestCount indicates an expected call of GetGuestCount +func (mr *MockCandidaterMockRecorder) GetGuestCount() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGuestCount", reflect.TypeOf((*MockCandidater)(nil).GetGuestCount)) +} + +// GetResourceType mocks base method +func (m *MockCandidater) GetResourceType() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetResourceType") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetResourceType indicates an expected call of GetResourceType +func (mr *MockCandidaterMockRecorder) GetResourceType() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResourceType", reflect.TypeOf((*MockCandidater)(nil).GetResourceType)) +} + +// GetSchedDesc mocks base method +func (m *MockCandidater) GetSchedDesc() *jsonutils.JSONDict { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSchedDesc") + ret0, _ := ret[0].(*jsonutils.JSONDict) + return ret0 +} + +// GetSchedDesc indicates an expected call of GetSchedDesc +func (mr *MockCandidaterMockRecorder) GetSchedDesc() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSchedDesc", reflect.TypeOf((*MockCandidater)(nil).GetSchedDesc)) +} + +// Getter mocks base method +func (m *MockCandidater) Getter() core.CandidatePropertyGetter { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Getter") + ret0, _ := ret[0].(core.CandidatePropertyGetter) + return ret0 +} + +// Getter indicates an expected call of Getter +func (mr *MockCandidaterMockRecorder) Getter() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Getter", reflect.TypeOf((*MockCandidater)(nil).Getter)) +} + +// IndexKey mocks base method +func (m *MockCandidater) IndexKey() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexKey") + ret0, _ := ret[0].(string) + return ret0 +} + +// IndexKey indicates an expected call of IndexKey +func (mr *MockCandidaterMockRecorder) IndexKey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexKey", reflect.TypeOf((*MockCandidater)(nil).IndexKey)) +} + +// Type mocks base method +func (m *MockCandidater) Type() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Type") + ret0, _ := ret[0].(int) + return ret0 +} + +// Type indicates an expected call of Type +func (mr *MockCandidaterMockRecorder) Type() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*MockCandidater)(nil).Type)) +} + +// MockScheduler is a mock of Scheduler interface +type MockScheduler struct { + ctrl *gomock.Controller + recorder *MockSchedulerMockRecorder +} + +// MockSchedulerMockRecorder is the mock recorder for MockScheduler +type MockSchedulerMockRecorder struct { + mock *MockScheduler +} + +// NewMockScheduler creates a new mock instance +func NewMockScheduler(ctrl *gomock.Controller) *MockScheduler { + mock := &MockScheduler{ctrl: ctrl} + mock.recorder = &MockSchedulerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockScheduler) EXPECT() *MockSchedulerMockRecorder { + return m.recorder +} + +// BeforePredicate mocks base method +func (m *MockScheduler) BeforePredicate() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeforePredicate") + ret0, _ := ret[0].(error) + return ret0 +} + +// BeforePredicate indicates an expected call of BeforePredicate +func (mr *MockSchedulerMockRecorder) BeforePredicate() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeforePredicate", reflect.TypeOf((*MockScheduler)(nil).BeforePredicate)) +} + +// Predicates mocks base method +func (m *MockScheduler) Predicates() (map[string]core.FitPredicate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Predicates") + ret0, _ := ret[0].(map[string]core.FitPredicate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Predicates indicates an expected call of Predicates +func (mr *MockSchedulerMockRecorder) Predicates() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Predicates", reflect.TypeOf((*MockScheduler)(nil).Predicates)) +} + +// PriorityConfigs mocks base method +func (m *MockScheduler) PriorityConfigs() ([]core.PriorityConfig, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PriorityConfigs") + ret0, _ := ret[0].([]core.PriorityConfig) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PriorityConfigs indicates an expected call of PriorityConfigs +func (mr *MockSchedulerMockRecorder) PriorityConfigs() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PriorityConfigs", reflect.TypeOf((*MockScheduler)(nil).PriorityConfigs)) +} diff --git a/pkg/scheduler/test/mock/doc.go b/pkg/scheduler/test/mock/doc.go new file mode 100644 index 0000000000..c3ea84bd09 --- /dev/null +++ b/pkg/scheduler/test/mock/doc.go @@ -0,0 +1,15 @@ +// Copyright 2019 Yunion +// +// 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 mock // import "yunion.io/x/onecloud/pkg/scheduler/test/mock" diff --git a/pkg/scheduler/test/prepare.go b/pkg/scheduler/test/prepare.go new file mode 100644 index 0000000000..5399897700 --- /dev/null +++ b/pkg/scheduler/test/prepare.go @@ -0,0 +1,343 @@ +// Copyright 2019 Yunion +// +// 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 test + +import ( + "fmt" + + "github.com/golang/mock/gomock" + + "yunion.io/x/pkg/tristate" + "yunion.io/x/pkg/util/netutils" + "yunion.io/x/pkg/util/sets" + + "yunion.io/x/onecloud/pkg/apis/compute" + computeapi "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/compute/models" + _ "yunion.io/x/onecloud/pkg/scheduler/algorithmprovider" + "yunion.io/x/onecloud/pkg/scheduler/api" + "yunion.io/x/onecloud/pkg/scheduler/core" + "yunion.io/x/onecloud/pkg/scheduler/data_manager/sku" + "yunion.io/x/onecloud/pkg/scheduler/factory" + "yunion.io/x/onecloud/pkg/scheduler/test/mock" +) + +type sPredicateName string + +var ( + HostStatus sPredicateName = "a-GuestHostStatusFilter" + Hypervisor sPredicateName = "b-GuestHypervisorFilter" + Migrate sPredicateName = "d-GuestMigrateFilter" + Domain sPredicateName = "e-GuestDomainFilter" + Image sPredicateName = "e-GuestImageFilter" + CPU sPredicateName = "g-GuestCPUFilter" + Memory sPredicateName = "h-GuestMemoryFilter" + Storage sPredicateName = "i-GuestStorageFilter" + Network sPredicateName = "j-GuestNetworkFilter" + IsolateDevice sPredicateName = "k-GuestIsolatedDeviceFilter" + ResourceType sPredicateName = "l-GuestResourceTypeFilter" + ServerSku sPredicateName = "n-ServerSkuFilter" + + // scheudle tag predicate, need db operator for now + HostSchedtag sPredicateName = "c-GuestAggregateFilter" + DiskSchedtag sPredicateName = "m-GuestDiskschedtagFilter" + NetworkSchedtag sPredicateName = "o-GuestNetschedtagFilter" + + // quota + Quota sPredicateName = "z-QuotaFilter" + + basePredicateNames = []sPredicateName{ + HostStatus, Hypervisor, Migrate, Domain, Image, CPU, Memory, Storage, Network, + IsolateDevice, ResourceType, ServerSku, + } +) + +var ( + GlobalDoamin = "default" + GlobalProject = "default" + GlobalZone = buildZone("default", "Default") + GlobalCloudregion = buildCloudregion("default", "Default", "") + GlobalWire = "default" + GlobaleVPC = "default" +) + +func buildCandidate(ctrl *gomock.Controller, param sGetterParams) *mock.MockCandidater { + cn := mock.NewMockCandidater(ctrl) + getter := buildGetter(ctrl, param) + cn.EXPECT().Getter().AnyTimes().Return(getter) + cn.EXPECT().IndexKey().AnyTimes().Return(getter.Id()) + cn.EXPECT().GetResourceType().AnyTimes().Return(getter.ResourceType()) + return cn +} + +type sGetterParams struct { + HostId string + HostName string + IsPublic *bool + Domain string + PublicScope string + Zone *models.SZone + CloudRegion *models.SCloudregion + CloudProvider *models.SCloudprovider + HostType string + Storages []*api.CandidateStorage + Networks []*api.CandidateNetwork + OvnCapable *bool + Status string + HostStatus string + Enabled *bool + ResourceType string + TotalCPUCount int64 + FreeCPUCount int64 + TotalMemorySize int64 + FreeMemorySize int64 + FreeStorageSizeAnyType int64 + FreePort int + QuotaKeys *models.SComputeResourceKeys + FreeGroupCount int + Skus []string +} + +func buildGetter(ctrl *gomock.Controller, param sGetterParams) *mock.MockCandidatePropertyGetter { + cg := mock.NewMockCandidatePropertyGetter(ctrl) + cg.EXPECT().Id().AnyTimes().Return(param.HostId) + cg.EXPECT().Name().AnyTimes().Return(param.HostName) + cg.EXPECT().Zone().AnyTimes().Return(param.Zone) + if param.IsPublic == nil { + cg.EXPECT().IsPublic().AnyTimes().Return(true) + } else { + cg.EXPECT().IsPublic().AnyTimes().Return(*param.IsPublic) + } + if param.Enabled == nil { + cg.EXPECT().Enabled().AnyTimes().Return(true) + } else { + cg.EXPECT().Enabled().AnyTimes().Return(*param.Enabled) + } + cg.EXPECT().DomainId().AnyTimes().Return(param.Domain) + cg.EXPECT().PublicScope().AnyTimes().Return(param.PublicScope) + cg.EXPECT().Region().AnyTimes().Return(param.CloudRegion) + cg.EXPECT().Cloudprovider().AnyTimes().Return(param.CloudProvider) + cg.EXPECT().HostType().AnyTimes().Return(param.HostType) + cg.EXPECT().Storages().AnyTimes().Return(param.Storages) + cg.EXPECT().Networks().AnyTimes().Return(param.Networks) + cg.EXPECT().Sku(gomock.Any()).AnyTimes().DoAndReturn(func(instanceType string) *sku.ServerSku { + for _, t := range param.Skus { + if t != instanceType { + continue + } + return &sku.ServerSku{ + Id: fmt.Sprintf("%s-%s", param.Zone.Id, instanceType), + ZoneId: param.Zone.Id, + } + } + return nil + }) + if param.OvnCapable == nil { + cg.EXPECT().OvnCapable().AnyTimes().Return(false) + } else { + cg.EXPECT().OvnCapable().AnyTimes().Return(*param.OvnCapable) + } + if len(param.Status) == 0 { + cg.EXPECT().Status().AnyTimes().Return(computeapi.HOST_HEALTH_STATUS_RUNNING) + } else { + cg.EXPECT().Status().AnyTimes().Return(param.Status) + } + if len(param.HostStatus) == 0 { + cg.EXPECT().HostStatus().AnyTimes().Return(computeapi.HOST_ONLINE) + } else { + cg.EXPECT().HostStatus().AnyTimes().Return(param.HostStatus) + } + if len(param.ResourceType) == 0 { + cg.EXPECT().ResourceType().AnyTimes().Return(computeapi.HostResourceTypeDefault) + } else { + cg.EXPECT().ResourceType().AnyTimes().Return(param.ResourceType) + } + cg.EXPECT().TotalCPUCount(gomock.Any()).AnyTimes().Return(param.TotalCPUCount) + cg.EXPECT().FreeCPUCount(gomock.Any()).AnyTimes().Return(param.FreeCPUCount) + cg.EXPECT().TotalMemorySize(gomock.Any()).AnyTimes().Return(param.TotalMemorySize) + cg.EXPECT().FreeMemorySize(gomock.Any()).AnyTimes().Return(param.FreeMemorySize) + cg.EXPECT().GetFreeStorageSizeOfType(gomock.Any(), gomock.Any()).AnyTimes().Return(param.FreeStorageSizeAnyType) + cg.EXPECT().GetFreePort(gomock.Any()).AnyTimes().Return(param.FreePort) + if param.QuotaKeys != nil { + cg.EXPECT().GetQuotaKeys(gomock.Any()).AnyTimes().Return(param.QuotaKeys) + } + cg.EXPECT().GetFreeGroupCount(gomock.Any()).AnyTimes().Return(param.FreeGroupCount, nil) + return cg +} + +func buildScheduler(ctrl *gomock.Controller, predicates ...sPredicateName) core.Scheduler { + pres := sets.NewString() + for _, pre := range predicates { + pres.Insert(string(pre)) + } + algorithmProvider, _ := factory.GetAlgorithmProvider(factory.DefaultProvider) + mockScheduler := mock.NewMockScheduler(ctrl) + mockScheduler.EXPECT().BeforePredicate().AnyTimes().Return(nil) + mockScheduler.EXPECT().Predicates().AnyTimes().DoAndReturn(func() (map[string]core.FitPredicate, error) { + return factory.GetPredicates(pres) + }) + mockScheduler.EXPECT().PriorityConfigs().AnyTimes().DoAndReturn(func() ([]core.PriorityConfig, error) { + return factory.GetPriorityConfigs(algorithmProvider.PriorityKeys) + }) + return mockScheduler +} + +func buildZone(id, name string) *models.SZone { + zone := &models.SZone{} + zone.Id = id + zone.Name = name + zone.Status = "enable" + return zone +} + +func buildCloudregion(id, name, provider string) *models.SCloudregion { + if len(provider) == 0 { + provider = computeapi.CLOUD_PROVIDER_ONECLOUD + } + return &models.SCloudregion{ + SEnabledStatusStandaloneResourceBase: db.SEnabledStatusStandaloneResourceBase{ + SStatusStandaloneResourceBase: db.SStatusStandaloneResourceBase{ + SStandaloneResourceBase: db.SStandaloneResourceBase{ + Id: id, + Name: name, + }, + SStatusResourceBase: db.SStatusResourceBase{ + Status: "inservice", + }, + }, + SEnabledResourceBase: db.SEnabledResourceBase{ + Enabled: "true", + }, + }, + Provider: provider, + } +} + +func buildStorage(id, name string, capacity int64) *api.CandidateStorage { + storage := &models.SStorage{} + storage.Id, storage.Name = id, name + storage.DomainId = GlobalDoamin + storage.IsPublic = true + storage.PublicScope = "system" + storage.Status = computeapi.STORAGE_ONLINE + storage.Enabled = "true" + storage.ZoneId = GlobalZone.GetId() + storage.Capacity = capacity + storage.StorageType = computeapi.STORAGE_LOCAL + storage.MediumType = computeapi.DISK_TYPE_ROTATE + storage.Cmtbound = 1.0 + storage.IsSysDiskStore = "true" + return &api.CandidateStorage{ + SStorage: storage, + } +} + +func buildNetwork(id, name string, cidr string) *api.CandidateNetwork { + network := &models.SNetwork{} + network.Id, network.Name = id, name + network.Status = computeapi.NETWORK_STATUS_AVAILABLE + network.DomainId = GlobalDoamin + network.ProjectId = GlobalProject + network.WireId = GlobalWire + network.ServerType = computeapi.NETWORK_TYPE_GUEST + prefix, err := netutils.NewIPV4Prefix(cidr) + if err == nil { + network.GuestIpMask = prefix.MaskLen + ipRange := prefix.ToIPRange() + network.GuestIpStart = ipRange.StartIp().String() + network.GuestIpEnd = ipRange.EndIp().String() + } + return &api.CandidateNetwork{ + SNetwork: network, + VpcId: GlobaleVPC, + } +} + +func buildInstanceGroup(id string, granularity int, force bool) *models.SGroup { + group := &models.SGroup{} + group.Id = id + group.Status = "ready" + group.DomainId = GlobalDoamin + group.ProjectId = GlobalProject + group.Granularity = granularity + group.ForceDispersion = tristate.NewFromBool(force) + return nil +} + +func preSchedule(info *api.SchedInfo, candidates []core.Candidater, isForcast bool) (*core.Unit, []core.Candidater, core.IResultHelper) { + resultHelper := core.SResultHelperFunc(core.ResultHelp) + if isForcast { + resultHelper = core.SResultHelperFunc(core.ResultHelpForForcast) + info.Suggestion = true + info.IsSuggestion = true + info.SuggestionAll = true + info.ShowSuggestionDetails = true + info.SuggestionLimit = 100 + } + return core.NewScheduleUnit(info, nil), candidates, resultHelper +} + +func deepCopy(info *api.SchedInfo) *api.SchedInfo { + serverConfigs := *info.ServerConfigs + // disks + disks := make([]*compute.DiskConfig, len(info.Disks)) + for i := range disks { + disk := *info.Disks[i] + disks[i] = &disk + } + // network + networks := make([]*compute.NetworkConfig, len(info.Networks)) + for i := range networks { + network := *info.Networks[i] + networks[i] = &network + } + // baremetal_disk_config + bareConfigs := make([]*compute.BaremetalDiskConfig, len(info.BaremetalDiskConfigs)) + for i := range bareConfigs { + bareConfig := *info.BaremetalDiskConfigs[i] + bareConfigs[i] = &bareConfig + } + // instancegroup + instancegroupIds := make([]string, len(info.InstanceGroupIds)) + for i := range instancegroupIds { + instancegroupIds[i] = info.InstanceGroupIds[i] + } + serverConfigs.Disks = disks + serverConfigs.Networks = networks + serverConfigs.BaremetalDiskConfigs = bareConfigs + + serverConfig := info.ServerConfig + serverConfig.ServerConfigs = &serverConfigs + + scheduInput := *info.ScheduleInput + scheduInput.ServerConfig = serverConfig + + copyInfo := *info + copyInfo.ScheduleInput = &scheduInput + preferCandidates := make([]string, len(info.PreferCandidates)) + for i := range preferCandidates { + preferCandidates[i] = info.PreferCandidates[i] + } + instanceGroupDetail := make(map[string]*models.SGroup, len(info.InstanceGroupsDetail)) + for k, v := range info.InstanceGroupsDetail { + // v only read + instanceGroupDetail[k] = v + } + copyInfo.PreferCandidates = preferCandidates + copyInfo.InstanceGroupsDetail = instanceGroupDetail + + return ©Info +} diff --git a/vendor/github.com/golang/mock/AUTHORS b/vendor/github.com/golang/mock/AUTHORS new file mode 100644 index 0000000000..660b8ccc8a --- /dev/null +++ b/vendor/github.com/golang/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/github.com/golang/mock/CONTRIBUTORS b/vendor/github.com/golang/mock/CONTRIBUTORS new file mode 100644 index 0000000000..def849cab1 --- /dev/null +++ b/vendor/github.com/golang/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/github.com/golang/mock/LICENSE b/vendor/github.com/golang/mock/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/golang/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/golang/mock/gomock/call.go b/vendor/github.com/golang/mock/gomock/call.go new file mode 100644 index 0000000000..3d54d9f5d0 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/call.go @@ -0,0 +1,420 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestHelper // for triggering test failures on invalid call setup + + receiver interface{} // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]interface{}) []interface{} +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + t.Helper() + + // TODO: check arity, types. + margs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + margs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + margs[i] = Nil() + } else { + margs[i] = Eq(arg) + } + } + + origin := callerInfo(3) + actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} { + // Synthesize the zero value for each of the return args' types. + rets := make([]interface{}, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{t: t, receiver: receiver, method: method, methodType: methodType, + args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions} +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also +// sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also +// sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) DoAndReturn(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + vrets := v.Call(vargs) + rets := make([]interface{}, len(vrets)) + for i, ret := range vrets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) Do(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vargs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...interface{}) *Call { + c.t.Helper() + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]interface{}) []interface{} { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice, SetArg +// will copy value's elements into the nth argument. +func (c *Call) SetArg(n int, value interface{}) *Call { + c.t.Helper() + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface: + // nothing to do + case reflect.Slice: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []interface{}) []interface{} { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + c.t.Helper() + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true iff the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vargsType := c.methodType.In(c.methodType.NumIn() - 1) + vargs := reflect.MakeSlice(vargsType, 0, len(args)-i) + for _, arg := range args[i:] { + vargs = reflect.Append(vargs, reflect.ValueOf(arg)) + } + if m.Matches(vargs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i:], c.args[i]) + + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +func setSlice(arg interface{}, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func (c *Call) addAction(action func([]interface{}) []interface{}) { + c.actions = append(c.actions, action) +} diff --git a/vendor/github.com/golang/mock/gomock/callset.go b/vendor/github.com/golang/mock/gomock/callset.go new file mode 100644 index 0000000000..c44a8a585b --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/callset.go @@ -0,0 +1,108 @@ +// Copyright 2011 Google Inc. +// +// 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 gomock + +import ( + "bytes" + "fmt" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver interface{} + fname string +} + +func newCallSet() *callSet { + return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)} +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { + key := callSetKey{receiver, method} + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } + } + + if len(expected)+len(exhausted) == 0 { + fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, fmt.Errorf(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} diff --git a/vendor/github.com/golang/mock/gomock/controller.go b/vendor/github.com/golang/mock/gomock/controller.go new file mode 100644 index 0000000000..0651c91e44 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/controller.go @@ -0,0 +1,264 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock is a mock framework for Go. +// +// Standard usage: +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +// +// TODO: +// - Handle different argument/return types (e.g. ..., chan, map, interface). +package gomock + +import ( + "context" + "fmt" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. It +// is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +// TestHelper is a TestReporter that has the Helper method. It is satisfied +// by the standard library's *testing.T. +type TestHelper interface { + TestReporter + Helper() +} + +// A Controller represents the top-level control of a mock ecosystem. It +// defines the scope and lifetime of mock objects, as well as their +// expectations. It is safe to call Controller's methods from multiple +// goroutines. Each test should create a new Controller and invoke Finish via +// defer. +// +// func TestFoo(t *testing.T) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// } +// +// func TestBar(t *testing.T) { +// t.Run("Sub-Test-1", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// t.Run("Sub-Test-2", st) { +// ctrl := gomock.NewController(st) +// defer ctrl.Finish() +// // .. +// }) +// }) +type Controller struct { + // T should only be called within a generated mock. It is not intended to + // be used in user code and may be changed in future versions. T is the + // TestReporter passed in when creating the Controller via NewController. + // If the TestReporter does not implement a TestHelper it will be wrapped + // with a nopTestHelper. + T TestHelper + mu sync.Mutex + expectedCalls *callSet + finished bool +} + +// NewController returns a new Controller. It is the preferred way to create a +// Controller. +func NewController(t TestReporter) *Controller { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + return &Controller{ + T: h, + expectedCalls: newCallSet(), + } +} + +type cancelReporter struct { + TestHelper + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...interface{}) { + r.TestHelper.Errorf(format, args...) +} +func (r *cancelReporter) Fatalf(format string, args ...interface{}) { + defer r.cancel() + r.TestHelper.Fatalf(format, args...) +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{h, cancel}), ctx +} + +type nopTestHelper struct { + TestReporter +} + +func (h nopTestHelper) Helper() {} + +// RecordCall is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call { + ctrl.T.Helper() + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +// RecordCallWithMethodType is called by a mock. It should not be called by user code. +func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + ctrl.T.Helper() + + call := newCall(ctrl.T, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +// Call is called by a mock. It should not be called by user code. +func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} { + ctrl.T.Helper() + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]interface{}) []interface{} { + ctrl.T.Helper() + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + origin := callerInfo(2) + ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequite calls, + // * and the prerequite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call(args) + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []interface{} + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +// Finish checks to see if all the methods that were expected to be called +// were called. It should be invoked for each Controller. It is not idempotent +// and therefore can only be invoked once. +func (ctrl *Controller) Finish() { + ctrl.T.Helper() + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + ctrl.finished = true + + // If we're currently panicking, probably because this is a deferred call, + // pass through the panic. + if err := recover(); err != nil { + panic(err) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.T.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + ctrl.T.Fatalf("aborting test due to missing call(s)") + } +} + +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} diff --git a/vendor/github.com/golang/mock/gomock/matchers.go b/vendor/github.com/golang/mock/gomock/matchers.go new file mode 100644 index 0000000000..fbff06062f --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/matchers.go @@ -0,0 +1,141 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x interface{}) bool + + // String describes what the matcher matches. + String() string +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(x interface{}) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type eqMatcher struct { + x interface{} +} + +func (e eqMatcher) Matches(x interface{}) bool { + return reflect.DeepEqual(e.x, x) +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %v", e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x interface{}) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x interface{}) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + // TODO: Improve this if we add a NotString method to the Matcher interface. + return "not(" + n.m.String() + ")" +} + +type assignableToTypeOfMatcher struct { + targetType reflect.Type +} + +func (m assignableToTypeOfMatcher) Matches(x interface{}) bool { + return reflect.TypeOf(x).AssignableTo(m.targetType) +} + +func (m assignableToTypeOfMatcher) String() string { + return "is assignable to " + m.targetType.Name() +} + +// Constructors +// Any returns a matcher that always matches. +func Any() Matcher { return anyMatcher{} } + +// Eq returns a matcher that matches on equality. +// +// Example usage: +// Eq(5).Matches(5) // returns true +// Eq(5).Matches(4) // returns false +func Eq(x interface{}) Matcher { return eqMatcher{x} } + +// Nil returns a matcher that matches if the received value is nil. +// +// Example usage: +// var x *bytes.Buffer +// Nil().Matches(x) // returns true +// x = &bytes.Buffer{} +// Nil().Matches(x) // returns false +func Nil() Matcher { return nilMatcher{} } + +// Not reverses the results of its given child matcher. +// +// Example usage: +// Not(Eq(5)).Matches(4) // returns true +// Not(Eq(5)).Matches(5) // returns false +func Not(x interface{}) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} + +// AssignableToTypeOf is a Matcher that matches if the parameter to the mock +// function is assignable to the type of the parameter to this function. +// +// Example usage: +// var s fmt.Stringer = &bytes.Buffer{} +// AssignableToTypeOf(s).Matches(time.Second) // returns true +// AssignableToTypeOf(s).Matches(99) // returns false +func AssignableToTypeOf(x interface{}) Matcher { + return assignableToTypeOfMatcher{reflect.TypeOf(x)} +} diff --git a/vendor/modules.txt b/vendor/modules.txt index b3aea46294..4120622a5c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -318,6 +318,8 @@ github.com/golang-plus/uuid/internal/random github.com/golang-plus/uuid/internal/timebased # github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 github.com/golang/groupcache/lru +# github.com/golang/mock v1.3.1 +github.com/golang/mock/gomock # github.com/golang/protobuf v1.3.2 github.com/golang/protobuf/proto github.com/golang/protobuf/protoc-gen-go