diff --git a/internal/server/server.go b/internal/server/server.go index e2ca743..1cd43b2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -17,6 +17,7 @@ import ( "github.com/jsiebens/ionscale/internal/service" "github.com/jsiebens/ionscale/internal/stunserver" "github.com/jsiebens/ionscale/internal/templates" + "github.com/jsiebens/ionscale/internal/util" "github.com/labstack/echo-contrib/echoprometheus" "github.com/labstack/echo-contrib/pprof" "github.com/labstack/echo/v4" @@ -53,6 +54,8 @@ func Start(ctx context.Context, c *config.Config) error { return err } + util.EnsureIDProvider() + derpMap, err := derp.LoadDERPSources(c) if err != nil { logger.Warn("not all derp sources are read successfully", zap.Error(err)) diff --git a/internal/util/id.go b/internal/util/id.go index 54e05d5..79b2e80 100644 --- a/internal/util/id.go +++ b/internal/util/id.go @@ -1,8 +1,10 @@ package util import ( + "errors" "fmt" "github.com/sony/sonyflake" + "go.uber.org/zap" "net" "os" "strconv" @@ -11,30 +13,66 @@ import ( ) var ( - sf *sonyflake.Sonyflake + sf provider sfOnce sync.Once ) func NextID() uint64 { - ensureProvider() - id, _ := sf.NextID() + EnsureIDProvider() + id, err := sf.NextID() + if err != nil { + panic(err) + } return id } -func ensureProvider() { - sfOnce.Do(func() { - sfInstance, err := sonyflake.New(sonyflake.Settings{ - MachineID: machineID(), - StartTime: time.Date(2022, 05, 01, 00, 0, 0, 0, time.UTC), - }) - if err != nil { - panic("unable to initialize sonyflake: " + err.Error()) - } +type provider interface { + NextID() (uint64, error) +} - sf = sfInstance +type errorProvider struct { + err error +} + +func (e errorProvider) NextID() (uint64, error) { + return 0, fmt.Errorf("unable to generate ID, sonyflake not configured properly: %w", e.err) +} + +func EnsureIDProvider() { + sfOnce.Do(func() { + sf = createIDProvider() }) } +func createIDProvider() provider { + startTime := time.Date(2022, 05, 01, 00, 0, 0, 0, time.UTC) + + sfInstance, err := sonyflake.New(sonyflake.Settings{ + MachineID: machineID(), + StartTime: startTime, + }) + + if err != nil && errors.Is(err, sonyflake.ErrNoPrivateAddress) { + id := RandUint16() + zap.L().Warn("failed to generate sonyflake machine id from private ip address, using a random machine id", zap.Uint16("id", id)) + + sfInstance, err = sonyflake.New(sonyflake.Settings{ + MachineID: func() (uint16, error) { return id, nil }, + StartTime: startTime, + }) + + if err != nil { + return errorProvider{err} + } + } + + if err != nil { + return errorProvider{err} + } + + return sfInstance +} + func machineID() func() (uint16, error) { envMachineID := os.Getenv("IONSCALE_MACHINE_ID") if len(envMachineID) != 0 { diff --git a/internal/util/util.go b/internal/util/util.go index 71cef3c..a0d44b8 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "crypto/rand" "crypto/rsa" + "encoding/binary" "encoding/hex" "encoding/json" "math/big" @@ -31,6 +32,15 @@ func RandUint64(n uint64) uint64 { return val.Uint64() } +func RandUint16() uint16 { + var randomBytes [2]byte + _, err := rand.Read(randomBytes[:]) + if err != nil { + panic(err) + } + return binary.BigEndian.Uint16(randomBytes[:]) +} + func RandomBytes(size int) ([]byte, error) { buf := make([]byte, size) if _, err := rand.Read(buf); err != nil {