-
Notifications
You must be signed in to change notification settings - Fork 126
/
main.go
187 lines (161 loc) · 4.79 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types/swarm"
"github.com/fsouza/go-dockerclient"
"log"
"os"
"strings"
"sync"
"time"
)
var filters = map[string][]string{"desired-state": {"running"}}
var networkID = ""
var networkName = ""
var ignoredServices = make([]string, 0)
func init() {
networkName = *flag.String("network", "my_network", "Specify the name of the network you want to scrape")
ignoredServicesStr := *flag.String("ignoredServices", "prometheus", "Comma-separated list of service names we do not want to scrape")
// Stuff any services we don't want to scrape (such as ourselves and prometheus server)
// into a slice.
parseIgnoredServices(ignoredServicesStr)
}
func main() {
logrus.Println("Starting Swarm-scraper!")
// Connect to the Docker API
endpoint := "unix:///var/run/docker.sock"
dockerClient, err := docker.NewClient(endpoint)
if err != nil {
panic(err)
}
// Find the networkID we want to address tasks on.
findNetworkId(dockerClient, networkName)
// Start the task poller
go func(dockerClient *docker.Client) {
for {
time.Sleep(time.Second * 15)
pollTasks(dockerClient)
}
}(dockerClient)
// Block...
log.Println("Waiting at block...")
wg := sync.WaitGroup{} // Use a WaitGroup to block main() exit
wg.Add(1)
wg.Wait()
}
func findNetworkId(dockerClient *docker.Client, networkName string) {
networks, _ := dockerClient.ListNetworks()
for _, val := range networks {
if val.Name == networkName {
networkID = val.ID
return
}
}
logrus.Errorf("Could not find NetworkID of %v, will assume 'ingress'\n", networkName)
for _, val := range networks {
if val.Name == "ingress" {
networkID = val.ID
return
}
}
panic("Could neither resolve network " + networkName + " nor ingress network, panic!")
}
func parseIgnoredServices(ignoredServicesStr string) {
if strings.Contains(ignoredServicesStr, ",") {
copy(ignoredServices, strings.Split(ignoredServicesStr, ","))
} else {
ignoredServices = append(ignoredServices, ignoredServicesStr)
}
logrus.Printf("Ignored services: %v\n", ignoredServices)
}
func pollTasks(client *docker.Client) {
tasks, _ := client.ListTasks(docker.ListTasksOptions{Filters: filters})
tasksMap := make(map[string]*ScrapedTask)
for _, task := range tasks {
// Lookup service
service, _ := client.InspectService(task.ServiceID)
// Skip if service is in ignoredList, e.g. don't scrape prometheus...
if isInIgnoredList(service.Spec.Name) {
continue
}
portNumber := "-1"
// Find HTTP port?
for _, port := range service.Endpoint.Ports {
if port.Protocol == "tcp" {
portNumber = fmt.Sprint(port.PublishedPort)
}
}
// Skip if no exposed tcp port
if portNumber == "-1" {
continue
}
// Iterate network attachments on task
for _, netw := range task.NetworksAttachments {
// Only extract IP if on expected network.
if netw.Network.ID == networkID {
if taskEntry, ok := tasksMap[task.ServiceID]; ok {
processExistingTask(taskEntry, netw, portNumber, service)
} else {
processNewTask(netw, portNumber, service, tasksMap)
}
}
}
}
// Transform values of map into slice.
taskList := make([]ScrapedTask, 0)
for _, value := range tasksMap {
taskList = append(taskList, *value)
}
// Write config file
bytes, err := json.Marshal(taskList)
if err != nil {
panic(err)
}
file, err := os.Create("/etc/swarm-endpoints/swarm-endpoints.json")
if err != nil {
logrus.Errorf("Error writing file: %v\n", err.Error())
panic(err.Error())
}
file.Write(bytes)
file.Close()
}
func processNewTask(netw swarm.NetworkAttachment, portNumber string, service *swarm.Service, tasksMap map[string]*ScrapedTask) {
// New task
taskEntry := ScrapedTask{Targets: make([]string, 0), Labels: make(map[string]string)}
for _, adr := range netw.Addresses {
taskEntry.Targets = append(taskEntry.Targets, formatIp(adr, portNumber))
}
taskEntry.Labels["task"] = service.Spec.Name
tasksMap[service.ID] = &taskEntry
}
func processExistingTask(taskEntry *ScrapedTask, netw swarm.NetworkAttachment, portNumber string, service *swarm.Service) {
// Existing task
localTargets := make([]string, len(taskEntry.Targets))
copy(localTargets, taskEntry.Targets)
for _, adr := range netw.Addresses {
localTargets = append(localTargets, formatIp(adr, portNumber))
}
taskEntry.Targets = localTargets
taskEntry.Labels["task"] = service.Spec.Name
}
func isInIgnoredList(s string) bool {
for _, ignored := range ignoredServices {
if ignored == s {
return true
}
}
return false
}
func formatIp(ip string, port string) string {
// Remove /NN part of ip
index := strings.Index(ip, "/")
ip = ip[:index] + ":" + port
return ip
}
type ScrapedTask struct {
Targets []string `json:"targets"`
Labels map[string]string `json:"labels"`
}