Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(dslx): start making functions stateless #1379

Merged
merged 11 commits into from
Oct 25, 2023
Merged
193 changes: 84 additions & 109 deletions internal/dslx/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,117 +72,92 @@ type ResolvedAddresses struct {
// DNSLookupGetaddrinfo returns a function that resolves a domain name to
// IP addresses using libc's getaddrinfo function.
func DNSLookupGetaddrinfo(rt Runtime) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] {
return &dnsLookupGetaddrinfoFunc{rt}
}

// dnsLookupGetaddrinfoFunc is the function returned by DNSLookupGetaddrinfo.
type dnsLookupGetaddrinfoFunc struct {
rt Runtime
}

// Apply implements Func.
func (f *dnsLookupGetaddrinfoFunc) Apply(
ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] {

// create trace
trace := f.rt.NewTrace(f.rt.IDGenerator().Add(1), f.rt.ZeroTime(), input.Tags...)

// start the operation logger
ol := logx.NewOperationLogger(
f.rt.Logger(),
"[#%d] DNSLookup[getaddrinfo] %s",
trace.Index(),
input.Domain,
)

// setup
const timeout = 4 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

// create the resolver
resolver := trace.NewStdlibResolver(f.rt.Logger())

// lookup
addrs, err := resolver.LookupHost(ctx, input.Domain)

// stop the operation logger
ol.Stop(err)

state := &ResolvedAddresses{
Addresses: addrs, // maybe empty
Domain: input.Domain,
Trace: trace,
}

return &Maybe[*ResolvedAddresses]{
Error: err,
Observations: maybeTraceToObservations(trace),
Operation: netxlite.ResolveOperation,
State: state,
}
return FuncAdapter[*DomainToResolve, *Maybe[*ResolvedAddresses]](func(ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] {
// create trace
trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...)

// start the operation logger
ol := logx.NewOperationLogger(
rt.Logger(),
"[#%d] DNSLookup[getaddrinfo] %s",
trace.Index(),
input.Domain,
)

// setup
const timeout = 4 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

// create the resolver
resolver := trace.NewStdlibResolver(rt.Logger())

// lookup
addrs, err := resolver.LookupHost(ctx, input.Domain)

// stop the operation logger
ol.Stop(err)

state := &ResolvedAddresses{
Addresses: addrs, // maybe empty
Domain: input.Domain,
Trace: trace,
}

return &Maybe[*ResolvedAddresses]{
Error: err,
Observations: maybeTraceToObservations(trace),
Operation: netxlite.ResolveOperation,
State: state,
}
})
}

// DNSLookupUDP returns a function that resolves a domain name to
// IP addresses using the given DNS-over-UDP resolver.
func DNSLookupUDP(rt Runtime, resolver string) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] {
return &dnsLookupUDPFunc{
Resolver: resolver,
rt: rt,
}
}

// dnsLookupUDPFunc is the function returned by DNSLookupUDP.
type dnsLookupUDPFunc struct {
// Resolver is the MANDATORY endpointed of the resolver to use.
Resolver string
rt Runtime
}

