Files
zeekayandHanzo Dev 1ab6e799c1 cd: the module is github.com/hanzoai/cd, where it is actually hosted
go.mod still declared `module github.com/hanzoai/deploy` after the repo was
renamed. A Go module path is not cosmetic — it is the resolvable location. So
`go get github.com/hanzoai/cd` could not work, and anything importing this
module had to spell a name that no longer exists at that address. The build log
of v3.6.0 is where it surfaced: the compiler was still emitting
github.com/hanzoai/deploy/cmd/cd/commands.

3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile
and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to
ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from
the module path, replaced separately so neither could clobber the other.

go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 21:16:24 -07:00

132 lines
4.0 KiB
Go

package appstate
import (
"context"
"fmt"
"sort"
"time"
"github.com/spf13/cobra"
appv1 "github.com/hanzoai/cd/pkg/apis/application/v1alpha1"
cacheutil "github.com/hanzoai/cd/util/cache"
"github.com/hanzoai/cd/util/env"
)
var (
ErrCacheMiss = cacheutil.ErrCacheMiss
treeShardSize = env.ParseInt64FromEnv("CD_APPLICATION_TREE_SHARD_SIZE", 0, 0, 1000)
)
const (
clusterInfoCacheExpiration = 10 * time.Minute
)
type Cache struct {
Cache *cacheutil.Cache
appStateCacheExpiration time.Duration
}
func NewCache(cache *cacheutil.Cache, appStateCacheExpiration time.Duration) *Cache {
return &Cache{cache, appStateCacheExpiration}
}
func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) {
var appStateCacheExpiration time.Duration
cmd.Flags().DurationVar(&appStateCacheExpiration, "app-state-cache-expiration", env.ParseDurationFromEnv("CD_APP_STATE_CACHE_EXPIRATION", 1*time.Hour, 0, 10*time.Hour), "Cache expiration for app state")
cacheFactory := cacheutil.AddCacheFlagsToCmd(cmd, opts...)
return func() (*Cache, error) {
cache, err := cacheFactory()
if err != nil {
return nil, err
}
return NewCache(cache, appStateCacheExpiration), nil
}
}
func (c *Cache) GetItem(key string, item any) error {
return c.Cache.GetItem(key, item)
}
func (c *Cache) SetItem(key string, item any, expiration time.Duration, deletion bool) error {
return c.Cache.SetItem(key, item, &cacheutil.CacheActionOpts{Expiration: expiration, Delete: deletion})
}
func appManagedResourcesKey(appName string) string {
return "app|managed-resources|" + appName
}
func (c *Cache) GetAppManagedResources(appName string, res *[]*appv1.ResourceDiff) error {
err := c.GetItem(appManagedResourcesKey(appName), &res)
return err
}
func (c *Cache) SetAppManagedResources(appName string, managedResources []*appv1.ResourceDiff) error {
sort.Slice(managedResources, func(i, j int) bool {
return managedResources[i].FullName() < managedResources[j].FullName()
})
return c.SetItem(appManagedResourcesKey(appName), managedResources, c.appStateCacheExpiration, managedResources == nil)
}
func appResourcesTreeKey(appName string, shard int64) string {
key := "app|resources-tree|" + appName
if shard > 0 {
key = fmt.Sprintf("%s|%d", key, shard)
}
return key
}
func clusterInfoKey(server string) string {
return "cluster|info|" + server
}
func (c *Cache) GetAppResourcesTree(appName string, res *appv1.ApplicationTree) error {
err := c.GetItem(appResourcesTreeKey(appName, 0), &res)
if res.ShardsCount > 1 {
for i := int64(1); i < res.ShardsCount; i++ {
var shard appv1.ApplicationTree
err = c.GetItem(appResourcesTreeKey(appName, i), &shard)
if err != nil {
return err
}
res.Merge(&shard)
}
}
return err
}
func (c *Cache) OnAppResourcesTreeChanged(ctx context.Context, appName string, callback func() error) error {
return c.Cache.OnUpdated(ctx, appManagedResourcesKey(appName), callback)
}
func (c *Cache) SetAppResourcesTree(appName string, resourcesTree *appv1.ApplicationTree) error {
if resourcesTree == nil {
if err := c.SetItem(appResourcesTreeKey(appName, 0), resourcesTree, c.appStateCacheExpiration, true); err != nil {
return err
}
} else {
// Splitting resource tree into shards reduces number of Redis SET calls and therefore amount of traffic sent
// from controller to Redis. Controller still stores each shard in cache but util/cache/twolevelclient.go
// forwards request to Redis only if shard actually changes.
for i, shard := range resourcesTree.GetShards(treeShardSize) {
if err := c.SetItem(appResourcesTreeKey(appName, int64(i)), shard, c.appStateCacheExpiration, false); err != nil {
return err
}
}
}
return c.Cache.NotifyUpdated(appManagedResourcesKey(appName))
}
func (c *Cache) SetClusterInfo(server string, info *appv1.ClusterInfo) error {
return c.SetItem(clusterInfoKey(server), info, clusterInfoCacheExpiration, info == nil)
}
func (c *Cache) GetClusterInfo(server string, res *appv1.ClusterInfo) error {
err := c.GetItem(clusterInfoKey(server), &res)
return err
}