blob: ad6677fbee01d84392a19e0ad5f562eb3a94dafe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package krt
import (
"fmt"
"github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
"github.com/apache/dubbo-kubernetes/pkg/slices"
"github.com/apache/dubbo-kubernetes/pkg/util/ptr"
"sync/atomic"
)
type dummyValue struct{}
type StaticSingleton[T any] interface {
Singleton[T]
Set(*T)
MarkSynced()
}
type static[T any] struct {
val *atomic.Pointer[T]
synced *atomic.Bool
id collectionUID
eventHandlers *handlers[T]
collectionName string
syncer Syncer
metadata Metadata
}
type staticHandler struct {
Syncer
remove func()
}
var _ Collection[dummyValue] = &static[dummyValue]{}
type collectionAdapter[T any] struct {
c Collection[T]
}
var (
_ Singleton[any] = &collectionAdapter[any]{}
)
func NewStatic[T any](initial *T, startSynced bool, opts ...CollectionOption) StaticSingleton[T] {
val := new(atomic.Pointer[T])
val.Store(initial)
x := &static[T]{
val: val,
synced: &atomic.Bool{},
id: nextUID(),
eventHandlers: &handlers[T]{},
}
x.synced.Store(startSynced)
o := buildCollectionOptions(opts...)
if o.name == "" {
o.name = fmt.Sprintf("Static[%v]", ptr.TypeName[T]())
}
x.collectionName = o.name
x.syncer = pollSyncer{
name: x.collectionName,
f: func() bool {
return x.synced.Load()
},
}
if o.metadata != nil {
x.metadata = o.metadata
}
return collectionAdapter[T]{x}
}
func NewSingleton[O any](hf TransformationEmpty[O], opts ...CollectionOption) Singleton[O] {
staticOpts := append(slices.Clone(opts), WithDebugging(nil))
dummyCollection := NewStatic[dummyValue](&dummyValue{}, true, staticOpts...).AsCollection()
col := NewCollection[dummyValue, O](dummyCollection, func(ctx HandlerContext, _ dummyValue) *O {
return hf(ctx)
}, opts...)
return collectionAdapter[O]{col}
}
func (d *static[T]) GetKey(k string) *T {
return d.val.Load()
}
func (d *static[T]) List() []T {
v := d.val.Load()
if v == nil {
return nil
}
return []T{*v}
}
func (d *static[T]) Metadata() Metadata {
return d.metadata
}
func (d *static[T]) Register(f func(o Event[T])) HandlerRegistration {
return registerHandlerAsBatched[T](d, f)
}
func (d *static[T]) RegisterBatch(f func(o []Event[T]), runExistingState bool) HandlerRegistration {
reg := d.eventHandlers.Insert(f)
if runExistingState {
v := d.val.Load()
if v != nil {
f([]Event[T]{{
New: v,
Event: controllers.EventAdd,
}})
}
}
return staticHandler{Syncer: d.syncer, remove: func() {
d.eventHandlers.Delete(reg)
}}
}
func (d *static[T]) Synced() Syncer {
return pollSyncer{
name: d.collectionName,
f: func() bool {
return d.synced.Load()
},
}
}
func (d *static[T]) HasSynced() bool {
return d.syncer.HasSynced()
}
func (d *static[T]) WaitUntilSynced(stop <-chan struct{}) bool {
return d.syncer.WaitUntilSynced(stop)
}
func (d *static[T]) Set(now *T) {
old := d.val.Swap(now)
if old == now {
return
}
for _, h := range d.eventHandlers.Get() {
h([]Event[T]{toEvent[T](old, now)})
}
}
// nolint: unused // (not true, its to implement an interface)
func (d *static[T]) dump() CollectionDump {
return CollectionDump{
Outputs: map[string]any{
"static": d.val.Load(),
},
Synced: d.HasSynced(),
}
}
// nolint: unused // (not true, its to implement an interface)
func (d *static[T]) augment(a any) any {
// not supported in this collection type
return a
}
// nolint: unused // (not true, its to implement an interface)
func (d *static[T]) name() string {
return d.collectionName
}
// nolint: unused // (not true, its to implement an interface)
func (d *static[T]) uid() collectionUID {
return d.id
}
func (d *static[T]) index(name string, extract func(o T) []string) indexer[T] {
panic("TODO")
}
func (c collectionAdapter[T]) uid() collectionUID {
return c.c.(uidable).uid()
}
func (c collectionAdapter[T]) MarkSynced() {
c.c.(*static[T]).synced.Store(true)
}
func (c collectionAdapter[T]) Get() *T {
// Guaranteed to be 0 or 1 len
res := c.c.List()
if len(res) == 0 {
return nil
}
return &res[0]
}
func (c collectionAdapter[T]) Set(t *T) {
c.c.(*static[T]).Set(t)
}
func (c collectionAdapter[T]) Metadata() Metadata {
// The metadata is passed to the internal dummy collection so just return that
return c.c.Metadata()
}
func (c collectionAdapter[T]) Register(f func(o Event[T])) HandlerRegistration {
return c.c.Register(f)
}
func (c collectionAdapter[T]) AsCollection() Collection[T] {
return c.c
}
func (d dummyValue) ResourceName() string {
return ""
}
func (s staticHandler) UnregisterHandler() {
s.remove()
}
func toEvent[T any](old, now *T) Event[T] {
if old == nil {
return Event[T]{
New: now,
Event: controllers.EventAdd,
}
} else if now == nil {
return Event[T]{
Old: old,
Event: controllers.EventDelete,
}
}
return Event[T]{
New: now,
Old: old,
Event: controllers.EventUpdate,
}
}