istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/schema/collection/schemas.go (about) 1 // Copyright Istio 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 collection 16 17 import ( 18 "fmt" 19 20 "github.com/google/go-cmp/cmp" 21 "github.com/hashicorp/go-multierror" 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 24 "istio.io/istio/pkg/config" 25 "istio.io/istio/pkg/config/schema/resource" 26 "istio.io/istio/pkg/slices" 27 "istio.io/istio/pkg/util/sets" 28 ) 29 30 // Schemas contains metadata about configuration resources. 31 type Schemas struct { 32 byCollection map[config.GroupVersionKind]resource.Schema 33 byAddOrder []resource.Schema 34 } 35 36 // SchemasFor is a shortcut for creating Schemas. It uses MustAdd for each element. 37 func SchemasFor(schemas ...resource.Schema) Schemas { 38 b := NewSchemasBuilder() 39 for _, s := range schemas { 40 b.MustAdd(s) 41 } 42 return b.Build() 43 } 44 45 // SchemasBuilder is a builder for the schemas type. 46 type SchemasBuilder struct { 47 schemas Schemas 48 } 49 50 // NewSchemasBuilder returns a new instance of SchemasBuilder. 51 func NewSchemasBuilder() *SchemasBuilder { 52 s := Schemas{ 53 byCollection: make(map[config.GroupVersionKind]resource.Schema), 54 } 55 56 return &SchemasBuilder{ 57 schemas: s, 58 } 59 } 60 61 // Add a new collection to the schemas. 62 func (b *SchemasBuilder) Add(s resource.Schema) error { 63 if _, found := b.schemas.byCollection[s.GroupVersionKind()]; found { 64 return fmt.Errorf("collection already exists: %v", s.GroupVersionKind()) 65 } 66 67 b.schemas.byCollection[s.GroupVersionKind()] = s 68 b.schemas.byAddOrder = append(b.schemas.byAddOrder, s) 69 return nil 70 } 71 72 // MustAdd calls Add and panics if it fails. 73 func (b *SchemasBuilder) MustAdd(s resource.Schema) *SchemasBuilder { 74 if err := b.Add(s); err != nil { 75 panic(fmt.Sprintf("SchemasBuilder.MustAdd: %v", err)) 76 } 77 return b 78 } 79 80 // Build a new schemas from this SchemasBuilder. 81 func (b *SchemasBuilder) Build() Schemas { 82 s := b.schemas 83 84 // Avoid modify after Build. 85 b.schemas = Schemas{} 86 87 return s 88 } 89 90 // ForEach executes the given function on each contained schema, until the function returns true. 91 func (s Schemas) ForEach(handleSchema func(resource.Schema) (done bool)) { 92 for _, schema := range s.byAddOrder { 93 if handleSchema(schema) { 94 return 95 } 96 } 97 } 98 99 func (s Schemas) Union(otherSchemas Schemas) Schemas { 100 resultBuilder := NewSchemasBuilder() 101 for _, myschema := range s.All() { 102 // an error indicates the schema has already been added, which doesn't negatively impact intersect 103 _ = resultBuilder.Add(myschema) 104 } 105 for _, myschema := range otherSchemas.All() { 106 // an error indicates the schema has already been added, which doesn't negatively impact intersect 107 _ = resultBuilder.Add(myschema) 108 } 109 return resultBuilder.Build() 110 } 111 112 func (s Schemas) Intersect(otherSchemas Schemas) Schemas { 113 resultBuilder := NewSchemasBuilder() 114 115 schemaLookup := sets.String{} 116 for _, myschema := range s.All() { 117 schemaLookup.Insert(myschema.String()) 118 } 119 120 // Only add schemas that are in both sets 121 for _, myschema := range otherSchemas.All() { 122 if schemaLookup.Contains(myschema.String()) { 123 _ = resultBuilder.Add(myschema) 124 } 125 } 126 return resultBuilder.Build() 127 } 128 129 // FindByGroupVersionKind searches and returns the first schema with the given GVK 130 func (s Schemas) FindByGroupVersionKind(gvk config.GroupVersionKind) (resource.Schema, bool) { 131 for _, rs := range s.byAddOrder { 132 if rs.GroupVersionKind() == gvk { 133 return rs, true 134 } 135 } 136 137 return nil, false 138 } 139 140 // FindByGroupVersionAliasesKind searches and returns the first schema with the given GVK, 141 // if not found, it will search for version aliases for the schema to see if there is a match. 142 func (s Schemas) FindByGroupVersionAliasesKind(gvk config.GroupVersionKind) (resource.Schema, bool) { 143 for _, rs := range s.byAddOrder { 144 for _, va := range rs.GroupVersionAliasKinds() { 145 if va == gvk { 146 return rs, true 147 } 148 } 149 } 150 return nil, false 151 } 152 153 // FindByGroupVersionResource searches and returns the first schema with the given GVR 154 func (s Schemas) FindByGroupVersionResource(gvr schema.GroupVersionResource) (resource.Schema, bool) { 155 for _, rs := range s.byAddOrder { 156 if rs.GroupVersionResource() == gvr { 157 return rs, true 158 } 159 } 160 161 return nil, false 162 } 163 164 // All returns all known Schemas 165 func (s Schemas) All() []resource.Schema { 166 return slices.Clone(s.byAddOrder) 167 } 168 169 // GroupVersionKinds returns all known GroupVersionKinds 170 func (s Schemas) GroupVersionKinds() []config.GroupVersionKind { 171 res := []config.GroupVersionKind{} 172 for _, r := range s.All() { 173 res = append(res, r.GroupVersionKind()) 174 } 175 return res 176 } 177 178 // Add creates a copy of this Schemas with the given schemas added. 179 func (s Schemas) Add(toAdd ...resource.Schema) Schemas { 180 b := NewSchemasBuilder() 181 182 for _, s := range s.byAddOrder { 183 b.MustAdd(s) 184 } 185 186 for _, s := range toAdd { 187 b.MustAdd(s) 188 } 189 190 return b.Build() 191 } 192 193 // Remove creates a copy of this Schemas with the given schemas removed. 194 func (s Schemas) Remove(toRemove ...resource.Schema) Schemas { 195 b := NewSchemasBuilder() 196 197 for _, s := range s.byAddOrder { 198 shouldAdd := true 199 for _, r := range toRemove { 200 if r.Equal(s) { 201 shouldAdd = false 202 break 203 } 204 } 205 if shouldAdd { 206 b.MustAdd(s) 207 } 208 } 209 210 return b.Build() 211 } 212 213 // Kinds returns all known resource kinds. 214 func (s Schemas) Kinds() []string { 215 kinds := sets.NewWithLength[string](len(s.byAddOrder)) 216 for _, s := range s.byAddOrder { 217 kinds.Insert(s.Kind()) 218 } 219 220 out := kinds.UnsortedList() 221 return slices.Sort(out) 222 } 223 224 // Validate the schemas. Returns error if there is a problem. 225 func (s Schemas) Validate() (err error) { 226 for _, c := range s.byAddOrder { 227 err = multierror.Append(err, c.Validate()).ErrorOrNil() 228 } 229 return 230 } 231 232 func (s Schemas) Equal(o Schemas) bool { 233 return cmp.Equal(s.byAddOrder, o.byAddOrder) 234 }