lib: consistently use f-tests instead of table-driven tests

This makes easier to read and debug these tests. This also reduces test lines count by 15% from 3K to 2.5K
See https://itnext.io/f-tests-as-a-replacement-for-table-driven-tests-in-go-8814a8b19e9e

While at it, consistently use t.Fatal* instead of t.Error*, since t.Error* usually leads
to more complicated and fragile tests, while it doesn't bring any practical benefits over t.Fatal*.
This commit is contained in:
Aliaksandr Valialkin 2024-07-09 22:32:54 +02:00
parent 662e026279
commit a9525da8a4
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
35 changed files with 1795 additions and 2272 deletions

View file

@ -135,9 +135,13 @@ func TestAuthKeyMetrics(t *testing.T) {
} }
func TestHandlerWrapper(t *testing.T) { func TestHandlerWrapper(t *testing.T) {
*headerHSTS = "foo" const hstsHeader = "foo"
*headerFrameOptions = "bar" const frameOptionsHeader = "bar"
*headerCSP = "baz" const cspHeader = "baz"
*headerHSTS = hstsHeader
*headerFrameOptions = frameOptionsHeader
*headerCSP = cspHeader
defer func() { defer func() {
*headerHSTS = "" *headerHSTS = ""
*headerFrameOptions = "" *headerFrameOptions = ""
@ -152,13 +156,14 @@ func TestHandlerWrapper(t *testing.T) {
return true return true
}) })
if w.Header().Get("Strict-Transport-Security") != "foo" { h := w.Header()
t.Errorf("HSTS header not set") if got := h.Get("Strict-Transport-Security"); got != hstsHeader {
t.Fatalf("unexpected HSTS header; got %q; want %q", got, hstsHeader)
} }
if w.Header().Get("X-Frame-Options") != "bar" { if got := h.Get("X-Frame-Options"); got != frameOptionsHeader {
t.Errorf("X-Frame-Options header not set") t.Fatalf("unexpected X-Frame-Options header; got %q; want %q", got, frameOptionsHeader)
} }
if w.Header().Get("Content-Security-Policy") != "baz" { if got := h.Get("Content-Security-Policy"); got != cspHeader {
t.Errorf("CSP header not set") t.Fatalf("unexpected CSP header; got %q; want %q", got, cspHeader)
} }
} }

View file

