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  }