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,88 +89,70 @@ 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{
Droplets: []droplet{ responseExpected := &listDropletResponse{
{ Droplets: []droplet{
Image: struct { {
Name string `json:"name"` Image: dropletImage{
Slug string `json:"slug"` Name: "14.04 x64",
}(struct { Slug: "ubuntu-16-04-x64",
Name string },
Slug string Region: dropletRegion{
}{Name: "14.04 x64", Slug: "ubuntu-16-04-x64"}), Slug: "nyc3",
Region: struct { },
Slug string `json:"slug"` Networks: networks{
}(struct{ Slug string }{Slug: "nyc3"}), V6: []network{
Networks: networks{ {
V6: []network{ IPAddress: "2604:A880:0800:0010:0000:0000:02DD:4001",
{ Type: "public",
IPAddress: "2604:A880:0800:0010:0000:0000:02DD:4001", },
Type: "public", },
}, V4: []network{
}, {
V4: []network{ IPAddress: "104.236.32.182",
{ Type: "public",
IPAddress: "104.236.32.182",
Type: "public",
},
},
}, },
SizeSlug: "s-1vcpu-1gb",
Features: []string{"backups", "ipv6", "virtio"},
Tags: []string{"tag1", "tag2"},
Status: "active",
Name: "example.com",
ID: 3164444,
VpcUUID: "f9b0769c-e118-42fb-a0c4-fed15ef69662",
}, },
}, },
Links: links{ SizeSlug: "s-1vcpu-1gb",
Pages: struct { Features: []string{"backups", "ipv6", "virtio"},
Last string `json:"last,omitempty"` Tags: []string{"tag1", "tag2"},
Next string `json:"next,omitempty"` Status: "active",
}(struct { Name: "example.com",
Last string ID: 3164444,
Next string VpcUUID: "f9b0769c-e118-42fb-a0c4-fed15ef69662",
}{Last: "https://api.digitalocean.com/v2/droplets?page=3&per_page=1", Next: "https://api.digitalocean.com/v2/droplets?page=2&per_page=1"}), },
}, },
Links: links{
Pages: linksPages{
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)
}
if len(resp) != expectedDropletCount {
t.Fatalf("unexpected droplets count; got %d; want %d\ndroplets:\n%v", len(resp), expectedDropletCount, resp)
}
} }
tests := []struct {
name string getAPIResponse := func(s string) ([]byte, error) {
args args var resp []byte
wantDropletCount int switch s {
wantErr bool case dropletsAPIPath:
}{ // return next
{ resp = []byte(`{ "droplets": [
name: "get 4 droples",
args: args{
func(s string) ([]byte, error) {
var resp []byte
switch s {
case dropletsAPIPath:
// return next
resp = []byte(`{ "droplets": [
{ {
"id": 3164444, "id": 3164444,
"name": "example.com", "name": "example.com",
@ -267,9 +250,9 @@ func Test_getDroplets(t *testing.T) {
} }
} }
}`) }`)
default: default:
// return with empty next // return with empty next
resp = []byte(`{ "droplets": [ resp = []byte(`{ "droplets": [
{ {
"id": 3164444, "id": 3164444,
"name": "example.com", "name": "example.com",
@ -326,24 +309,8 @@ 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

@ -49,18 +49,22 @@ type droplet struct {
Name string `json:"name"` Name string `json:"name"`
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"` SizeSlug string `json:"size_slug"`
Slug string `json:"slug"` Networks networks `json:"networks"`
} `json:"image"` Region dropletRegion `json:"region"`
SizeSlug string `json:"size_slug"` Tags []string `json:"tags"`
Networks networks `json:"networks"` VpcUUID string `json:"vpc_uuid"`
Region struct { }
Slug string `json:"slug"`
} `json:"region"` type dropletImage struct {
Tags []string `json:"tags"` Name string `json:"name"`
VpcUUID string `json:"vpc_uuid"` 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 {
@ -98,10 +102,12 @@ type listDropletResponse struct {
} }
type links struct { type links struct {
Pages struct { Pages linksPages `json:"pages,omitempty"`
Last string `json:"last,omitempty"` }
Next string `json:"next,omitempty"`
} `json:"pages,omitempty"` type linksPages struct {
Last string `json:"last,omitempty"`
Next string `json:"next,omitempty"`
} }
func (r *listDropletResponse) nextURLPath() (string, error) { func (r *listDropletResponse) nextURLPath() (string, error) {

View file

@ -7,84 +7,68 @@ 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", ID: 15,
args: args{ Tags: []string{"private", "test"},
droplets: []droplet{ Status: "active",
Name: "ubuntu-1",
Region: dropletRegion{
Slug: "do",
},
Features: []string{"feature-1", "feature-2"},
SizeSlug: "base-1",
VpcUUID: "vpc-1",
Image: dropletImage{
Name: "ubuntu",
Slug: "18",
},
Networks: networks{
V4: []network{
{ {
ID: 15, Type: "public",
Tags: []string{"private", "test"}, IPAddress: "100.100.100.100",
Status: "active", },
Name: "ubuntu-1", {
Region: struct { Type: "private",
Slug string `json:"slug"` IPAddress: "10.10.10.10",
}(struct{ Slug string }{Slug: "do"}), },
Features: []string{"feature-1", "feature-2"}, },
SizeSlug: "base-1", V6: []network{
VpcUUID: "vpc-1", {
Image: struct { Type: "public",
Name string `json:"name"` IPAddress: "::1",
Slug string `json:"slug"`
}(struct {
Name string
Slug string
}{Name: "ubuntu", Slug: "18"}),
Networks: networks{
V4: []network{
{
Type: "public",
IPAddress: "100.100.100.100",
},
{
Type: "private",
IPAddress: "10.10.10.10",
},
},
V6: []network{
{
Type: "public",
IPAddress: "::1",
},
},
},
}, },
}, },
defaultPort: 9100,
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "100.100.100.100:9100",
"__meta_digitalocean_droplet_id": "15",
"__meta_digitalocean_droplet_name": "ubuntu-1",
"__meta_digitalocean_features": ",feature-1,feature-2,",
"__meta_digitalocean_image": "18",
"__meta_digitalocean_image_name": "ubuntu",
"__meta_digitalocean_private_ipv4": "10.10.10.10",
"__meta_digitalocean_public_ipv4": "100.100.100.100",
"__meta_digitalocean_public_ipv6": "::1",
"__meta_digitalocean_region": "do",
"__meta_digitalocean_size": "base-1",
"__meta_digitalocean_status": "active",
"__meta_digitalocean_tags": ",private,test,",
"__meta_digitalocean_vpc": "vpc-1",
}),
}, },
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addDropletLabels(tt.args.droplets, tt.args.defaultPort) "__address__": "100.100.100.100:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_digitalocean_droplet_id": "15",
}) "__meta_digitalocean_droplet_name": "ubuntu-1",
"__meta_digitalocean_features": ",feature-1,feature-2,",
"__meta_digitalocean_image": "18",
"__meta_digitalocean_image_name": "ubuntu",
"__meta_digitalocean_private_ipv4": "10.10.10.10",
"__meta_digitalocean_public_ipv4": "100.100.100.100",
"__meta_digitalocean_public_ipv6": "::1",
"__meta_digitalocean_region": "do",
"__meta_digitalocean_size": "base-1",
"__meta_digitalocean_status": "active",
"__meta_digitalocean_tags": ",private,test,",
"__meta_digitalocean_vpc": "vpc-1",
}),
} }
f(droplets, labelssExpected)
} }

View file

@ -11,24 +11,32 @@ import (
// See https://github.com/moby/moby/blob/314759dc2f4745925d8dec6d15acc7761c6e5c92/docs/api/v1.41.yaml#L4024 // See https://github.com/moby/moby/blob/314759dc2f4745925d8dec6d15acc7761c6e5c92/docs/api/v1.41.yaml#L4024
type container struct { type container struct {
ID string ID string
Names []string Names []string
Labels map[string]string Labels map[string]string
Ports []struct { Ports []containerPort
IP string HostConfig containerHostConfig
PrivatePort int NetworkSettings containerNetworkSettings
PublicPort int }
Type string
} type containerPort struct {
HostConfig struct { IP string
NetworkMode string PrivatePort int
} PublicPort int
NetworkSettings struct { Type string
Networks map[string]struct { }
IPAddress string
NetworkID string type containerHostConfig struct {
} NetworkMode string
} }
type containerNetworkSettings struct {
Networks map[string]containerNetwork
}
type containerNetwork struct {
IPAddress 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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string data := `[
args args
want []container
wantErr bool
}{
{
name: "parse two containers",
args: args{
data: []byte(`[
{ {
"Id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "Id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"Names": [ "Names": [
@ -116,115 +116,83 @@ func Test_parseContainers(t *testing.T) {
} }
} }
} }
]`), ]`
resultExpected := []container{
{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"},
Labels: map[string]string{
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
}, },
want: []container{ Ports: []containerPort{
{ {
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", IP: "0.0.0.0",
Names: []string{"/crow-server"}, PrivatePort: 8080,
Labels: map[string]string{ PublicPort: 18081,
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec", Type: "tcp",
"com.docker.compose.container-number": "1", },
"com.docker.compose.oneoff": "False", },
"com.docker.compose.project": "crowserver", HostConfig: containerHostConfig{
"com.docker.compose.service": "crow-server", NetworkMode: "bridge",
"com.docker.compose.version": "1.11.2", },
}, NetworkSettings: containerNetworkSettings{
Ports: []struct { Networks: map[string]containerNetwork{
IP string "bridge": {
PrivatePort int IPAddress: "172.17.0.2",
PublicPort int NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
Type string
}{{
IP: "0.0.0.0",
PrivatePort: 8080,
PublicPort: 18081,
Type: "tcp",
}},
HostConfig: struct {
NetworkMode string
}{
NetworkMode: "bridge",
},
NetworkSettings: struct {
Networks map[string]struct {
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
}, },
}, },
},
},
{
ID: "0e0f72a6eb7d9fb443f0426a66f7b8dd7d3283ab7e3a308b2bed584ac03a33dc",
Names: []string{"/crow-web"},
Labels: map[string]string{
"com.docker.compose.config-hash": "d99ebd0fde8512366c2d78c367e95ddc74528bb60b7cf0c991c9f4835981e00e",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowweb",
"com.docker.compose.service": "crow-web",
"com.docker.compose.version": "1.11.2",
},
Ports: []containerPort{
{ {
ID: "0e0f72a6eb7d9fb443f0426a66f7b8dd7d3283ab7e3a308b2bed584ac03a33dc", IP: "0.0.0.0",
Names: []string{"/crow-web"}, PrivatePort: 8080,
Labels: map[string]string{ PublicPort: 18082,
"com.docker.compose.config-hash": "d99ebd0fde8512366c2d78c367e95ddc74528bb60b7cf0c991c9f4835981e00e", Type: "tcp",
"com.docker.compose.container-number": "1", },
"com.docker.compose.oneoff": "False", },
"com.docker.compose.project": "crowweb", HostConfig: containerHostConfig{
"com.docker.compose.service": "crow-web", NetworkMode: "bridge",
"com.docker.compose.version": "1.11.2", },
}, NetworkSettings: containerNetworkSettings{
Ports: []struct { Networks: map[string]containerNetwork{
IP string "bridge": {
PrivatePort int IPAddress: "172.17.0.3",
PublicPort int NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
Type string
}{{
IP: "0.0.0.0",
PrivatePort: 8080,
PublicPort: 18082,
Type: "tcp",
}},
HostConfig: struct {
NetworkMode string
}{
NetworkMode: "bridge",
},
NetworkSettings: struct {
Networks map[string]struct {
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": {
IPAddress: "172.17.0.3",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
}, },
}, },
}, },
}, },
} }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { f(data, resultExpected)
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)
}
})
}
} }
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,204 +282,152 @@ func Test_addContainerLabels(t *testing.T) {
} }
networkLabels := getNetworkLabelsByNetworkID(networks) networkLabels := getNetworkLabelsByNetworkID(networks)
tests := []struct { // NetworkMode != host
name string c := container{
c container ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
want []*promutils.Labels Names: []string{"/crow-server"},
wantErr bool Labels: map[string]string{
}{ "com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
{ "com.docker.compose.container-number": "1",
name: "NetworkMode!=host", "com.docker.compose.oneoff": "False",
c: container{ "com.docker.compose.project": "crowserver",
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", "com.docker.compose.service": "crow-server",
Names: []string{"/crow-server"}, "com.docker.compose.version": "1.11.2",
Labels: map[string]string{
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
},
HostConfig: struct {
NetworkMode string
}{
NetworkMode: "bridge",
},
NetworkSettings: struct {
Networks map[string]struct {
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"host": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8012",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "bridge",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
}),
},
}, },
{ HostConfig: containerHostConfig{
name: "NetworkMode=host", NetworkMode: "bridge",
c: container{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"},
Labels: map[string]string{
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
},
HostConfig: struct {
NetworkMode string
}{
NetworkMode: "host",
},
NetworkSettings: struct {
Networks map[string]struct {
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"host": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "foobar",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "host",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
}),
},
}, },
{ NetworkSettings: containerNetworkSettings{
name: "get labels from a container", Networks: map[string]containerNetwork{
c: container{ "host": {
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700", IPAddress: "172.17.0.2",
Names: []string{"/crow-server"}, NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
Labels: map[string]string{
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
}, },
Ports: []struct {
IP string
PrivatePort int
PublicPort int
Type string
}{{
IP: "0.0.0.0",
PrivatePort: 8080,
PublicPort: 18081,
Type: "tcp",
}},
HostConfig: struct {
NetworkMode string
}{
NetworkMode: "bridge",
},
NetworkSettings: struct {
Networks map[string]struct {
IPAddress string
NetworkID string
}
}{
Networks: map[string]struct {
IPAddress string
NetworkID string
}{
"bridge": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8080",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "bridge",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
"__meta_docker_port_private": "8080",
"__meta_docker_port_public": "18081",
"__meta_docker_port_public_ip": "0.0.0.0",
}),
}, },
}, },
} }
labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8012",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "bridge",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
}),
}
f(c, networkLabels, labelssExpected)
for _, tt := range tests { // NetworkMode=host
t.Run(tt.name, func(t *testing.T) { c = container{
labelss := addContainersLabels([]container{tt.c}, networkLabels, 8012, "foobar") ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
if (err != nil) != tt.wantErr { Names: []string{"/crow-server"},
t.Errorf("addContainersLabels() error = %v, wantErr %v", err, tt.wantErr) Labels: map[string]string{
return "com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
} "com.docker.compose.container-number": "1",
discoveryutils.TestEqualLabelss(t, labelss, tt.want) "com.docker.compose.oneoff": "False",
}) "com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
},
HostConfig: containerHostConfig{
NetworkMode: "host",
},
NetworkSettings: containerNetworkSettings{
Networks: map[string]containerNetwork{
"host": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
},
} }
labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "foobar",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "host",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
}),
}
f(c, networkLabels, labelssExpected)
// get labels from a container
c = container{
ID: "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
Names: []string{"/crow-server"},
Labels: map[string]string{
"com.docker.compose.config-hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"com.docker.compose.container-number": "1",
"com.docker.compose.oneoff": "False",
"com.docker.compose.project": "crowserver",
"com.docker.compose.service": "crow-server",
"com.docker.compose.version": "1.11.2",
},
Ports: []containerPort{
{
IP: "0.0.0.0",
PrivatePort: 8080,
PublicPort: 18081,
Type: "tcp",
},
},
HostConfig: containerHostConfig{
NetworkMode: "bridge",
},
NetworkSettings: containerNetworkSettings{
Networks: map[string]containerNetwork{
"bridge": {
IPAddress: "172.17.0.2",
NetworkID: "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
},
},
},
}
labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.17.0.2:8080",
"__meta_docker_container_id": "90bc3b31aa13da5c0b11af2e228d54b38428a84e25d4e249ae9e9c95e51a0700",
"__meta_docker_container_label_com_docker_compose_config_hash": "c9f0bd5bb31921f94cff367d819a30a0cc08d4399080897a6c5cd74b983156ec",
"__meta_docker_container_label_com_docker_compose_container_number": "1",
"__meta_docker_container_label_com_docker_compose_oneoff": "False",
"__meta_docker_container_label_com_docker_compose_project": "crowserver",
"__meta_docker_container_label_com_docker_compose_service": "crow-server",
"__meta_docker_container_label_com_docker_compose_version": "1.11.2",
"__meta_docker_container_name": "/crow-server",
"__meta_docker_container_network_mode": "bridge",
"__meta_docker_network_id": "1dd8d1a8bef59943345c7231d7ce8268333ff5a8c5b3c94881e6b4742b447634",
"__meta_docker_network_ingress": "false",
"__meta_docker_network_internal": "false",
"__meta_docker_network_ip": "172.17.0.2",
"__meta_docker_network_name": "bridge",
"__meta_docker_network_scope": "local",
"__meta_docker_port_private": "8080",
"__meta_docker_port_public": "18081",
"__meta_docker_port_public_ip": "0.0.0.0",
}),
}
f(c, networkLabels, labelssExpected)
} }

