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

Set last gateway location on claim #7262

Merged
merged 2 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ require (
go.packetbroker.org/api/mapping/v2 v2.3.2
go.packetbroker.org/api/routing v1.9.2
go.packetbroker.org/api/v3 v3.17.1
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e
go.thethings.network/lorawan-stack-legacy/v2 v2.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,8 @@ go.packetbroker.org/api/routing v1.9.2 h1:J4+4vYZxa60UWC70Y9yy7sktU7DXaAp9Q13Bfq
go.packetbroker.org/api/routing v1.9.2/go.mod h1:kd2K7gieDI35YfPA8/zDmLX3qiKPuXia/MA77BEAeUA=
go.packetbroker.org/api/v3 v3.17.1 h1:LcyFPUGqVubGWMvQ16tZlQIKd+noGx7urzEYhSLiEQA=
go.packetbroker.org/api/v3 v3.17.1/go.mod h1:6bVbdWAYLnvZ5kgXxA7GBQvZTN7vxI0DoF1Di1NoAT4=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd h1:FvD516hdD/iWqoS20SFdcoiUgwRJP3egNXNiN+Ux2d0=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5 h1:w4NvcBAq7JCwlfthvGw1PRWuI6pCf5TlIjKgFHKml8M=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df h1:IMSctPllwEtJLyM/1AWgBiN1NPwBKqEVkCiK0I/MNY4=
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df/go.mod h1:89OU623VYKW9i3W4CZgIGFmtgb/jsN8JV2PAuCsj+7w=
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e h1:TWGQ3lh7gI2W5hnb6qPdpoAa0d7s/XPwvgf2VVCMJaY=
Expand Down
4 changes: 3 additions & 1 deletion pkg/deviceclaimingserver/gateways/gateways.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func ParseGatewayEUIRanges(conf map[string][]string) (map[string][]dcstypes.EUI6
// Claimer provides methods for claiming Gateways.
type Claimer interface {
// Claim claims a gateway.
Claim(ctx context.Context, eui types.EUI64, ownerToken string, clusterAddress string) error
Claim(
ctx context.Context, eui types.EUI64, ownerToken string, clusterAddress string,
) (*dcstypes.GatewayMetadata, error)
// Unclaim unclaims a gateway.
Unclaim(ctx context.Context, eui types.EUI64) error
// IsManagedGateway returns true if the gateway is a managed gateway.
Expand Down
47 changes: 37 additions & 10 deletions pkg/deviceclaimingserver/gateways/ttgc/ttgc.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import (

northboundv1 "go.thethings.industries/pkg/api/gen/tti/gateway/controller/northbound/v1"
"go.thethings.network/lorawan-stack/v3/pkg/config/tlsconfig"
dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/log"
"go.thethings.network/lorawan-stack/v3/pkg/ttgc"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
"go.thethings.network/lorawan-stack/v3/pkg/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -61,7 +64,9 @@ func New(ctx context.Context, c ttgc.Component, config ttgc.Config) (*Upstream,
// 2. Upsert a LoRa Packet Forwarder profile with the root CA presented by the given Gateway Server
// 3. Upsert a Geolocation profile
// 4. Update the gateway with the profiles
func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clusterAddress string) error {
func (u *Upstream) Claim(
ctx context.Context, eui types.EUI64, ownerToken, clusterAddress string,
) (*dcstypes.GatewayMetadata, error) {
logger := log.FromContext(ctx)

// Claim the gateway.
Expand All @@ -72,7 +77,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
OwnerToken: ownerToken,
})
if err != nil {
return err
return nil, err
}

// Get the root CA from the Gateway Server and upsert the LoRa Packet Forwarder profile.
Expand All @@ -83,7 +88,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
clusterAddress = net.JoinHostPort(host, "8889")
rootCA, err := u.getRootCA(ctx, clusterAddress)
if err != nil {
return err
return nil, err
}
var (
loraPFProfileID []byte
Expand All @@ -107,7 +112,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
if err != nil {
if status.Code(err) != codes.NotFound {
logger.WithError(err).Warn("Failed to get LoRa Packet Forwarder profile")
return err
return nil, err
}
res, err := loraPFProfileClient.Create(ctx, &northboundv1.LoraPacketForwarderProfileServiceCreateRequest{
Domain: u.client.Domain(ctx),
Expand All @@ -116,7 +121,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
})
if err != nil {
logger.WithError(err).Warn("Failed to create LoRa Packet Forwarder profile")
return err
return nil, err
}
loraPFProfileID = res.ProfileId
} else {
Expand All @@ -131,7 +136,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
})
if err != nil {
logger.WithError(err).Warn("Failed to update LoRa Packet Forwarder profile")
return err
return nil, err
}
}
loraPFProfileID = loraPFGetRes.ProfileId
Expand All @@ -158,7 +163,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
if err != nil {
if status.Code(err) != codes.NotFound {
logger.WithError(err).Warn("Failed to get geolocation profile")
return err
return nil, err
}
res, err := geolocationProfileClient.Create(ctx, &northboundv1.GeolocationProfileServiceCreateRequest{
Domain: u.client.Domain(ctx),
Expand All @@ -167,7 +172,7 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
})
if err != nil {
logger.WithError(err).Warn("Failed to create geolocation profile")
return err
return nil, err
}
geolocationProfileID = res.ProfileId
} else {
Expand All @@ -187,10 +192,29 @@ func (u *Upstream) Claim(ctx context.Context, eui types.EUI64, ownerToken, clust
})
if err != nil {
logger.WithError(err).Warn("Failed to update gateway with profiles")
return err
return nil, err
}

return nil
gatewayMetadata := &dcstypes.GatewayMetadata{}
locationRes, err := gtwClient.GetLastLocation(ctx, &northboundv1.GatewayServiceGetLastLocationRequest{
GatewayId: eui.MarshalNumber(),
Domain: u.client.Domain(ctx),
})
if err != nil && !errors.IsNotFound(err) {
logger.WithError(err).Warn("Failed to get gateway location")
} else if err == nil {
gatewayMetadata.Antennas = []*ttnpb.GatewayAntenna{
{
Location: &ttnpb.Location{
Latitude: locationRes.Location.Latitude,
Longitude: locationRes.Location.Longitude,
Accuracy: int32(locationRes.Location.Accuracy),
},
},
}
}

return gatewayMetadata, nil
}

// Unclaim implements gateways.GatewayClaimer.
Expand All @@ -201,6 +225,9 @@ func (u *Upstream) Unclaim(ctx context.Context, eui types.EUI64) error {
Domain: u.client.Domain(ctx),
})
if err != nil {
if errors.IsNotFound(err) { // The gateway does not exist or is already unclaimed.
return nil
}
return err
}
return nil
Expand Down
4 changes: 3 additions & 1 deletion pkg/deviceclaimingserver/grpc_gateways.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ func (gcls *gatewayClaimingServer) Claim(
}

