volcano.sh/volcano@v1.9.0/pkg/scheduler/api/resource_info.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package api 18 19 import ( 20 "fmt" 21 "math" 22 "strings" 23 24 v1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/api/resource" 26 "k8s.io/apimachinery/pkg/util/sets" 27 "k8s.io/klog/v2" 28 v1helper "k8s.io/kubernetes/pkg/scheduler/util" 29 30 "volcano.sh/volcano/pkg/scheduler/util/assert" 31 ) 32 33 const ( 34 // GPUResourceName need to follow https://github.com/NVIDIA/k8s-device-plugin/blob/66a35b71ac4b5cbfb04714678b548bd77e5ba719/server.go#L20 35 GPUResourceName = "nvidia.com/gpu" 36 ) 37 38 const ( 39 minResource float64 = 0.1 40 ) 41 42 // DimensionDefaultValue means default value for black resource dimension 43 type DimensionDefaultValue int 44 45 const ( 46 // Zero means resource dimension not defined will be treated as zero 47 Zero DimensionDefaultValue = 0 48 // Infinity means resource dimension not defined will be treated as infinity 49 Infinity DimensionDefaultValue = -1 50 ) 51 52 // Resource struct defines all the resource type 53 type Resource struct { 54 MilliCPU float64 55 Memory float64 56 57 // ScalarResources 58 ScalarResources map[v1.ResourceName]float64 59 60 // MaxTaskNum is only used by predicates; it should NOT 61 // be accounted in other operators, e.g. Add. 62 MaxTaskNum int 63 } 64 65 // EmptyResource creates a empty resource object and returns 66 func EmptyResource() *Resource { 67 return &Resource{} 68 } 69 70 // NewResource creates a new resource object from resource list 71 func NewResource(rl v1.ResourceList) *Resource { 72 r := EmptyResource() 73 for rName, rQuant := range rl { 74 switch rName { 75 case v1.ResourceCPU: 76 r.MilliCPU += float64(rQuant.MilliValue()) 77 case v1.ResourceMemory: 78 r.Memory += float64(rQuant.Value()) 79 case v1.ResourcePods: 80 r.MaxTaskNum += int(rQuant.Value()) 81 r.AddScalar(rName, float64(rQuant.Value())) 82 case v1.ResourceEphemeralStorage: 83 r.AddScalar(rName, float64(rQuant.MilliValue())) 84 default: 85 if IsCountQuota(rName) { 86 continue 87 } 88 //NOTE: When converting this back to k8s resource, we need record the format as well as / 1000 89 if v1helper.IsScalarResourceName(rName) { 90 ignore := false 91 IgnoredDevicesList.Range(func(_ int, val string) bool { 92 if rName.String() == val { 93 ignore = true 94 return false 95 } 96 return true 97 }) 98 if !ignore { 99 r.AddScalar(rName, float64(rQuant.MilliValue())) 100 } else { 101 klog.V(4).Infof("Ignoring resource %s", rName.String()) 102 } 103 } 104 } 105 } 106 return r 107 } 108 109 // ResFloat642Quantity transform resource quantity 110 func ResFloat642Quantity(resName v1.ResourceName, quantity float64) resource.Quantity { 111 var resQuantity *resource.Quantity 112 switch resName { 113 case v1.ResourceCPU: 114 resQuantity = resource.NewMilliQuantity(int64(quantity), resource.DecimalSI) 115 default: 116 resQuantity = resource.NewQuantity(int64(quantity), resource.BinarySI) 117 } 118 119 return *resQuantity 120 } 121 122 // ResQuantity2Float64 transform resource quantity 123 func ResQuantity2Float64(resName v1.ResourceName, quantity resource.Quantity) float64 { 124 var resQuantity float64 125 switch resName { 126 case v1.ResourceCPU: 127 resQuantity = float64(quantity.MilliValue()) 128 default: 129 resQuantity = float64(quantity.Value()) 130 } 131 132 return resQuantity 133 } 134 135 // Clone is used to clone a resource type, which is a deep copy function. 136 func (r *Resource) Clone() *Resource { 137 clone := &Resource{ 138 MilliCPU: r.MilliCPU, 139 Memory: r.Memory, 140 MaxTaskNum: r.MaxTaskNum, 141 } 142 143 if r.ScalarResources != nil { 144 clone.ScalarResources = make(map[v1.ResourceName]float64) 145 for k, v := range r.ScalarResources { 146 clone.ScalarResources[k] = v 147 } 148 } 149 150 return clone 151 } 152 153 // String returns resource details in string format 154 func (r *Resource) String() string { 155 str := fmt.Sprintf("cpu %0.2f, memory %0.2f", r.MilliCPU, r.Memory) 156 for rName, rQuant := range r.ScalarResources { 157 str = fmt.Sprintf("%s, %s %0.2f", str, rName, rQuant) 158 } 159 return str 160 } 161 162 // ResourceNames returns all resource types 163 func (r *Resource) ResourceNames() ResourceNameList { 164 resNames := ResourceNameList{} 165 166 if r.MilliCPU >= minResource { 167 resNames = append(resNames, v1.ResourceCPU) 168 } 169 170 if r.Memory >= minResource { 171 resNames = append(resNames, v1.ResourceMemory) 172 } 173 174 for rName, rMount := range r.ScalarResources { 175 if rMount >= minResource { 176 resNames = append(resNames, rName) 177 } 178 } 179 180 return resNames 181 } 182 183 // Get returns the resource value for that particular resource type 184 func (r *Resource) Get(rn v1.ResourceName) float64 { 185 switch rn { 186 case v1.ResourceCPU: 187 return r.MilliCPU 188 case v1.ResourceMemory: 189 return r.Memory 190 default: 191 if r.ScalarResources == nil { 192 return 0 193 } 194 return r.ScalarResources[rn] 195 } 196 } 197 198 // Skip checking "pods" resource. 199 // All pods request one "pods" resource now, no need to check it 200 var ignoredScalarResources = sets.NewString(string(v1.ResourcePods)) 201 202 func IsIgnoredScalarResource(name v1.ResourceName) bool { 203 return ignoredScalarResources.Has(string(name)) 204 } 205 206 // IsEmpty returns false if any kind of resource other than IgnoredResources is not less than min value, otherwise returns true 207 func (r *Resource) IsEmpty() bool { 208 if !(r.MilliCPU < minResource && r.Memory < minResource) { 209 return false 210 } 211 212 for rName, rQuant := range r.ScalarResources { 213 if IsIgnoredScalarResource(rName) { 214 continue 215 } 216 if rQuant >= minResource { 217 return false 218 } 219 } 220 221 return true 222 } 223 224 // IsZero returns false if the given kind of resource is not less than min value 225 func (r *Resource) IsZero(rn v1.ResourceName) bool { 226 switch rn { 227 case v1.ResourceCPU: 228 return r.MilliCPU < minResource 229 case v1.ResourceMemory: 230 return r.Memory < minResource 231 default: 232 if r.ScalarResources == nil { 233 return true 234 } 235 236 _, found := r.ScalarResources[rn] 237 assert.Assertf(found, "unknown resource %s", rn) 238 239 return r.ScalarResources[rn] < minResource 240 } 241 } 242 243 // Add is used to add two given resources 244 func (r *Resource) Add(rr *Resource) *Resource { 245 r.MilliCPU += rr.MilliCPU 246 r.Memory += rr.Memory 247 248 for rName, rQuant := range rr.ScalarResources { 249 if r.ScalarResources == nil { 250 r.ScalarResources = map[v1.ResourceName]float64{} 251 } 252 r.ScalarResources[rName] += rQuant 253 } 254 255 return r 256 } 257 258 // Sub subtracts two Resource objects with assertion. 259 func (r *Resource) Sub(rr *Resource) *Resource { 260 assert.Assertf(rr.LessEqual(r, Zero), "resource is not sufficient to do operation: <%v> sub <%v>", r, rr) 261 return r.sub(rr) 262 } 263 264 // SubWithoutAssert subtracts two Resource objects without assertion, 265 // this function is added because some resource subtraction allows negative results, while others do not. 266 func (r *Resource) SubWithoutAssert(rr *Resource) *Resource { 267 ok, resources := rr.LessEqualWithResourcesName(r, Zero) 268 if !ok { 269 klog.Errorf("resources <%v> are not sufficient to do operation: <%v> sub <%v>", resources, r, rr) 270 } 271 return r.sub(rr) 272 } 273 274 // sub subtracts two Resource objects. 275 func (r *Resource) sub(rr *Resource) *Resource { 276 r.MilliCPU -= rr.MilliCPU 277 r.Memory -= rr.Memory 278 279 if r.ScalarResources == nil { 280 return r 281 } 282 for rrName, rrQuant := range rr.ScalarResources { 283 r.ScalarResources[rrName] -= rrQuant 284 } 285 286 return r 287 } 288 289 // Multi multiples the resource with ratio provided 290 func (r *Resource) Multi(ratio float64) *Resource { 291 r.MilliCPU *= ratio 292 r.Memory *= ratio 293 for rName, rQuant := range r.ScalarResources { 294 r.ScalarResources[rName] = rQuant * ratio 295 } 296 return r 297 } 298 299 // SetMaxResource compares with ResourceList and takes max value for each Resource. 300 func (r *Resource) SetMaxResource(rr *Resource) { 301 if r == nil || rr == nil { 302 return 303 } 304 305 if rr.MilliCPU > r.MilliCPU { 306 r.MilliCPU = rr.MilliCPU 307 } 308 if rr.Memory > r.Memory { 309 r.Memory = rr.Memory 310 } 311 312 for rrName, rrQuant := range rr.ScalarResources { 313 if r.ScalarResources == nil { 314 r.ScalarResources = make(map[v1.ResourceName]float64) 315 for k, v := range rr.ScalarResources { 316 r.ScalarResources[k] = v 317 } 318 return 319 } 320 _, ok := r.ScalarResources[rrName] 321 if !ok || rrQuant > r.ScalarResources[rrName] { 322 r.ScalarResources[rrName] = rrQuant 323 } 324 } 325 } 326 327 // FitDelta Computes the delta between a resource object representing available 328 // resources an operand representing resources being requested. Any 329 // field that is less than 0 after the operation represents an 330 // insufficient resource. 331 func (r *Resource) FitDelta(rr *Resource) *Resource { 332 if rr.MilliCPU > 0 { 333 r.MilliCPU -= rr.MilliCPU + minResource 334 } 335 336 if rr.Memory > 0 { 337 r.Memory -= rr.Memory + minResource 338 } 339 340 if r.ScalarResources == nil { 341 r.ScalarResources = make(map[v1.ResourceName]float64) 342 } 343 344 for rrName, rrQuant := range rr.ScalarResources { 345 if rrQuant > 0 { 346 _, ok := r.ScalarResources[rrName] 347 if !ok { 348 r.ScalarResources[rrName] = 0 349 } 350 r.ScalarResources[rrName] -= rrQuant + minResource 351 } 352 } 353 354 return r 355 } 356 357 // Less returns true only on condition that all dimensions of resources in r are less than that of rr, 358 // Otherwise returns false. 359 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 360 func (r *Resource) Less(rr *Resource, defaultValue DimensionDefaultValue) bool { 361 lessFunc := func(l, r float64) bool { 362 return l < r 363 } 364 365 if !lessFunc(r.MilliCPU, rr.MilliCPU) { 366 return false 367 } 368 if !lessFunc(r.Memory, rr.Memory) { 369 return false 370 } 371 372 if defaultValue == Infinity { 373 for name := range rr.ScalarResources { 374 if _, ok := r.ScalarResources[name]; !ok { 375 return false 376 } 377 } 378 } 379 380 for resourceName, leftValue := range r.ScalarResources { 381 rightValue, ok := rr.ScalarResources[resourceName] 382 if !ok && defaultValue == Infinity { 383 continue 384 } 385 386 if !lessFunc(leftValue, rightValue) { 387 return false 388 } 389 } 390 return true 391 } 392 393 // LessEqual returns true only on condition that all dimensions of resources in r are less than or equal with that of rr, 394 // Otherwise returns false. 395 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 396 func (r *Resource) LessEqual(rr *Resource, defaultValue DimensionDefaultValue) bool { 397 lessEqualFunc := func(l, r, diff float64) bool { 398 if l < r || math.Abs(l-r) < diff { 399 return true 400 } 401 return false 402 } 403 404 if !lessEqualFunc(r.MilliCPU, rr.MilliCPU, minResource) { 405 return false 406 } 407 if !lessEqualFunc(r.Memory, rr.Memory, minResource) { 408 return false 409 } 410 411 if defaultValue == Infinity { 412 for name := range rr.ScalarResources { 413 if _, ok := r.ScalarResources[name]; !ok { 414 return false 415 } 416 } 417 } 418 419 for resourceName, leftValue := range r.ScalarResources { 420 rightValue, ok := rr.ScalarResources[resourceName] 421 if !ok && defaultValue == Infinity { 422 continue 423 } 424 425 if !lessEqualFunc(leftValue, rightValue, minResource) { 426 return false 427 } 428 } 429 return true 430 } 431 432 // LessEqualWithResourcesName returns true, []string{} only on condition that all dimensions of resources in r are less than or equal with that of rr, 433 // Otherwise returns false and err string ,which show what resources are insufficient. 434 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 435 // this function is the same as LessEqual , and it will be merged to LessEqual in the future 436 func (r *Resource) LessEqualWithResourcesName(rr *Resource, defaultValue DimensionDefaultValue) (bool, []string) { 437 resources := []string{} 438 lessEqualFunc := func(l, r, diff float64) bool { 439 if l < r || math.Abs(l-r) < diff { 440 return true 441 } 442 return false 443 } 444 445 if !lessEqualFunc(r.MilliCPU, rr.MilliCPU, minResource) { 446 resources = append(resources, "cpu") 447 } 448 if !lessEqualFunc(r.Memory, rr.Memory, minResource) { 449 resources = append(resources, "memory") 450 } 451 452 for resourceName, leftValue := range r.ScalarResources { 453 rightValue, ok := rr.ScalarResources[resourceName] 454 if !ok && defaultValue == Infinity { 455 continue 456 } 457 458 if !lessEqualFunc(leftValue, rightValue, minResource) { 459 resources = append(resources, string(resourceName)) 460 } 461 } 462 if len(resources) > 0 { 463 return false, resources 464 } 465 return true, resources 466 } 467 468 // LessPartly returns true if there exists any dimension whose resource amount in r is less than that in rr. 469 // Otherwise returns false. 470 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 471 func (r *Resource) LessPartly(rr *Resource, defaultValue DimensionDefaultValue) bool { 472 lessFunc := func(l, r float64) bool { 473 return l < r 474 } 475 476 if lessFunc(r.MilliCPU, rr.MilliCPU) || lessFunc(r.Memory, rr.Memory) { 477 return true 478 } 479 480 if defaultValue == Zero { 481 for name := range rr.ScalarResources { 482 if _, ok := r.ScalarResources[name]; !ok { 483 return true 484 } 485 } 486 } 487 488 for resourceName, leftValue := range r.ScalarResources { 489 rightValue, ok := rr.ScalarResources[resourceName] 490 if !ok && defaultValue == Infinity { 491 return true 492 } 493 494 if lessFunc(leftValue, rightValue) { 495 return true 496 } 497 } 498 return false 499 } 500 501 // LessEqualPartly returns true if there exists any dimension whose resource amount in r is less than or equal with that in rr. 502 // Otherwise returns false. 503 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 504 func (r *Resource) LessEqualPartly(rr *Resource, defaultValue DimensionDefaultValue) bool { 505 lessEqualFunc := func(l, r, diff float64) bool { 506 if l < r || math.Abs(l-r) < diff { 507 return true 508 } 509 return false 510 } 511 512 if lessEqualFunc(r.MilliCPU, rr.MilliCPU, minResource) || lessEqualFunc(r.Memory, rr.Memory, minResource) { 513 return true 514 } 515 516 if defaultValue == Zero { 517 for name := range rr.ScalarResources { 518 if _, ok := r.ScalarResources[name]; !ok { 519 return true 520 } 521 } 522 } 523 524 for resourceName, leftValue := range r.ScalarResources { 525 rightValue, ok := rr.ScalarResources[resourceName] 526 if !ok && defaultValue == Infinity { 527 return true 528 } 529 530 if lessEqualFunc(leftValue, rightValue, minResource) { 531 return true 532 } 533 } 534 return false 535 } 536 537 // Equal returns true only on condition that values in all dimension are equal with each other for r and rr 538 // Otherwise returns false. 539 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 540 func (r *Resource) Equal(rr *Resource, defaultValue DimensionDefaultValue) bool { 541 equalFunc := func(l, r, diff float64) bool { 542 return l == r || math.Abs(l-r) < diff 543 } 544 545 if !equalFunc(r.MilliCPU, rr.MilliCPU, minResource) || !equalFunc(r.Memory, rr.Memory, minResource) { 546 return false 547 } 548 549 for resourceName, leftValue := range r.ScalarResources { 550 rightValue := rr.ScalarResources[resourceName] 551 if !equalFunc(leftValue, rightValue, minResource) { 552 return false 553 } 554 } 555 return true 556 } 557 558 // Diff calculate the difference between two resource object 559 // Note: if `defaultValue` equals `Infinity`, the difference between two values will be `Infinity`, marked as -1 560 func (r *Resource) Diff(rr *Resource, defaultValue DimensionDefaultValue) (*Resource, *Resource) { 561 leftRes := r.Clone() 562 rightRes := rr.Clone() 563 increasedVal := EmptyResource() 564 decreasedVal := EmptyResource() 565 r.setDefaultValue(leftRes, rightRes, defaultValue) 566 567 if leftRes.MilliCPU > rightRes.MilliCPU { 568 increasedVal.MilliCPU = leftRes.MilliCPU - rightRes.MilliCPU 569 } else { 570 decreasedVal.MilliCPU = rightRes.MilliCPU - leftRes.MilliCPU 571 } 572 573 if leftRes.Memory > rightRes.Memory { 574 increasedVal.Memory = leftRes.Memory - rightRes.Memory 575 } else { 576 decreasedVal.Memory = rightRes.Memory - leftRes.Memory 577 } 578 579 increasedVal.ScalarResources = make(map[v1.ResourceName]float64) 580 decreasedVal.ScalarResources = make(map[v1.ResourceName]float64) 581 for lName, lQuant := range leftRes.ScalarResources { 582 rQuant := rightRes.ScalarResources[lName] 583 if lQuant == float64(Infinity) { 584 increasedVal.ScalarResources[lName] = lQuant 585 continue 586 } 587 if rQuant == float64(Infinity) { 588 decreasedVal.ScalarResources[lName] = rQuant 589 continue 590 } 591 if lQuant > rQuant { 592 increasedVal.ScalarResources[lName] = lQuant - rQuant 593 } else { 594 decreasedVal.ScalarResources[lName] = rQuant - lQuant 595 } 596 } 597 598 return increasedVal, decreasedVal 599 } 600 601 // AddScalar adds a resource by a scalar value of this resource. 602 func (r *Resource) AddScalar(name v1.ResourceName, quantity float64) { 603 r.SetScalar(name, r.ScalarResources[name]+quantity) 604 } 605 606 // SetScalar sets a resource by a scalar value of this resource. 607 func (r *Resource) SetScalar(name v1.ResourceName, quantity float64) { 608 // Lazily allocate scalar resource map. 609 if r.ScalarResources == nil { 610 r.ScalarResources = map[v1.ResourceName]float64{} 611 } 612 r.ScalarResources[name] = quantity 613 } 614 615 // MinDimensionResource is used to reset the r resource dimension which is less than rr 616 // e.g r resource is <cpu 2000.00, memory 4047845376.00, hugepages-2Mi 0.00, hugepages-1Gi 0.00> 617 // rr resource is <cpu 3000.00, memory 1000.00> 618 // return r resource is <cpu 2000.00, memory 1000.00, hugepages-2Mi 0.00, hugepages-1Gi 0.00> 619 // @param defaultValue "default value for resource dimension not defined in ScalarResources. Its value can only be one of 'Zero' and 'Infinity'" 620 func (r *Resource) MinDimensionResource(rr *Resource, defaultValue DimensionDefaultValue) *Resource { 621 if rr.MilliCPU < r.MilliCPU { 622 r.MilliCPU = rr.MilliCPU 623 } 624 if rr.Memory < r.Memory { 625 r.Memory = rr.Memory 626 } 627 628 if r.ScalarResources == nil { 629 return r 630 } 631 632 if rr.ScalarResources == nil { 633 if defaultValue == Infinity { 634 return r 635 } 636 637 for name := range r.ScalarResources { 638 r.ScalarResources[name] = 0 639 } 640 return r 641 } 642 643 for name, quant := range r.ScalarResources { 644 rQuant, ok := rr.ScalarResources[name] 645 if ok { 646 r.ScalarResources[name] = math.Min(quant, rQuant) 647 } else { 648 if defaultValue == Infinity { 649 continue 650 } 651 652 r.ScalarResources[name] = 0 653 } 654 } 655 return r 656 } 657 658 // setDefaultValue sets default value for resource dimension not defined of ScalarResource in leftResource and rightResource 659 // @param defaultValue "default value for resource dimension not defined in ScalarResources. It can only be one of 'Zero' or 'Infinity'" 660 func (r *Resource) setDefaultValue(leftResource, rightResource *Resource, defaultValue DimensionDefaultValue) { 661 if leftResource.ScalarResources == nil { 662 leftResource.ScalarResources = map[v1.ResourceName]float64{} 663 } 664 if rightResource.ScalarResources == nil { 665 rightResource.ScalarResources = map[v1.ResourceName]float64{} 666 } 667 for resourceName := range leftResource.ScalarResources { 668 _, ok := rightResource.ScalarResources[resourceName] 669 if !ok { 670 rightResource.ScalarResources[resourceName] = float64(defaultValue) 671 } 672 } 673 674 for resourceName := range rightResource.ScalarResources { 675 _, ok := leftResource.ScalarResources[resourceName] 676 if !ok { 677 leftResource.ScalarResources[resourceName] = float64(defaultValue) 678 } 679 } 680 } 681 682 // ParseResourceList parses the given configuration map into an API 683 // ResourceList or returns an error. 684 func ParseResourceList(m map[string]string) (v1.ResourceList, error) { 685 if len(m) == 0 { 686 return nil, nil 687 } 688 rl := make(v1.ResourceList) 689 for k, v := range m { 690 switch v1.ResourceName(k) { 691 // CPU, memory, local storage, and PID resources are supported. 692 case v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage: 693 q, err := resource.ParseQuantity(v) 694 if err != nil { 695 return nil, err 696 } 697 if q.Sign() == -1 { 698 return nil, fmt.Errorf("resource quantity for %q cannot be negative: %v", k, v) 699 } 700 rl[v1.ResourceName(k)] = q 701 default: 702 return nil, fmt.Errorf("cannot reserve %q resource", k) 703 } 704 } 705 return rl, nil 706 } 707 708 func GetMinResource() float64 { 709 return minResource 710 } 711 712 // ResourceNameList struct defines resource name collection 713 type ResourceNameList []v1.ResourceName 714 715 // Contains judges whether rr is subset of r 716 func (r ResourceNameList) Contains(rr ResourceNameList) bool { 717 for _, rrName := range ([]v1.ResourceName)(rr) { 718 isResourceExist := false 719 for _, rName := range ([]v1.ResourceName)(r) { 720 if rName == rrName { 721 isResourceExist = true 722 break 723 } 724 } 725 if !isResourceExist { 726 return false 727 } 728 } 729 return true 730 } 731 732 func IsCountQuota(name v1.ResourceName) bool { 733 return strings.HasPrefix(string(name), "count/") 734 }