View file

@ -9,72 +9,64 @@ 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)
}
sort.Strings(networkIDs)
var labelss []*promutils.Labels
for _, networkID := range networkIDs {
labelss = append(labelss, networkLabels[networkID])
}
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // ingress network
args args networks := []network{
want []*promutils.Labels
}{
{ {
name: "ingress network", ID: "qs0hog6ldlei9ct11pr3c77v1",
args: args{ Ingress: true,
networks: []network{ Scope: "swarm",
{ Name: "ingress",
ID: "qs0hog6ldlei9ct11pr3c77v1", Labels: map[string]string{
Ingress: true, "key1": "value1",
Scope: "swarm",
Name: "ingress",
Labels: map[string]string{
"key1": "value1",
},
},
},
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__meta_docker_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_docker_network_ingress": "true",
"__meta_docker_network_internal": "false",
"__meta_docker_network_label_key1": "value1",
"__meta_docker_network_name": "ingress",
"__meta_docker_network_scope": "swarm",
})},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := getNetworkLabelsByNetworkID(tt.args.networks) "__meta_docker_network_id": "qs0hog6ldlei9ct11pr3c77v1",
var networkIDs []string "__meta_docker_network_ingress": "true",
for networkID := range got { "__meta_docker_network_internal": "false",
networkIDs = append(networkIDs, networkID) "__meta_docker_network_label_key1": "value1",
} "__meta_docker_network_name": "ingress",
sort.Strings(networkIDs) "__meta_docker_network_scope": "swarm",
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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected networks\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string // parse two networks
args args data := `[
want []network
wantErr bool
}{
{
name: "parse two networks",
args: args{
data: []byte(`[
{ {
"Name": "ingress", "Name": "ingress",
"Id": "qs0hog6ldlei9ct11pr3c77v1", "Id": "qs0hog6ldlei9ct11pr3c77v1",
@ -132,39 +124,25 @@ func Test_parseNetworks(t *testing.T) {
"key": "value" "key": "value"
} }
} }
]`), ]`
resultExpected := []network{
{
ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true,
Scope: "swarm",
Name: "ingress",
Labels: map[string]string{
"key1": "value1",
}, },
want: []network{ },
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "317f0384d7e5f5c26304a0b04599f9f54bc08def4d0535059ece89955e9c4b7b",
Ingress: true, Scope: "local",
Scope: "swarm", Name: "host",
Name: "ingress", Labels: map[string]string{
Labels: map[string]string{ "key": "value",
"key1": "value1",
},
},
{
ID: "317f0384d7e5f5c26304a0b04599f9f54bc08def4d0535059ece89955e9c4b7b",
Scope: "local",
Name: "host",
Labels: map[string]string{
"key": "value",
},
},
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }

View file

@ -9,72 +9,64 @@ 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)
}
sort.Strings(networkIDs)
var labelss []*promutils.Labels
for _, networkID := range networkIDs {
labelss = append(labelss, networkLabels[networkID])
}
discoveryutils.TestEqualLabelss(t, labelss, labelssExpected)
} }
tests := []struct {
name string // ingress network
args args networks := []network{
want []*promutils.Labels
}{
{ {
name: "ingress network", ID: "qs0hog6ldlei9ct11pr3c77v1",
args: args{ Ingress: true,
networks: []network{ Scope: "swarm",
{ Name: "ingress",
ID: "qs0hog6ldlei9ct11pr3c77v1", Labels: map[string]string{
Ingress: true, "key1": "value1",
Scope: "swarm",
Name: "ingress",
Labels: map[string]string{
"key1": "value1",
},
},
},
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
})},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := getNetworkLabelsByNetworkID(tt.args.networks) "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
var networkIDs []string "__meta_dockerswarm_network_ingress": "true",
for networkID := range got { "__meta_dockerswarm_network_internal": "false",
networkIDs = append(networkIDs, networkID) "__meta_dockerswarm_network_label_key1": "value1",
} "__meta_dockerswarm_network_name": "ingress",
sort.Strings(networkIDs) "__meta_dockerswarm_network_scope": "swarm",
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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string // parse two networks
args args data := `[
want []network
wantErr bool
}{
{
name: "parse two networks",
args: args{
data: []byte(`[
{ {
"Name": "ingress", "Name": "ingress",
"Id": "qs0hog6ldlei9ct11pr3c77v1", "Id": "qs0hog6ldlei9ct11pr3c77v1",
@ -132,39 +124,25 @@ func Test_parseNetworks(t *testing.T) {
"key": "value" "key": "value"
} }
} }
]`), ]`
resultExpected := []network{
{
ID: "qs0hog6ldlei9ct11pr3c77v1",
Ingress: true,
Scope: "swarm",
Name: "ingress",
Labels: map[string]string{
"key1": "value1",
}, },
want: []network{ },
{ {
ID: "qs0hog6ldlei9ct11pr3c77v1", ID: "317f0384d7e5f5c26304a0b04599f9f54bc08def4d0535059ece89955e9c4b7b",
Ingress: true, Scope: "local",
Scope: "swarm", Name: "host",
Name: "ingress", Labels: map[string]string{
Labels: map[string]string{ "key": "value",
"key1": "value1",
},
},
{
ID: "317f0384d7e5f5c26304a0b04599f9f54bc08def4d0535059ece89955e9c4b7b",
Scope: "local",
Name: "host",
Labels: map[string]string{
"key": "value",
},
},
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }

View file

@ -10,32 +10,44 @@ 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
Labels map[string]string Description nodeDescription
Role string Status nodeStatus
Availability string ManagerStatus nodeManagerStatus
} }
Description struct {
Hostname string type nodeSpec struct {
Platform struct { Labels map[string]string
Architecture string Role string
OS string Availability string
} }
Engine struct {
EngineVersion string type nodeDescription struct {
} Hostname string
} Platform nodePlatform
Status struct { Engine nodeEngine
State string }
Message string
Addr string type nodePlatform struct {
} Architecture string
ManagerStatus struct { OS string
Leader bool }
Reachability string
Addr string type nodeEngine struct {
} EngineVersion string
}
type nodeStatus struct {
State string
Message string
Addr string
}
type nodeManagerStatus struct {
Leader bool
Reachability 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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result;\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string // parse ok
args args data := `[
want []node
wantErr bool
}{
{
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "qauwmifceyvqs0sipvzu8oslu", "ID": "qauwmifceyvqs0sipvzu8oslu",
"Version": { "Version": {
@ -51,131 +52,81 @@ func Test_parseNodes(t *testing.T) {
} }
} }
] ]
`), `
resultExpected := []node{
{
ID: "qauwmifceyvqs0sipvzu8oslu",
Spec: nodeSpec{
Role: "manager",
Availability: "active",
}, },
want: []node{ Status: nodeStatus{
{ State: "ready",
ID: "qauwmifceyvqs0sipvzu8oslu", Addr: "172.31.40.97",
Spec: struct { },
Labels map[string]string Description: nodeDescription{
Role string Hostname: "ip-172-31-40-97",
Availability string Platform: nodePlatform{
}{Role: "manager", Availability: "active"}, Architecture: "x86_64",
Status: struct { OS: "linux",
State string },
Message string Engine: nodeEngine{
Addr string EngineVersion: "19.03.11",
}{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",
Platform: struct {
Architecture string
OS string
}{
Architecture: "x86_64",
OS: "linux",
},
Engine: struct{ EngineVersion string }{
EngineVersion: "19.03.11",
},
},
}, },
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }
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", ID: "qauwmifceyvqs0sipvzu8oslu",
args: args{ Spec: nodeSpec{
nodes: []node{ Role: "manager",
{ Availability: "active",
ID: "qauwmifceyvqs0sipvzu8oslu", },
Spec: struct { Status: nodeStatus{
Labels map[string]string State: "ready",
Role string Addr: "172.31.40.97",
Availability string },
}{Role: "manager", Availability: "active"}, Description: nodeDescription{
Status: struct { Hostname: "ip-172-31-40-97",
State string Platform: nodePlatform{
Message string Architecture: "x86_64",
Addr string OS: "linux",
}{State: "ready", Addr: "172.31.40.97"}, },
Description: struct { Engine: nodeEngine{
Hostname string EngineVersion: "19.03.11",
Platform struct { },
Architecture string
OS string
}
Engine struct{ EngineVersion string }
}{
Hostname: "ip-172-31-40-97",
Platform: struct {
Architecture string
OS string
}{
Architecture: "x86_64",
OS: "linux",
},
Engine: struct{ EngineVersion string }{
EngineVersion: "19.03.11",
},
},
},
},
port: 9100,
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_manager_address": "",
"__meta_dockerswarm_node_manager_leader": "false",
"__meta_dockerswarm_node_manager_reachability": "",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
})},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addNodeLabels(tt.args.nodes, tt.args.port) "__address__": "172.31.40.97:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_dockerswarm_node_address": "172.31.40.97",
}) "__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_manager_address": "",
"__meta_dockerswarm_node_manager_leader": "false",
"__meta_dockerswarm_node_manager_reachability": "",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
}),
} }
f(nodes, 9100, labelssExpected)
} }

View file

@ -12,31 +12,45 @@ 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
Labels map[string]string UpdateStatus serviceUpdateStatus
Name string Endpoint serviceEndpoint
TaskTemplate struct { }
ContainerSpec struct {
Hostname string type serviceSpec struct {
Image string Labels map[string]string
} Name string
} TaskTemplate taskTemplate
Mode struct { Mode serviceSpecMode
Global interface{} }
Replicated interface{}
} type taskTemplate struct {
} ContainerSpec containerSpec
UpdateStatus struct { }
State string
} type containerSpec struct {
Endpoint struct { Hostname string
Ports []portConfig Image string
VirtualIPs []struct { }
NetworkID string
Addr string type serviceSpecMode struct {
} Global interface{}
} Replicated interface{}
}
type serviceUpdateStatus struct {
State string
}
type serviceEndpoint struct {
Ports []portConfig
VirtualIPs []virtualIP
}
type virtualIP struct {
NetworkID 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)
}
if !reflect.DeepEqual(services, servicesExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", services, servicesExpected)
}
} }
tests := []struct {
name string // parse ok
args args data := `[
want []service
wantErr bool
}{
{
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "tgsci5gd31aai3jyudv98pqxf", "ID": "tgsci5gd31aai3jyudv98pqxf",
"Version": { "Version": {
@ -87,202 +88,113 @@ func Test_parseServicesResponse(t *testing.T) {
] ]
} }
} }
]`), ]`
servicesExpected := []service{
{
ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: serviceSpec{
Labels: map[string]string{},
Name: "redis2",
TaskTemplate: taskTemplate{
ContainerSpec: containerSpec{
Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
},
},
Mode: serviceSpecMode{
Replicated: map[string]interface{}{},
},
}, },
want: []service{ Endpoint: serviceEndpoint{
{ Ports: []portConfig{
ID: "tgsci5gd31aai3jyudv98pqxf", {
Spec: struct { Protocol: "tcp",
Labels map[string]string PublishMode: "ingress",
Name string PublishedPort: 8081,
TaskTemplate struct {
ContainerSpec struct {
Hostname string
Image string
}
}
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: "",
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",
PublishMode: "ingress",
PublishedPort: 8081,
},
}, VirtualIPs: []struct {
NetworkID string
Addr string
}{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
},
}, },
},
VirtualIPs: []virtualIP{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
}, },
}, },
}, },
}, },
} }
for _, tt := range tests { f(data, servicesExpected)
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)
}
})
}
} }
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",
"qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{ TaskTemplate: taskTemplate{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", ContainerSpec: containerSpec{
"__meta_dockerswarm_network_ingress": "true", Hostname: "node1",
"__meta_dockerswarm_network_internal": "false", Image: "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
"__meta_dockerswarm_network_label_key1": "value1", },
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
}),
}, },
services: []service{ Mode: serviceSpecMode{
Replicated: map[string]interface{}{},
},
},
Endpoint: serviceEndpoint{
Ports: []portConfig{
{ {
ID: "tgsci5gd31aai3jyudv98pqxf", Protocol: "tcp",
Spec: struct { Name: "redis",
Labels map[string]string PublishMode: "ingress",
Name string },
TaskTemplate struct { },
ContainerSpec struct { VirtualIPs: []virtualIP{
Hostname string {
Image string NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
} Addr: "10.0.0.3/24",
}
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{
"__address__": "10.0.0.3:0",
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
"__meta_dockerswarm_service_endpoint_port_name": "redis",
"__meta_dockerswarm_service_endpoint_port_publish_mode": "ingress",
"__meta_dockerswarm_service_id": "tgsci5gd31aai3jyudv98pqxf",
"__meta_dockerswarm_service_mode": "replicated",
"__meta_dockerswarm_service_name": "redis2",
"__meta_dockerswarm_service_task_container_hostname": "node1",
"__meta_dockerswarm_service_task_container_image": "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
"__meta_dockerswarm_service_updating_status": "",
})},
}, },
} }
for _, tt := range tests { networksLabels := map[string]*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { "qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{
got := addServicesLabels(tt.args.services, tt.args.networksLabels, tt.args.port) "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_dockerswarm_network_ingress": "true",
}) "__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
}),
} }
labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.0.0.3:0",
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
"__meta_dockerswarm_service_endpoint_port_name": "redis",
"__meta_dockerswarm_service_endpoint_port_publish_mode": "ingress",
"__meta_dockerswarm_service_id": "tgsci5gd31aai3jyudv98pqxf",
"__meta_dockerswarm_service_mode": "replicated",
"__meta_dockerswarm_service_name": "redis2",
"__meta_dockerswarm_service_task_container_hostname": "node1",
"__meta_dockerswarm_service_task_container_image": "redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842",
"__meta_dockerswarm_service_updating_status": "",
}),
}
f(services, networksLabels, labelssExpected)
} }

