55 "errors"
66 "fmt"
77 "math/rand/v2"
8- "net"
98 "net/http"
10- "strconv"
119 "sync"
1210 "time"
1311
@@ -16,7 +14,6 @@ import (
1614 "go.uber.org/zap"
1715
1816 "github.com/e2b-dev/infra/packages/api/internal/cfg"
19- "github.com/e2b-dev/infra/packages/api/internal/clusters/discovery"
2017 clickhouse "github.com/e2b-dev/infra/packages/clickhouse/pkg"
2118 "github.com/e2b-dev/infra/packages/shared/pkg/consts"
2219 "github.com/e2b-dev/infra/packages/shared/pkg/featureflags"
@@ -25,6 +22,7 @@ import (
2522 "github.com/e2b-dev/infra/packages/shared/pkg/logger"
2623 "github.com/e2b-dev/infra/packages/shared/pkg/logs/loki"
2724 "github.com/e2b-dev/infra/packages/shared/pkg/machineinfo"
25+ "github.com/e2b-dev/infra/packages/shared/pkg/servicediscovery"
2826 "github.com/e2b-dev/infra/packages/shared/pkg/smap"
2927 "github.com/e2b-dev/infra/packages/shared/pkg/synchronization"
3028 "github.com/e2b-dev/infra/packages/shared/pkg/telemetry"
@@ -43,7 +41,7 @@ type Cluster struct {
4341 AuthOrgID string
4442
4543 instances * smap.Map [* Instance ]
46- synchronization * synchronization.Synchronize [discovery. Item , * Instance ]
44+ synchronization * synchronization.Synchronize [servicediscovery. Instance , * Instance ]
4745 resources ClusterResource
4846}
4947
@@ -57,7 +55,7 @@ func NewCluster(
5755 domain * string ,
5856 authOrgID string ,
5957 sandboxes * smap.Map [* Instance ],
60- synchronization * synchronization.Synchronize [discovery. Item , * Instance ],
58+ synchronization * synchronization.Synchronize [servicediscovery. Instance , * Instance ],
6159 resources ClusterResource ,
6260) * Cluster {
6361 return & Cluster {
@@ -73,7 +71,7 @@ func NewCluster(
7371func newLocalCluster (
7472 ctx context.Context ,
7573 tel * telemetry.Client ,
76- storeDiscovery discovery. Discovery ,
74+ storeDiscovery servicediscovery. Discoverer ,
7775 clickhouse clickhouse.Clickhouse ,
7876 queryLogsProvider * loki.LokiQueryProvider ,
7977 sandboxLogsReader ClickhouseLogsReader ,
@@ -83,9 +81,9 @@ func newLocalCluster(
8381 clusterID := consts .LocalClusterID
8482
8583 instances := smap .New [* Instance ]()
86- instanceCreation := func (ctx context.Context , item discovery. Item ) (* Instance , error ) {
84+ instanceCreation := func (ctx context.Context , item servicediscovery. Instance ) (* Instance , error ) {
8785 // For local cluster we are doing direct connection to instance IP and API port and without additional cluster auth.
88- return newInstance (ctx , tel , nil , clusterID , item , net . JoinHostPort ( item .LocalIPAddress , strconv . FormatUint ( uint64 ( item . LocalInstanceApiPort ), 10 ) ), false )
86+ return newInstance (ctx , tel , nil , clusterID , item , item .Address ( ), false )
8987 }
9088
9189 store := instancesSyncStore {clusterID : clusterID , instances : instances , discovery : storeDiscovery , instanceCreation : instanceCreation }
@@ -105,6 +103,14 @@ func newLocalCluster(
105103 return c
106104}
107105
106+ // remoteInstanceAuthorization routes a call through the remote proxy to one
107+ // instance. The discovery ID is that instance's service id: the edge API
108+ // reports it per run, which is what the proxy keys on — the machine would send
109+ // the call to whatever is on it now.
110+ func remoteInstanceAuthorization (secret string , tls bool , sd servicediscovery.Instance ) * instanceAuthorization {
111+ return & instanceAuthorization {secret : secret , tls : tls , serviceInstanceID : sd .WorkloadID }
112+ }
113+
108114func newRemoteCluster (
109115 ctx context.Context ,
110116 tel * telemetry.Client ,
@@ -142,14 +148,13 @@ func newRemoteCluster(
142148 }
143149
144150 instances := smap .New [* Instance ]()
145- instanceCreation := func (ctx context.Context , item discovery.Item ) (* Instance , error ) {
146- // For remote cluster we are doing connection to endpoint that works as gRPC proxy and handles auth and routing for us.
147- auth := & instanceAuthorization {secret : secret , tls : endpointTLS , serviceInstanceID : item .InstanceID }
148-
149- return newInstance (ctx , tel , auth , clusterID , item , endpoint , endpointTLS )
151+ instanceCreation := func (ctx context.Context , item servicediscovery.Instance ) (* Instance , error ) {
152+ // A remote cluster is reached through an endpoint acting as a gRPC proxy,
153+ // which handles auth and routing for us.
154+ return newInstance (ctx , tel , remoteInstanceAuthorization (secret , endpointTLS , item ), clusterID , item , endpoint , endpointTLS )
150155 }
151156
152- storeDiscovery := discovery . NewRemoteServiceDiscovery ( clusterID , httpClient )
157+ storeDiscovery := servicediscovery . NewRemote ( httpClient )
153158 store := instancesSyncStore {clusterID : clusterID , instances : instances , instanceCreation : instanceCreation , discovery : storeDiscovery }
154159
155160 c := NewCluster (
@@ -191,17 +196,61 @@ func (c *Cluster) Close(ctx context.Context) error {
191196 return nil
192197}
193198
199+ // Scanned rather than looked up: the pool is keyed by the running process, and
200+ // a build persists the machine it was placed on (env_builds.cluster_node_id),
201+ // so this resolves the machine to whichever process is on it now.
194202func (c * Cluster ) GetTemplateBuilderByNodeID (nodeID string ) (* Instance , error ) {
195- instance , found := c .instances . Get ( nodeID )
203+ instance , found := builderOnNode ( c .instances , nodeID )
196204 if ! found {
197205 return nil , ErrTemplateBuilderNotFound
198206 }
199207
200- if info := instance .GetInfo (); info .Status == infogrpc .ServiceInfoStatus_Unhealthy || ! info .IsBuilder {
201- return nil , ErrTemplateBuilderNotFound
208+ return instance , nil
209+ }
210+
211+ // instanceOnNode finds an instance running on a machine, preferring one that is
212+ // not unhealthy. Like builderOnNode this cannot be a map lookup, but it applies
213+ // no further filter: callers that only need to reach the machine should not
214+ // start rejecting instances the old lookup accepted.
215+ func instanceOnNode (instances * smap.Map [* Instance ], nodeID string ) (* Instance , bool ) {
216+ var unhealthy * Instance
217+
218+ for _ , instance := range instances .Items () {
219+ if instance .NodeID != nodeID {
220+ continue
221+ }
222+
223+ if instance .GetInfo ().Status == infogrpc .ServiceInfoStatus_Unhealthy {
224+ unhealthy = instance
225+
226+ continue
227+ }
228+
229+ return instance , true
202230 }
203231
204- return instance , nil
232+ return unhealthy , unhealthy != nil
233+ }
234+
235+ // builderOnNode finds the usable template builder running on a machine. The
236+ // pool is keyed by process, so this cannot be a map lookup, and every entry on
237+ // the machine has to be considered rather than the first: a restart leaves the
238+ // dead run and the live one sharing a machine for a sync round, and stopping
239+ // early would answer with whichever the map happened to yield.
240+ func builderOnNode (instances * smap.Map [* Instance ], nodeID string ) (* Instance , bool ) {
241+ for _ , instance := range instances .Items () {
242+ if instance .NodeID != nodeID {
243+ continue
244+ }
245+
246+ if info := instance .GetInfo (); info .Status == infogrpc .ServiceInfoStatus_Unhealthy || ! info .IsBuilder {
247+ continue
248+ }
249+
250+ return instance , true
251+ }
252+
253+ return nil , false
205254}
206255
207256func (c * Cluster ) GetByServiceInstanceID (serviceInstanceID string ) (* Instance , bool ) {
0 commit comments