github.com/maxgio92/test-infra@v0.1.0/kubetest/boskos/common/config.go (about) 1 /* 2 Copyright 2019 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 common 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 24 utilerrors "k8s.io/apimachinery/pkg/util/errors" 25 "k8s.io/apimachinery/pkg/util/validation" 26 "sigs.k8s.io/yaml" 27 ) 28 29 // ValidateConfig validates config with existing resources 30 // In: boskosConfig - a boskos config defining resources 31 // Out: nil on success, error on failure 32 func ValidateConfig(config *BoskosConfig) error { 33 if len(config.Resources) == 0 { 34 return errors.New("empty config") 35 } 36 resourceNames := map[string]bool{} 37 resourceTypes := map[string]bool{} 38 resourcesNeeds := map[string]int{} 39 actualResources := map[string]int{} 40 41 var errs []error 42 for idx, e := range config.Resources { 43 if e.Type == "" { 44 errs = append(errs, fmt.Errorf(".%d.type: must be set", idx)) 45 } 46 47 if resourceTypes[e.Type] { 48 errs = append(errs, fmt.Errorf(".%d.type.%s already exists", idx, e.Type)) 49 } 50 51 names := e.Names 52 if e.IsDRLC() { 53 // Dynamic Resource 54 if e.MaxCount == 0 { 55 errs = append(errs, fmt.Errorf(".%d.max-count: must be >0", idx)) 56 } 57 if e.MinCount > e.MaxCount { 58 errs = append(errs, fmt.Errorf(".%d.min-count: must be <= .%d.max-count", idx, idx)) 59 } 60 for i := 0; i < e.MaxCount; i++ { 61 name := GenerateDynamicResourceName() 62 names = append(names, name) 63 } 64 65 // Updating resourceNeeds 66 for k, v := range e.Needs { 67 resourcesNeeds[k] += v * e.MaxCount 68 } 69 70 } else { 71 if e.MinCount != 0 { 72 errs = append(errs, fmt.Errorf(".%d.min-count must be unset when the names property is set", idx)) 73 } 74 if e.MaxCount != 0 { 75 errs = append(errs, fmt.Errorf(".%d.max-count must be unset when the names property is set", idx)) 76 } 77 } 78 actualResources[e.Type] += len(names) 79 for nameIdx, name := range names { 80 validationErrs := validation.IsDNS1123Subdomain(name) 81 if len(validationErrs) != 0 { 82 errs = append(errs, fmt.Errorf(".%d.names.%d(%s) is invalid: %v", idx, nameIdx, name, validationErrs)) 83 } 84 85 if _, ok := resourceNames[name]; ok { 86 errs = append(errs, fmt.Errorf(".%d.names.%d(%s) is a duplicate", idx, nameIdx, name)) 87 continue 88 } 89 resourceNames[name] = true 90 } 91 } 92 93 for rType, needs := range resourcesNeeds { 94 actual, ok := actualResources[rType] 95 if !ok { 96 errs = append(errs, fmt.Errorf("need for resource %s that does not exist", rType)) 97 } 98 if needs > actual { 99 errs = append(errs, fmt.Errorf("not enough resource of type %s for provisioning", rType)) 100 } 101 } 102 return utilerrors.NewAggregate(errs) 103 } 104 105 // ParseConfig reads in configPath and returns a list of resource objects 106 // on success. 107 func ParseConfig(configPath string) (*BoskosConfig, error) { 108 file, err := os.ReadFile(configPath) 109 if err != nil { 110 return nil, err 111 } 112 113 var data BoskosConfig 114 if err := yaml.Unmarshal(file, &data); err != nil { 115 return nil, err 116 } 117 return &data, nil 118 }