View file

@ -17,27 +17,37 @@ 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 Slot int
} }
}
Status struct { type networkAttachment struct {
State string Addresses []string
ContainerStatus struct { Network network
ContainerID string }
}
PortStatus struct { type taskStatus struct {
Ports []portConfig State string
} ContainerStatus containerStatus
} PortStatus portStatus
Spec struct { }
ContainerSpec struct {
Labels map[string]string type containerStatus struct {
} ContainerID string
} }
Slot int
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) {

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)
}
if !reflect.DeepEqual(tasks, tasksExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", tasks, tasksExpected)
}
} }
tests := []struct {
name string // parse ok
args args data := `[
want []task
wantErr bool
}{
{
name: "parse ok",
args: args{
data: []byte(`[
{ {
"ID": "t4rdm7j2y9yctbrksiwvsgpu5", "ID": "t4rdm7j2y9yctbrksiwvsgpu5",
"Version": { "Version": {
@ -63,297 +64,217 @@ 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 Labels: map[string]string{
} "label1": "value1",
}{
ContainerSpec: struct {
Labels map[string]string
}{
Labels: map[string]string{
"label1": "value1",
},
},
}, },
DesiredState: "running",
Slot: 1,
Status: struct {
State string
ContainerStatus struct{ ContainerID string }
PortStatus struct{ Ports []portConfig }
}{
State: "running",
ContainerStatus: struct{ ContainerID string }{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
},
PortStatus: struct{ Ports []portConfig }{}},
}, },
}, },
DesiredState: "running",
Slot: 1,
Status: taskStatus{
State: "running",
ContainerStatus: containerStatus{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
},
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", ID: "t4rdm7j2y9yctbrksiwvsgpu5",
args: args{ ServiceID: "t91nf284wzle1ya09lqvyjgnq",
port: 9100, NodeID: "qauwmifceyvqs0sipvzu8oslu",
tasks: []task{ DesiredState: "running",
{ Slot: 1,
ID: "t4rdm7j2y9yctbrksiwvsgpu5", Status: taskStatus{
ServiceID: "t91nf284wzle1ya09lqvyjgnq", State: "running",
NodeID: "qauwmifceyvqs0sipvzu8oslu", ContainerStatus: containerStatus{
DesiredState: "running", ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
Slot: 1, },
Status: struct { PortStatus: portStatus{
State string Ports: []portConfig{
ContainerStatus struct{ ContainerID string } {
PortStatus struct{ Ports []portConfig } PublishMode: "ingress",
}{ Name: "redis",
State: "running", Protocol: "tcp",
ContainerStatus: struct{ ContainerID string }{ PublishedPort: 6379,
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8", },
},
PortStatus: struct{ Ports []portConfig }{
Ports: []portConfig{
{
PublishMode: "ingress",
Name: "redis",
Protocol: "tcp",
PublishedPort: 6379,
},
},
}},
}, },
}, }},
nodesLabels: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
}),
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:6379",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
"__meta_dockerswarm_task_container_id": "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
"__meta_dockerswarm_task_desired_state": "running",
"__meta_dockerswarm_task_id": "t4rdm7j2y9yctbrksiwvsgpu5",
"__meta_dockerswarm_task_port_publish_mode": "ingress",
"__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running",
})},
}, },
}
nodesLabels := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
}),
}
labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:6379",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
"__meta_dockerswarm_task_container_id": "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
"__meta_dockerswarm_task_desired_state": "running",
"__meta_dockerswarm_task_id": "t4rdm7j2y9yctbrksiwvsgpu5",
"__meta_dockerswarm_task_port_publish_mode": "ingress",
"__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running",
}),
}
f(tasks, nodesLabels, nil, nil, labelssExpected)
// adds 1 task with nodes, network and services labels
tasks = []task{
{ {
name: "adds 1 task with nodes, network and services labels", ID: "t4rdm7j2y9yctbrksiwvsgpu5",
args: args{ ServiceID: "tgsci5gd31aai3jyudv98pqxf",
port: 9100, NodeID: "qauwmifceyvqs0sipvzu8oslu",
tasks: []task{ DesiredState: "running",
{ Slot: 1,
ID: "t4rdm7j2y9yctbrksiwvsgpu5", NetworksAttachments: []networkAttachment{
ServiceID: "tgsci5gd31aai3jyudv98pqxf", {
NodeID: "qauwmifceyvqs0sipvzu8oslu", Network: network{
DesiredState: "running", ID: "qs0hog6ldlei9ct11pr3c77v1",
Slot: 1,
NetworksAttachments: []struct {
Addresses []string
Network struct{ ID string }
}{
{
Network: struct {
ID string
}{
ID: "qs0hog6ldlei9ct11pr3c77v1",
},
Addresses: []string{"10.10.15.15/24"},
},
},
Status: struct {
State string
ContainerStatus struct{ ContainerID string }
PortStatus struct{ Ports []portConfig }
}{
State: "running",
ContainerStatus: struct{ ContainerID string }{
ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
},
PortStatus: struct{ Ports []portConfig }{}},
}, },
Addresses: []string{"10.10.15.15/24"},
}, },
networksLabels: map[string]*promutils.Labels{
"qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
}),
},
nodesLabels: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
}),
},
services: []service{
{
ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: struct {
Labels map[string]string
Name string
TaskTemplate struct {
ContainerSpec struct {
Hostname string
Image string
}
}
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",
PublishedPort: 6379,
},
}, VirtualIPs: []struct {
NetworkID string
Addr string
}{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
},
},
},
},
},
servicesLabels: []*promutils.Labels{},
}, },
want: []*promutils.Labels{ Status: taskStatus{
promutils.NewLabelsFromMap(map[string]string{ State: "running",
"__address__": "10.10.15.15:6379", ContainerStatus: containerStatus{
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1", ContainerID: "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
"__meta_dockerswarm_network_ingress": "true", },
"__meta_dockerswarm_network_internal": "false", PortStatus: portStatus{},
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
"__meta_dockerswarm_task_container_id": "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
"__meta_dockerswarm_task_desired_state": "running",
"__meta_dockerswarm_task_id": "t4rdm7j2y9yctbrksiwvsgpu5",
"__meta_dockerswarm_task_port_publish_mode": "ingress",
"__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running",
}),
}, },
}, },
} }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { networksLabels := map[string]*promutils.Labels{
got := addTasksLabels(tt.args.tasks, tt.args.nodesLabels, tt.args.servicesLabels, tt.args.networksLabels, tt.args.services, tt.args.port) "qs0hog6ldlei9ct11pr3c77v1": promutils.NewLabelsFromMap(map[string]string{
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
}) "__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
}),
} }
nodesLabels = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "172.31.40.97:9100",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
}),
}
services := []service{
{
ID: "tgsci5gd31aai3jyudv98pqxf",
Spec: serviceSpec{
Labels: map[string]string{},
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",
PublishedPort: 6379,
},
},
VirtualIPs: []virtualIP{
{
NetworkID: "qs0hog6ldlei9ct11pr3c77v1",
Addr: "10.0.0.3/24",
},
},
},
},
}
labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.10.15.15:6379",
"__meta_dockerswarm_network_id": "qs0hog6ldlei9ct11pr3c77v1",
"__meta_dockerswarm_network_ingress": "true",
"__meta_dockerswarm_network_internal": "false",
"__meta_dockerswarm_network_label_key1": "value1",
"__meta_dockerswarm_network_name": "ingress",
"__meta_dockerswarm_network_scope": "swarm",
"__meta_dockerswarm_node_address": "172.31.40.97",
"__meta_dockerswarm_node_availability": "active",
"__meta_dockerswarm_node_engine_version": "19.03.11",
"__meta_dockerswarm_node_hostname": "ip-172-31-40-97",
"__meta_dockerswarm_node_id": "qauwmifceyvqs0sipvzu8oslu",
"__meta_dockerswarm_node_platform_architecture": "x86_64",
"__meta_dockerswarm_node_platform_os": "linux",
"__meta_dockerswarm_node_role": "manager",
"__meta_dockerswarm_node_status": "ready",
"__meta_dockerswarm_task_container_id": "33034b69f6fa5f808098208752fd1fe4e0e1ca86311988cea6a73b998cdc62e8",
"__meta_dockerswarm_task_desired_state": "running",
"__meta_dockerswarm_task_id": "t4rdm7j2y9yctbrksiwvsgpu5",
"__meta_dockerswarm_task_port_publish_mode": "ingress",
"__meta_dockerswarm_task_slot": "1",
"__meta_dockerswarm_task_state": "running",
}),
}
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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string // parse ok 1 app with instance
args args data := `<applications>
want *applications
wantErr bool
}{
{
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,53 +56,40 @@ 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",
Instances: []Instance{
{ {
Name: "HELLO-NETFLIX-OSS", HostName: "98de25ebef42",
Instances: []Instance{ HomePageURL: "http://98de25ebef42:8080/",
{ StatusPageURL: "http://98de25ebef42:8080/Status",
HostName: "98de25ebef42", HealthCheckURL: "http://98de25ebef42:8080/healthcheck",
HomePageURL: "http://98de25ebef42:8080/", App: "HELLO-NETFLIX-OSS",
StatusPageURL: "http://98de25ebef42:8080/Status", IPAddr: "10.10.0.3",
HealthCheckURL: "http://98de25ebef42:8080/healthcheck", VipAddress: "HELLO-NETFLIX-OSS",
App: "HELLO-NETFLIX-OSS", SecureVipAddress: "",
IPAddr: "10.10.0.3", Status: "UP",
VipAddress: "HELLO-NETFLIX-OSS", Port: Port{
SecureVipAddress: "", Enabled: true,
Status: "UP", Port: 8080,
Port: Port{
Enabled: true,
Port: 8080,
},
SecurePort: Port{
Port: 443,
},
DataCenterInfo: DataCenterInfo{
Name: "MyOwn",
},
Metadata: MetaData{},
CountryID: 1,
InstanceID: "",
},
}, },
SecurePort: Port{
Port: 443,
},
DataCenterInfo: DataCenterInfo{
Name: "MyOwn",
},
Metadata: MetaData{},
CountryID: 1,
InstanceID: "",
}, },
}, },
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }

View file

@ -7,74 +7,65 @@ 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 Applications: []Application{
}{ {
{ Name: "test-app",
name: "1 application", Instances: []Instance{
args: args{ {
applications: &applications{ Status: "Ok",
Applications: []Application{ HealthCheckURL: "some-url",
{ HomePageURL: "some-home-url",
Name: "test-app", StatusPageURL: "some-status-url",
Instances: []Instance{ HostName: "host-1",
IPAddr: "10.15.11.11",
CountryID: 5,
VipAddress: "10.15.11.11",
InstanceID: "some-id",
Metadata: MetaData{
Items: []Tag{
{ {
Status: "Ok", Content: "value-1",
HealthCheckURL: "some-url", XMLName: struct{ Space, Local string }{Local: "key-1"},
HomePageURL: "some-home-url",
StatusPageURL: "some-status-url",
HostName: "host-1",
IPAddr: "10.15.11.11",
CountryID: 5,
VipAddress: "10.15.11.11",
InstanceID: "some-id",
Metadata: MetaData{Items: []Tag{
{
Content: "value-1",
XMLName: struct{ Space, Local string }{Local: "key-1"},
},
}},
Port: Port{
Port: 9100,
},
}, },
}, },
}, },
Port: Port{
Port: 9100,
},
}, },
}, },
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "host-1:9100",
"instance": "some-id",
"__meta_eureka_app_instance_hostname": "host-1",
"__meta_eureka_app_name": "test-app",
"__meta_eureka_app_instance_healthcheck_url": "some-url",
"__meta_eureka_app_instance_ip_addr": "10.15.11.11",
"__meta_eureka_app_instance_vip_address": "10.15.11.11",
"__meta_eureka_app_instance_secure_vip_address": "",
"__meta_eureka_app_instance_country_id": "5",
"__meta_eureka_app_instance_homepage_url": "some-home-url",
"__meta_eureka_app_instance_statuspage_url": "some-status-url",
"__meta_eureka_app_instance_id": "some-id",
"__meta_eureka_app_instance_metadata_key_1": "value-1",
"__meta_eureka_app_instance_port": "9100",
"__meta_eureka_app_instance_port_enabled": "false",
"__meta_eureka_app_instance_status": "Ok",
}),
},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addInstanceLabels(tt.args.applications) "__address__": "host-1:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "instance": "some-id",
}) "__meta_eureka_app_instance_hostname": "host-1",
"__meta_eureka_app_name": "test-app",
"__meta_eureka_app_instance_healthcheck_url": "some-url",
"__meta_eureka_app_instance_ip_addr": "10.15.11.11",
"__meta_eureka_app_instance_vip_address": "10.15.11.11",
"__meta_eureka_app_instance_secure_vip_address": "",
"__meta_eureka_app_instance_country_id": "5",
"__meta_eureka_app_instance_homepage_url": "some-home-url",
"__meta_eureka_app_instance_statuspage_url": "some-status-url",
"__meta_eureka_app_instance_id": "some-id",
"__meta_eureka_app_instance_metadata_key_1": "value-1",
"__meta_eureka_app_instance_port": "9100",
"__meta_eureka_app_instance_port_enabled": "false",
"__meta_eureka_app_instance_status": "Ok",
}),
} }
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 { f(data, path, resultExpected)
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)
}
})
}
} }

View file

@ -7,45 +7,34 @@ 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", Targets: []string{"127.0.0.1:9100", "127.0.0.2:91001"},
args: args{ Labels: promutils.NewLabelsFromMap(map[string]string{"__meta_kubernetes_pod": "pod-1", "__meta_consul_dc": "dc-2"}),
src: []httpGroupTarget{
{
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"}),
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "127.0.0.1:9100",
"__meta_kubernetes_pod": "pod-1",
"__meta_consul_dc": "dc-2",
"__meta_url": "http://foo.bar/baz?aaa=bb",
}),
promutils.NewLabelsFromMap(map[string]string{
"__address__": "127.0.0.2:91001",
"__meta_kubernetes_pod": "pod-1",
"__meta_consul_dc": "dc-2",
"__meta_url": "http://foo.bar/baz?aaa=bb",
}),
},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addHTTPTargetLabels(tt.args.src, "http://foo.bar/baz?aaa=bb") "__address__": "127.0.0.1:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_kubernetes_pod": "pod-1",
}) "__meta_consul_dc": "dc-2",
"__meta_url": "http://foo.bar/baz?aaa=bb",
}),
promutils.NewLabelsFromMap(map[string]string{
"__address__": "127.0.0.2:91001",
"__meta_kubernetes_pod": "pod-1",
"__meta_consul_dc": "dc-2",
"__meta_url": "http://foo.bar/baz?aaa=bb",
}),
} }
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
ResourceVersion string }
}
// BookmarkMetadata is metadata for Bookmark
type BookmarkMetadata struct {
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

@ -20,21 +20,27 @@ type apiConfig struct {
// Config represent configuration file for kubernetes API server connection // Config represent configuration file for kubernetes API server connection
// https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/api/v1/types.go#L28 // https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/api/v1/types.go#L28
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"`
Name string `yaml:"name"` AuthInfos []authInfo `yaml:"users"`
Cluster *Cluster `yaml:"cluster"` Contexts []configContext `yaml:"contexts"`
} `yaml:"clusters"` CurrentContext string `yaml:"current-context"`
AuthInfos []struct { }
Name string `yaml:"name"`
AuthInfo *AuthInfo `yaml:"user"` type configCluster struct {
} `yaml:"users"` Name string `yaml:"name"`
Contexts []struct { Cluster *Cluster `yaml:"cluster"`
Name string `yaml:"name"` }
Context *Context `yaml:"context"`
} `yaml:"contexts"` type authInfo struct {
CurrentContext string `yaml:"current-context"` Name string `yaml:"name"`
AuthInfo *AuthInfo `yaml:"user"`
}
type configContext struct {
Name string `yaml:"name"`
Context *Context `yaml:"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

@ -264,18 +264,22 @@ type discoveryRequestNode struct {
// discoveryResponse represent xDS-requests for Kuma Service Mesh // discoveryResponse represent xDS-requests for Kuma Service Mesh
// 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"`
Mesh string `json:"mesh"` Nonce string `json:"nonce"`
Service string `json:"service"` }
Targets []struct {
Name string `json:"name"` type resource struct {
Scheme string `json:"scheme"` Mesh string `json:"mesh"`
Address string `json:"address"` Service string `json:"service"`
MetricsPath string `json:"metrics_path"` Targets []target `json:"targets"`
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
} `json:"targets"` }
Labels map[string]string `json:"labels"`
} `json:"resources"` type target struct {
Nonce string `json:"nonce"` Name string `json:"name"`
Scheme string `json:"scheme"`
Address string `json:"address"`
MetricsPath string `json:"metrics_path"`
Labels map[string]string `json:"labels"`
} }

