Files
akvorado/orchestrator/clickhouse/http_test.go
Vincent Bernat c6a9319b57 common/schema: turns into a component
This is a first step to make it accept configuration. Most of the
changes are quite trivial, but I also ran into some difficulties with
query columns and filters. They need the schema for parsing, but parsing
happens before dependencies are instantiated (and even if it was not the
case, parsing is stateless). Therefore, I have added a `Validate()`
method that must be called after instantiation. Various bits `panic()`
if not validated to ensure we catch all cases.

The alternative to make the component manages a global state would have
been simpler but it would break once we add the ability to add or
disable columns.
2023-01-18 12:22:10 +01:00

222 lines
5.5 KiB
Go

// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only
package clickhouse
import (
"context"
"fmt"
"net"
netHTTP "net/http"
"testing"
"time"
"akvorado/common/daemon"
"akvorado/common/helpers"
"akvorado/common/http"
"akvorado/common/reporter"
"akvorado/common/schema"
)
func TestHTTPEndpoints(t *testing.T) {
r := reporter.NewMock(t)
config := DefaultConfiguration()
config.SkipMigrations = true
config.Networks = helpers.MustNewSubnetMap(map[string]NetworkAttributes{
"::ffff:192.0.2.0/120": {Name: "infra"},
})
c, err := New(r, config, Dependencies{
Daemon: daemon.NewMock(t),
HTTP: http.NewMock(t, r),
Schema: schema.NewMock(t),
})
if err != nil {
t.Fatalf("New() error:\n%+v", err)
}
helpers.StartStop(t, c)
cases := helpers.HTTPEndpointCases{
{
URL: "/api/v0/orchestrator/clickhouse/protocols.csv",
ContentType: "text/csv; charset=utf-8",
FirstLines: []string{
`proto,name,description`,
`0,HOPOPT,IPv6 Hop-by-Hop Option`,
`1,ICMP,Internet Control Message`,
},
}, {
URL: "/api/v0/orchestrator/clickhouse/asns.csv",
ContentType: "text/csv; charset=utf-8",
FirstLines: []string{
`"asn","name"`,
`1,"Level 3 Communications"`,
},
}, {
URL: "/api/v0/orchestrator/clickhouse/networks.csv",
ContentType: "text/csv; charset=utf-8",
FirstLines: []string{
`network,name,role,site,region,tenant`,
`192.0.2.0/24,infra,,,,`,
},
}, {
URL: "/api/v0/orchestrator/clickhouse/init.sh",
ContentType: "text/x-shellscript",
FirstLines: []string{
`#!/bin/sh`,
``,
`# Install Protobuf schema`,
fmt.Sprintf(`cat > /var/lib/clickhouse/format_schemas/flow-%s.proto <<'EOPROTO'`,
c.d.Schema.ProtobufMessageHash()),
"",
`syntax = "proto3";`,
},
},
}
helpers.TestHTTPEndpoints(t, c.d.HTTP.LocalAddr(), cases)
}
func TestAdditionalASNs(t *testing.T) {
r := reporter.NewMock(t)
config := DefaultConfiguration()
config.ASNs = map[uint32]string{
1: "New network",
}
c, err := New(r, config, Dependencies{
Daemon: daemon.NewMock(t),
HTTP: http.NewMock(t, r),
Schema: schema.NewMock(t),
})
if err != nil {
t.Fatalf("New() error:\n%+v", err)
}
cases := helpers.HTTPEndpointCases{
{
URL: "/api/v0/orchestrator/clickhouse/asns.csv",
ContentType: "text/csv; charset=utf-8",
FirstLines: []string{
`asn,name`,
`1,New network`,
`2,University of Delaware`,
},
},
}
helpers.TestHTTPEndpoints(t, c.d.HTTP.LocalAddr(), cases)
}
func TestNetworkSources(t *testing.T) {
// Mux to answer requests
ready := make(chan bool)
mux := netHTTP.NewServeMux()
mux.Handle("/amazon.json", netHTTP.HandlerFunc(func(w netHTTP.ResponseWriter, r *netHTTP.Request) {
select {
case <-ready:
default:
w.WriteHeader(404)
return
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(200)
w.Write([]byte(`
{
"syncToken": "1665609189",
"createDate": "2022-10-12-21-13-09",
"prefixes": [
{
"ip_prefix": "3.2.34.0/26",
"region": "af-south-1",
"service": "AMAZON",
"network_border_group": "af-south-1"
}
],
"ipv6_prefixes": [
{
"ipv6_prefix": "2600:1ff2:4000::/40",
"region": "us-west-2",
"service": "AMAZON",
"network_border_group": "us-west-2"
},
{
"ipv6_prefix": "2600:1f14:fff:f800::/56",
"region": "us-west-2",
"service": "ROUTE53_HEALTHCHECKS",
"network_border_group": "us-west-2"
}
]
}
`))
}))
// Setup an HTTP server to serve the JSON
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("Listen() error:\n%+v", err)
}
server := &netHTTP.Server{
Addr: listener.Addr().String(),
Handler: mux,
}
address := listener.Addr()
go server.Serve(listener)
defer server.Shutdown(context.Background())
r := reporter.NewMock(t)
config := DefaultConfiguration()
config.SkipMigrations = true
config.NetworkSourcesTimeout = 10 * time.Millisecond
config.NetworkSources = map[string]NetworkSource{
"amazon": {
URL: fmt.Sprintf("http://%s/amazon.json", address),
Interval: 100 * time.Millisecond,
Transform: MustParseTransformQuery(`
(.prefixes + .ipv6_prefixes)[] |
{ prefix: (.ip_prefix // .ipv6_prefix), tenant: "amazon", region: .region, role: .service|ascii_downcase }
`),
},
}
c, err := New(r, config, Dependencies{
Daemon: daemon.NewMock(t),
HTTP: http.NewMock(t, r),
Schema: schema.NewMock(t),
})
if err != nil {
t.Fatalf("New() error:\n%+v", err)
}
helpers.StartStop(t, c)
// When not ready, we get a 503
helpers.TestHTTPEndpoints(t, c.d.HTTP.LocalAddr(), helpers.HTTPEndpointCases{
{
Description: "try when not ready",
URL: "/api/v0/orchestrator/clickhouse/networks.csv",
StatusCode: 503,
},
})
close(ready)
time.Sleep(50 * time.Millisecond)
helpers.TestHTTPEndpoints(t, c.d.HTTP.LocalAddr(), helpers.HTTPEndpointCases{
{
Description: "try when ready",
URL: "/api/v0/orchestrator/clickhouse/networks.csv",
ContentType: "text/csv; charset=utf-8",
FirstLines: []string{
`network,name,role,site,region,tenant`,
`3.2.34.0/26,,amazon,,af-south-1,amazon`,
`2600:1ff2:4000::/40,,amazon,,us-west-2,amazon`,
`2600:1f14:fff:f800::/56,,route53_healthchecks,,us-west-2,amazon`,
},
},
})
gotMetrics := r.GetMetrics("akvorado_orchestrator_clickhouse_network_source_networks_")
expectedMetrics := map[string]string{
`total{source="amazon"}`: "3",
}
if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" {
t.Fatalf("Metrics (-got, +want):\n%s", diff)
}
}