github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/cluster/cache.go (about) 1 package cluster 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 "k8s.io/apimachinery/pkg/types" 11 12 "github.com/tilt-dev/tilt/internal/controllers/apis/cluster" 13 "github.com/tilt-dev/tilt/internal/docker" 14 "github.com/tilt-dev/tilt/internal/k8s" 15 "github.com/tilt-dev/tilt/pkg/apis" 16 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 17 ) 18 19 func NewConnectionManager() *ConnectionManager { 20 return &ConnectionManager{} 21 } 22 23 type ConnectionManager struct { 24 connections sync.Map 25 } 26 27 var _ cluster.ClientProvider = &ConnectionManager{} 28 29 type connectionType string 30 31 const ( 32 connectionTypeK8s connectionType = "kubernetes" 33 connectionTypeDocker connectionType = "docker" 34 ) 35 36 type connection struct { 37 connType connectionType 38 spec v1alpha1.ClusterSpec 39 40 // createdAt is when the connection object was created. 41 // If initError is empty, it's effectively the time we connected to the 42 // cluster. Otherwise, it's when we _attempted_ to initialize the client 43 // and is used for retry/backoff. 44 createdAt time.Time 45 46 // initError is populated when the client cannot be instantiated. 47 // For example, if there's no ~/.kube/config, a Kubernetes client 48 // can't be created. 49 initError string 50 51 dockerClient docker.Client 52 k8sClient k8s.Client 53 54 arch string 55 serverVersion string 56 registry *v1alpha1.RegistryHosting 57 connStatus *v1alpha1.ClusterConnectionStatus 58 } 59 60 func (k *ConnectionManager) GetK8sClient(clusterKey types.NamespacedName) (k8s.Client, metav1.MicroTime, error) { 61 conn, err := k.validConnOrError(clusterKey, connectionTypeK8s) 62 if err != nil { 63 return nil, metav1.MicroTime{}, err 64 } 65 return conn.k8sClient, apis.NewMicroTime(conn.createdAt), nil 66 } 67 68 // GetComposeDockerClient gets the Docker client for the instance that Docker Compose is deploying to. 69 // 70 // This is not currently exposed by the ClientCache interface as Docker Compose logic has not been migrated 71 // to the apiserver. 72 func (k *ConnectionManager) GetComposeDockerClient(key types.NamespacedName) (docker.Client, error) { 73 conn, err := k.validConnOrError(key, connectionTypeDocker) 74 if err != nil { 75 return nil, err 76 } 77 return conn.dockerClient, nil 78 } 79 80 func (k *ConnectionManager) validConnOrError(key types.NamespacedName, connType connectionType) (connection, error) { 81 conn, ok := k.load(key) 82 if !ok { 83 return connection{}, cluster.NotFoundError 84 } 85 if conn.connType != connType { 86 return connection{}, fmt.Errorf("incorrect cluster client type: got %s, expected %s", 87 conn.connType, connType) 88 } 89 if conn.initError != "" { 90 return connection{}, errors.New(conn.initError) 91 } 92 // N.B. even if there is a statusError, the client is still returned, as it 93 // might still be functional even though it's in a degraded state 94 return conn, nil 95 } 96 97 func (k *ConnectionManager) store(key types.NamespacedName, conn connection) { 98 k.connections.Store(key, conn) 99 } 100 101 func (k *ConnectionManager) load(key types.NamespacedName) (connection, bool) { 102 v, ok := k.connections.Load(key) 103 if !ok { 104 return connection{}, false 105 } 106 return v.(connection), true 107 } 108 109 func (k *ConnectionManager) delete(key types.NamespacedName) { 110 k.connections.LoadAndDelete(key) 111 }