// Apply implements Func.
func (f *dnsLookupUDPFunc) Apply(
ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] {

// create trace
trace := f.rt.NewTrace(f.rt.IDGenerator().Add(1), f.rt.ZeroTime(), input.Tags...)

// start the operation logger
ol := logx.NewOperationLogger(
f.rt.Logger(),
"[#%d] DNSLookup[%s/udp] %s",
trace.Index(),
f.Resolver,
input.Domain,
)

// setup
const timeout = 4 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

// create the resolver
resolver := trace.NewParallelUDPResolver(
f.rt.Logger(),
trace.NewDialerWithoutResolver(f.rt.Logger()),
f.Resolver,
)

// lookup
addrs, err := resolver.LookupHost(ctx, input.Domain)

// stop the operation logger
ol.Stop(err)

state := &ResolvedAddresses{
Addresses: addrs, // maybe empty
Domain: input.Domain,
Trace: trace,
}

return &Maybe[*ResolvedAddresses]{
Error: err,
Observations: maybeTraceToObservations(trace),
Operation: netxlite.ResolveOperation,
State: state,
}
func DNSLookupUDP(rt Runtime, endpoint string) Func[*DomainToResolve, *Maybe[*ResolvedAddresses]] {
return FuncAdapter[*DomainToResolve, *Maybe[*ResolvedAddresses]](func(ctx context.Context, input *DomainToResolve) *Maybe[*ResolvedAddresses] {
// create trace
trace := rt.NewTrace(rt.IDGenerator().Add(1), rt.ZeroTime(), input.Tags...)

// start the operation logger
ol := logx.NewOperationLogger(
rt.Logger(),
"[#%d] DNSLookup[%s/udp] %s",
trace.Index(),
endpoint,
input.Domain,
)

// setup
const timeout = 4 * time.Second
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

// create the resolver
resolver := trace.NewParallelUDPResolver(
rt.Logger(),
trace.NewDialerWithoutResolver(rt.Logger()),
endpoint,
)

// lookup
addrs, err := resolver.LookupHost(ctx, input.Domain)

// stop the operation logger
ol.Stop(err)

state := &ResolvedAddresses{
Addresses: addrs, // maybe empty
Domain: input.Domain,
Trace: trace,
}

return &Maybe[*ResolvedAddresses]{
Error: err,
Observations: maybeTraceToObservations(trace),
Operation: netxlite.ResolveOperation,
State: state,
}
})
}
51 changes: 18 additions & 33 deletions internal/dslx/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,16 @@ Test cases:
- with success
*/
func TestGetaddrinfo(t *testing.T) {
t.Run("Get dnsLookupGetaddrinfoFunc", func(t *testing.T) {
f := DNSLookupGetaddrinfo(NewMinimalRuntime(model.DiscardLogger, time.Now()))
if _, ok := f.(*dnsLookupGetaddrinfoFunc); !ok {
t.Fatal("unexpected type, want dnsLookupGetaddrinfoFunc")
}
})

t.Run("Apply dnsLookupGetaddrinfoFunc", func(t *testing.T) {
domain := &DomainToResolve{
Domain: "example.com",
Tags: []string{"antani"},
}

t.Run("with nil resolver", func(t *testing.T) {
f := dnsLookupGetaddrinfoFunc{
rt: NewMinimalRuntime(model.DiscardLogger, time.Now()),
}
f := DNSLookupGetaddrinfo(
NewMinimalRuntime(model.DiscardLogger, time.Now()),
)
ctx, cancel := context.WithCancel(context.Background())
cancel() // immediately cancel the lookup
res := f.Apply(ctx, domain)
Expand All @@ -84,8 +77,8 @@ func TestGetaddrinfo(t *testing.T) {

t.Run("with lookup error", func(t *testing.T) {
mockedErr := errors.New("mocked")
f := dnsLookupGetaddrinfoFunc{
rt: NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{
f := DNSLookupGetaddrinfo(
NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{
MockNewStdlibResolver: func(logger model.DebugLogger) model.Resolver {
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
Expand All @@ -94,7 +87,7 @@ func TestGetaddrinfo(t *testing.T) {
}
},
})),
}
)
res := f.Apply(context.Background(), domain)
if res.Observations == nil || len(res.Observations) <= 0 {
t.Fatal("unexpected empty observations")
Expand All @@ -111,8 +104,8 @@ func TestGetaddrinfo(t *testing.T) {
})

t.Run("with success", func(t *testing.T) {
f := dnsLookupGetaddrinfoFunc{
rt: NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{
f := DNSLookupGetaddrinfo(
NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{
MockNewStdlibResolver: func(logger model.DebugLogger) model.Resolver {
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
Expand All @@ -121,7 +114,7 @@ func TestGetaddrinfo(t *testing.T) {
}
},
})),
}
)
res := f.Apply(context.Background(), domain)
if res.Observations == nil || len(res.Observations) <= 0 {
t.Fatal("unexpected empty observations")
Expand Down Expand Up @@ -151,22 +144,14 @@ Test cases:
- with success
*/
func TestLookupUDP(t *testing.T) {
t.Run("Get dnsLookupUDPFunc", func(t *testing.T) {
rt := NewMinimalRuntime(model.DiscardLogger, time.Now())
f := DNSLookupUDP(rt, "1.1.1.1:53")
if _, ok := f.(*dnsLookupUDPFunc); !ok {
t.Fatal("unexpected type, want dnsLookupUDPFunc")
}
})

t.Run("Apply dnsLookupGetaddrinfoFunc", func(t *testing.T) {
domain := &DomainToResolve{
Domain: "example.com",
Tags: []string{"antani"},
}

t.Run("with nil resolver", func(t *testing.T) {
f := dnsLookupUDPFunc{Resolver: "1.1.1.1:53", rt: NewMinimalRuntime(model.DiscardLogger, time.Now())}
f := DNSLookupUDP(NewMinimalRuntime(model.DiscardLogger, time.Now()), "1.1.1.1:53")
ctx, cancel := context.WithCancel(context.Background())
cancel()
res := f.Apply(ctx, domain)
Expand All @@ -180,9 +165,8 @@ func TestLookupUDP(t *testing.T) {

t.Run("with lookup error", func(t *testing.T) {
mockedErr := errors.New("mocked")
f := dnsLookupUDPFunc{
Resolver: "1.1.1.1:53",
rt: NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{
f := DNSLookupUDP(
NewMinimalRuntime(model.DiscardLogger, time.Now(), MinimalRuntimeOptionMeasuringNetwork(&mocks.MeasuringNetwork{
MockNewParallelUDPResolver: func(logger model.DebugLogger, dialer model.Dialer, endpoint string) model.Resolver {
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
Expand All @@ -198,7 +182,8 @@ func TestLookupUDP(t *testing.T) {
}
},
})),
}
"1.1.1.1:53",
)
res := f.Apply(context.Background(), domain)
if res.Observations == nil || len(res.Observations) <= 0 {
t.Fatal("unexpected empty observations")
Expand All @@ -215,9 +200,8 @@ func TestLookupUDP(t *testing.T) {
})

t.Run("with success", func(t *testing.T) {
f := dnsLookupUDPFunc{
Resolver: "1.1.1.1:53",
rt: NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{
f := DNSLookupUDP(
NewRuntimeMeasurexLite(model.DiscardLogger, time.Now(), RuntimeMeasurexLiteOptionMeasuringNetwork(&mocks.MeasuringNetwork{
MockNewParallelUDPResolver: func(logger model.DebugLogger, dialer model.Dialer, address string) model.Resolver {
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
Expand All @@ -233,7 +217,8 @@ func TestLookupUDP(t *testing.T) {
}
},
})),
}
"1.1.1.1:53",
)
res := f.Apply(context.Background(), domain)
if res.Observations == nil || len(res.Observations) <= 0 {
t.Fatal("unexpected empty observations")
Expand Down
8 changes: 8 additions & 0 deletions internal/dslx/fxcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ type Func[A, B any] interface {
Apply(ctx context.Context, a A) B
}

// FuncAdapter adapts a func to be a Func.
type FuncAdapter[A, B any] func(ctx context.Context, a A) B

// Apply implements Func.
func (fa FuncAdapter[A, B]) Apply(ctx context.Context, a A) B {
return fa(ctx, a)
}

// Maybe is the result of an operation implemented by this package
// that may fail such as [TCPConnect] or [TLSHandshake].
type Maybe[State any] struct {
Expand Down
Loading