Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Commit

Permalink
objectmap: Add objectmap library code
Browse files Browse the repository at this point in the history
Change-Id: I71e16b48d974cbe165e6d75b0ba3f416a8eb6c7a
  • Loading branch information
grafael committed Sep 21, 2020
1 parent 0070754 commit c60d472
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ require (
github.com/btcsuite/btcutil v1.0.2 // indirect
github.com/calebcase/tmpfile v1.0.2 // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/oschwald/maxminddb-golang v1.7.0
github.com/spacemonkeygo/monkit/v3 v3.0.7-0.20200515175308-072401d8c752
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.5.1
github.com/stretchr/testify v1.6.1
github.com/zeebo/errs v1.2.2
go.uber.org/zap v1.15.0
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/oschwald/maxminddb-golang v1.7.0 h1:JmU4Q1WBv5Q+2KZy5xJI+98aUwTIrPPxZUkd5Cwr8Zc=
github.com/oschwald/maxminddb-golang v1.7.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -228,6 +230,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k=
Expand Down Expand Up @@ -361,6 +365,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107144601-ef85f5a75ddf h1:9cZxTVBvFZgOnVi/DobY3JsafbPFPnP2rtN81d4wPpw=
golang.org/x/sys v0.0.0-20200107144601-ef85f5a75ddf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA=
Expand Down Expand Up @@ -454,6 +459,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
88 changes: 88 additions & 0 deletions objectmap/mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.

package objectmap

import (
"net"

"github.com/oschwald/maxminddb-golang"
"github.com/zeebo/errs"
)

// Error is the default error class for objectmap.
var Error = errs.Class("objectmap error")

// IPInfo represents the geolocation data from maxmind db.
type IPInfo struct {
Location struct {
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
} `maxminddb:"location"`
Postal struct {
Code string `maxminddb:"code"`
} `maxminddb:"postal"`
Country struct {
IsoCode string `maxminddb:"iso_code"`
} `maxminddb:"country"`
}

// Reader is a maxmind database reader interface.
type Reader interface {
Lookup(ip net.IP, result interface{}) error
Close() error
}

// IPDB holds the database file path and its reader.
type IPDB struct {
reader Reader
}

// NewIPDB creates a new IPMapper instance.
func NewIPDB(dbPath string) (*IPDB, error) {
reader, err := maxminddb.Open(dbPath)
if err != nil {
return nil, Error.Wrap(err)
}

return &IPDB{
reader: reader,
}, nil
}

// Close closes the IPMapper reader.
func (mapper *IPDB) Close() (err error) {
if mapper.reader != nil {
return mapper.reader.Close()
}
return nil
}

// ValidateIP validate and remove port from IP address.
func ValidateIP(ipAddress string) (net.IP, error) {

ip, _, err := net.SplitHostPort(ipAddress)
if err != nil {
ip = ipAddress // assume it had no port
}

parsed := net.ParseIP(ip)
if parsed == nil {
return nil, errs.New("invalid IP address: %s", ip)
}
return parsed, nil
}

// GetIPInfos returns the geolocation information from an IP address.
func (mapper *IPDB) GetIPInfos(ipAddress string) (_ *IPInfo, err error) {

var record IPInfo
parsed, err := ValidateIP(ipAddress)
if err != nil {
return nil, Error.Wrap(err)
}

err = mapper.reader.Lookup(parsed, &record)

return &record, Error.Wrap(err)
}
80 changes: 80 additions & 0 deletions objectmap/mapper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.

package objectmap

import (
"errors"
"net"
"testing"

"github.com/stretchr/testify/require"
)

type MockReader struct{}

func (mr *MockReader) Lookup(ip net.IP, result interface{}) error {

// Valid geolocation case
if ip.Equal(net.IPv4(172, 146, 10, 1)) {
result.(*IPInfo).Location = mockIPInfo(-19.456, 20.123).Location
return nil
}
// Location not found
if ip.Equal(net.IPv4(1, 1, 1, 1)) {
return errors.New("Not found")
}
return nil
}

func (mr *MockReader) Close() error {
return nil
}

func mockIPInfo(latitude, longitude float64) *IPInfo {
return &IPInfo{
Location: struct {
Latitude float64 `maxminddb:"latitude"`
Longitude float64 `maxminddb:"longitude"`
}{
Latitude: latitude,
Longitude: longitude,
},
}
}
func TestIPDB_GetIPInfos(t *testing.T) {

mockReader := &MockReader{}

tests := []struct {
name string
reader *MockReader
ipAddress string
expected *IPInfo
expectedErr bool
}{
{"invalid IP", mockReader, "999.999.999.999", nil, true},
{"invalid (IP:PORT)", mockReader, "999.999.999.999:42", nil, true},
{"valid IP found geolocation", mockReader, "172.146.10.1", mockIPInfo(-19.456, 20.123), false},
{"valid (IP:PORT) found geolocation", mockReader, "172.146.10.1:4545", mockIPInfo(-19.456, 20.123), false},
{"valid IP geolocation not found", mockReader, "1.1.1.1", &IPInfo{}, true},
{"valid (IP:PORT) geolocation not found", mockReader, "1.1.1.1:1000", &IPInfo{}, true},
}
for _, tt := range tests {
mapper := &IPDB{
reader: tt.reader,
}
testCase := tt
t.Run(tt.name, func(t *testing.T) {
got, err := mapper.GetIPInfos(testCase.ipAddress)

if testCase.expectedErr {
require.Error(t, err)
return
}

require.NoError(t, err)
require.EqualValues(t, testCase.expected, got)
})
}
}

0 comments on commit c60d472

Please sign in to comment.