1// Copyright 2019 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 15package collection 16 17import ( 18 "fmt" 19 "sort" 20 "strings" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/hashicorp/go-multierror" 24 25 "istio.io/istio/pkg/config/schema/resource" 26) 27 28// Schemas contains metadata about configuration resources. 29type Schemas struct { 30 byCollection map[Name]Schema 31 byAddOrder []Schema 32} 33 34// SchemasFor is a shortcut for creating Schemas. It uses MustAdd for each element. 35func SchemasFor(schemas ...Schema) Schemas { 36 b := NewSchemasBuilder() 37 for _, s := range schemas { 38 b.MustAdd(s) 39 } 40 return b.Build() 41} 42 43// SchemasBuilder is a builder for the schemas type. 44type SchemasBuilder struct { 45 schemas Schemas 46} 47 48// NewSchemasBuilder returns a new instance of SchemasBuilder. 49func NewSchemasBuilder() *SchemasBuilder { 50 s := Schemas{ 51 byCollection: make(map[Name]Schema), 52 } 53 54 return &SchemasBuilder{ 55 schemas: s, 56 } 57} 58 59// Add a new collection to the schemas. 60func (b *SchemasBuilder) Add(s Schema) error { 61 if _, found := b.schemas.byCollection[s.Name()]; found { 62 return fmt.Errorf("collection already exists: %v", s.Name()) 63 } 64 65 b.schemas.byCollection[s.Name()] = s 66 b.schemas.byAddOrder = append(b.schemas.byAddOrder, s) 67 return nil 68} 69 70// MustAdd calls Add and panics if it fails. 71func (b *SchemasBuilder) MustAdd(s Schema) *SchemasBuilder { 72 if err := b.Add(s); err != nil { 73 panic(fmt.Sprintf("SchemasBuilder.MustAdd: %v", err)) 74 } 75 return b 76} 77 78// Build a new schemas from this SchemasBuilder. 79func (b *SchemasBuilder) Build() Schemas { 80 s := b.schemas 81 82 // Avoid modify after Build. 83 b.schemas = Schemas{} 84 85 return s 86} 87 88// ForEach executes the given function on each contained schema, until the function returns true. 89func (s Schemas) ForEach(handleSchema func(Schema) (done bool)) { 90 for _, schema := range s.byAddOrder { 91 if handleSchema(schema) { 92 return 93 } 94 } 95} 96 97// Find looks up a Schema by its collection name. 98func (s Schemas) Find(collection string) (Schema, bool) { 99 i, ok := s.byCollection[Name(collection)] 100 return i, ok 101} 102 103// MustFind calls Find and panics if not found. 104func (s Schemas) MustFind(collection string) Schema { 105 i, ok := s.Find(collection) 106 if !ok { 107 panic(fmt.Sprintf("schemas.MustFind: matching entry not found for collection: %q", collection)) 108 } 109 return i 110} 111 112// FindByKind searches and returns the first schema with the given kind 113func (s Schemas) FindByGroupVersionKind(gvk resource.GroupVersionKind) (Schema, bool) { 114 for _, rs := range s.byAddOrder { 115 if rs.Resource().GroupVersionKind() == gvk { 116 return rs, true 117 } 118 } 119 120 return nil, false 121} 122 123// FindByKind searches and returns the first schema with the given kind 124func (s Schemas) FindByPlural(plural string) (Schema, bool) { 125 for _, rs := range s.byAddOrder { 126 if rs.Resource().Plural() == plural { 127 return rs, true 128 } 129 } 130 131 return nil, false 132} 133 134// MustFind calls FindByGroupVersionKind and panics if not found. 135func (s Schemas) MustFindByGroupVersionKind(gvk resource.GroupVersionKind) Schema { 136 r, found := s.FindByGroupVersionKind(gvk) 137 if !found { 138 panic(fmt.Sprintf("Schemas.MustFindByGroupVersionKind: unable to find %s", gvk)) 139 } 140 return r 141} 142 143// All returns all known Schemas 144func (s Schemas) All() []Schema { 145 return append(make([]Schema, 0, len(s.byAddOrder)), s.byAddOrder...) 146} 147 148// Add creates a copy of this Schemas with the given schemas added. 149func (s Schemas) Add(toAdd ...Schema) Schemas { 150 b := NewSchemasBuilder() 151 152 for _, s := range s.byAddOrder { 153 b.MustAdd(s) 154 } 155 156 for _, s := range toAdd { 157 b.MustAdd(s) 158 } 159 160 return b.Build() 161 162} 163 164// Remove creates a copy of this Schemas with the given schemas removed. 165func (s Schemas) Remove(toRemove ...Schema) Schemas { 166 b := NewSchemasBuilder() 167 168 for _, s := range s.byAddOrder { 169 shouldAdd := true 170 for _, r := range toRemove { 171 if r.Name() == s.Name() { 172 shouldAdd = false 173 break 174 } 175 } 176 if shouldAdd { 177 b.MustAdd(s) 178 } 179 } 180 181 return b.Build() 182} 183 184// CollectionNames returns all known collections. 185func (s Schemas) CollectionNames() Names { 186 result := make(Names, 0, len(s.byAddOrder)) 187 188 for _, info := range s.byAddOrder { 189 result = append(result, info.Name()) 190 } 191 192 sort.Slice(result, func(i, j int) bool { 193 return strings.Compare(result[i].String(), result[j].String()) < 0 194 }) 195 196 return result 197} 198 199// Kinds returns all known resource kinds. 200func (s Schemas) Kinds() []string { 201 kinds := make(map[string]struct{}, len(s.byAddOrder)) 202 for _, s := range s.byAddOrder { 203 kinds[s.Resource().Kind()] = struct{}{} 204 } 205 206 out := make([]string, 0, len(kinds)) 207 for kind := range kinds { 208 out = append(out, kind) 209 } 210 211 sort.Strings(out) 212 return out 213} 214 215// DisabledCollectionNames returns the names of disabled collections 216func (s Schemas) DisabledCollectionNames() Names { 217 disabledCollections := make(Names, 0) 218 for _, i := range s.byAddOrder { 219 if i.IsDisabled() { 220 disabledCollections = append(disabledCollections, i.Name()) 221 } 222 } 223 return disabledCollections 224} 225 226// Validate the schemas. Returns error if there is a problem. 227func (s Schemas) Validate() (err error) { 228 for _, c := range s.byAddOrder { 229 err = multierror.Append(err, c.Resource().Validate()).ErrorOrNil() 230 } 231 return 232} 233 234func (s Schemas) Equal(o Schemas) bool { 235 return cmp.Equal(s.byAddOrder, o.byAddOrder) 236} 237