pkg/model/history/resourceinfo/resourcelease/history.go (106 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed 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 resourcelease import ( "errors" "log/slog" "time" "github.com/GoogleCloudPlatform/khi/pkg/common" ) // Resource identifier itself couldn't be found var NoResourceFound = errors.New("no resource identifier found") // Resource identifier was found, but no holder found at the given time var NoResourceLeaseHolderFoundAtTheTime = errors.New("no resource holder at the time") type LeaseHolder interface { Equals(holder LeaseHolder) bool } type lease[H LeaseHolder] struct { StartAt time.Time Holder H } // ResourceLeaseHistory is a common interface for memorizing resources used in the cluster. // This is used for example: IPs associated to a Pod, Load balancer names associated to Ingress ..etc. type ResourceLeaseHistory[H LeaseHolder] struct { leaseHolders *common.ShardingMap[[]*lease[H]] } func NewResourceLeaseHistory[H LeaseHolder]() *ResourceLeaseHistory[H] { return &ResourceLeaseHistory[H]{ leaseHolders: common.NewShardingMap[[]*lease[H]](common.NewSuffixShardingProvider(128, 4)), } } func (r *ResourceLeaseHistory[H]) TouchResourceLease(resourceIdentifier string, holdsAtLeastAt time.Time, holder H) { leaseHolderMap := r.leaseHolders.AcquireShard(resourceIdentifier) defer r.leaseHolders.ReleaseShard(resourceIdentifier) lastLease, index, err := r.getResourceLeaseHolderAtWithIndex(leaseHolderMap, resourceIdentifier, holdsAtLeastAt) newLease := &lease[H]{ Holder: holder, StartAt: holdsAtLeastAt, } if errors.Is(NoResourceFound, err) { leaseHolderMap[resourceIdentifier] = []*lease[H]{ newLease, } return } if errors.Is(NoResourceLeaseHolderFoundAtTheTime, err) { prevLeaseSeries := leaseHolderMap[resourceIdentifier] next := prevLeaseSeries[0] newLease := lease[H]{ Holder: holder, StartAt: holdsAtLeastAt, } current := []*lease[H]{&newLease} if holder.Equals(next.Holder) { current = append(current, prevLeaseSeries[1:]...) } else { current = append(current, prevLeaseSeries...) } leaseHolderMap[resourceIdentifier] = current return } if lastLease == nil { slog.Info("last lease was nil!") } if holder.Equals(lastLease.Holder) { // No change at the holder,ignore it. return } previous := leaseHolderMap[resourceIdentifier] if len(previous)-1 == index { leaseHolderMap[resourceIdentifier] = append(leaseHolderMap[resourceIdentifier], newLease) return } current := []*lease[H]{} current = append(current, previous[:index+1]...) current = append(current, newLease) current = append(current, previous[index+1:]...) leaseHolderMap[resourceIdentifier] = current } func (r *ResourceLeaseHistory[H]) GetResourceLeaseHolderAt(resourceIdentifier string, time time.Time) (*lease[H], error) { leaseHolderMap := r.leaseHolders.AcquireShard(resourceIdentifier) defer r.leaseHolders.ReleaseShard(resourceIdentifier) lease, _, err := r.getResourceLeaseHolderAtWithIndex(leaseHolderMap, resourceIdentifier, time) return lease, err } func (r *ResourceLeaseHistory[H]) getResourceLeaseHolderAtWithIndex(leaseHolderMap map[string][]*lease[H], resourceIdentifier string, time time.Time) (*lease[H], int, error) { if leases, found := leaseHolderMap[resourceIdentifier]; !found { return nil, 0, NoResourceFound } else { if time.Sub(leases[len(leases)-1].StartAt) >= 0 { return leases[len(leases)-1], len(leases) - 1, nil } if time.Sub(leases[0].StartAt) < 0 { return nil, 0, NoResourceLeaseHolderFoundAtTheTime } var min = 0 var max = len(leases) - 1 for { if max-min == 1 { break } mid := (max + min) / 2 if time.Sub(leases[mid].StartAt) >= 0 { min = mid } else { max = mid } } return leases[min], min, nil } } func (r *ResourceLeaseHistory[H]) GetAllIdentifiers() []string { return r.leaseHolders.AllKeys() }