// Claim the gateway on the upstream.
if err := claimer.Claim(ctx, gatewayEUI, string(authCode), req.TargetGatewayServerAddress); err != nil {
res, err := claimer.Claim(ctx, gatewayEUI, string(authCode), req.TargetGatewayServerAddress)
if err != nil {
observability.RegisterFailClaim(ctx, ids.GetEntityIdentifiers(), err)
return nil, errClaim.WithCause(err)
}
Expand All @@ -143,6 +144,7 @@ func (gcls *gatewayClaimingServer) Claim(
EnforceDutyCycle: true,
RequireAuthenticatedConnection: true,
FrequencyPlanIds: req.TargetFrequencyPlanIds,
Antennas: res.Antennas,
}

_, err = gcls.registry.Create(ctx, &ttnpb.CreateGatewayRequest{
Expand Down
28 changes: 14 additions & 14 deletions pkg/deviceclaimingserver/grpc_gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
},
})

mockGatewayclaimer := &MockGatewayClaimer{
mockGatewayClaimer := &MockGatewayClaimer{
IsManagedGatewayFunc: func(_ context.Context, e types.EUI64) (bool, error) {
return e.Equal(supportedEUI), nil
},
Expand All @@ -88,7 +88,7 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
Length: 48,
}),
},
mockGatewayclaimer,
mockGatewayClaimer,
),
)
if err != nil {
Expand Down Expand Up @@ -180,7 +180,7 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
Name string
Req *ttnpb.ClaimGatewayRequest
CallOpt grpc.CallOption
ClaimFunc func(context.Context, types.EUI64, string, string) error
ClaimFunc func(context.Context, types.EUI64, string, string) (*dcstypes.GatewayMetadata, error)
CreateFunc func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error)
UnclaimFunc func(context.Context, types.EUI64) error
ErrorAssertion func(error) bool
Expand Down Expand Up @@ -271,8 +271,8 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
TargetGatewayServerAddress: "things.example.com",
},
CallOpt: authorizedCallOpt,
ClaimFunc: func(_ context.Context, _ types.EUI64, _, _ string) error {
return errClaim.New()
ClaimFunc: func(_ context.Context, _ types.EUI64, _, _ string) (*dcstypes.GatewayMetadata, error) {
return nil, errClaim.New()
},
ErrorAssertion: errors.IsAborted,
},
Expand All @@ -290,8 +290,8 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
TargetGatewayServerAddress: "things.example.com",
},
CallOpt: authorizedCallOpt,
ClaimFunc: func(context.Context, types.EUI64, string, string) error {
return nil
ClaimFunc: func(context.Context, types.EUI64, string, string) (*dcstypes.GatewayMetadata, error) {
return &dcstypes.GatewayMetadata{}, nil
},
CreateFunc: func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
return nil, errCreate.New()
Expand All @@ -318,8 +318,8 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
TargetGatewayServerAddress: "things.example.com",
},
CallOpt: authorizedCallOpt,
ClaimFunc: func(context.Context, types.EUI64, string, string) error {
return nil
ClaimFunc: func(context.Context, types.EUI64, string, string) (*dcstypes.GatewayMetadata, error) {
return &dcstypes.GatewayMetadata{}, nil
},
CreateFunc: func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
return nil, errCreate.New()
Expand All @@ -342,8 +342,8 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
TargetGatewayId: "test-gateway",
TargetGatewayServerAddress: "things.example.com",
},
ClaimFunc: func(context.Context, types.EUI64, string, string) error {
return nil
ClaimFunc: func(context.Context, types.EUI64, string, string) (*dcstypes.GatewayMetadata, error) {
return &dcstypes.GatewayMetadata{}, nil
},
CreateFunc: func(_ context.Context, in *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
return in.Gateway, nil
Expand All @@ -354,10 +354,10 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
tc := tc
t.Run(tc.Name, func(t *testing.T) {
if tc.ClaimFunc != nil {
mockGatewayclaimer.ClaimFunc = tc.ClaimFunc
mockGatewayClaimer.ClaimFunc = tc.ClaimFunc
}
if tc.UnclaimFunc != nil {
mockGatewayclaimer.UnclaimFunc = tc.UnclaimFunc
mockGatewayClaimer.UnclaimFunc = tc.UnclaimFunc
}
if tc.CreateFunc != nil {
mockGatewayRegistry.createFunc = tc.CreateFunc
Expand Down Expand Up @@ -480,7 +480,7 @@ func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
tc := tc
t.Run(tc.Name, func(t *testing.T) {
if tc.UnclaimFunc != nil {
mockGatewayclaimer.UnclaimFunc = tc.UnclaimFunc
mockGatewayClaimer.UnclaimFunc = tc.UnclaimFunc
}
if tc.GetFunc != nil {
mockGatewayRegistry.getFunc = tc.GetFunc
Expand Down
10 changes: 9 additions & 1 deletion pkg/deviceclaimingserver/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
// Package types provides types for the Device Claiming Server.
package types

import "go.thethings.network/lorawan-stack/v3/pkg/types"
import (
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
"go.thethings.network/lorawan-stack/v3/pkg/types"
)

// EUI64Range is a range of EUI64s.
type EUI64Range interface {
Expand Down Expand Up @@ -56,3 +59,8 @@ func RangeFromEUI64Range(start, end types.EUI64) EUI64Range {
end: end.MarshalNumber(),
}
}

// GatewayMetadata contains metadata of a gateway, typically returned on claiming.
type GatewayMetadata struct {
Antennas []*ttnpb.GatewayAntenna
}
5 changes: 3 additions & 2 deletions pkg/deviceclaimingserver/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package deviceclaimingserver_test
import (
"context"

dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
Expand Down Expand Up @@ -80,7 +81,7 @@ func (m MockEndDeviceClaimer) BatchUnclaim(
type MockGatewayClaimer struct {
EUIs []types.EUI64

ClaimFunc func(context.Context, types.EUI64, string, string) error
ClaimFunc func(context.Context, types.EUI64, string, string) (*dcstypes.GatewayMetadata, error)
UnclaimFunc func(context.Context, types.EUI64) error
IsManagedGatewayFunc func(context.Context, types.EUI64) (bool, error)
}
Expand All @@ -91,7 +92,7 @@ func (claimer MockGatewayClaimer) Claim(
eui types.EUI64,
ownerToken string,
clusterAddress string,
) error {
) (*dcstypes.GatewayMetadata, error) {
return claimer.ClaimFunc(ctx, eui, ownerToken, clusterAddress)
}

Expand Down
6 changes: 4 additions & 2 deletions pkg/gatewayserver/io/ttigw/ttigw.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ var _ io.Frontend = (*Frontend)(nil)

// New returns a new The Things Industries V1 gateway frontend.
func New(ctx context.Context, server io.Server, cfg Config) (*Frontend, error) {
ctx = log.NewContextWithField(ctx, "namespace", "gatewayserver/io/ttigw")

var proxyConfiguration webmiddleware.ProxyConfiguration
if err := proxyConfiguration.ParseAndAddTrusted(server.GetBaseConfig(ctx).HTTP.TrustedProxies...); err != nil {
return nil, err
Expand Down Expand Up @@ -130,7 +132,7 @@ func (f *Frontend) handleGet(w http.ResponseWriter, r *http.Request) {
}
ctx, ids, err := f.authenticate(ctx, cert)
if err != nil {
logger.WithError(err).Debug("Client certificate verification failed")
logger.WithError(err).Warn("Client certificate verification failed")
writeError(w, err)
return
}
Expand All @@ -140,7 +142,7 @@ func (f *Frontend) handleGet(w http.ResponseWriter, r *http.Request) {
Ip: remoteIP(r),
})
if err != nil {
logger.WithError(err).Info("Failed to connect")
logger.WithError(err).Warn("Failed to connect")
writeError(w, err)
return
}
Expand Down
2 changes: 1 addition & 1 deletion tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ require (
go.packetbroker.org/api/mapping/v2 v2.3.2 // indirect
go.packetbroker.org/api/routing v1.9.2 // indirect
go.packetbroker.org/api/v3 v3.17.1 // indirect
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd // indirect
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5 // indirect
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df // indirect
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e // indirect
go.thethings.network/lorawan-stack-legacy/v2 v2.1.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions tools/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -713,8 +713,8 @@ go.packetbroker.org/api/routing v1.9.2 h1:J4+4vYZxa60UWC70Y9yy7sktU7DXaAp9Q13Bfq
go.packetbroker.org/api/routing v1.9.2/go.mod h1:kd2K7gieDI35YfPA8/zDmLX3qiKPuXia/MA77BEAeUA=
go.packetbroker.org/api/v3 v3.17.1 h1:LcyFPUGqVubGWMvQ16tZlQIKd+noGx7urzEYhSLiEQA=
go.packetbroker.org/api/v3 v3.17.1/go.mod h1:6bVbdWAYLnvZ5kgXxA7GBQvZTN7vxI0DoF1Di1NoAT4=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd h1:FvD516hdD/iWqoS20SFdcoiUgwRJP3egNXNiN+Ux2d0=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5 h1:w4NvcBAq7JCwlfthvGw1PRWuI6pCf5TlIjKgFHKml8M=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240823134410-68cd7baab6c5/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df h1:IMSctPllwEtJLyM/1AWgBiN1NPwBKqEVkCiK0I/MNY4=
go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df/go.mod h1:89OU623VYKW9i3W4CZgIGFmtgb/jsN8JV2PAuCsj+7w=
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e h1:TWGQ3lh7gI2W5hnb6qPdpoAa0d7s/XPwvgf2VVCMJaY=
Expand Down
Loading