bgpwtf/cccampix/peeringdb: init

First pass at a proxy to expose PeeringDB data.

Change-Id: I844973755473b3abc5d334586744004b86d1c3a3
diff --git a/bgpwtf/cccampix/peeringdb/schema/BUILD.bazel b/bgpwtf/cccampix/peeringdb/schema/BUILD.bazel
new file mode 100644
index 0000000..8eded08
--- /dev/null
+++ b/bgpwtf/cccampix/peeringdb/schema/BUILD.bazel
@@ -0,0 +1,11 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "schema.go",
+        "urls.go",
+    ],
+    importpath = "code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb/schema",
+    visibility = ["//visibility:public"],
+)
diff --git a/bgpwtf/cccampix/peeringdb/schema/schema.go b/bgpwtf/cccampix/peeringdb/schema/schema.go
new file mode 100644
index 0000000..0d48aed
--- /dev/null
+++ b/bgpwtf/cccampix/peeringdb/schema/schema.go
@@ -0,0 +1,34 @@
+package schema
+
+// Partial definition from https://www.peeringdb.com/apidocs/
+
+type IX struct {
+	ID       int64   `json:"id"`
+	OrgID    int64   `json:"org_id"`
+	Name     string  `json:"name"`
+	IXLanSet []IXLan `json:"ixlan_set"`
+}
+
+type IXLan struct {
+	ID     int64  `json:"id"`
+	Name   string `json:"name"`
+	NetSet []Net  `json:"net_set"`
+}
+
+type Net struct {
+	ID    int64  `json:"id"`
+	OrgID int64  `json:"org_id"`
+	Name  string `json:"name"`
+	ASN   int64  `json:"asn"`
+}
+
+type NetIXLan struct {
+	ID    int64  `json:"id"`
+	NetID int64  `json:"net_id"`
+	IXID  int64  `json:"ix_id"`
+	Name  string `json:"name"`
+	Speed int64  `json:"speed"`
+	ASN   int64  `json:"asn"`
+	IPv4  string `json:"ipaddr4"`
+	IPv6  string `json:"ipaddr6"`
+}
diff --git a/bgpwtf/cccampix/peeringdb/schema/urls.go b/bgpwtf/cccampix/peeringdb/schema/urls.go
new file mode 100644
index 0000000..abe5f56
--- /dev/null
+++ b/bgpwtf/cccampix/peeringdb/schema/urls.go
@@ -0,0 +1,61 @@
+package schema
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+func IXURL(id int64) string {
+	return fmt.Sprintf("https://peeringdb.com/api/ix/%d.json?depth=4", id)
+}
+
+func NetIXLanInIXURL(id int64) string {
+	return fmt.Sprintf("https://peeringdb.com/api/netixlan?ix_id__in=%d", id)
+}
+
+func NetURLMulti(ids []int64) string {
+	sid := make([]string, len(ids))
+	for i, id := range ids {
+		sid[i] = fmt.Sprintf("%d", id)
+	}
+	return fmt.Sprintf("https://peeringdb.com/api/net?id__in=%s", strings.Join(sid, ","))
+}
+
+func Get(ctx context.Context, obj interface{}, url string) error {
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return fmt.Errorf("http.NewRequest(GET, %q): %v", url, err)
+	}
+
+	req.Header.Add("User-Agent", "bgpwtf-cccampix-peeringdbproxy/1.0 (https://code.hackerspace.pl/hscloud/bgpwtf/cccampix/peeringdb)")
+
+	req = req.WithContext(ctx)
+
+	client := http.DefaultClient
+	res, err := client.Do(req)
+	if err != nil {
+		return fmt.Errorf("client.Do(%v): %v", req, err)
+	}
+
+	defer res.Body.Close()
+
+	if res.StatusCode != 200 {
+		return fmt.Errorf("got status code %d", res.StatusCode)
+	}
+
+	data, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		return fmt.Errorf("could not read response: %v", err)
+	}
+
+	err = json.Unmarshal(data, &obj)
+	if err != nil {
+		return fmt.Errorf("could not parse response JSON: %v", err)
+	}
+
+	return nil
+}