77 "fmt"
88 "io"
99 "net/http"
10+ "net/netip"
11+ "os"
1012 "time"
1113
1214 "github.com/rs/zerolog"
@@ -15,8 +17,11 @@ import (
1517
1618 "github.com/e2b-dev/infra/packages/envd/internal/host"
1719 "github.com/e2b-dev/infra/packages/envd/internal/logs"
20+ "github.com/e2b-dev/infra/packages/shared/pkg/utils"
1821)
1922
23+ const hostsFilePermissions = 0o644
24+
2025var ErrAccessTokenAlreadySet = errors .New ("access token is already set" )
2126
2227const (
@@ -119,19 +124,68 @@ func (a *API) SetupHyperloop(address string) {
119124 a .hyperloopLock .Lock ()
120125 defer a .hyperloopLock .Unlock ()
121126
122- hosts , err := txeh .NewHosts (& txeh.HostsConfig {ReadFilePath : "/etc/hosts" , WriteFilePath : "/etc/hosts" })
127+ if err := rewriteHostsFile (address , "/etc/hosts" ); err != nil {
128+ a .logger .Error ().Err (err ).Msg ("failed to modify hosts file" )
129+ } else {
130+ a .envVars .Store ("E2B_EVENTS_ADDRESS" , fmt .Sprintf ("http://%s" , address ))
131+ }
132+ }
133+
134+ const eventsHost = "events.e2b.local"
135+
136+ func rewriteHostsFile (address , path string ) error {
137+ data , err := os .ReadFile (path )
138+ if err != nil {
139+ return fmt .Errorf ("failed to read hosts file: %w" , err )
140+ }
141+
142+ // the txeh library drops an entry if the file does not end with a newline
143+ if len (data ) > 0 && data [len (data )- 1 ] != '\n' {
144+ data = append (data , '\n' )
145+ }
146+
147+ hosts , err := txeh .NewHosts (& txeh.HostsConfig {RawText : utils .ToPtr (string (data ))})
123148 if err != nil {
124- a .logger .Error ().Msgf ("Failed to create hosts: %v" , err )
125- return
149+ return fmt .Errorf ("failed to create hosts: %w" , err )
126150 }
127151
128152 // Update /etc/hosts to point events.e2b.local to the hyperloop IP
129153 // This will remove any existing entries for events.e2b.local first
130- hosts .AddHost (address , "events.e2b.local" )
131- err = hosts .Save ()
154+ ipFamily , err := getIPFamily (address )
155+ if err != nil {
156+ return fmt .Errorf ("failed to get ip family: %w" , err )
157+ }
158+
159+ if ok , current , _ := hosts .HostAddressLookup (eventsHost , ipFamily ); ok && current == address {
160+ return nil // nothing to be done
161+ }
162+
163+ hosts .AddHost (address , eventsHost )
164+
165+ if err = os .WriteFile (path , []byte (hosts .RenderHostsFile ()), hostsFilePermissions ); err != nil {
166+ return fmt .Errorf ("failed to save hosts file: %w" , err )
167+ }
168+
169+ return nil
170+ }
171+
172+ var (
173+ ErrInvalidAddress = errors .New ("invalid IP address" )
174+ ErrUnknownAddressFormat = errors .New ("unknown IP address format" )
175+ )
176+
177+ func getIPFamily (address string ) (txeh.IPFamily , error ) {
178+ addressIP , err := netip .ParseAddr (address )
132179 if err != nil {
133- a . logger . Error (). Msgf ( "Failed to add events host entry : %v " , err )
180+ return txeh . IPFamilyV4 , fmt . Errorf ( "failed to parse IP address : %w " , err )
134181 }
135182
136- a .envVars .Store ("E2B_EVENTS_ADDRESS" , fmt .Sprintf ("http://%s" , address ))
183+ switch {
184+ case addressIP .Is4 ():
185+ return txeh .IPFamilyV4 , nil
186+ case addressIP .Is6 ():
187+ return txeh .IPFamilyV6 , nil
188+ default :
189+ return txeh .IPFamilyV4 , fmt .Errorf ("%w: %s" , ErrUnknownAddressFormat , address )
190+ }
137191}
0 commit comments