blob: f2eb0a4409445f195702e1596f1921f1a71dc774 [file] [log] [blame]
package collection
import (
"fmt"
"github.com/apache/dubbo-kubernetes/pkg/slices"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-multierror"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/apache/dubbo-kubernetes/pkg/config"
"github.com/apache/dubbo-kubernetes/pkg/util/sets"
)
// Schemas contains metadata about configuration resources.
type Schemas struct {
byCollection map[config.GroupVersionKind]Schema
byAddOrder []Schema
}
// SchemasFor is a shortcut for creating Schemas. It uses MustAdd for each element.
func SchemasFor(schemas ...Schema) Schemas {
b := NewSchemasBuilder()
for _, s := range schemas {
b.MustAdd(s)
}
return b.Build()
}
// SchemasBuilder is a builder for the schemas type.
type SchemasBuilder struct {
schemas Schemas
}
// NewSchemasBuilder returns a new instance of SchemasBuilder.
func NewSchemasBuilder() *SchemasBuilder {
s := Schemas{
byCollection: make(map[config.GroupVersionKind]Schema),
}
return &SchemasBuilder{
schemas: s,
}
}
// Add a new collection to the schemas.
func (b *SchemasBuilder) Add(s Schema) error {
if _, found := b.schemas.byCollection[s.GroupVersionKind()]; found {
return fmt.Errorf("collection already exists: %v", s.GroupVersionKind())
}
b.schemas.byCollection[s.GroupVersionKind()] = s
b.schemas.byAddOrder = append(b.schemas.byAddOrder, s)
return nil
}
// MustAdd calls Add and panics if it fails.
func (b *SchemasBuilder) MustAdd(s Schema) *SchemasBuilder {
if err := b.Add(s); err != nil {
panic(fmt.Sprintf("SchemasBuilder.MustAdd: %v", err))
}
return b
}
// Build a new schemas from this SchemasBuilder.
func (b *SchemasBuilder) Build() Schemas {
s := b.schemas
// Avoid modify after Build.
b.schemas = Schemas{}
return s
}
// ForEach executes the given function on each contained schema, until the function returns true.
func (s Schemas) ForEach(handleSchema func(Schema) (done bool)) {
for _, schema := range s.byAddOrder {
if handleSchema(schema) {
return
}
}
}
func (s Schemas) Union(otherSchemas Schemas) Schemas {
resultBuilder := NewSchemasBuilder()
for _, myschema := range s.All() {
// an error indicates the schema has already been added, which doesn't negatively impact intersect
_ = resultBuilder.Add(myschema)
}
for _, myschema := range otherSchemas.All() {
// an error indicates the schema has already been added, which doesn't negatively impact intersect
_ = resultBuilder.Add(myschema)
}
return resultBuilder.Build()
}
func (s Schemas) Intersect(otherSchemas Schemas) Schemas {
resultBuilder := NewSchemasBuilder()
schemaLookup := sets.String{}
for _, myschema := range s.All() {
schemaLookup.Insert(myschema.String())
}
// Only add schemas that are in both sets
for _, myschema := range otherSchemas.All() {
if schemaLookup.Contains(myschema.String()) {
_ = resultBuilder.Add(myschema)
}
}
return resultBuilder.Build()
}
// FindByGroupVersionKind searches and returns the first schema with the given GVK
func (s Schemas) FindByGroupVersionKind(gvk config.GroupVersionKind) (Schema, bool) {
for _, rs := range s.byAddOrder {
if rs.GroupVersionKind() == gvk {
return rs, true
}
}
return nil, false
}
// FindByGroupVersionAliasesKind searches and returns the first schema with the given GVK,
// if not found, it will search for version aliases for the schema to see if there is a match.
func (s Schemas) FindByGroupVersionAliasesKind(gvk config.GroupVersionKind) (Schema, bool) {
for _, rs := range s.byAddOrder {
for _, va := range rs.GroupVersionAliasKinds() {
if va == gvk {
return rs, true
}
}
}
return nil, false
}
// FindByGroupKind searches and returns the first schema with the given GVK, ignoring versions.
// Generally it's a good idea to use FindByGroupVersionAliasesKind, which validates the version as well.
// FindByGroupKind provides future proofing against versions we don't yet know about; given we don't know them, its risky.
func (s Schemas) FindByGroupKind(gvk config.GroupVersionKind) (Schema, bool) {
for _, rs := range s.byAddOrder {
if rs.Group() == gvk.Group && rs.Kind() == gvk.Kind {
return rs, true
}
}
return nil, false
}
// FindByGroupVersionResource searches and returns the first schema with the given GVR
func (s Schemas) FindByGroupVersionResource(gvr schema.GroupVersionResource) (Schema, bool) {
for _, rs := range s.byAddOrder {
if rs.GroupVersionResource() == gvr {
return rs, true
}
}
return nil, false
}
// All returns all known Schemas
func (s Schemas) All() []Schema {
return slices.Clone(s.byAddOrder)
}
// GroupVersionKinds returns all known GroupVersionKinds
func (s Schemas) GroupVersionKinds() []config.GroupVersionKind {
res := []config.GroupVersionKind{}
for _, r := range s.All() {
res = append(res, r.GroupVersionKind())
}
return res
}
// Add creates a copy of this Schemas with the given schemas added.
func (s Schemas) Add(toAdd ...Schema) Schemas {
b := NewSchemasBuilder()
for _, s := range s.byAddOrder {
b.MustAdd(s)
}
for _, s := range toAdd {
b.MustAdd(s)
}
return b.Build()
}
// Remove creates a copy of this Schemas with the given schemas removed.
func (s Schemas) Remove(toRemove ...Schema) Schemas {
b := NewSchemasBuilder()
for _, s := range s.byAddOrder {
shouldAdd := true
for _, r := range toRemove {
if r.Equal(s) {
shouldAdd = false
break
}
}
if shouldAdd {
b.MustAdd(s)
}
}
return b.Build()
}
// Kinds returns all known resource kinds.
func (s Schemas) Kinds() []string {
kinds := sets.NewWithLength[string](len(s.byAddOrder))
for _, s := range s.byAddOrder {
kinds.Insert(s.Kind())
}
out := kinds.UnsortedList()
return slices.Sort(out)
}
// Validate the schemas. Returns error if there is a problem.
func (s Schemas) Validate() (err error) {
for _, c := range s.byAddOrder {
err = multierror.Append(err, c.Validate()).ErrorOrNil()
}
return
}
func (s Schemas) Equal(o Schemas) bool {
return cmp.Equal(s.byAddOrder, o.byAddOrder)
}