View file

@ -24,11 +24,13 @@ func getServiceLabels(cfg *apiConfig) []*promutils.Labels {
// ServiceList is a list of Nomad services. // ServiceList is a list of Nomad services.
// 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"`
ServiceName string `json:"ServiceName"` }
Tags []string `json:"Tags"`
} `json:"Services"` type service struct {
ServiceName string `json:"ServiceName"`
Tags []string `json:"Tags"`
} }
// 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
ExpiresAt time.Time `json:"expires_at,omitempty"` }
Catalog []catalogItem `json:"catalog,omitempty"`
} type authToken struct {
ExpiresAt time.Time `json:"expires_at,omitempty"`
Catalog []catalogItem `json:"catalog,omitempty"`
} }
type catalogItem struct { type catalogItem struct {

View file

@ -1,124 +1,104 @@
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()
}
tests := []struct { _, err := buildAuthRequestBody(sdc)
name string if err == nil {
args args t.Fatalf("expecting non-nil error")
want []byte }
wantErr bool
}{
{
name: "empty config",
args: args{
sdc: &SDConfig{},
},
wantErr: true,
},
{
name: "username password auth with domain",
args: args{
sdc: &SDConfig{
Username: "some-user",
Password: promauth.NewSecret("some-password"),
DomainName: "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"}}}}`),
},
{
name: "application credentials auth",
args: args{
sdc: &SDConfig{
ApplicationCredentialID: "some-id",
ApplicationCredentialSecret: promauth.NewSecret("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)
}
})
} }
// empty config
f(&SDConfig{})
} }
func Test_getComputeEndpointURL1(t *testing.T) { func TestBuildAuthRequestBody_Success(t *testing.T) {
type args struct { f := func(sdc *SDConfig, resultExpected string) {
catalog []catalogItem t.Helper()
availability string
region string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "bad catalog data",
args: args{
catalog: []catalogItem{
{
Type: "keystone",
Endpoints: []endpoint{},
},
},
},
wantErr: true,
},
{
name: "good private url",
args: args{
availability: "private",
catalog: []catalogItem{
{
Type: "compute",
Endpoints: []endpoint{
{
Interface: "private",
Type: "compute",
URL: "https://compute.test.local:8083/v2.1",
},
},
},
{
Type: "keystone",
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) { result, err := buildAuthRequestBody(sdc)
t.Errorf("getComputeEndpointURL() got = %v, want %v", got.String(), tt.want) if err != nil {
} t.Fatalf("buildAuthRequestBody() error: %s", err)
}) }
if string(result) != resultExpected {
t.Fatalf("unexpected result\ngot\n%s\nwant\n%s", result, resultExpected)
}
} }
// username password auth with domain
f(&SDConfig{
Username: "some-user",
Password: promauth.NewSecret("some-password"),
DomainName: "some-domain",
}, `{"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{
ApplicationCredentialID: "some-id",
ApplicationCredentialSecret: promauth.NewSecret("some-secret"),
}, `{"auth":{"identity":{"methods":["application_credential"],"application_credential":{"id":"some-id","secret":"some-secret"}}}}`)
}
func TestGetComputeEndpointURL_Failure(t *testing.T) {
f := func(catalog []catalogItem) {
t.Helper()
_, err := getComputeEndpointURL(catalog, "", "")
if err == nil {
t.Fatalf("expecting non-nil error")
}
}
// bad catalog data
catalog := []catalogItem{
{
Type: "keystone",
Endpoints: []endpoint{},
},
}
f(catalog)
}
func TestGetComputeEndpointURL_Success(t *testing.T) {
f := func(catalog []catalogItem, availability, region, resultExpected string) {
t.Helper()
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",
Endpoints: []endpoint{
{
Interface: "private",
Type: "compute",
URL: "https://compute.test.local:8083/v2.1",
},
},
},
{
Type: "keystone",
Endpoints: []endpoint{},
},
}
availability := "private"
resultExpected := "https://compute.test.local:8083/v2.1"
f(catalog, availability, "", resultExpected)
} }

