mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-20 23:46:23 +01:00
c06e7a142c
Cache sanitized label names and return them next time. This reduces the number of allocations and speeds up the SanitizeLabelName() function for common case when the number of unique label names is smaller than 100k
168 lines
4.5 KiB
Go
168 lines
4.5 KiB
Go
package dockerswarm
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
|
|
)
|
|
|
|
// See https://docs.docker.com/engine/api/v1.40/#tag/Task
|
|
type task struct {
|
|
ID string
|
|
ServiceID string
|
|
NodeID string
|
|
DesiredState string
|
|
NetworksAttachments []struct {
|
|
Addresses []string
|
|
Network struct {
|
|
ID string
|
|
}
|
|
}
|
|
Status struct {
|
|
State string
|
|
ContainerStatus struct {
|
|
ContainerID string
|
|
}
|
|
PortStatus struct {
|
|
Ports []portConfig
|
|
}
|
|
}
|
|
Spec struct {
|
|
ContainerSpec struct {
|
|
Labels map[string]string
|
|
}
|
|
}
|
|
Slot int
|
|
}
|
|
|
|
func getTasksLabels(cfg *apiConfig) ([]map[string]string, error) {
|
|
tasks, err := getTasks(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
services, err := getServices(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
networkLabels, err := getNetworksLabelsByNetworkID(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
svcLabels := addServicesLabels(services, networkLabels, cfg.port)
|
|
nodeLabels, err := getNodesLabels(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return addTasksLabels(tasks, nodeLabels, svcLabels, networkLabels, services, cfg.port), nil
|
|
}
|
|
|
|
func getTasks(cfg *apiConfig) ([]task, error) {
|
|
resp, err := cfg.getAPIResponse("/tasks")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot query dockerswarm api for tasks: %w", err)
|
|
}
|
|
return parseTasks(resp)
|
|
}
|
|
|
|
func parseTasks(data []byte) ([]task, error) {
|
|
var tasks []task
|
|
if err := json.Unmarshal(data, &tasks); err != nil {
|
|
return nil, fmt.Errorf("cannot parse tasks: %w", err)
|
|
}
|
|
return tasks, nil
|
|
}
|
|
|
|
func addTasksLabels(tasks []task, nodesLabels, servicesLabels []map[string]string, networksLabels map[string]map[string]string, services []service, port int) []map[string]string {
|
|
var ms []map[string]string
|
|
for _, task := range tasks {
|
|
commonLabels := map[string]string{
|
|
"__meta_dockerswarm_task_id": task.ID,
|
|
"__meta_dockerswarm_task_container_id": task.Status.ContainerStatus.ContainerID,
|
|
"__meta_dockerswarm_task_desired_state": task.DesiredState,
|
|
"__meta_dockerswarm_task_slot": strconv.Itoa(task.Slot),
|
|
"__meta_dockerswarm_task_state": task.Status.State,
|
|
}
|
|
for k, v := range task.Spec.ContainerSpec.Labels {
|
|
commonLabels[discoveryutils.SanitizeLabelName("__meta_dockerswarm_container_label_"+k)] = v
|
|
}
|
|
var svcPorts []portConfig
|
|
for i, v := range services {
|
|
if v.ID == task.ServiceID {
|
|
svcPorts = services[i].Endpoint.Ports
|
|
break
|
|
}
|
|
}
|
|
addLabels(commonLabels, servicesLabels, "__meta_dockerswarm_service_id", task.ServiceID)
|
|
addLabels(commonLabels, nodesLabels, "__meta_dockerswarm_node_id", task.NodeID)
|
|
|
|
for _, port := range task.Status.PortStatus.Ports {
|
|
if port.Protocol != "tcp" {
|
|
continue
|
|
}
|
|
m := make(map[string]string, len(commonLabels)+2)
|
|
for k, v := range commonLabels {
|
|
m[k] = v
|
|
}
|
|
m["__address__"] = discoveryutils.JoinHostPort(commonLabels["__meta_dockerswarm_node_address"], port.PublishedPort)
|
|
m["__meta_dockerswarm_task_port_publish_mode"] = port.PublishMode
|
|
ms = append(ms, m)
|
|
}
|
|
for _, na := range task.NetworksAttachments {
|
|
networkLabels := networksLabels[na.Network.ID]
|
|
for _, address := range na.Addresses {
|
|
ip, _, err := net.ParseCIDR(address)
|
|
if err != nil {
|
|
logger.Errorf("cannot parse task network attachments address: %s as net CIDR: %v", address, err)
|
|
continue
|
|
}
|
|
added := false
|
|
for _, ep := range svcPorts {
|
|
if ep.Protocol != "tcp" {
|
|
continue
|
|
}
|
|
m := make(map[string]string, len(commonLabels)+len(networkLabels)+2)
|
|
for k, v := range commonLabels {
|
|
m[k] = v
|
|
}
|
|
for k, v := range networkLabels {
|
|
m[k] = v
|
|
}
|
|
m["__address__"] = discoveryutils.JoinHostPort(ip.String(), ep.PublishedPort)
|
|
m["__meta_dockerswarm_task_port_publish_mode"] = ep.PublishMode
|
|
ms = append(ms, m)
|
|
added = true
|
|
}
|
|
if !added {
|
|
m := make(map[string]string, len(commonLabels)+len(networkLabels)+1)
|
|
for k, v := range commonLabels {
|
|
m[k] = v
|
|
}
|
|
for k, v := range networkLabels {
|
|
m[k] = v
|
|
}
|
|
m["__address__"] = discoveryutils.JoinHostPort(ip.String(), port)
|
|
ms = append(ms, m)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ms
|
|
}
|
|
|
|
// addLabels adds lables from src to dst if they contain the given `key: value` pair.
|
|
func addLabels(dst map[string]string, src []map[string]string, key, value string) {
|
|
for _, m := range src {
|
|
if m[key] != value {
|
|
continue
|
|
}
|
|
for k, v := range m {
|
|
dst[k] = v
|
|
}
|
|
return
|
|
}
|
|
}
|