2021-06-25 10:20:18 +00:00
|
|
|
package dockerswarm
|
2020-10-12 10:38:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
|
|
|
|
)
|
|
|
|
|
|
|
|
// https://docs.docker.com/engine/api/v1.40/#tag/Service
|
|
|
|
type service struct {
|
|
|
|
ID string
|
|
|
|
Spec struct {
|
|
|
|
Labels map[string]string
|
|
|
|
Name string
|
|
|
|
TaskTemplate struct {
|
|
|
|
ContainerSpec struct {
|
|
|
|
Hostname string
|
|
|
|
Image string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Mode struct {
|
|
|
|
Global interface{}
|
|
|
|
Replicated interface{}
|
|
|
|
}
|
|
|
|
}
|
2020-10-12 13:12:36 +00:00
|
|
|
UpdateStatus struct {
|
2020-10-12 10:38:21 +00:00
|
|
|
State string
|
|
|
|
}
|
|
|
|
Endpoint struct {
|
|
|
|
Ports []portConfig
|
|
|
|
VirtualIPs []struct {
|
|
|
|
NetworkID string
|
|
|
|
Addr string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type portConfig struct {
|
|
|
|
Protocol string
|
|
|
|
Name string
|
|
|
|
PublishMode string
|
|
|
|
PublishedPort int
|
|
|
|
}
|
|
|
|
|
|
|
|
func getServicesLabels(cfg *apiConfig) ([]map[string]string, error) {
|
|
|
|
services, err := getServices(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-25 10:20:18 +00:00
|
|
|
networksLabels, err := getNetworksLabelsByNetworkID(cfg)
|
2020-10-12 10:38:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return addServicesLabels(services, networksLabels, cfg.port), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getServices(cfg *apiConfig) ([]service, error) {
|
2020-11-23 14:26:52 +00:00
|
|
|
data, err := cfg.getAPIResponse("/services")
|
2020-10-12 10:38:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot query dockerswarm api for services: %w", err)
|
|
|
|
}
|
|
|
|
return parseServicesResponse(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseServicesResponse(data []byte) ([]service, error) {
|
|
|
|
var services []service
|
|
|
|
if err := json.Unmarshal(data, &services); err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot parse services: %w", err)
|
|
|
|
}
|
|
|
|
return services, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getServiceMode(svc service) string {
|
|
|
|
if svc.Spec.Mode.Global != nil {
|
|
|
|
return "global"
|
|
|
|
}
|
|
|
|
if svc.Spec.Mode.Replicated != nil {
|
|
|
|
return "replicated"
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-10-12 13:12:36 +00:00
|
|
|
func addServicesLabels(services []service, networksLabels map[string]map[string]string, port int) []map[string]string {
|
2020-10-12 10:38:21 +00:00
|
|
|
var ms []map[string]string
|
|
|
|
for _, service := range services {
|
2020-10-12 13:12:36 +00:00
|
|
|
commonLabels := map[string]string{
|
2020-10-12 10:38:21 +00:00
|
|
|
"__meta_dockerswarm_service_id": service.ID,
|
|
|
|
"__meta_dockerswarm_service_name": service.Spec.Name,
|
2020-10-12 13:12:36 +00:00
|
|
|
"__meta_dockerswarm_service_mode": getServiceMode(service),
|
2020-10-12 10:38:21 +00:00
|
|
|
"__meta_dockerswarm_service_task_container_hostname": service.Spec.TaskTemplate.ContainerSpec.Hostname,
|
|
|
|
"__meta_dockerswarm_service_task_container_image": service.Spec.TaskTemplate.ContainerSpec.Image,
|
2020-10-12 13:12:36 +00:00
|
|
|
"__meta_dockerswarm_service_updating_status": service.UpdateStatus.State,
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
|
|
|
for k, v := range service.Spec.Labels {
|
2020-10-12 13:12:36 +00:00
|
|
|
commonLabels["__meta_dockerswarm_service_label_"+discoveryutils.SanitizeLabelName(k)] = v
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
|
|
|
for _, vip := range service.Endpoint.VirtualIPs {
|
2021-02-04 13:56:42 +00:00
|
|
|
// skip services without virtual address.
|
|
|
|
// usually its host services.
|
|
|
|
if vip.Addr == "" {
|
|
|
|
continue
|
|
|
|
}
|
2020-10-12 10:38:21 +00:00
|
|
|
ip, _, err := net.ParseCIDR(vip.Addr)
|
|
|
|
if err != nil {
|
|
|
|
logger.Errorf("cannot parse: %q as cidr for service label add, err: %v", vip.Addr, err)
|
|
|
|
continue
|
|
|
|
}
|
2020-10-12 13:12:36 +00:00
|
|
|
added := false
|
2020-10-12 10:38:21 +00:00
|
|
|
for _, ep := range service.Endpoint.Ports {
|
|
|
|
if ep.Protocol != "tcp" {
|
|
|
|
continue
|
|
|
|
}
|
2020-10-12 13:12:36 +00:00
|
|
|
m := map[string]string{
|
|
|
|
"__address__": discoveryutils.JoinHostPort(ip.String(), ep.PublishedPort),
|
2020-10-12 10:38:21 +00:00
|
|
|
"__meta_dockerswarm_service_endpoint_port_name": ep.Name,
|
|
|
|
"__meta_dockerswarm_service_endpoint_port_publish_mode": ep.PublishMode,
|
|
|
|
}
|
2020-10-12 13:12:36 +00:00
|
|
|
for k, v := range commonLabels {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range networksLabels[vip.NetworkID] {
|
|
|
|
m[k] = v
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
|
|
|
added = true
|
2020-10-12 13:12:36 +00:00
|
|
|
ms = append(ms, m)
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
|
|
|
if !added {
|
2020-10-12 13:12:36 +00:00
|
|
|
m := map[string]string{
|
|
|
|
"__address__": discoveryutils.JoinHostPort(ip.String(), port),
|
|
|
|
}
|
|
|
|
for k, v := range commonLabels {
|
|
|
|
m[k] = v
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
2020-10-12 13:12:36 +00:00
|
|
|
for k, v := range networksLabels[vip.NetworkID] {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
ms = append(ms, m)
|
2020-10-12 10:38:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ms
|
|
|
|
}
|