View file

@ -12,11 +12,13 @@ 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"`
HREF string `json:"href"` }
Rel string `json:"rel,omitempty"`
} `json:"hypervisors_links,omitempty"` type hypervisorLink struct {
HREF string `json:"href"`
Rel string `json:"rel,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 // bad data
args args f(`{ff}`)
want hypervisorDetail }
wantErr bool
}{ func TestParseHypervisorDetail_Success(t *testing.T) {
{ f := func(data string, resultExpected *hypervisorDetail) {
name: "bad data", t.Helper()
args: args{
data: []byte(`{ff}`), result, err := parseHypervisorDetail([]byte(data))
}, if err != nil {
wantErr: true, t.Fatalf("parseHypervisorDetail() error: %s", err)
}, }
{ if !reflect.DeepEqual(result, resultExpected) {
name: "1 hypervisor", t.Fatalf("unexpected result\ngot\n%#v\nwant\n%#v", result, resultExpected)
args: args{ }
data: []byte(`{ }
// 1 hypervisor
data := `{
"hypervisors": [ "hypervisors": [
{ {
"cpu_info": { "cpu_info": {
@ -69,78 +77,51 @@ 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",
ID: 2, ID: 2,
Hostname: "host1", Hostname: "host1",
Status: "enabled", Status: "enabled",
State: "up", State: "up",
Type: "fake", Type: "fake",
},
},
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }
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: "", Type: "fake",
args: args{ ID: 5,
port: 9100, State: "enabled",
hvs: []hypervisor{ Status: "up",
{ Hostname: "fakehost",
Type: "fake", HostIP: "1.2.2.2",
ID: 5,
State: "enabled",
Status: "up",
Hostname: "fakehost",
HostIP: "1.2.2.2",
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "1.2.2.2:9100",
"__meta_openstack_hypervisor_host_ip": "1.2.2.2",
"__meta_openstack_hypervisor_hostname": "fakehost",
"__meta_openstack_hypervisor_id": "5",
"__meta_openstack_hypervisor_state": "enabled",
"__meta_openstack_hypervisor_status": "up",
"__meta_openstack_hypervisor_type": "fake",
}),
},
}, },
} }
for _, tt := range tests { labelssExpected := []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addHypervisorLabels(tt.args.hvs, tt.args.port) "__address__": "1.2.2.2:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_openstack_hypervisor_host_ip": "1.2.2.2",
}) "__meta_openstack_hypervisor_hostname": "fakehost",
"__meta_openstack_hypervisor_id": "5",
"__meta_openstack_hypervisor_state": "enabled",
"__meta_openstack_hypervisor_status": "up",
"__meta_openstack_hypervisor_type": "fake",
}),
} }
f(hvs, labelssExpected)
} }

View file

@ -14,28 +14,34 @@ 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"`
HREF string `json:"href"` }
Rel string `json:"rel"`
} `json:"servers_links,omitempty"` type link struct {
HREF string `json:"href"`
Rel string `json:"rel"`
} }
type server struct { type server struct {
ID string `json:"id"` ID string `json:"id"`
TenantID string `json:"tenant_id"` TenantID string `json:"tenant_id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
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"`
Address string `json:"addr"` Metadata map[string]string `json:"metadata,omitempty"`
Version int `json:"version"` Flavor serverFlavor `json:"flavor"`
Type string `json:"OS-EXT-IPS:type"` }
} `json:"addresses"`
Metadata map[string]string `json:"metadata,omitempty"` type serverAddress struct {
Flavor struct { Address string `json:"addr"`
ID string `json:"id"` Version int `json:"version"`
} `json:"flavor"` Type string `json:"OS-EXT-IPS:type"`
}
type serverFlavor struct {
ID string `json:"id"`
} }
func parseServersDetail(data []byte) (*serversDetail, error) { func parseServersDetail(data []byte) (*serversDetail, error) {

View file

@ -8,159 +8,133 @@ 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", ID: "10",
args: args{ Status: "enabled",
port: 9100, Name: "server-1",
HostID: "some-host-id",
TenantID: "some-tenant-id",
UserID: "some-user-id",
Flavor: serverFlavor{
ID: "5",
}, },
}, Addresses: map[string][]serverAddress{
{ "test": {
name: "one_server",
args: args{
port: 9100,
servers: []server{
{ {
ID: "10", Address: "192.168.0.1",
Status: "enabled", Version: 4,
Name: "server-1", Type: "fixed",
HostID: "some-host-id",
TenantID: "some-tenant-id",
UserID: "some-user-id",
Flavor: struct {
ID string `json:"id"`
}{ID: "5"},
Addresses: map[string][]struct {
Address string `json:"addr"`
Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"`
}{
"test": {
{
Address: "192.168.0.1",
Version: 4,
Type: "fixed",
},
},
},
}, },
}, },
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "192.168.0.1:9100",
"__meta_openstack_address_pool": "test",
"__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-1",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "192.168.0.1",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
},
}, },
}
labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "192.168.0.1:9100",
"__meta_openstack_address_pool": "test",
"__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-1",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "192.168.0.1",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
}
f(servers, labelssExpected)
// with public ip
servers = []server{
{ {
name: "with_public_ip", ID: "10",
args: args{ Status: "enabled",
port: 9100, Name: "server-2",
servers: []server{ HostID: "some-host-id",
TenantID: "some-tenant-id",
UserID: "some-user-id",
Flavor: serverFlavor{
ID: "5",
},
Addresses: map[string][]serverAddress{
"test": {
{ {
ID: "10", Address: "192.168.0.1",
Status: "enabled", Version: 4,
Name: "server-2", Type: "fixed",
HostID: "some-host-id", },
TenantID: "some-tenant-id", {
UserID: "some-user-id", Address: "1.5.5.5",
Flavor: struct { Version: 4,
ID string `json:"id"` Type: "floating",
}{ID: "5"}, },
Addresses: map[string][]struct { },
Address string `json:"addr"` "internal": {
Version int `json:"version"` {
Type string `json:"OS-EXT-IPS:type"` Address: "10.10.0.1",
}{ Version: 4,
"test": { Type: "fixed",
{
Address: "192.168.0.1",
Version: 4,
Type: "fixed",
},
{
Address: "1.5.5.5",
Version: 4,
Type: "floating",
},
},
"internal": {
{
Address: "10.10.0.1",
Version: 4,
Type: "fixed",
},
},
},
}, },
}, },
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "10.10.0.1:9100",
"__meta_openstack_address_pool": "internal",
"__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-2",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "10.10.0.1",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
promutils.NewLabelsFromMap(map[string]string{
"__address__": "192.168.0.1:9100",
"__meta_openstack_address_pool": "test",
"__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-2",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "192.168.0.1",
"__meta_openstack_public_ip": "1.5.5.5",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
},
}, },
} }
for _, tt := range tests { labelssExpected = []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addInstanceLabels(tt.args.servers, tt.args.port) "__address__": "10.10.0.1:9100",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_openstack_address_pool": "internal",
}) "__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-2",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "10.10.0.1",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
promutils.NewLabelsFromMap(map[string]string{
"__address__": "192.168.0.1:9100",
"__meta_openstack_address_pool": "test",
"__meta_openstack_instance_flavor": "5",
"__meta_openstack_instance_id": "10",
"__meta_openstack_instance_name": "server-2",
"__meta_openstack_instance_status": "enabled",
"__meta_openstack_private_ip": "192.168.0.1",
"__meta_openstack_public_ip": "1.5.5.5",
"__meta_openstack_project_id": "some-tenant-id",
"__meta_openstack_user_id": "some-user-id",
}),
} }
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)
}
if !reflect.DeepEqual(result, resultExpected) {
t.Fatalf("unexpected result\ngot\n%v\nwant\n%v", result, resultExpected)
}
} }
tests := []struct {
name string // parse ok
args args data := `{
want serversDetail
wantErr bool
}{
{
name: "parse ok",
args: args{
data: []byte(`{
"servers":[ "servers":[
{ {
"id":"c9f68076-01a3-489a-aebe-8b773c71e7f3", "id":"c9f68076-01a3-489a-aebe-8b773c71e7f3",
@ -210,54 +184,36 @@ func Test_parseServersDetail(t *testing.T) {
] ]
} }
] ]
}`), }`
}, resultExpected := &serversDetail{
want: serversDetail{ Servers: []server{
Servers: []server{ {
{ Flavor: serverFlavor{
Flavor: struct { ID: "1",
ID string `json:"id"` },
}{ID: "1"}, ID: "c9f68076-01a3-489a-aebe-8b773c71e7f3",
ID: "c9f68076-01a3-489a-aebe-8b773c71e7f3", TenantID: "d34be4e44f9c444eab9a5ec7b953951f",
TenantID: "d34be4e44f9c444eab9a5ec7b953951f", UserID: "e55737f142ac42f18093037760656bd7",
UserID: "e55737f142ac42f18093037760656bd7", Name: "test10",
Name: "test10", HostID: "e26db8db23736877aa92ebbbe11743b2a2a3b107aada00a8a0cf474b",
HostID: "e26db8db23736877aa92ebbbe11743b2a2a3b107aada00a8a0cf474b", Status: "ACTIVE",
Status: "ACTIVE", Metadata: map[string]string{},
Metadata: map[string]string{}, Addresses: map[string][]serverAddress{
Addresses: map[string][]struct { "test": {
Address string `json:"addr"` {
Version int `json:"version"` Address: "192.168.222.15",
Type string `json:"OS-EXT-IPS:type"` Version: 4,
}{ Type: "fixed",
"test": { },
{ {
Address: "192.168.222.15", Address: "10.20.20.69",
Version: 4, Version: 4,
Type: "fixed", Type: "floating",
},
{
Address: "10.20.20.69",
Version: 4,
Type: "floating",
},
},
}, },
}, },
}, },
}, },
}, },
} }
for _, tt := range tests { f(data, resultExpected)
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)
}
})
}
} }

