mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-06 05:46:05 +02:00
134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
package reconciler
|
|
|
|
/*
|
|
* Bundle is a representation of a bundle resource
|
|
*
|
|
*/
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type bundle struct {
|
|
id string
|
|
synced bool
|
|
}
|
|
|
|
// Bundles is a list of bundles to sync
|
|
type Bundles struct {
|
|
sync.Mutex
|
|
bundles []bundle
|
|
}
|
|
|
|
// Set sets the list of bundles to sync.
|
|
// bundles would be synced in the order they are provided.
|
|
func (b *Bundles) Set(bundles []string) {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
|
|
b.bundles = make([]bundle, len(bundles))
|
|
for i, id := range bundles {
|
|
b.bundles[i] = bundle{id: id, synced: false}
|
|
}
|
|
}
|
|
|
|
// MarkForSync marks the bundle with the given ID for synchronization
|
|
// if bundle does not exist, it is added to the end of the list as we do not know its relative priority.
|
|
// we will have just a handful of bundles, so it is not a big deal to scan the list on each (infrequent) update.
|
|
func (b *Bundles) MarkForSync(id string) {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
|
|
for i := range b.bundles {
|
|
if b.bundles[i].id == id {
|
|
b.bundles[i].synced = false
|
|
return
|
|
}
|
|
}
|
|
|
|
b.bundles = append(b.bundles, bundle{id: id, synced: false})
|
|
}
|
|
|
|
// GetNextBundleToSync returns the next bundle to sync.
|
|
// If there is no bundle to sync, it returns false.
|
|
func (b *Bundles) GetNextBundleToSync() (string, bool) {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
|
|
for i, bundle := range b.bundles {
|
|
if !bundle.synced {
|
|
b.bundles[i].synced = true
|
|
return bundle.id, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// GetBundles returns the list of bundles that have to be present in the cluster.
|
|
func (c *service) RefreshBundleList(ctx context.Context) error {
|
|
resp, err := c.config.clusterAPI.GetClusterResourceBundlesWithResponse(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("get bundles: %w", err)
|
|
}
|
|
if resp.JSON200 == nil {
|
|
return fmt.Errorf("get bundles: unexpected response: %d/%s", resp.StatusCode(), resp.Status())
|
|
}
|
|
|
|
ids := make([]string, 0, len(resp.JSON200.Bundles))
|
|
for _, v := range resp.JSON200.Bundles {
|
|
ids = append(ids, v.Id)
|
|
}
|
|
|
|
c.bundles.Set(ids)
|
|
return nil
|
|
}
|
|
|
|
// GetBundleDownloadURL returns the up to date download URL for the given bundle.
|
|
func (c *service) GetBundleDownloadURL(ctx context.Context, key string) (*url.URL, error) {
|
|
entry, ok := c.downloadURLCache[key]
|
|
if ok && entry.ExpiresAt.After(time.Now().Add(c.config.minDownloadTTL)) {
|
|
return &entry.URL, nil
|
|
}
|
|
|
|
delete(c.downloadURLCache, key)
|
|
|
|
p, err := c.getBundleDownloadURLFromAPI(ctx, key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get bundle download url (from api): %w", err)
|
|
}
|
|
|
|
c.downloadURLCache[key] = *p
|
|
return &p.URL, nil
|
|
}
|
|
|
|
func (c *service) getBundleDownloadURLFromAPI(ctx context.Context, key string) (*urlEntry, error) {
|
|
now := time.Now()
|
|
|
|
resp, err := c.config.clusterAPI.DownloadClusterResourceBundleWithResponse(ctx, key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("api: %w", err)
|
|
}
|
|
if resp.JSON200 == nil {
|
|
return nil, fmt.Errorf("unexpected api response: %d/%s", resp.StatusCode(), resp.Status())
|
|
}
|
|
|
|
expiresSeconds, err := strconv.ParseInt(resp.JSON200.ExpiresInSeconds, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse expiration: %w", err)
|
|
}
|
|
|
|
u, err := url.Parse(resp.JSON200.Url)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse url: %w", err)
|
|
}
|
|
|
|
return &urlEntry{
|
|
URL: *u,
|
|
ExpiresAt: now.Add(time.Duration(expiresSeconds) * time.Second),
|
|
}, nil
|
|
}
|