Skip to content

Commit

Permalink
Add register package for creating RegisterResponse records (#12)
Browse files Browse the repository at this point in the history
* Add package for register params
  • Loading branch information
stephen-soltesz authored Mar 6, 2024
1 parent 20cebb7 commit 8be6ac7
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 0 deletions.
98 changes: 98 additions & 0 deletions internal/register/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package register

import (
"encoding/hex"
"fmt"
"net"
"strings"

v0 "github.com/m-lab/autojoin/api/v0"
"github.com/m-lab/autojoin/iata"
v2 "github.com/m-lab/locate/api/v2"
"github.com/m-lab/uuid-annotator/annotator"
"github.com/oschwald/geoip2-golang"
)

var (
mlabDomain = "measurement-lab.org"
)

// Params is used internally to collect multiple parameters.
type Params struct {
Project string
Service string
Org string
IPv4 string
IPv6 string
Geo *geoip2.City
Metro iata.Row
Network *annotator.Network
}

// CreateRegisterResponse generates a RegisterResponse from the given
// parameters. As an internal package, the caller is required to validate all
// input parameters.
func CreateRegisterResponse(p *Params) v0.RegisterResponse {
// Calculate machine, site, and hostname.
machine := hex.EncodeToString(net.ParseIP(p.IPv4).To4())
site := fmt.Sprintf("%s%d", p.Metro.IATA, p.Network.ASNumber)
hostname := fmt.Sprintf("%s-%s-%s.%s.%s.%s", p.Service, site, machine, p.Org, strings.TrimPrefix(p.Project, "mlab-"), mlabDomain)

// Using these, create geo annotation.
geo := &annotator.Geolocation{
ContinentCode: p.Geo.Continent.Code,
CountryCode: p.Geo.Country.IsoCode,
CountryName: p.Geo.Country.Names["en"],
MetroCode: int64(p.Geo.Location.MetroCode),
City: p.Geo.City.Names["en"],
PostalCode: p.Geo.Postal.Code,
// Use iata location as authoritative.
Latitude: p.Metro.Latitude,
Longitude: p.Metro.Longitude,
}
if len(p.Geo.Subdivisions) > 0 {
geo.Subdivision1ISOCode = p.Geo.Subdivisions[0].IsoCode
geo.Subdivision1Name = p.Geo.Subdivisions[0].Names["en"]
if len(p.Geo.Subdivisions) > 1 {
geo.Subdivision2ISOCode = p.Geo.Subdivisions[1].IsoCode
geo.Subdivision2Name = p.Geo.Subdivisions[1].Names["en"]
}
}

// Put everything together into a RegisterResponse.
r := v0.RegisterResponse{
Registration: &v0.Registration{
Hostname: hostname,
Annotation: &v0.ServerAnnotation{
Annotation: annotator.ServerAnnotations{
Site: site,
Machine: machine,
Geo: geo,
Network: p.Network,
},
Network: v0.Network{
IPv4: p.IPv4,
IPv6: p.IPv6,
},
Type: "unknown", // should be overridden by node.
},
Heartbeat: &v2.Registration{
City: geo.City,
CountryCode: geo.CountryCode,
ContinentCode: geo.ContinentCode,
Experiment: p.Service,
Hostname: hostname,
Latitude: geo.Latitude,
Longitude: geo.Longitude,
Machine: machine,
Metro: site[:3],
Project: p.Project,
Probability: 1,
Site: site,
Type: "unknown", // should be overridden by node.
Uplink: "unknown", // should be overridden by node.
},
},
}
return r
}
117 changes: 117 additions & 0 deletions internal/register/register_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package register

import (
"strings"
"testing"

"github.com/go-test/deep"
v0 "github.com/m-lab/autojoin/api/v0"
"github.com/m-lab/autojoin/iata"
v2 "github.com/m-lab/locate/api/v2"
"github.com/m-lab/uuid-annotator/annotator"
"github.com/oschwald/geoip2-golang"
)

func TestCreateRegisterResponse(t *testing.T) {
tests := []struct {
name string
p *Params
want v0.RegisterResponse
}{
{
name: "success",
p: &Params{
Project: "mlab-sandbox",
Service: "ndt",
Org: "bar",
IPv4: "192.168.0.1",
IPv6: "",
Geo: &geoip2.City{
Country: struct {
GeoNameID uint `maxminddb:"geoname_id"`
IsInEuropeanUnion bool `maxminddb:"is_in_european_union"`
IsoCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
}{
IsoCode: "US",
},
Subdivisions: []struct {
GeoNameID uint `maxminddb:"geoname_id"`
IsoCode string `maxminddb:"iso_code"`
Names map[string]string `maxminddb:"names"`
}{
{IsoCode: "NY", Names: map[string]string{"en": "New York"}},
{IsoCode: "ZZ", Names: map[string]string{"en": "fake thing"}},
},
Location: struct {
AccuracyRadius uint16 `maxminddb:"accuracy_radius"`
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
MetroCode uint `maxminddb:"metro_code"`
TimeZone string `maxminddb:"time_zone"`
}{
Latitude: 41,
Longitude: -73,
},
},
Metro: iata.Row{
IATA: "lga",
Latitude: -10,
Longitude: -10,
},
Network: &annotator.Network{
ASNumber: 12345,
},
},
want: v0.RegisterResponse{
Registration: &v0.Registration{
Hostname: "ndt-lga12345-c0a80001.bar.sandbox.measurement-lab.org",
Annotation: &v0.ServerAnnotation{
Annotation: annotator.ServerAnnotations{
Site: "lga12345",
Machine: "c0a80001",
Geo: &annotator.Geolocation{
CountryCode: "US",
Subdivision1ISOCode: "NY",
Subdivision1Name: "New York",
Subdivision2ISOCode: "ZZ",
Subdivision2Name: "fake thing",
Latitude: -10,
Longitude: -10,
},
Network: &annotator.Network{
ASNumber: 12345,
},
},
Network: v0.Network{
IPv4: "192.168.0.1",
},
Type: "unknown",
},
Heartbeat: &v2.Registration{
CountryCode: "US",
Experiment: "ndt",
Hostname: "ndt-lga12345-c0a80001.bar.sandbox.measurement-lab.org",
Latitude: -10,
Longitude: -10,
Machine: "c0a80001",
Metro: "lga",
Project: "mlab-sandbox",
Probability: 1,
Site: "lga12345",
Type: "unknown",
Uplink: "unknown",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CreateRegisterResponse(tt.p)
if diff := deep.Equal(got, tt.want); diff != nil {
t.Errorf("CreateRegisterResponse() returned != expected: \n%s", strings.Join(diff, "\n"))
}
})
}
}

0 comments on commit 8be6ac7

Please sign in to comment.