View file

@ -7,169 +7,157 @@ 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", Name: "server-1",
args: args{}, ID: "test",
FQDN: "server-1.ru-central1.internal",
FolderID: "test",
Status: "RUNNING",
PlatformID: "s2.micro",
Resources: resources{
Cores: "2",
CoreFraction: "20",
Memory: "4",
},
NetworkInterfaces: []networkInterface{
{
Index: "0",
PrimaryV4Address: primaryV4Address{
Address: "192.168.1.1",
},
},
},
}, },
}
labelssExpected := []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
}),
}
f(instances, labelssExpected)
// with public ip
instances = []instance{
{ {
name: "one_server", Name: "server-1",
args: args{ ID: "test",
instances: []instance{ FQDN: "server-1.ru-central1.internal",
{ FolderID: "test",
Name: "server-1", Status: "RUNNING",
ID: "test", PlatformID: "s2.micro",
FQDN: "server-1.ru-central1.internal", Resources: resources{
FolderID: "test", Cores: "2",
Status: "RUNNING", CoreFraction: "20",
PlatformID: "s2.micro", Memory: "4",
Resources: resources{ },
Cores: "2", NetworkInterfaces: []networkInterface{
CoreFraction: "20", {
Memory: "4", Index: "0",
PrimaryV4Address: primaryV4Address{
Address: "192.168.1.1",
OneToOneNat: oneToOneNat{
Address: "1.1.1.1",
}, },
NetworkInterfaces: []networkInterface{ },
{ },
Index: "0", },
PrimaryV4Address: primaryV4Address{ },
Address: "192.168.1.1", }
labelssExpected = []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
"__meta_yandexcloud_instance_public_ip_0": "1.1.1.1",
}),
}
f(instances, labelssExpected)
// with dns record
instances = []instance{
{
Name: "server-1",
ID: "test",
FQDN: "server-1.ru-central1.internal",
FolderID: "test",
Status: "RUNNING",
PlatformID: "s2.micro",
Resources: resources{
Cores: "2",
CoreFraction: "20",
Memory: "4",
},
NetworkInterfaces: []networkInterface{
{
Index: "0",
PrimaryV4Address: primaryV4Address{
Address: "192.168.1.1",
OneToOneNat: oneToOneNat{
Address: "1.1.1.1",
DNSRecords: []dnsRecord{
{
FQDN: "server-1.example.com",
}, },
}, },
}, },
DNSRecords: []dnsRecord{
{
FQDN: "server-1.example.local",
},
},
}, },
}, },
}, },
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
}),
},
},
{
name: "with_public_ip",
args: args{
instances: []instance{
{
Name: "server-1",
ID: "test",
FQDN: "server-1.ru-central1.internal",
FolderID: "test",
Status: "RUNNING",
PlatformID: "s2.micro",
Resources: resources{
Cores: "2",
CoreFraction: "20",
Memory: "4",
},
NetworkInterfaces: []networkInterface{
{
Index: "0",
PrimaryV4Address: primaryV4Address{
Address: "192.168.1.1",
OneToOneNat: oneToOneNat{
Address: "1.1.1.1",
},
},
},
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
"__meta_yandexcloud_instance_public_ip_0": "1.1.1.1",
}),
},
},
{
name: "with_dns_record",
args: args{
instances: []instance{
{
Name: "server-1",
ID: "test",
FQDN: "server-1.ru-central1.internal",
FolderID: "test",
Status: "RUNNING",
PlatformID: "s2.micro",
Resources: resources{
Cores: "2",
CoreFraction: "20",
Memory: "4",
},
NetworkInterfaces: []networkInterface{
{
Index: "0",
PrimaryV4Address: primaryV4Address{
Address: "192.168.1.1",
OneToOneNat: oneToOneNat{
Address: "1.1.1.1",
DNSRecords: []dnsRecord{
{FQDN: "server-1.example.com"},
},
},
DNSRecords: []dnsRecord{
{FQDN: "server-1.example.local"},
},
},
},
},
},
},
},
want: []*promutils.Labels{
promutils.NewLabelsFromMap(map[string]string{
"__address__": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_name": "server-1",
"__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
"__meta_yandexcloud_instance_public_ip_0": "1.1.1.1",
"__meta_yandexcloud_instance_private_dns_0": "server-1.example.local",
"__meta_yandexcloud_instance_public_dns_0": "server-1.example.com",
}),
},
}, },
} }
for _, tt := range tests { labelssExpected = []*promutils.Labels{
t.Run(tt.name, func(t *testing.T) { promutils.NewLabelsFromMap(map[string]string{
got := addInstanceLabels(tt.args.instances) "__address__": "server-1.ru-central1.internal",
discoveryutils.TestEqualLabelss(t, got, tt.want) "__meta_yandexcloud_instance_name": "server-1",
}) "__meta_yandexcloud_instance_fqdn": "server-1.ru-central1.internal",
"__meta_yandexcloud_instance_id": "test",
"__meta_yandexcloud_instance_status": "RUNNING",
"__meta_yandexcloud_instance_platform_id": "s2.micro",
"__meta_yandexcloud_instance_resources_cores": "2",
"__meta_yandexcloud_instance_resources_core_fraction": "20",
"__meta_yandexcloud_instance_resources_memory": "4",
"__meta_yandexcloud_folder_id": "test",
"__meta_yandexcloud_instance_private_ip_0": "192.168.1.1",
"__meta_yandexcloud_instance_public_ip_0": "1.1.1.1",
"__meta_yandexcloud_instance_private_dns_0": "server-1.example.local",
"__meta_yandexcloud_instance_public_dns_0": "server-1.example.com",
}),
} }
f(instances, labelssExpected)
} }

View file

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