@@ -26,6 +26,9 @@ import (
2626 "github.com/pkg/errors"
2727 "github.com/spf13/viper"
2828 fly "github.com/superfly/fly-go"
29+ "go.opentelemetry.io/otel/attribute"
30+ "go.opentelemetry.io/otel/trace"
31+
2932 "github.com/superfly/flyctl/agent"
3033 "github.com/superfly/flyctl/flyctl"
3134 "github.com/superfly/flyctl/helpers"
@@ -37,8 +40,6 @@ import (
3740 "github.com/superfly/flyctl/internal/tracing"
3841 "github.com/superfly/flyctl/iostreams"
3942 "github.com/superfly/flyctl/terminal"
40- "go.opentelemetry.io/otel/attribute"
41- "go.opentelemetry.io/otel/trace"
4243)
4344
4445var (
@@ -47,30 +48,39 @@ var (
4748)
4849
4950type dockerClientFactory struct {
50- mode DockerDaemonType
51- remote bool
52- buildFn func (ctx context.Context , build * build ) (* dockerclient.Client , error )
53- apiClient flyutil.Client
54- appName string
51+ mode DockerDaemonType
52+ remote bool
53+ buildFn func (ctx context.Context , build * build ) (* dockerclient.Client , error )
54+ apiClient flyutil.Client
55+ appName string
56+ builderRegion string // Store the region for remote builders
5557}
5658
5759func newDockerClientFactory (daemonType DockerDaemonType , apiClient flyutil.Client , appName string , streams * iostreams.IOStreams , connectOverWireguard , recreateBuilder bool ) * dockerClientFactory {
5860 useManagedBuilder := daemonType .UseManagedBuilder ()
5961 remoteFactory := func () * dockerClientFactory {
6062 terminal .Debug ("trying remote docker daemon" )
61- return & dockerClientFactory {
62- mode : daemonType ,
63- remote : true ,
64- buildFn : func (ctx context.Context , build * build ) (* dockerclient.Client , error ) {
65- cfg := config .FromContext (ctx )
66- if cfg .DisableManagedBuilders {
67- useManagedBuilder = false
68- }
69- return newRemoteDockerClient (ctx , apiClient , appName , streams , build , cachedDocker , connectOverWireguard , useManagedBuilder , recreateBuilder )
70- },
63+ factory := & dockerClientFactory {
64+ mode : daemonType ,
65+ remote : true ,
7166 apiClient : apiClient ,
7267 appName : appName ,
7368 }
69+
70+ factory .buildFn = func (ctx context.Context , build * build ) (* dockerclient.Client , error ) {
71+ cfg := config .FromContext (ctx )
72+ if cfg .DisableManagedBuilders {
73+ useManagedBuilder = false
74+ }
75+ client , region , err := newRemoteDockerClient (ctx , apiClient , appName , streams , build , cachedDocker , connectOverWireguard , useManagedBuilder , recreateBuilder )
76+ if err == nil {
77+ // Store the region in the factory for later use
78+ factory .builderRegion = region
79+ }
80+ return client , err
81+ }
82+
83+ return factory
7484 }
7585
7686 localFactory := func () * dockerClientFactory {
@@ -236,14 +246,14 @@ func logClearLinesAbove(streams *iostreams.IOStreams, count int) {
236246 }
237247}
238248
239- func newRemoteDockerClient (ctx context.Context , apiClient flyutil.Client , appName string , streams * iostreams.IOStreams , build * build , cachedClient * dockerclient.Client , connectOverWireguard , useManagedBuilder bool , recreateBuilder bool ) (c * dockerclient.Client , err error ) {
249+ func newRemoteDockerClient (ctx context.Context , apiClient flyutil.Client , appName string , streams * iostreams.IOStreams , build * build , cachedClient * dockerclient.Client , connectOverWireguard , useManagedBuilder bool , recreateBuilder bool ) (c * dockerclient.Client , region string , err error ) {
240250 ctx , span := tracing .GetTracer ().Start (ctx , "build_remote_docker_client" , trace .WithAttributes (
241251 attribute .Bool ("connect_over_wireguard" , connectOverWireguard ),
242252 ))
243253 defer span .End ()
244254 if cachedClient != nil {
245255 span .AddEvent ("using cached docker client" )
246- return cachedClient , nil
256+ return cachedClient , "" , nil
247257 }
248258
249259 startedAt := time .Now ()
@@ -264,7 +274,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
264274 }
265275 if err != nil {
266276 tracing .RecordError (span , err , "failed to init remote builder machine" )
267- return nil , err
277+ return nil , "" , err
268278 }
269279
270280 if useManagedBuilder {
@@ -286,7 +296,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
286296 req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
287297 if err != nil {
288298 tracing .RecordError (span , err , "failed to create remote builder request" )
289- return nil , err
299+ return nil , "" , err
290300 }
291301
292302 req .SetBasicAuth (appName , config .Tokens (ctx ).Docker ())
@@ -297,7 +307,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
297307 res , err := client .Do (req )
298308 if err != nil {
299309 tracing .RecordError (span , err , "failed to get remote builder settings" )
300- return nil , err
310+ return nil , "" , err
301311 }
302312
303313 if res .StatusCode == http .StatusNotFound {
@@ -308,14 +318,14 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
308318 err := apiClient .DeleteApp (ctx , app .Name )
309319 if err != nil {
310320 tracing .RecordError (span , err , "failed to destroy old incompatible remote builder" )
311- return nil , err
321+ return nil , "" , err
312322 }
313323
314324 fmt .Fprintln (streams .Out , streams .ColorScheme ().Yellow ("🔧 creating fresh remote builder, (this might take a while ...)" ))
315325 machine , app , err = remoteBuilderMachine (ctx , apiClient , appName , false )
316326 if err != nil {
317327 tracing .RecordError (span , err , "failed to init remote builder machine" )
318- return nil , err
328+ return nil , "" , err
319329 }
320330 logClearLinesAbove (streams , 1 )
321331 fmt .Fprintln (streams .Out , streams .ColorScheme ().Green ("✓ compatible remote builder created" ))
@@ -329,6 +339,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
329339
330340 remoteBuilderAppName := app .Name
331341 remoteBuilderOrg := app .Organization .Slug
342+ builderRegion := machine .Region
332343
333344 build .SetBuilderMetaPart1 (remoteBuilderType , remoteBuilderAppName , machine .ID )
334345
@@ -384,7 +395,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
384395 if host == "" {
385396 err = errors .New ("machine did not have a private IP" )
386397 tracing .RecordError (span , err , "failed to boot remote builder" )
387- return nil , err
398+ return nil , "" , err
388399 }
389400
390401 builderHostOverride , ok := os .LookupEnv ("FLY_RCHAB_OVERRIDE_HOST" )
@@ -409,10 +420,10 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
409420 captureError (err )
410421
411422 if strings .Contains (err .Error (), "timed out" ) || strings .Contains (err .Error (), "websocket" ) {
412- return nil , generateBrokenWGError (err )
423+ return nil , "" , generateBrokenWGError (err )
413424 }
414425
415- return nil , err
426+ return nil , "" , err
416427 }
417428
418429 wireguardHttpClient , err := dockerclient .NewClientWithOpts (wireguardOpts ... )
@@ -423,7 +434,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
423434 captureError (err )
424435 tracing .RecordError (span , err , "failed to initialize remote client" )
425436
426- return nil , err
437+ return nil , "" , err
427438 }
428439
429440 cachedClient = wireguardHttpClient
@@ -434,7 +445,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
434445
435446 err = fmt .Errorf ("failed building wgless options: %w" , err )
436447 captureError (err )
437- return nil , err
448+ return nil , "" , err
438449 }
439450
440451 wireguardlessHttpsClient , err := dockerclient .NewClientWithOpts (wglessOpts ... )
@@ -445,7 +456,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
445456 captureError (err )
446457 tracing .RecordError (span , err , "failed to initialize wgLessHttpClient" )
447458
448- return nil , err
459+ return nil , "" , err
449460 }
450461 cachedClient = wireguardlessHttpsClient
451462 }
@@ -459,18 +470,18 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
459470 tracing .RecordError (span , err , "failed to wait for docker daemon" )
460471
461472 if errors .Is (err , agent .ErrTunnelUnavailable ) {
462- return nil , generateBrokenWGError (err )
473+ return nil , "" , generateBrokenWGError (err )
463474 }
464475
465- return nil , err
476+ return nil , "" , err
466477 case ! up :
467478 streams .StopProgressIndicator ()
468479 err := errors .New ("remote builder app unavailable" )
469480
470481 terminal .Warnf ("Remote builder did not start in time. Check remote builder logs with `flyctl logs -a %s`\n " , remoteBuilderAppName )
471482 tracing .RecordError (span , err , "remote builder failed to start" )
472483
473- return nil , err
484+ return nil , "" , err
474485 default :
475486 if msg := fmt .Sprintf ("Remote builder %s ready" , remoteBuilderAppName ); streams .IsInteractive () {
476487 streams .StopProgressIndicatorMsg (msg )
@@ -479,7 +490,7 @@ func newRemoteDockerClient(ctx context.Context, apiClient flyutil.Client, appNam
479490 }
480491 }
481492
482- return cachedClient , nil
493+ return cachedClient , builderRegion , nil
483494}
484495
485496func generateBrokenWGError (err error ) flyerr.GenericErr {
@@ -789,3 +800,32 @@ func (d *dockerClientFactory) IsRemote() bool {
789800func (d * dockerClientFactory ) IsLocal () bool {
790801 return ! d .remote
791802}
803+
804+ // GetBuilderRegion returns the region of the builder
805+ func (d * dockerClientFactory ) GetBuilderRegion () string {
806+ return d .builderRegion
807+ }
808+
809+ // getBuilderInfo determines the builder type and region for metrics and logging
810+ func getBuilderInfo (ctx context.Context , dockerFactory * dockerClientFactory ) (builderType , builderRegion string ) {
811+ builderType = "local"
812+ builderRegion = "unknown"
813+
814+ if dockerFactory .IsRemote () {
815+ builderType = "remote"
816+ builderRegion = dockerFactory .GetBuilderRegion ()
817+ if builderRegion == "" {
818+ builderRegion = "remote"
819+ }
820+ } else {
821+ // For local builders, try to get the nearest region
822+ apiClient := flyutil .ClientFromContext (ctx )
823+ if apiClient != nil {
824+ if nearestRegion , err := apiClient .GetNearestRegion (ctx ); err == nil {
825+ builderRegion = nearestRegion .Code
826+ }
827+ }
828+ }
829+
830+ return builderType , builderRegion
831+ }
0 commit comments