go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/cfg/pools.go (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cfg 16 17 import ( 18 "strings" 19 20 "go.chromium.org/luci/common/data/stringset" 21 "go.chromium.org/luci/common/errors" 22 "go.chromium.org/luci/config/validation" 23 "go.chromium.org/luci/server/auth/realms" 24 25 configpb "go.chromium.org/luci/swarming/proto/config" 26 "go.chromium.org/luci/swarming/server/validate" 27 ) 28 29 // Pool is a parsed config of some single pool. 30 type Pool struct { 31 // Realm is a realm with ACLs for this pool's resources. 32 // 33 // This is a global realm name, e.g. `infra:pool/flex/try`. 34 Realm string 35 36 // DefaultTaskRealm is a realm for tasks if they don't have a realm set. 37 // 38 // It is optional. If not set, all tasks must have the realm specified by 39 // the caller when they are created. 40 DefaultTaskRealm string 41 42 // TODO(vadimsh): Implement task templates. 43 } 44 45 // newPoolsConfig converts pools.cfg proto it a queryable map. 46 // 47 // pools.cfg here already passed the validation when it was first ingested. It 48 // is possible the server code itself changed and the existing config is no 49 // longer correct in some bad way. An error is returned in that case. 50 // 51 // On success returns a map "pool name => its config". 52 func newPoolsConfig(cfg *configpb.PoolsCfg) (map[string]*Pool, error) { 53 pools := map[string]*Pool{} 54 for _, pb := range cfg.Pool { 55 cfg, err := newPool(pb) 56 if err != nil { 57 return nil, errors.Annotate(err, "broken pools.cfg entry: %s", pb).Err() 58 } 59 for _, name := range pb.Name { 60 pools[name] = cfg 61 } 62 } 63 return pools, nil 64 } 65 66 // newPool processes a single configpb.Pool definition. 67 func newPool(pb *configpb.Pool) (*Pool, error) { 68 // TODO(vadimsh): Process TaskDeploymentScheme. 69 return &Pool{ 70 Realm: pb.Realm, 71 DefaultTaskRealm: pb.DefaultTaskRealm, 72 }, nil 73 } 74 75 // validatePoolsCfg validates pools.cfg, writing errors into `ctx`. 76 func validatePoolsCfg(ctx *validation.Context, cfg *configpb.PoolsCfg) { 77 pools := stringset.New(0) 78 79 validatePool := func(pb *configpb.Pool) { 80 // Deprecated fields that must not be set. 81 if pb.Schedulers != nil { 82 ctx.Errorf("setting deprecated field `schedulers`") 83 } 84 if pb.BotMonitoring != "" { 85 ctx.Errorf("setting deprecated field `bot_monitoring`") 86 } 87 88 if len(pb.Name) == 0 { 89 ctx.Errorf("at least one pool name must be given") 90 } 91 for _, name := range pb.Name { 92 if err := validate.DimensionValue(name); err != nil { 93 ctx.Errorf("bad pool name %q: %s", name, err) 94 } 95 if !pools.Add(name) { 96 ctx.Errorf("pool %q was already declared", name) 97 } 98 } 99 100 // Realm is required. 101 if pb.Realm == "" { 102 ctx.Errorf("missing required `realm` field") 103 } else if err := realms.ValidateRealmName(pb.Realm, realms.GlobalScope); err != nil { 104 ctx.Errorf("bad `realm` field: %s", err) 105 } 106 107 // DefaultTaskRealm is optional. 108 if pb.DefaultTaskRealm != "" { 109 if err := realms.ValidateRealmName(pb.DefaultTaskRealm, realms.GlobalScope); err != nil { 110 ctx.Errorf("bad `default_task_realm` field: %s", err) 111 } 112 } 113 114 // TODO(vadimsh): Validate task template settings. 115 116 // Silently skip remaining fields that are used by the Python implementation 117 // but ignored by the Go implementation: 118 // ExternalSchedulers: external schedulers not supported in Go. 119 // EnforcedRealmPermissions: realm permissions are always enforced. 120 // RbeMigration: RBE is the only supported scheduler. 121 // SchedulingAlgorithm: RBE doesn't support custom scheduling algorithms. 122 } 123 124 for idx, pool := range cfg.Pool { 125 title := "unnamed" 126 if len(pool.Name) != 0 { 127 title = strings.Join(pool.Name, ",") 128 } 129 ctx.Enter("pool #%d (%s)", idx+1, title) 130 validatePool(pool) 131 ctx.Exit() 132 } 133 }