@ -9,28 +9,27 @@ func TestTLSConfig(t *testing.T) {
insecureSkipVerify = true insecureSkipVerify = true
tlsCfg, err := TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify) tlsCfg, err := TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify)
if err != nil { if err != nil {
t.Errorf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
if tlsCfg == nil { if tlsCfg == nil {
t.Errorf("expected tlsConfig to be set, got nil") t.Fatalf("expected tlsConfig to be set, got nil")
return
} }
if tlsCfg.ServerName != serverName { if tlsCfg.ServerName != serverName {
t.Errorf("unexpected ServerName, want %s, got %s", serverName, tlsCfg.ServerName) t.Fatalf("unexpected ServerName, want %s, got %s", serverName, tlsCfg.ServerName)
} }
if tlsCfg.InsecureSkipVerify != insecureSkipVerify { if tlsCfg.InsecureSkipVerify != insecureSkipVerify {
t.Errorf("unexpected InsecureSkipVerify, want %v, got %v", insecureSkipVerify, tlsCfg.InsecureSkipVerify) t.Fatalf("unexpected InsecureSkipVerify, want %v, got %v", insecureSkipVerify, tlsCfg.InsecureSkipVerify)
} }
certFile = "/path/to/nonexisting/cert/file" certFile = "/path/to/nonexisting/cert/file"
_, err = TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify) _, err = TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify)
if err == nil { if err == nil {
t.Errorf("expected keypair error, got nil") t.Fatalf("expected keypair error, got nil")
} }
certFile = "" certFile = ""
CAFile = "/path/to/nonexisting/cert/file" CAFile = "/path/to/nonexisting/cert/file"
_, err = TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify) _, err = TLSConfig(certFile, keyFile, CAFile, serverName, insecureSkipVerify)
if err == nil { if err == nil {
t.Errorf("expected read error, got nil") t.Fatalf("expected read error, got nil")
} }
} }
@ -40,14 +39,14 @@ func TestTransport(t *testing.T) {
URL := "http://victoriametrics.com" URL := "http://victoriametrics.com"
_, err := Transport(URL, certFile, keyFile, CAFile, serverName, insecureSkipVerify) _, err := Transport(URL, certFile, keyFile, CAFile, serverName, insecureSkipVerify)
if err != nil { if err != nil {
t.Errorf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
URL = "https://victoriametrics.com" URL = "https://victoriametrics.com"
tr, err := Transport(URL, certFile, keyFile, CAFile, serverName, insecureSkipVerify) tr, err := Transport(URL, certFile, keyFile, CAFile, serverName, insecureSkipVerify)
if err != nil { if err != nil {
t.Errorf("unexpected error %s", err) t.Fatalf("unexpected error %s", err)
} }
if tr.TLSClientConfig == nil { if tr.TLSClientConfig == nil {
t.Errorf("expected TLSClientConfig to be set, got nil") t.Fatalf("expected TLSClientConfig to be set, got nil")
} }
} }

View file

@ -129,8 +129,7 @@ func TestParseTenantID(t *testing.T) {
got, err := ParseTenantID(tenant) got, err := ParseTenantID(tenant)
if err != nil { if err != nil {
t.Errorf("unexpected error: %s", err) t.Fatalf("unexpected error: %s", err)
return
} }
if got.String() != expected.String() { if got.String() != expected.String() {

View file

@ -5,20 +5,21 @@ import (
"testing" "testing"
) )
func Test_parseAPIResponse(t *testing.T) { func TestParseAPIResponse(t *testing.T) {
type args struct { f := func(data string, responseExpected *listDropletResponse) {
data []byte t.Helper()
}
tests := []struct {
name string
args args
want *listDropletResponse
wantErr bool
}{
{ response, err := parseAPIResponse([]byte(data))
name: "simple parse", if err != nil {
args: args{data: []byte(`{ t.Fatalf("unexpected parseAPIResponse() error: %s", err)
}
if !reflect.DeepEqual(response, responseExpected) {
t.Fatalf("unexpected response\ngot\n%v\nwant\n%v", response, responseExpected)
}
}
data := `
{
"droplets": [ "droplets": [
{ {
"id": 3164444, "id": 3164444,
@ -88,20 +89,18 @@ func Test_parseAPIResponse(t *testing.T) {
"next": "https://api.digitalocean.com/v2/droplets?page=2&per_page=1" "next": "https://api.digitalocean.com/v2/droplets?page=2&per_page=1"
} }
} }
}`)}, }`
want: &listDropletResponse{
responseExpected := &listDropletResponse{
Droplets: []droplet{ Droplets: []droplet{
{ {
Image: struct { Image: dropletImage{
Name string `json:"name"` Name: "14.04 x64",
Slug string `json:"slug"` Slug: "ubuntu-16-04-x64",
}(struct { },
Name string Region: dropletRegion{
Slug string Slug: "nyc3",
}{Name: "14.04 x64", Slug: "ubuntu-16-04-x64"}), },
Region: struct {
Slug string `json:"slug"`
}(struct{ Slug string }{Slug: "nyc3"}),
Networks: networks{ Networks: networks{
V6: []network{ V6: []network{
{ {
@ -126,45 +125,29 @@ func Test_parseAPIResponse(t *testing.T) {
}, },
}, },
Links: links{ Links: links{
Pages: struct { Pages: linksPages{
Last string `json:"last,omitempty"` Last: "https://api.digitalocean.com/v2/droplets?page=3&per_page=1",
Next string `json:"next,omitempty"` Next: "https://api.digitalocean.com/v2/droplets?page=2&per_page=1",
}(struct {
Last string
Next string
}{Last: "https://api.digitalocean.com/v2/droplets?page=3&per_page=1", Next: "https://api.digitalocean.com/v2/droplets?page=2&per_page=1"}),
},
}, },
}, },
} }
for _, tt := range tests { f(data, responseExpected)
t.Run(tt.name, func(t *testing.T) {
got, err := parseAPIResponse(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseAPIResponse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseAPIResponse() got = \n%v\n, \nwant \n%v\n", got, tt.want)
}
})
}
} }
func Test_getDroplets(t *testing.T) { func TestGetDroplets(t *testing.T) {
type args struct { f := func(getAPIResponse func(string) ([]byte, error), expectedDropletCount int) {
getAPIResponse func(string) ([]byte, error) t.Helper()
resp, err := getDroplets(getAPIResponse)
if err != nil {
t.Fatalf("getDroplets() error: %s", err)
} }
tests := []struct { if len(resp) != expectedDropletCount {
name string t.Fatalf("unexpected droplets count; got %d; want %d\ndroplets:\n%v", len(resp), expectedDropletCount, resp)
args args }
wantDropletCount int }
wantErr bool
}{ getAPIResponse := func(s string) ([]byte, error) {
{
name: "get 4 droples",
args: args{
func(s string) ([]byte, error) {
var resp []byte var resp []byte
switch s { switch s {
case dropletsAPIPath: case dropletsAPIPath:
@ -328,22 +311,6 @@ func Test_getDroplets(t *testing.T) {
}`) }`)
} }
return resp, nil return resp, nil
},
},
wantDropletCount: 5,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getDroplets(tt.args.getAPIResponse)
if (err != nil) != tt.wantErr {
t.Errorf("getDroplets() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != tt.wantDropletCount {
t.Fatalf("unexpected droplets count: %d, want: %d, \n droplets: %v\n", len(got), tt.wantDropletCount, got)
}
})
} }
f(getAPIResponse, 5)
} }

View file

@ -50,19 +50,23 @@ type droplet struct {
Status string `json:"status"` Status string `json:"status"`
Features []string `json:"features"` Features []string `json:"features"`
Image struct { Image dropletImage `json:"image"`
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"image"`
SizeSlug string `json:"size_slug"` SizeSlug string `json:"size_slug"`
Networks networks `json:"networks"` Networks networks `json:"networks"`
Region struct { Region dropletRegion `json:"region"`
Slug string `json:"slug"`
} `json:"region"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
VpcUUID string `json:"vpc_uuid"` VpcUUID string `json:"vpc_uuid"`
} }
type dropletImage struct {
Name string `json:"name"`
Slug string `json:"slug"`
}
type dropletRegion struct {
Slug string `json:"slug"`
}
func (d *droplet) getIPByNet(netVersion, netType string) string { func (d *droplet) getIPByNet(netVersion, netType string) string {
var dropletNetworks []network var dropletNetworks []network
switch netVersion { switch netVersion {
@ -98,10 +102,12 @@ type listDropletResponse struct {
} }
type links struct { type links struct {
Pages struct { Pages linksPages `json:"pages,omitempty"`
}
type linksPages struct {
Last string `json:"last,omitempty"` Last string `json:"last,omitempty"`
Next string `json:"next,omitempty"` Next string `json:"next,omitempty"`
} `json:"pages,omitempty"`
} }
func (r *listDropletResponse) nextURLPath() (string, error) { func (r *listDropletResponse) nextURLPath() (string, error) {

View file

@ -7,38 +7,31 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addDropletLabels(t *testing.T) { func TestAddDropletLabels(t *testing.T) {
type args struct { f := func(droplets []droplet, labelssExpected []*promutils.Labels) {
droplets []droplet t.Helper()
defaultPort int
labelss := addDropletLabels(droplets, 9100)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // base labels add test
args args droplets := []droplet{
want []*promutils.Labels
}{
{
name: "base labels add test",
args: args{
droplets: []droplet{
{ {
ID: 15, ID: 15,
Tags: []string{"private", "test"}, Tags: []string{"private", "test"},
Status: "active", Status: "active",
Name: "ubuntu-1", Name: "ubuntu-1",
Region: struct { Region: dropletRegion{
Slug string `json:"slug"` Slug: "do",
}(struct{ Slug string }{Slug: "do"}), },
Features: []string{"feature-1", "feature-2"}, Features: []string{"feature-1", "feature-2"},
SizeSlug: "base-1", SizeSlug: "base-1",
VpcUUID: "vpc-1", VpcUUID: "vpc-1",
Image: struct { Image: dropletImage{
Name string `json:"name"` Name: "ubuntu",
Slug string `json:"slug"` Slug: "18",
}(struct { },
Name string
Slug string
}{Name: "ubuntu", Slug: "18"}),
Networks: networks{ Networks: networks{
V4: []network{ V4: []network{
{ {
@ -58,10 +51,8 @@ func Test_addDropletLabels(t *testing.T) {
}, },
}, },
}, },
}, }
defaultPort: 9100, labelssExpected := []*promutils.Labels{
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "100.100.100.100:9100", "__address__": "100.100.100.100:9100",
"__meta_digitalocean_droplet_id": "15", "__meta_digitalocean_droplet_id": "15",
@ -78,13 +69,6 @@ func Test_addDropletLabels(t *testing.T) {
"__meta_digitalocean_tags": ",private,test,", "__meta_digitalocean_tags": ",private,test,",
"__meta_digitalocean_vpc": "vpc-1", "__meta_digitalocean_vpc": "vpc-1",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addDropletLabels(tt.args.droplets, tt.args.defaultPort)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(droplets, labelssExpected)
} }

View file

@ -14,21 +14,29 @@ type container struct {
ID string ID string
Names []string Names []string
Labels map[string]string Labels map[string]string
Ports []struct { Ports []containerPort
HostConfig containerHostConfig
NetworkSettings containerNetworkSettings
}
type containerPort struct {
IP string IP string
PrivatePort int PrivatePort int
PublicPort int PublicPort int
Type string Type string
} }
HostConfig struct {
type containerHostConfig struct {
NetworkMode string NetworkMode string
} }
NetworkSettings struct {
Networks map[string]struct { type containerNetworkSettings struct {
Networks map[string]containerNetwork
}
type containerNetwork struct {
IPAddress string IPAddress string
NetworkID string NetworkID string
}
}
} }
func getContainersLabels(cfg *apiConfig) ([]*promutils.Labels, error) { func getContainersLabels(cfg *apiConfig) ([]*promutils.Labels, error) {

View file

@ -8,20 +8,20 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseContainers(t *testing.T) { func TePParseContainers(t *testing.T) {
type args struct { f := func(data string, resultExpected []container) {
data []byte t.Helper()
result, err := parseContainers([]byte(data))
if err != nil {
t.Fatalf("parseContainers() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want []container }
wantErr bool
}{ data := `[
{
name: "parse two containers",
args: args{
data: []byte(`[
{ {
"Id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "Id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"Names": [ "Names": [
@ -116,9 +116,8 @@ func Test_parseContainers(t *testing.T) {
} }
} }
} }
]`), ]`
}, resultExpected := []container{
want: []container{
{ {
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"}, Names: []string{"/crow-server"},
@ -130,32 +129,19 @@ func Test_parseContainers(t *testing.T) {
"com.docker.compose.service": "crow-server", "com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2", "com.docker.compose.version": "1.11.2",
}, },
Ports: []struct { Ports: []containerPort{
IP string {
PrivatePort int
PublicPort int
Type string
}{{
IP: "0.0.0.0", IP: "0.0.0.0",
PrivatePort: 8080, PrivatePort: 8080,
PublicPort: 18081, PublicPort: 18081,
Type: "tcp", Type: "tcp",
}}, },
HostConfig: struct { },
NetworkMode string HostConfig: containerHostConfig{
}{
NetworkMode: "bridge", NetworkMode: "bridge",
}, },
NetworkSettings: struct { NetworkSettings: containerNetworkSettings{
Networks map[string]struct { Networks: map[string]containerNetwork{
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": { "bridge": {
IPAddress: "172.17.0.2", IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634", NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
@ -174,32 +160,19 @@ func Test_parseContainers(t *testing.T) {
"com.docker.compose.service": "crow-web", "com.docker.compose.service": "crow-web",
"com.docker.compose.version": "1.11.2", "com.docker.compose.version": "1.11.2",
}, },
Ports: []struct { Ports: []containerPort{
IP string {
PrivatePort int
PublicPort int
Type string
}{{
IP: "0.0.0.0", IP: "0.0.0.0",
PrivatePort: 8080, PrivatePort: 8080,
PublicPort: 18082, PublicPort: 18082,
Type: "tcp", Type: "tcp",
}}, },
HostConfig: struct { },
NetworkMode string HostConfig: containerHostConfig{
}{
NetworkMode: "bridge", NetworkMode: "bridge",
}, },
NetworkSettings: struct { NetworkSettings: containerNetworkSettings{
Networks map[string]struct { Networks: map[string]containerNetwork{
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": { "bridge": {
IPAddress: "172.17.0.3", IPAddress: "172.17.0.3",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634", NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
@ -207,24 +180,19 @@ func Test_parseContainers(t *testing.T) {
}, },
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseContainers(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseContainers() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseNetworks() \ngot %v, \nwant %v", got, tt.want)
}
})
} }
f(data, resultExpected)
} }
func Test_addContainerLabels(t *testing.T) { func TestAddContainerLabels(t *testing.T) {
f := func(c container, networkLabels map[string]*promutils.Labels, labelssExpected []*promutils.Labels) {
t.Helper()
labelss := addContainersLabels([]container{c}, networkLabels, 8012, "foobar")
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
}
data := []byte(`[ data := []byte(`[
{ {
"Name": "host", "Name": "host",
@ -314,15 +282,8 @@ func Test_addContainerLabels(t *testing.T) {
} }
networkLabels := getNetworkLabelsByNetworkID(networks) networkLabels := getNetworkLabelsByNetworkID(networks)
tests := []struct { // NetworkMode != host
name string c := container{
c container
want []*promutils.Labels
wantErr bool
}{
{
name: "NetworkMode!=host",
c: container{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"}, Names: []string{"/crow-server"},
Labels: map[string]string{ Labels: map[string]string{
@ -333,29 +294,19 @@ func Test_addContainerLabels(t *testing.T) {
"com.docker.compose.service": "crow-server", "com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2", "com.docker.compose.version": "1.11.2",
}, },
HostConfig: struct { HostConfig: containerHostConfig{
NetworkMode string
}{
NetworkMode: "bridge", NetworkMode: "bridge",
}, },
NetworkSettings: struct { NetworkSettings: containerNetworkSettings{
Networks map[string]struct { Networks: map[string]containerNetwork{
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"host": { "host": {
IPAddress: "172.17.0.2", IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634", NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
}, },
}, },
}, },
}, }
want: []*promutils.Labels{ labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8012", "__address__": "172.17.0.2:8012",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
@ -374,11 +325,11 @@ func Test_addContainerLabels(t *testing.T) {
"__meta_docker_network_name": "bridge", "__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local", "__meta_docker_network_scope": "local",
}), }),
}, }
}, f(c, networkLabels, labelssExpected)
{
name: "NetworkMode=host", // NetworkMode=host
c: container{ c = container{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"}, Names: []string{"/crow-server"},
Labels: map[string]string{ Labels: map[string]string{
@ -389,29 +340,19 @@ func Test_addContainerLabels(t *testing.T) {
"com.docker.compose.service": "crow-server", "com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2", "com.docker.compose.version": "1.11.2",
}, },
HostConfig: struct { HostConfig: containerHostConfig{
NetworkMode string
}{
NetworkMode: "host", NetworkMode: "host",
}, },
NetworkSettings: struct { NetworkSettings: containerNetworkSettings{
Networks map[string]struct { Networks: map[string]containerNetwork{
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"host": { "host": {
IPAddress: "172.17.0.2", IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634", NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
}, },
}, },
}, },
}, }
want: []*promutils.Labels{ labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "foobar", "__address__": "foobar",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
@ -430,11 +371,11 @@ func Test_addContainerLabels(t *testing.T) {
"__meta_docker_network_name": "bridge", "__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local", "__meta_docker_network_scope": "local",
}), }),
}, }
}, f(c, networkLabels, labelssExpected)
{
name: "get labels from a container", // get labels from a container
c: container{ c = container{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"}, Names: []string{"/crow-server"},
Labels: map[string]string{ Labels: map[string]string{
@ -445,40 +386,27 @@ func Test_addContainerLabels(t *testing.T) {
"com.docker.compose.service": "crow-server", "com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2", "com.docker.compose.version": "1.11.2",
}, },
Ports: []struct { Ports: []containerPort{
IP string {
PrivatePort int
PublicPort int
Type string
}{{
IP: "0.0.0.0", IP: "0.0.0.0",
PrivatePort: 8080, PrivatePort: 8080,
PublicPort: 18081, PublicPort: 18081,
Type: "tcp", Type: "tcp",
}}, },
HostConfig: struct { },
NetworkMode string HostConfig: containerHostConfig{
}{
NetworkMode: "bridge", NetworkMode: "bridge",
}, },
NetworkSettings: struct { NetworkSettings: containerNetworkSettings{
Networks map[string]struct { Networks: map[string]containerNetwork{
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": { "bridge": {
IPAddress: "172.17.0.2", IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634", NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
}, },
}, },
}, },
}, }
want: []*promutils.Labels{ labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8080", "__address__": "172.17.0.2:8080",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
@ -500,18 +428,6 @@ func Test_addContainerLabels(t *testing.T) {
"__meta_docker_port_public": "18081", "__meta_docker_port_public": "18081",
"__meta_docker_port_public_ip": "0.0.0.0", "__meta_docker_port_public_ip": "0.0.0.0",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
labelss := addContainersLabels([]container{tt.c}, networkLabels, 8012, "foobar")
if (err != nil) != tt.wantErr {
t.Errorf("addContainersLabels() error = %v, wantErr %v", err, tt.wantErr)
return
}
discoveryutils.TestEqualLabelss(t, labelss, tt.want)
})
} }
f(c, networkLabels, labelssExpected)
} }

View file

@ -9,19 +9,26 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addNetworkLabels(t *testing.T) { func TestAddNetworkLabels(t *testing.T) {
type args struct { f := func(networks []network, labelssExpected []*promutils.Labels) {
networks []network t.Helper()
networkLabels := getNetworkLabelsByNetworkID(networks)
var networkIDs []string
for networkID := range networkLabels {
networkIDs = append(networkIDs, networkID)
} }
tests := []struct { sort.Strings(networkIDs)
name string var labelss []*promutils.Labels
args args for _, networkID := range networkIDs {
want []*promutils.Labels labelss = append(labelss, networkLabels[networkID])
}{ }
{ discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
name: "ingress network", }
args: args{
networks: []network{ // ingress network
networks := []network{
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true, Ingress: true,
@ -31,9 +38,8 @@ func Test_addNetworkLabels(t *testing.T) {
"key1": "value1", "key1": "value1",
}, },
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__meta_docker_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_docker_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_docker_network_ingress": "true", "__meta_docker_network_ingress": "true",
@ -41,40 +47,26 @@ func Test_addNetworkLabels(t *testing.T) {
"__meta_docker_network_label_key1": "value1", "__meta_docker_network_label_key1": "value1",
"__meta_docker_network_name": "ingress", "__meta_docker_network_name": "ingress",
"__meta_docker_network_scope": "swarm", "__meta_docker_network_scope": "swarm",
})}, }),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getNetworkLabelsByNetworkID(tt.args.networks)
var networkIDs []string
for networkID := range got {
networkIDs = append(networkIDs, networkID)
}
sort.Strings(networkIDs)
var labelss []*promutils.Labels
for _, networkID := range networkIDs {
labelss = append(labelss, got[networkID])
}
discoveryutils.TestEqualLabelss(t, labelss, tt.want)
})
} }
f(networks, labelssExpected)
} }
func Test_parseNetworks(t *testing.T) { func TestParseNetworks(t *testing.T) {
type args struct { f := func(data string, resultExpected []network) {
data []byte t.Helper()
result, err := parseNetworks([]byte(data))
if err != nil {
t.Fatalf("parseNetworks() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected networks\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want []network }
wantErr bool
}{ // parse two networks
{ data := `[
name: "parse two networks",
args: args{
data: []byte(`[
{ {
"Name": "ingress", "Name": "ingress",
"Id": "qs0hog6ldlei9ct11pr3c77v1", "Id": "qs0hog6ldlei9ct11pr3c77v1",
@ -132,9 +124,8 @@ func Test_parseNetworks(t *testing.T) {
"key": "value" "key": "value"
} }
} }
]`), ]`
}, resultExpected := []network{
want: []network{
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true, Ingress: true,
@ -152,19 +143,6 @@ func Test_parseNetworks(t *testing.T) {
"key": "value", "key": "value",
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseNetworks(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseNetworks() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseNetworks() \ngot %v, \nwant %v", got, tt.want)
}
})
} }
f(data, resultExpected)
} }

View file

@ -9,19 +9,26 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addNetworkLabels(t *testing.T) { func TestAddNetworkLabels(t *testing.T) {
type args struct { f := func(networks []network, labelssExpected []*promutils.Labels) {
networks []network t.Helper()
networkLabels := getNetworkLabelsByNetworkID(networks)
var networkIDs []string
for networkID := range networkLabels {
networkIDs = append(networkIDs, networkID)
} }
tests := []struct { sort.Strings(networkIDs)
name string var labelss []*promutils.Labels
args args for _, networkID := range networkIDs {
want []*promutils.Labels labelss = append(labelss, networkLabels[networkID])
}{ }
{ discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
name: "ingress network", }
args: args{
networks: []network{ // ingress network
networks := []network{
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true, Ingress: true,
@ -31,9 +38,8 @@ func Test_addNetworkLabels(t *testing.T) {
"key1": "value1", "key1": "value1",
}, },
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true", "__meta_dockerswarm_network_ingress": "true",
@ -41,40 +47,26 @@ func Test_addNetworkLabels(t *testing.T) {
"__meta_dockerswarm_network_label_key1": "value1", "__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress", "__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm", "__meta_dockerswarm_network_scope": "swarm",
})}, }),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := getNetworkLabelsByNetworkID(tt.args.networks)
var networkIDs []string
for networkID := range got {
networkIDs = append(networkIDs, networkID)
}
sort.Strings(networkIDs)
var labelss []*promutils.Labels
for _, networkID := range networkIDs {
labelss = append(labelss, got[networkID])
}
discoveryutils.TestEqualLabelss(t, labelss, tt.want)
})
} }
f(networks, labelssExpected)
} }
func Test_parseNetworks(t *testing.T) { func TestParseNetworks(t *testing.T) {
type args struct { f := func(data string, resultExpected []network) {
data []byte t.Helper()
result, err := parseNetworks([]byte(data))
if err != nil {
t.Fatalf("parseNetworks() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want []network }
wantErr bool
}{ // parse two networks
{ data := `[
name: "parse two networks",
args: args{
data: []byte(`[
{ {
"Name": "ingress", "Name": "ingress",
"Id": "qs0hog6ldlei9ct11pr3c77v1", "Id": "qs0hog6ldlei9ct11pr3c77v1",
@ -132,9 +124,8 @@ func Test_parseNetworks(t *testing.T) {
"key": "value" "key": "value"
} }
} }
]`), ]`
}, resultExpected := []network{
want: []network{
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true, Ingress: true,
@ -152,19 +143,6 @@ func Test_parseNetworks(t *testing.T) {
"key": "value", "key": "value",
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseNetworks(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseNetworks() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseNetworks() \ngot %v, \nwant %v", got, tt.want)
}
})
} }
f(data, resultExpected)
} }

View file

@ -11,31 +11,43 @@ import (
// See https://docs.docker.com/engine/api/v1.40/#tag/Node // See https://docs.docker.com/engine/api/v1.40/#tag/Node
type node struct { type node struct {
ID string ID string
Spec struct { Spec nodeSpec
Description nodeDescription
Status nodeStatus
ManagerStatus nodeManagerStatus
}
type nodeSpec struct {
Labels map[string]string Labels map[string]string
Role string Role string
Availability string Availability string
} }
Description struct {
type nodeDescription struct {
Hostname string Hostname string
Platform struct { Platform nodePlatform
Engine nodeEngine
}
type nodePlatform struct {
Architecture string Architecture string
OS string OS string
} }
Engine struct {
type nodeEngine struct {
EngineVersion string EngineVersion string
} }
}
Status struct { type nodeStatus struct {
State string State string
Message string Message string
Addr string Addr string
} }
ManagerStatus struct {
type nodeManagerStatus struct {
Leader bool Leader bool
Reachability string Reachability string
Addr string Addr string
}
} }
func getNodesLabels(cfg *apiConfig) ([]*promutils.Labels, error) { func getNodesLabels(cfg *apiConfig) ([]*promutils.Labels, error) {

View file

@ -8,20 +8,21 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseNodes(t *testing.T) { func TestParseNodes(t *testing.T) {
type args struct { f := func(data string, resultExpected []node) {
data []byte t.Helper()
result, err := parseNodes([]byte(data))
if err != nil {
t.Fatalf("parseNodes() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected result;\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want []node }
wantErr bool
}{ // parse ok
{ data := `[
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "qauwmifceyvqs0sipvzu8oslu", "ID": "qauwmifceyvqs0sipvzu8oslu",
"Version": { "Version": {
@ -51,110 +52,66 @@ func Test_parseNodes(t *testing.T) {
} }
} }
] ]
`), `
}, resultExpected := []node{
want: []node{
{ {
ID: "qauwmifceyvqs0sipvzu8oslu", ID: "qauwmifceyvqs0sipvzu8oslu",
Spec: struct { Spec: nodeSpec{
Labels map[string]string Role: "manager",
Role string Availability: "active",
Availability string },
}{Role: "manager", Availability: "active"}, Status: nodeStatus{
Status: struct { State: "ready",
State string Addr: "172.31.40.97",
Message string },
Addr string Description: nodeDescription{
}{State: "ready", Addr: "172.31.40.97"},
Description: struct {
Hostname string
Platform struct {
Architecture string
OS string
}
Engine struct{ EngineVersion string }
}{
Hostname: "ip-172-31-40-97", Hostname: "ip-172-31-40-97",
Platform: struct { Platform: nodePlatform{
Architecture string
OS string
}{
Architecture: "x86_64", Architecture: "x86_64",
OS: "linux", OS: "linux",
}, },
Engine: struct{ EngineVersion string }{ Engine: nodeEngine{
EngineVersion: "19.03.11", EngineVersion: "19.03.11",
}, },
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseNodes(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseNodes() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseNodes() \ngot %v, \nwant %v", got, tt.want)
}
})
} }
f(data, resultExpected)
} }
func Test_addNodeLabels(t *testing.T) { func TestAddNodeLabels(t *testing.T) {
type args struct { f := func(nodes []node, port int, resultExpected []*promutils.Labels) {
nodes []node t.Helper()
port int
result := addNodeLabels(nodes, port)
discoveryutils.TestEqualLabelss(t, result, resultExpected)
} }
tests := []struct {
name string // add labels to one node
args args nodes := []node{
want []*promutils.Labels
}{
{
name: "add labels to one node",
args: args{
nodes: []node{
{ {
ID: "qauwmifceyvqs0sipvzu8oslu", ID: "qauwmifceyvqs0sipvzu8oslu",
Spec: struct { Spec: nodeSpec{
Labels map[string]string Role: "manager",
Role string Availability: "active",
Availability string },
}{Role: "manager", Availability: "active"}, Status: nodeStatus{
Status: struct { State: "ready",
State string Addr: "172.31.40.97",
Message string },
Addr string Description: nodeDescription{
}{State: "ready", Addr: "172.31.40.97"},
Description: struct {
Hostname string
Platform struct {
Architecture string
OS string
}
Engine struct{ EngineVersion string }
}{
Hostname: "ip-172-31-40-97", Hostname: "ip-172-31-40-97",
Platform: struct { Platform: nodePlatform{
Architecture string
OS string
}{
Architecture: "x86_64", Architecture: "x86_64",
OS: "linux", OS: "linux",
}, },
Engine: struct{ EngineVersion string }{ Engine: nodeEngine{
EngineVersion: "19.03.11", EngineVersion: "19.03.11",
}, },
}, },
}, },
}, }
port: 9100, labelssExpected := []*promutils.Labels{
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100", "__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97", "__meta_dockerswarm_node_address": "172.31.40.97",
@ -169,13 +126,7 @@ func Test_addNodeLabels(t *testing.T) {
"__meta_dockerswarm_node_platform_os": "linux", "__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager", "__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready", "__meta_dockerswarm_node_status": "ready",
})}, }),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addNodeLabels(tt.args.nodes, tt.args.port)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(nodes, 9100, labelssExpected)
} }

View file

@ -13,30 +13,44 @@ import (
// https://docs.docker.com/engine/api/v1.40/#tag/Service // https://docs.docker.com/engine/api/v1.40/#tag/Service
type service struct { type service struct {
ID string ID string
Spec struct { Spec serviceSpec
UpdateStatus serviceUpdateStatus
Endpoint serviceEndpoint
}
type serviceSpec struct {
Labels map[string]string Labels map[string]string
Name string Name string
TaskTemplate struct { TaskTemplate taskTemplate
ContainerSpec struct { Mode serviceSpecMode
}
type taskTemplate struct {
ContainerSpec containerSpec
}
type containerSpec struct {
Hostname string Hostname string
Image string Image string
} }
}
Mode struct { type serviceSpecMode struct {
Global interface{} Global interface{}
Replicated interface{} Replicated interface{}
} }
}
UpdateStatus struct { type serviceUpdateStatus struct {
State string State string
} }
Endpoint struct {
type serviceEndpoint struct {
Ports []portConfig Ports []portConfig
VirtualIPs []struct { VirtualIPs []virtualIP
}
type virtualIP struct {
NetworkID string NetworkID string
Addr string Addr string
}
}
} }
type portConfig struct { type portConfig struct {

View file

@ -8,20 +8,21 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseServicesResponse(t *testing.T) { func TestParseServicesResponse(t *testing.T) {
type args struct { f := func(data string, servicesExpected []service) {
data []byte t.Helper()
services, err := parseServicesResponse([]byte(data))
if err != nil {
t.Fatalf("parseServicesResponse() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(services, servicesExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", services, servicesExpected)
args args }
want []service }
wantErr bool
}{ // parse ok
{ data := `[
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "tgsci5gd31aai3jyudv98pqxf", "ID": "tgsci5gd31aai3jyudv98pqxf",
"Version": { "Version": {
@ -87,64 +88,32 @@ func Test_parseServicesResponse(t *testing.T) {
] ]
} }
} }
]`), ]`
},
want: []service{ servicesExpected := []service{
{ {
ID: "tgsci5gd31aai3jyudv98pqxf", ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: struct { Spec: serviceSpec{
Labels map[string]string
Name string
TaskTemplate struct {
ContainerSpec struct {
Hostname string
Image string
}
}
Mode struct {
Global interface{}
Replicated interface{}
}
}{
Labels: map[string]string{}, Labels: map[string]string{},
Name: "redis2", Name: "redis2",
TaskTemplate: struct { TaskTemplate: taskTemplate{
ContainerSpec struct { ContainerSpec: containerSpec{
Hostname string
Image string
}
}{
ContainerSpec: struct {
Hostname string
Image string
}{
Hostname: "",
Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842", Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
}, },
}, },
Mode: struct { Mode: serviceSpecMode{
Global interface{}
Replicated interface{}
}{
Replicated: map[string]interface{}{}, Replicated: map[string]interface{}{},
}, },
}, },
Endpoint: struct { Endpoint: serviceEndpoint{
Ports []portConfig Ports: []portConfig{
VirtualIPs []struct {
NetworkID string
Addr string
}
}{Ports: []portConfig{
{ {
Protocol: "tcp", Protocol: "tcp",
PublishMode: "ingress", PublishMode: "ingress",
PublishedPort: 8081, PublishedPort: 8081,
}, },
}, VirtualIPs: []struct { },
NetworkID string VirtualIPs: []virtualIP{
Addr string
}{
{ {
NetworkID: "qs0hog6ldlei9ct11pr3c77v1", NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24", Addr: "10.0.0.3/24",
@ -152,39 +121,53 @@ func Test_parseServicesResponse(t *testing.T) {
}, },
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseServicesResponse(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseServicesResponse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseServicesResponse() \ngot %v, \nwant %v", got, tt.want)
}
})
} }
f(data, servicesExpected)
} }
func Test_addServicesLabels(t *testing.T) { func TestAddServicesLabels(t *testing.T) {
type args struct { f := func(services []service, networksLabels map[string]*promutils.Labels, labelssExpected []*promutils.Labels) {
services []service t.Helper()
networksLabels map[string]*promutils.Labels
port int labelss := addServicesLabels(services, networksLabels, 9100)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // add 2 services with network labels join
args args services := []service{
want []*promutils.Labels
}{
{ {
name: "add 2 services with network labels join", ID: "tgsci5gd31aai3jyudv98pqxf",
args: args{ Spec: serviceSpec{
port: 9100, Labels: map[string]string{},
networksLabels: map[string]*promutils.Labels{ Name: "redis2",
TaskTemplate: taskTemplate{
ContainerSpec: containerSpec{
Hostname: "node1",
Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
},
},
Mode: serviceSpecMode{
Replicated: map[string]interface{}{},
},
},
Endpoint: serviceEndpoint{
Ports: []portConfig{
{
Protocol: "tcp",
Name: "redis",
PublishMode: "ingress",
},
},
VirtualIPs: []virtualIP{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
},
},
},
},
}
networksLabels := map[string]*promutils.Labels{
"qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{ "qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true", "__meta_dockerswarm_network_ingress": "true",
@ -193,73 +176,8 @@ func Test_addServicesLabels(t *testing.T) {
"__meta_dockerswarm_network_name": "ingress", "__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm", "__meta_dockerswarm_network_scope": "swarm",
}), }),
},
services: []service{
{
ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: struct {
Labels map[string]string
Name string
TaskTemplate struct {
ContainerSpec struct {
Hostname string
Image string
} }
} labelssExpected := []*promutils.Labels{
Mode struct {
Global interface{}
Replicated interface{}
}
}{
Labels: map[string]string{},
Name: "redis2",
TaskTemplate: struct {
ContainerSpec struct {
Hostname string
Image string
}
}{
ContainerSpec: struct {
Hostname string
Image string
}{
Hostname: "node1",
Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
},
},
Mode: struct {
Global interface{}
Replicated interface{}
}{
Replicated: map[string]interface{}{},
},
},
Endpoint: struct {
Ports []portConfig
VirtualIPs []struct {
NetworkID string
Addr string
}
}{Ports: []portConfig{
{
Protocol: "tcp",
Name: "redis",
PublishMode: "ingress",
},
}, VirtualIPs: []struct {
NetworkID string
Addr string
}{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
},
},
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.0.0.3:0", "__address__": "10.0.0.3:0",
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
@ -276,13 +194,7 @@ func Test_addServicesLabels(t *testing.T) {
"__meta_dockerswarm_service_task_container_hostname": "node1", "__meta_dockerswarm_service_task_container_hostname": "node1",
"__meta_dockerswarm_service_task_container_image": "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842", "__meta_dockerswarm_service_task_container_image": "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
"__meta_dockerswarm_service_updating_status": "", "__meta_dockerswarm_service_updating_status": "",
})}, }),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addServicesLabels(tt.args.services, tt.args.networksLabels, tt.args.port)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(services, networksLabels, labelssExpected)
} }

View file

@ -17,29 +17,39 @@ type task struct {
ServiceID string ServiceID string
NodeID string NodeID string
DesiredState string DesiredState string
NetworksAttachments []struct { NetworksAttachments []networkAttachment
Addresses []string Status taskStatus
Network struct { Spec taskSpec
ID string
}
}
Status struct {
State string
ContainerStatus struct {
ContainerID string
}
PortStatus struct {
Ports []portConfig
}
}
Spec struct {
ContainerSpec struct {
Labels map[string]string
}
}
Slot int Slot int
} }
type networkAttachment struct {
Addresses []string
Network network
}
type taskStatus struct {
State string
ContainerStatus containerStatus
PortStatus portStatus
}
type containerStatus struct {
ContainerID string
}
type portStatus struct {
Ports []portConfig
}
type taskSpec struct {
ContainerSpec taskContainerSpec
}
type taskContainerSpec struct {
Labels map[string]string
}
func getTasksLabels(cfg *apiConfig) ([]*promutils.Labels, error) { func getTasksLabels(cfg *apiConfig) ([]*promutils.Labels, error) {
tasks, err := getTasks(cfg) tasks, err := getTasks(cfg)
if err != nil { if err != nil {

View file

@ -8,20 +8,21 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseTasks(t *testing.T) { func TestParseTasks(t *testing.T) {
type args struct { f := func(data string, tasksExpected []task) {
data []byte t.Helper()
tasks, err := parseTasks([]byte(data))
if err != nil {
t.Fatalf("parseTasks() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(tasks, tasksExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", tasks, tasksExpected)
args args }
want []task }
wantErr bool
}{ // parse ok
{ data := `[
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "t4rdm7j2y9yctbrksiwvsgpu5", "ID": "t4rdm7j2y9yctbrksiwvsgpu5",
"Version": { "Version": {
@ -63,21 +64,15 @@ func Test_parseTasks(t *testing.T) {
"DesiredState": "running" "DesiredState": "running"
} }
] ]
`), `
},
want: []task{ tasksExpected := []task{
{ {
ID: "t4rdm7j2y9yctbrksiwvsgpu5", ID: "t4rdm7j2y9yctbrksiwvsgpu5",
ServiceID: "t91nf284wzle1ya09lqvyjgnq", ServiceID: "t91nf284wzle1ya09lqvyjgnq",
NodeID: "qauwmifceyvqs0sipvzu8oslu", NodeID: "qauwmifceyvqs0sipvzu8oslu",
Spec: struct { Spec: taskSpec{
ContainerSpec struct { ContainerSpec: taskContainerSpec{
Labels map[string]string
}
}{
ContainerSpec: struct {
Labels map[string]string
}{
Labels: map[string]string{ Labels: map[string]string{
"label1": "value1", "label1": "value1",
}, },
@ -85,69 +80,40 @@ func Test_parseTasks(t *testing.T) {
}, },
DesiredState: "running", DesiredState: "running",
Slot: 1, Slot: 1,
Status: struct { Status: taskStatus{
State string
ContainerStatus struct{ ContainerID string }
PortStatus struct{ Ports []portConfig }
}{
State: "running", State: "running",
ContainerStatus: struct{ ContainerID string }{ ContainerStatus: containerStatus{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8", ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
}, },
PortStatus: struct{ Ports []portConfig }{}}, PortStatus: portStatus{},
},
}, },
}, },
} }
for _, tt := range tests { f(data, tasksExpected)
t.Run(tt.name, func(t *testing.T) {
got, err := parseTasks(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseTasks() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseTasks() got\n%v\nwant\n%v", got, tt.want)
}
})
}
} }
func Test_addTasksLabels(t *testing.T) { func TestAddTasksLabels(t *testing.T) {
type args struct { f := func(tasks []task, nodesLabels []*promutils.Labels, networkLabels map[string]*promutils.Labels, services []service, labelssExpected []*promutils.Labels) {
tasks []task t.Helper()
nodesLabels []*promutils.Labels
servicesLabels []*promutils.Labels labelss := addTasksLabels(tasks, nodesLabels, nil, networkLabels, services, 9100)
networksLabels map[string]*promutils.Labels discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
services []service
port int
} }
tests := []struct {
name string // adds 1 task with nodes labels
args args tasks := []task{
want []*promutils.Labels
}{
{
name: "adds 1 task with nodes labels",
args: args{
port: 9100,
tasks: []task{
{ {
ID: "t4rdm7j2y9yctbrksiwvsgpu5", ID: "t4rdm7j2y9yctbrksiwvsgpu5",
ServiceID: "t91nf284wzle1ya09lqvyjgnq", ServiceID: "t91nf284wzle1ya09lqvyjgnq",
NodeID: "qauwmifceyvqs0sipvzu8oslu", NodeID: "qauwmifceyvqs0sipvzu8oslu",
DesiredState: "running", DesiredState: "running",
Slot: 1, Slot: 1,
Status: struct { Status: taskStatus{
State string
ContainerStatus struct{ ContainerID string }
PortStatus struct{ Ports []portConfig }
}{
State: "running", State: "running",
ContainerStatus: struct{ ContainerID string }{ ContainerStatus: containerStatus{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8", ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
}, },
PortStatus: struct{ Ports []portConfig }{ PortStatus: portStatus{
Ports: []portConfig{ Ports: []portConfig{
{ {
PublishMode: "ingress", PublishMode: "ingress",
@ -158,8 +124,9 @@ func Test_addTasksLabels(t *testing.T) {
}, },
}}, }},
}, },
}, }
nodesLabels: []*promutils.Labels{
nodesLabels := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100", "__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97", "__meta_dockerswarm_node_address": "172.31.40.97",
@ -172,9 +139,9 @@ func Test_addTasksLabels(t *testing.T) {
"__meta_dockerswarm_node_role": "manager", "__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready", "__meta_dockerswarm_node_status": "ready",
}), }),
}, }
},
want: []*promutils.Labels{ labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:6379", "__address__": "172.31.40.97:6379",
"__meta_dockerswarm_node_address": "172.31.40.97", "__meta_dockerswarm_node_address": "172.31.40.97",
@ -192,45 +159,37 @@ func Test_addTasksLabels(t *testing.T) {
"__meta_dockerswarm_task_port_publish_mode": "ingress", "__meta_dockerswarm_task_port_publish_mode": "ingress",
"__meta_dockerswarm_task_slot": "1", "__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running", "__meta_dockerswarm_task_state": "running",
})}, }),
}, }
{ f(tasks, nodesLabels, nil, nil, labelssExpected)
name: "adds 1 task with nodes, network and services labels",
args: args{ // adds 1 task with nodes, network and services labels
port: 9100, tasks = []task{
tasks: []task{
{ {
ID: "t4rdm7j2y9yctbrksiwvsgpu5", ID: "t4rdm7j2y9yctbrksiwvsgpu5",
ServiceID: "tgsci5gd31aai3jyudv98pqxf", ServiceID: "tgsci5gd31aai3jyudv98pqxf",
NodeID: "qauwmifceyvqs0sipvzu8oslu", NodeID: "qauwmifceyvqs0sipvzu8oslu",
DesiredState: "running", DesiredState: "running",
Slot: 1, Slot: 1,
NetworksAttachments: []struct { NetworksAttachments: []networkAttachment{
Addresses []string
Network struct{ ID string }
}{
{ {
Network: struct { Network: network{
ID string
}{
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "qs0hog6ldlei9ct11pr3c77v1",
}, },
Addresses: []string{"10.10.15.15/24"}, Addresses: []string{"10.10.15.15/24"},
}, },
}, },
Status: struct { Status: taskStatus{
State string
ContainerStatus struct{ ContainerID string }
PortStatus struct{ Ports []portConfig }
}{
State: "running", State: "running",
ContainerStatus: struct{ ContainerID string }{ ContainerStatus: containerStatus{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8", ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
}, },
PortStatus: struct{ Ports []portConfig }{}}, PortStatus: portStatus{},
}, },
}, },
networksLabels: map[string]*promutils.Labels{ }
networksLabels := map[string]*promutils.Labels{
"qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{ "qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true", "__meta_dockerswarm_network_ingress": "true",
@ -239,8 +198,9 @@ func Test_addTasksLabels(t *testing.T) {
"__meta_dockerswarm_network_name": "ingress", "__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm", "__meta_dockerswarm_network_scope": "swarm",
}), }),
}, }
nodesLabels: []*promutils.Labels{
nodesLabels = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100", "__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97", "__meta_dockerswarm_node_address": "172.31.40.97",
@ -253,54 +213,25 @@ func Test_addTasksLabels(t *testing.T) {
"__meta_dockerswarm_node_role": "manager", "__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready", "__meta_dockerswarm_node_status": "ready",
}), }),
}, }
services: []service{
services := []service{
{ {
ID: "tgsci5gd31aai3jyudv98pqxf", ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: struct { Spec: serviceSpec{
Labels map[string]string
Name string
TaskTemplate struct {
ContainerSpec struct {
Hostname string
Image string
}
}
Mode struct {
Global interface{}
Replicated interface{}
}
}{
Labels: map[string]string{}, Labels: map[string]string{},
Name: "redis2", Name: "redis2",
TaskTemplate: struct { TaskTemplate: taskTemplate{
ContainerSpec struct { ContainerSpec: containerSpec{
Hostname string
Image string
}
}{
ContainerSpec: struct {
Hostname string
Image string
}{
Hostname: "node1", Hostname: "node1",
Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842", Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
}, },
}, },
Mode: struct { Mode: serviceSpecMode{
Global interface{}
Replicated interface{}
}{
Replicated: map[string]interface{}{}, Replicated: map[string]interface{}{},
}, },
}, },
Endpoint: struct { Endpoint: serviceEndpoint{
Ports []portConfig
VirtualIPs []struct {
NetworkID string
Addr string
}
}{
Ports: []portConfig{ Ports: []portConfig{
{ {
Protocol: "tcp", Protocol: "tcp",
@ -308,10 +239,8 @@ func Test_addTasksLabels(t *testing.T) {
PublishMode: "ingress", PublishMode: "ingress",
PublishedPort: 6379, PublishedPort: 6379,
}, },
}, VirtualIPs: []struct { },
NetworkID string VirtualIPs: []virtualIP{
Addr string
}{
{ {
NetworkID: "qs0hog6ldlei9ct11pr3c77v1", NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24", Addr: "10.0.0.3/24",
@ -319,10 +248,9 @@ func Test_addTasksLabels(t *testing.T) {
}, },
}, },
}, },
}, }
servicesLabels: []*promutils.Labels{},
}, labelssExpected = []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.10.15.15:6379", "__address__": "10.10.15.15:6379",
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
@ -347,13 +275,6 @@ func Test_addTasksLabels(t *testing.T) {
"__meta_dockerswarm_task_slot": "1", "__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running", "__meta_dockerswarm_task_state": "running",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addTasksLabels(tt.args.tasks, tt.args.nodesLabels, tt.args.servicesLabels, tt.args.networksLabels, tt.args.services, tt.args.port)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(tasks, nodesLabels, networksLabels, services, labelssExpected)
} }

View file

@ -5,20 +5,21 @@ import (
"testing" "testing"
) )
func Test_parseAPIResponse(t *testing.T) { func TestParseAPIResponse(t *testing.T) {
type args struct { f := func(data string, resultExpected *applications) {
data []byte t.Helper()
result, err := parseAPIResponse([]byte(data))
if err != nil {
t.Fatalf("parseAPIResponse() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want *applications }
wantErr bool
}{ // parse ok 1 app with instance
{ data := `<applications>
name: "parse ok 1 app with instance",
args: args{
data: []byte(`<applications>
<versions__delta>1</versions__delta> <versions__delta>1</versions__delta>
<apps__hashcode>UP_1_</apps__hashcode> <apps__hashcode>UP_1_</apps__hashcode>
<application> <application>
@ -55,9 +56,9 @@ func Test_parseAPIResponse(t *testing.T) {
<actionType>ADDED</actionType> <actionType>ADDED</actionType>
</instance> </instance>
</application> </application>
</applications>`), </applications>`
},
want: &applications{ resultExpected := &applications{
Applications: []Application{ Applications: []Application{
{ {
Name: "HELLO-NETFLIX-OSS", Name: "HELLO-NETFLIX-OSS",
@ -89,19 +90,6 @@ func Test_parseAPIResponse(t *testing.T) {
}, },
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseAPIResponse(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseAPIResponse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unxpected response for parseAPIResponse() \ngot = %v, \nwant %v", got, tt.want)
}
})
} }
f(data, resultExpected)
} }

View file

@ -7,19 +7,16 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addInstanceLabels(t *testing.T) { func TestAddInstanceLabels(t *testing.T) {
type args struct { f := func(applications *applications, labelssExpected []*promutils.Labels) {
applications *applications t.Helper()
labelss := addInstanceLabels(applications)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // one application
args args applications := &applications{
want []*promutils.Labels
}{
{
name: "1 application",
args: args{
applications: &applications{
Applications: []Application{ Applications: []Application{
{ {
Name: "test-app", Name: "test-app",
@ -34,12 +31,14 @@ func Test_addInstanceLabels(t *testing.T) {
CountryID: 5, CountryID: 5,
VipAddress: "10.15.11.11", VipAddress: "10.15.11.11",
InstanceID: "some-id", InstanceID: "some-id",
Metadata: MetaData{Items: []Tag{ Metadata: MetaData{
Items: []Tag{
{ {
Content: "value-1", Content: "value-1",
XMLName: struct{ Space, Local string }{Local: "key-1"}, XMLName: struct{ Space, Local string }{Local: "key-1"},
}, },
}}, },
},
Port: Port{ Port: Port{
Port: 9100, Port: 9100,
}, },
@ -47,9 +46,8 @@ func Test_addInstanceLabels(t *testing.T) {
}, },
}, },
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "host-1:9100", "__address__": "host-1:9100",
"instance": "some-id", "instance": "some-id",
@ -68,13 +66,6 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_eureka_app_instance_port_enabled": "false", "__meta_eureka_app_instance_port_enabled": "false",
"__meta_eureka_app_instance_status": "Ok", "__meta_eureka_app_instance_status": "Ok",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addInstanceLabels(tt.args.applications)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(applications, labelssExpected)
} }

View file

@ -7,45 +7,30 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseAPIResponse(t *testing.T) { func TestParseAPIResponse(t *testing.T) {
type args struct { f := func(data, path string, resultExpected []httpGroupTarget) {
data []byte t.Helper()
path string
}
tests := []struct {
name string
args args
want []httpGroupTarget
wantErr bool
}{
{ result, err := parseAPIResponse([]byte(data), path)
name: "parse ok", if err != nil {
args: args{ t.Fatalf("parseAPIResponse() error: %s", err)
path: "/ok", }
data: []byte(`[ if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
}
}
// parse ok
data := `[
{"targets": ["http://target-1:9100","http://target-2:9150"], {"targets": ["http://target-1:9100","http://target-2:9150"],
"labels": {"label-1":"value-1"} } "labels": {"label-1":"value-1"} }
]`), ]`
}, path := "/ok"
want: []httpGroupTarget{ resultExpected := []httpGroupTarget{
{ {
Labels: promutils.NewLabelsFromMap(map[string]string{"label-1": "value-1"}), Labels: promutils.NewLabelsFromMap(map[string]string{"label-1": "value-1"}),
Targets: []string{"http://target-1:9100", "http://target-2:9150"}, Targets: []string{"http://target-1:9100", "http://target-2:9150"},
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseAPIResponse(tt.args.data, tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("parseAPIResponse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseAPIResponse() got = %v, want %v", got, tt.want)
}
})
} }
f(data, path, resultExpected)
} }

View file

@ -7,26 +7,22 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addHTTPTargetLabels(t *testing.T) { func TestAddHTTPTargetLabels(t *testing.T) {
type args struct { f := func(src []httpGroupTarget, labelssExpected []*promutils.Labels) {
src []httpGroupTarget t.Helper()
labelss := addHTTPTargetLabels(src, "http://foo.bar/baz?aaa=bb")
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // add ok
args args src := []httpGroupTarget{
want []*promutils.Labels
}{
{
name: "add ok",
args: args{
src: []httpGroupTarget{
{ {
Targets: []string{"127.0.0.1:9100", "127.0.0.2:91001"}, Targets: []string{"127.0.0.1:9100", "127.0.0.2:91001"},
Labels: promutils.NewLabelsFromMap(map[string]string{"__meta_kubernetes_pod": "pod-1", "__meta_consul_dc": "dc-2"}), Labels: promutils.NewLabelsFromMap(map[string]string{"__meta_kubernetes_pod": "pod-1", "__meta_consul_dc": "dc-2"}),
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "127.0.0.1:9100", "__address__": "127.0.0.1:9100",
"__meta_kubernetes_pod": "pod-1", "__meta_kubernetes_pod": "pod-1",
@ -39,13 +35,6 @@ func Test_addHTTPTargetLabels(t *testing.T) {
"__meta_consul_dc": "dc-2", "__meta_consul_dc": "dc-2",
"__meta_url": "http://foo.bar/baz?aaa=bb", "__meta_url": "http://foo.bar/baz?aaa=bb",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addHTTPTargetLabels(tt.args.src, "http://foo.bar/baz?aaa=bb")
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(src, labelssExpected)
} }

View file

@ -936,9 +936,12 @@ func (uw *urlWatcher) maybeUpdateDependedScrapeWorksLocked() {
// Bookmark is a bookmark message from Kubernetes Watch API. // Bookmark is a bookmark message from Kubernetes Watch API.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#watch-bookmarks // See https://kubernetes.io/docs/reference/using-api/api-concepts/#watch-bookmarks
type Bookmark struct { type Bookmark struct {
Metadata struct { Metadata BookmarkMetadata
}
// BookmarkMetadata is metadata for Bookmark
type BookmarkMetadata struct {
ResourceVersion string ResourceVersion string
}
} }
func parseBookmark(data []byte) (*Bookmark, error) { func parseBookmark(data []byte) (*Bookmark, error) {

View file

@ -12,10 +12,10 @@ func TestParseEndpointSliceListFail(t *testing.T) {
r := bytes.NewBufferString(data) r := bytes.NewBufferString(data)
objectsByKey, _, err := parseEndpointSliceList(r) objectsByKey, _, err := parseEndpointSliceList(r)
if err == nil { if err == nil {
t.Errorf("unexpected result, test must fail! data: %s", data) t.Fatalf("unexpected result, test must fail! data: %s", data)
} }
if len(objectsByKey) != 0 { if len(objectsByKey) != 0 {
t.Errorf("EndpointSliceList must be emptry, got: %v", objectsByKey) t.Fatalf("EndpointSliceList must be emptry, got: %v", objectsByKey)
} }
} }
@ -156,8 +156,7 @@ func TestParseEndpointSliceListSuccess(t *testing.T) {
r := bytes.NewBufferString(data) r := bytes.NewBufferString(data)
objectsByKey, meta, err := parseEndpointSliceList(r) objectsByKey, meta, err := parseEndpointSliceList(r)
if err != nil { if err != nil {
t.Errorf("cannot parse data for EndpointSliceList: %v", err) t.Fatalf("cannot parse data for EndpointSliceList: %v", err)
return
} }
expectedResourceVersion := "1177" expectedResourceVersion := "1177"
if meta.ResourceVersion != expectedResourceVersion { if meta.ResourceVersion != expectedResourceVersion {

View file

@ -22,19 +22,25 @@ type apiConfig struct {
type Config struct { type Config struct {
Kind string `yaml:"kind,omitempty"` Kind string `yaml:"kind,omitempty"`
APIVersion string `yaml:"apiVersion,omitempty"` APIVersion string `yaml:"apiVersion,omitempty"`
Clusters []struct { Clusters []configCluster `yaml:"clusters"`
AuthInfos []authInfo `yaml:"users"`
Contexts []configContext `yaml:"contexts"`
CurrentContext string `yaml:"current-context"`
}
type configCluster struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Cluster *Cluster `yaml:"cluster"` Cluster *Cluster `yaml:"cluster"`
} `yaml:"clusters"` }
AuthInfos []struct {
type authInfo struct {
Name string `yaml:"name"` Name string `yaml:"name"`
AuthInfo *AuthInfo `yaml:"user"` AuthInfo *AuthInfo `yaml:"user"`
} `yaml:"users"` }
Contexts []struct {
type configContext struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Context *Context `yaml:"context"` Context *Context `yaml:"context"`
} `yaml:"contexts"`
CurrentContext string `yaml:"current-context"`
} }
// Cluster contains information about how to communicate with a kubernetes cluster // Cluster contains information about how to communicate with a kubernetes cluster

View file

@ -265,17 +265,21 @@ type discoveryRequestNode struct {
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/discovery/v3/discovery.proto#envoy-v3-api-msg-service-discovery-v3-discoveryresponse // https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/discovery/v3/discovery.proto#envoy-v3-api-msg-service-discovery-v3-discoveryresponse
type discoveryResponse struct { type discoveryResponse struct {
VersionInfo string `json:"version_info"` VersionInfo string `json:"version_info"`
Resources []struct { Resources []resource `json:"resources"`
Nonce string `json:"nonce"`
}
type resource struct {
Mesh string `json:"mesh"` Mesh string `json:"mesh"`
Service string `json:"service"` Service string `json:"service"`
Targets []struct { Targets []target `json:"targets"`
Labels map[string]string `json:"labels"`
}
type target struct {
Name string `json:"name"` Name string `json:"name"`
Scheme string `json:"scheme"` Scheme string `json:"scheme"`
Address string `json:"address"` Address string `json:"address"`
MetricsPath string `json:"metrics_path"` MetricsPath string `json:"metrics_path"`
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
} `json:"targets"`
Labels map[string]string `json:"labels"`
} `json:"resources"`
Nonce string `json:"nonce"`
} }

View file

@ -25,10 +25,12 @@ func getServiceLabels(cfg *apiConfig) []*promutils.Labels {
// See https://developer.hashicorp.com/nomad/api-docs/services#list-services // See https://developer.hashicorp.com/nomad/api-docs/services#list-services
type ServiceList struct { type ServiceList struct {
Namespace string `json:"Namespace"` Namespace string `json:"Namespace"`
Services []struct { Services []service `json:"Services"`
}
type service struct {
ServiceName string `json:"ServiceName"` ServiceName string `json:"ServiceName"`
Tags []string `json:"Tags"` Tags []string `json:"Tags"`
} `json:"Services"`
} }
// Service is Nomad service. // Service is Nomad service.

View file

@ -14,10 +14,12 @@ import (
// //
// See https://docs.openstack.org/api-ref/identity/v3/#authentication-and-token-management // See https://docs.openstack.org/api-ref/identity/v3/#authentication-and-token-management
type authResponse struct { type authResponse struct {
Token struct { Token authToken
}
type authToken struct {
ExpiresAt time.Time `json:"expires_at,omitempty"` ExpiresAt time.Time `json:"expires_at,omitempty"`
Catalog []catalogItem `json:"catalog,omitempty"` Catalog []catalogItem `json:"catalog,omitempty"`
}
} }
type catalogItem struct { type catalogItem struct {

View file

@ -1,94 +1,88 @@
package openstack package openstack
import ( import (
"reflect"
"testing" "testing"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth"
) )
func Test_buildAuthRequestBody1(t *testing.T) { func TestBuildAuthRequestBody_Failure(t *testing.T) {
type args struct { f := func(sdc *SDConfig) {
sdc *SDConfig t.Helper()
_, err := buildAuthRequestBody(sdc)
if err == nil {
t.Fatalf("expecting non-nil error")
} }
tests := []struct { }
name string
args args // empty config
want []byte f(&SDConfig{})
wantErr bool }
}{
{ func TestBuildAuthRequestBody_Success(t *testing.T) {
name: "empty config", f := func(sdc *SDConfig, resultExpected string) {
args: args{ t.Helper()
sdc: &SDConfig{},
}, result, err := buildAuthRequestBody(sdc)
wantErr: true, if err != nil {
}, t.Fatalf("buildAuthRequestBody() error: %s", err)
{ }
name: "username password auth with domain", if string(result) != resultExpected {
args: args{ t.Fatalf("unexpected result\ngot\n%s\nwant\n%s", result, resultExpected)
sdc: &SDConfig{ }
}
// username password auth with domain
f(&SDConfig{
Username: "some-user", Username: "some-user",
Password: promauth.NewSecret("some-password"), Password: promauth.NewSecret("some-password"),
DomainName: "some-domain", DomainName: "some-domain",
}, }, `{"auth":{"identity":{"methods":["password"],"password":{"user":{"name":"some-user","password":"some-password","domain":{"name":"some-domain"}}}},"scope":{"domain":{"name":"some-domain"}}}}`)
},
want: []byte(`{"auth":{"identity":{"methods":["password"],"password":{"user":{"name":"some-user","password":"some-password","domain":{"name":"some-domain"}}}},"scope":{"domain":{"name":"some-domain"}}}}`), // application credentials auth
}, f(&SDConfig{
{
name: "application credentials auth",
args: args{
sdc: &SDConfig{
ApplicationCredentialID: "some-id", ApplicationCredentialID: "some-id",
ApplicationCredentialSecret: promauth.NewSecret("some-secret"), ApplicationCredentialSecret: promauth.NewSecret("some-secret"),
}, }, `{"auth":{"identity":{"methods":["application_credential"],"application_credential":{"id":"some-id","secret":"some-secret"}}}}`)
},
want: []byte(`{"auth":{"identity":{"methods":["application_credential"],"application_credential":{"id":"some-id","secret":"some-secret"}}}}`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := buildAuthRequestBody(tt.args.sdc)
if (err != nil) != tt.wantErr {
t.Errorf("buildAuthRequestBody() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("buildAuthRequestBody() got = %v, want %v", got, tt.want)
}
})
}
} }
func Test_getComputeEndpointURL1(t *testing.T) { func TestGetComputeEndpointURL_Failure(t *testing.T) {
type args struct { f := func(catalog []catalogItem) {
catalog []catalogItem t.Helper()
availability string
region string _, err := getComputeEndpointURL(catalog, "", "")
if err == nil {
t.Fatalf("expecting non-nil error")
} }
tests := []struct { }
name string
args args // bad catalog data
want string catalog := []catalogItem{
wantErr bool
}{
{
name: "bad catalog data",
args: args{
catalog: []catalogItem{
{ {
Type: "keystone", Type: "keystone",
Endpoints: []endpoint{}, Endpoints: []endpoint{},
}, },
}, }
}, f(catalog)
wantErr: true, }
},
{ func TestGetComputeEndpointURL_Success(t *testing.T) {
name: "good private url", f := func(catalog []catalogItem, availability, region, resultExpected string) {
args: args{ t.Helper()
availability: "private",
catalog: []catalogItem{ resultURL, err := getComputeEndpointURL(catalog, availability, region)
if err != nil {
t.Fatalf("getComputeEndpointURL() error: %s", err)
}
if resultURL.String() != resultExpected {
t.Fatalf("unexpected result\ngot\n%s\nwant\n%s", resultURL, resultExpected)
}
}
// good private url
catalog := []catalogItem{
{ {
Type: "compute", Type: "compute",
Endpoints: []endpoint{ Endpoints: []endpoint{
@ -103,22 +97,8 @@ func Test_getComputeEndpointURL1(t *testing.T) {
Type: "keystone", Type: "keystone",
Endpoints: []endpoint{}, Endpoints: []endpoint{},
}, },
},
},
want: "https://compute.test.local:8083/v2.1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getComputeEndpointURL(tt.args.catalog, tt.args.availability, tt.args.region)
if (err != nil) != tt.wantErr {
t.Errorf("getComputeEndpointURL() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && !reflect.DeepEqual(got.String(), tt.want) {
t.Errorf("getComputeEndpointURL() got = %v, want %v", got.String(), tt.want)
}
})
} }
availability := "private"
resultExpected := "https://compute.test.local:8083/v2.1"
f(catalog, availability, "", resultExpected)
} }

View file

@ -13,10 +13,12 @@ import (
// See https://docs.openstack.org/api-ref/compute/#list-hypervisors-details // See https://docs.openstack.org/api-ref/compute/#list-hypervisors-details
type hypervisorDetail struct { type hypervisorDetail struct {
Hypervisors []hypervisor `json:"hypervisors"` Hypervisors []hypervisor `json:"hypervisors"`
Links []struct { Links []hypervisorLink `json:"hypervisors_links,omitempty"`
}
type hypervisorLink struct {
HREF string `json:"href"` HREF string `json:"href"`
Rel string `json:"rel,omitempty"` Rel string `json:"rel,omitempty"`
} `json:"hypervisors_links,omitempty"`
} }
type hypervisor struct { type hypervisor struct {

View file

@ -8,27 +8,35 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_parseHypervisorDetail(t *testing.T) { func TestParseHypervisorDetail_Failure(t *testing.T) {
type args struct { f := func(data string) {
data []byte t.Helper()
_, err := parseHypervisorDetail([]byte(data))
if err == nil {
t.Fatalf("expecting non-nil error")
} }
tests := []struct { }
name string
args args // bad data
want hypervisorDetail f(`{ff}`)
wantErr bool }
}{
{ func TestParseHypervisorDetail_Success(t *testing.T) {
name: "bad data", f := func(data string, resultExpected *hypervisorDetail) {
args: args{ t.Helper()
data: []byte(`{ff}`),
}, result, err := parseHypervisorDetail([]byte(data))
wantErr: true, if err != nil {
}, t.Fatalf("parseHypervisorDetail() error: %s", err)
{ }
name: "1 hypervisor", if !reflect.DeepEqual(result, resultExpected) {
args: args{ t.Fatalf("unexpected result\ngot\n%#v\nwant\n%#v", result, resultExpected)
data: []byte(`{ }
}
// 1 hypervisor
data := `{
"hypervisors": [ "hypervisors": [
{ {
"cpu_info": { "cpu_info": {
@ -69,9 +77,9 @@ func Test_parseHypervisorDetail(t *testing.T) {
"vcpus": 2, "vcpus": 2,
"vcpus_used": 0 "vcpus_used": 0
} }
]}`), ]}`
},
want: hypervisorDetail{ resultExpected := &hypervisorDetail{
Hypervisors: []hypervisor{ Hypervisors: []hypervisor{
{ {
HostIP: "1.1.1.1", HostIP: "1.1.1.1",
@ -82,38 +90,19 @@ func Test_parseHypervisorDetail(t *testing.T) {
Type: "fake", Type: "fake",
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseHypervisorDetail(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseHypervisorDetail() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && !reflect.DeepEqual(*got, tt.want) {
t.Errorf("parseHypervisorDetail() got = %v, want %v", *got, tt.want)
}
})
} }
f(data, resultExpected)
} }
func Test_addHypervisorLabels(t *testing.T) { func TestAddHypervisorLabels(t *testing.T) {
type args struct { f := func(hvs []hypervisor, labelssExpected []*promutils.Labels) {
hvs []hypervisor t.Helper()
port int
labelss := addHypervisorLabels(hvs, 9100)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string hvs := []hypervisor{
args args
want []*promutils.Labels
}{
{
name: "",
args: args{
port: 9100,
hvs: []hypervisor{
{ {
Type: "fake", Type: "fake",
ID: 5, ID: 5,
@ -122,9 +111,8 @@ func Test_addHypervisorLabels(t *testing.T) {
Hostname: "fakehost", Hostname: "fakehost",
HostIP: "1.2.2.2", HostIP: "1.2.2.2",
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "1.2.2.2:9100", "__address__": "1.2.2.2:9100",
"__meta_openstack_hypervisor_host_ip": "1.2.2.2", "__meta_openstack_hypervisor_host_ip": "1.2.2.2",
@ -134,13 +122,6 @@ func Test_addHypervisorLabels(t *testing.T) {
"__meta_openstack_hypervisor_status": "up", "__meta_openstack_hypervisor_status": "up",
"__meta_openstack_hypervisor_type": "fake", "__meta_openstack_hypervisor_type": "fake",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addHypervisorLabels(tt.args.hvs, tt.args.port)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(hvs, labelssExpected)
} }

View file

@ -14,10 +14,12 @@ import (
// See https://docs.openstack.org/api-ref/compute/#list-servers // See https://docs.openstack.org/api-ref/compute/#list-servers
type serversDetail struct { type serversDetail struct {
Servers []server `json:"servers"` Servers []server `json:"servers"`
Links []struct { Links []link `json:"servers_links,omitempty"`
}
type link struct {
HREF string `json:"href"` HREF string `json:"href"`
Rel string `json:"rel"` Rel string `json:"rel"`
} `json:"servers_links,omitempty"`
} }
type server struct { type server struct {
@ -27,15 +29,19 @@ type server struct {
Name string `json:"name"` Name string `json:"name"`
HostID string `json:"hostid"` HostID string `json:"hostid"`
Status string `json:"status"` Status string `json:"status"`
Addresses map[string][]struct { Addresses map[string][]serverAddress `json:"addresses"`
Metadata map[string]string `json:"metadata,omitempty"`
Flavor serverFlavor `json:"flavor"`
}
type serverAddress struct {
Address string `json:"addr"` Address string `json:"addr"`
Version int `json:"version"` Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"` Type string `json:"OS-EXT-IPS:type"`
} `json:"addresses"` }
Metadata map[string]string `json:"metadata,omitempty"`
Flavor struct { type serverFlavor struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"flavor"`
} }
func parseServersDetail(data []byte) (*serversDetail, error) { func parseServersDetail(data []byte) (*serversDetail, error) {

View file

@ -8,27 +8,19 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addInstanceLabels(t *testing.T) { func TestAddInstanceLabels(t *testing.T) {
type args struct { f := func(servers []server, labelssExpected []*promutils.Labels) {
servers []server t.Helper()
port int
labelss := addInstanceLabels(servers, 9100)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // empty response
args args f(nil, nil)
want []*promutils.Labels
}{ // one server
{ servers := []server{
name: "empty_response",
args: args{
port: 9100,
},
},
{
name: "one_server",
args: args{
port: 9100,
servers: []server{
{ {
ID: "10", ID: "10",
Status: "enabled", Status: "enabled",
@ -36,14 +28,10 @@ func Test_addInstanceLabels(t *testing.T) {
HostID: "some-host-id", HostID: "some-host-id",
TenantID: "some-tenant-id", TenantID: "some-tenant-id",
UserID: "some-user-id", UserID: "some-user-id",
Flavor: struct { Flavor: serverFlavor{
ID string `json:"id"` ID: "5",
}{ID: "5"}, },
Addresses: map[string][]struct { Addresses: map[string][]serverAddress{
Address string `json:"addr"`
Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"`
}{
"test": { "test": {
{ {
Address: "192.168.0.1", Address: "192.168.0.1",
@ -53,9 +41,8 @@ func Test_addInstanceLabels(t *testing.T) {
}, },
}, },
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "192.168.0.1:9100", "__address__": "192.168.0.1:9100",
"__meta_openstack_address_pool": "test", "__meta_openstack_address_pool": "test",
@ -67,13 +54,11 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_openstack_project_id": "some-tenant-id", "__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id", "__meta_openstack_user_id": "some-user-id",
}), }),
}, }
}, f(servers, labelssExpected)
{
name: "with_public_ip", // with public ip
args: args{ servers = []server{
port: 9100,
servers: []server{
{ {
ID: "10", ID: "10",
Status: "enabled", Status: "enabled",
@ -81,14 +66,10 @@ func Test_addInstanceLabels(t *testing.T) {
HostID: "some-host-id", HostID: "some-host-id",
TenantID: "some-tenant-id", TenantID: "some-tenant-id",
UserID: "some-user-id", UserID: "some-user-id",
Flavor: struct { Flavor: serverFlavor{
ID string `json:"id"` ID: "5",
}{ID: "5"}, },
Addresses: map[string][]struct { Addresses: map[string][]serverAddress{
Address string `json:"addr"`
Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"`
}{
"test": { "test": {
{ {
Address: "192.168.0.1", Address: "192.168.0.1",
@ -110,9 +91,8 @@ func Test_addInstanceLabels(t *testing.T) {
}, },
}, },
}, },
}, }
}, labelssExpected = []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.10.0.1:9100", "__address__": "10.10.0.1:9100",
"__meta_openstack_address_pool": "internal", "__meta_openstack_address_pool": "internal",
@ -136,31 +116,25 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_openstack_project_id": "some-tenant-id", "__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id", "__meta_openstack_user_id": "some-user-id",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addInstanceLabels(tt.args.servers, tt.args.port)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(servers, labelssExpected)
} }
func Test_parseServersDetail(t *testing.T) { func TestParseServersDetail(t *testing.T) {
type args struct { f := func(data string, resultExpected *serversDetail) {
data []byte t.Helper()
result, err := parseServersDetail([]byte(data))
if err != nil {
t.Fatalf("parseServersDetail() error: %s", err)
} }
tests := []struct { if !reflect.DeepEqual(result, resultExpected) {
name string t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
args args }
want serversDetail }
wantErr bool
}{ // parse ok
{ data := `{
name: "parse ok",
args: args{
data: []byte(`{
"servers":[ "servers":[
{ {
"id":"c9f68076-01a3-489a-aebe-8b773c71e7f3", "id":"c9f68076-01a3-489a-aebe-8b773c71e7f3",
@ -210,14 +184,13 @@ func Test_parseServersDetail(t *testing.T) {
] ]
} }
] ]
}`), }`
}, resultExpected := &serversDetail{
want: serversDetail{
Servers: []server{ Servers: []server{
{ {
Flavor: struct { Flavor: serverFlavor{
ID string `json:"id"` ID: "1",
}{ID: "1"}, },
ID: "c9f68076-01a3-489a-aebe-8b773c71e7f3", ID: "c9f68076-01a3-489a-aebe-8b773c71e7f3",
TenantID: "d34be4e44f9c444eab9a5ec7b953951f", TenantID: "d34be4e44f9c444eab9a5ec7b953951f",
UserID: "e55737f142ac42f18093037760656bd7", UserID: "e55737f142ac42f18093037760656bd7",
@ -225,11 +198,7 @@ func Test_parseServersDetail(t *testing.T) {
HostID: "e26db8db23736877aa92ebbbe11743b2a2a3b107aada00a8a0cf474b", HostID: "e26db8db23736877aa92ebbbe11743b2a2a3b107aada00a8a0cf474b",
Status: "ACTIVE", Status: "ACTIVE",
Metadata: map[string]string{}, Metadata: map[string]string{},
Addresses: map[string][]struct { Addresses: map[string][]serverAddress{
Address string `json:"addr"`
Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"`
}{
"test": { "test": {
{ {
Address: "192.168.222.15", Address: "192.168.222.15",
@ -245,19 +214,6 @@ func Test_parseServersDetail(t *testing.T) {
}, },
}, },
}, },
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseServersDetail(tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("parseServersDetail() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && !reflect.DeepEqual(*got, tt.want) {
t.Errorf("parseServersDetail() \ngot = %v,\nwant= %v", *got, tt.want)
}
})
} }
f(data, resultExpected)
} }

View file

@ -7,23 +7,19 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
) )
func Test_addInstanceLabels(t *testing.T) { func TestAddInstanceLabels(t *testing.T) {
type args struct { f := func(instances []instance, labelssExpected []*promutils.Labels) {
instances []instance t.Helper()
labelss := addInstanceLabels(instances)
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // empty response
args args f(nil, nil)
want []*promutils.Labels
}{ // one server
{ instances := []instance{
name: "empty_response",
args: args{},
},
{
name: "one_server",
args: args{
instances: []instance{
{ {
Name: "server-1", Name: "server-1",
ID: "test", ID: "test",
@ -45,9 +41,8 @@ func Test_addInstanceLabels(t *testing.T) {
}, },
}, },
}, },
}, }
}, labelssExpected := []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal", "__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1", "__meta_yandexcloud_instance_name": "server-1",
@ -61,12 +56,11 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_yandexcloud_folder_id": "test", "__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1", "__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
}), }),
}, }
}, f(instances, labelssExpected)
{
name: "with_public_ip", // with public ip
args: args{ instances = []instance{
instances: []instance{
{ {
Name: "server-1", Name: "server-1",
ID: "test", ID: "test",
@ -91,9 +85,8 @@ func Test_addInstanceLabels(t *testing.T) {
}, },
}, },
}, },
}, }
}, labelssExpected = []*promutils.Labels{
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal", "__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal", "__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
@ -108,12 +101,11 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1", "__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
"__meta_yandexcloud_instance_public_ip_0": "1.1.1.1", "__meta_yandexcloud_instance_public_ip_0": "1.1.1.1",
}), }),
}, }
}, f(instances, labelssExpected)
{
name: "with_dns_record", // with dns record
args: args{ instances = []instance{
instances: []instance{
{ {
Name: "server-1", Name: "server-1",
ID: "test", ID: "test",
@ -134,19 +126,22 @@ func Test_addInstanceLabels(t *testing.T) {
OneToOneNat: oneToOneNat{ OneToOneNat: oneToOneNat{
Address: "1.1.1.1", Address: "1.1.1.1",
DNSRecords: []dnsRecord{ DNSRecords: []dnsRecord{
{FQDN: "server-1.example.com"}, {
FQDN: "server-1.example.com",
},
}, },
}, },
DNSRecords: []dnsRecord{ DNSRecords: []dnsRecord{
{FQDN: "server-1.example.local"}, {
FQDN: "server-1.example.local",
}, },
}, },
}, },
}, },
}, },
}, },
}, }
want: []*promutils.Labels{ labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{ promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal", "__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1", "__meta_yandexcloud_instance_name": "server-1",
@ -163,13 +158,6 @@ func Test_addInstanceLabels(t *testing.T) {
"__meta_yandexcloud_instance_private_dns_0": "server-1.example.local", "__meta_yandexcloud_instance_private_dns_0": "server-1.example.local",
"__meta_yandexcloud_instance_public_dns_0": "server-1.example.com", "__meta_yandexcloud_instance_public_dns_0": "server-1.example.com",
}), }),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := addInstanceLabels(tt.args.instances)
discoveryutils.TestEqualLabelss(t, got, tt.want)
})
} }
f(instances, labelssExpected)
} }

View file

@ -19,18 +19,40 @@ func newTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*httpt
} }
func TestNewClientFromConfig(t *testing.T) { func TestNewClientFromConfig(t *testing.T) {
f := func(h func(w http.ResponseWriter, r *http.Request), httpCfg *promauth.HTTPClientConfig, expectedMessage string) {
t.Helper()
s, err := newTestServer(h)
if err != nil {
t.Fatalf("cannot create test server: %s", err)
}
defer s.Close()
client, err := NewClient("http://0.0.0.0:1234", nil, &proxy.URL{}, nil, httpCfg)
if err != nil {
t.Fatalf("can't create a client from this config: %+v", httpCfg)
}
response, err := client.client.client.Get(s.URL)
if err != nil {
t.Fatalf("can't connect to the test server using this config: %+v: %v", httpCfg, err)
}
message, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil {
t.Fatalf("Can't read the server response body using this config: %+v", httpCfg)
}
trimMessage := strings.TrimSpace(string(message))
if expectedMessage != trimMessage {
t.Fatalf("The expected message (%s) differs from the obtained message (%s) using this config: %+v", expectedMessage, trimMessage, httpCfg)
}
}
// verify enabled redirects
allowed := true allowed := true
notAllowed := false handlerRedirect := func(w http.ResponseWriter, r *http.Request) {
newClientValidConfig := []struct {
httpCfg promauth.HTTPClientConfig
handler func(w http.ResponseWriter, r *http.Request)
expectedMessage string
}{
{
httpCfg: promauth.HTTPClientConfig{
FollowRedirects: &allowed,
},
handler: func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case "/redirected": case "/redirected":
fmt.Fprint(w, "I'm here to serve you!!!") fmt.Fprint(w, "I'm here to serve you!!!")
@ -39,14 +61,14 @@ func TestNewClientFromConfig(t *testing.T) {
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
fmt.Fprint(w, "It should follow the redirect.") fmt.Fprint(w, "It should follow the redirect.")
} }
}, }
expectedMessage: "I'm here to serve you!!!", f(handlerRedirect, &promauth.HTTPClientConfig{
}, FollowRedirects: &allowed,
{ }, "I'm here to serve you!!!")
httpCfg: promauth.HTTPClientConfig{
FollowRedirects: &notAllowed, // Verify disabled redirects
}, notAllowed := false
handler: func(w http.ResponseWriter, r *http.Request) { handlerNoRedirect := func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case "/redirected": case "/redirected":
fmt.Fprint(w, "The redirection was followed.") fmt.Fprint(w, "The redirection was followed.")
@ -55,41 +77,8 @@ func TestNewClientFromConfig(t *testing.T) {
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
fmt.Fprint(w, "I'm before redirect") fmt.Fprint(w, "I'm before redirect")
} }
},
expectedMessage: "I'm before redirect",
},
}
for _, validConfig := range newClientValidConfig {
testServer, err := newTestServer(validConfig.handler)
if err != nil {
t.Fatal(err.Error())
}
defer testServer.Close()
client, err := NewClient("http://0.0.0.0:1234", nil, &proxy.URL{}, nil, &validConfig.httpCfg)
if err != nil {
t.Errorf("Can't create a client from this config: %+v", validConfig.httpCfg)
continue
}
response, err := client.client.client.Get(testServer.URL)
if err != nil {
t.Errorf("Can't connect to the test server using this config: %+v: %v", validConfig.httpCfg, err)
continue
}
message, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil {
t.Errorf("Can't read the server response body using this config: %+v", validConfig.httpCfg)
continue
}
trimMessage := strings.TrimSpace(string(message))
if validConfig.expectedMessage != trimMessage {
t.Errorf("The expected message (%s) differs from the obtained message (%s) using this config: %+v",
validConfig.expectedMessage, trimMessage, validConfig.httpCfg)
}
} }
f(handlerNoRedirect, &promauth.HTTPClientConfig{
FollowRedirects: &notAllowed,
}, "I'm before redirect")
} }

View file

@ -9,7 +9,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/graphite" "github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/graphite"
) )
func Test_streamContext_Read(t *testing.T) { func TestStreamContextRead(t *testing.T) {
f := func(s string, rowsExpected *graphite.Rows) { f := func(s string, rowsExpected *graphite.Rows) {
t.Helper() t.Helper()
ctx := getStreamContext(strings.NewReader(s)) ctx := getStreamContext(strings.NewReader(s))

View file

@ -4,53 +4,45 @@ import (
"testing" "testing"
) )
func Test_Validate(t *testing.T) { func TestValidate_Failure(t *testing.T) {
tests := []struct { f := func(snapshotName string) {
name string t.Helper()
snapshotName string
want bool err := Validate(snapshotName)
}{ if err == nil {
{ t.Fatalf("expecting non-nil error")
name: "empty snapshot name",
snapshotName: "",
want: false,
},
{
name: "short snapshot name",
snapshotName: "",
want: false,
},
{
name: "short first part of the snapshot name",
snapshotName: "2022050312163-16EB56ADB4110CF2",
want: false,
},
{
name: "short second part of the snapshot name",
snapshotName: "20220503121638-16EB56ADB4110CF",
want: true,
},
{
name: "correct snapshot name",
snapshotName: "20220503121638-16EB56ADB4110CF2",
want: true,
},
{
name: "invalid time part snapshot name",
snapshotName: "00000000000000-16EB56ADB4110CF2",
want: false,
},
{
name: "not enough parts of the snapshot name",
snapshotName: "2022050312163816EB56ADB4110CF2",
want: false,
},
} }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := Validate(tt.snapshotName); (err == nil) != tt.want {
t.Errorf("checkSnapshotName() = %v, want %v", err, tt.want)
}
})
} }
// empty snapshot name
f("")
// short snapshot name
f("foo")
// short first part of the snapshot name
f("2022050312163-16EB56ADB4110CF2")
// invalid time part snapshot name
f("00000000000000-16EB56ADB4110CF2")
// not enough parts of the snapshot name
f("2022050312163816EB56ADB4110CF2")
}
func TestValidate_Success(t *testing.T) {
f := func(snapshotName string) {
t.Helper()
err := Validate(snapshotName)
if err != nil {
t.Fatalf("checkSnapshotName() error: %s", err)
}
}
// short second part of the snapshot name - this is OK
f("20220503121638-16EB56ADB4110CF")
//correct snapshot name
f("20220503121638-16EB56ADB4110CF2")
} }