first commit
This commit is contained in:
commit
0d7a8e706d
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
selenium-server-standalone-*.jar
|
||||
callgraph
|
||||
/.direnv
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Get microservice callgraph
|
||||
|
||||
$ go build .
|
||||
$ wget http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.0.jar
|
||||
$ java -jar selenium-server-standalone-2.53.0.jar
|
||||
$ ./callgraph http://sharelatex.local/login 192.168.8.33 192.168.8.34 192.168.8.35 192.168.8.36 192.168.8.37 192.168.8.38 192.168.8.17 192.168.8.18 192.168.8.19 | dot -Tpng > graph.png
|
98
capture-connections.lua
Normal file
98
capture-connections.lua
Normal file
@ -0,0 +1,98 @@
|
||||
description = "description";
|
||||
short_description = "desc";
|
||||
category = "Net";
|
||||
|
||||
args = {}
|
||||
|
||||
require "common"
|
||||
|
||||
local fields = {}
|
||||
function on_init()
|
||||
local mapping = {
|
||||
isread = "evt.is_io_read",
|
||||
buflen = "evt.buflen",
|
||||
proc = "proc.name",
|
||||
pid = "proc.pid",
|
||||
tid = "thread.tid",
|
||||
container = "container.name",
|
||||
sip = "fd.sip",
|
||||
sport = "fd.sport",
|
||||
cip = "fd.cip",
|
||||
cport = "fd.cport",
|
||||
evt_type = "evt.type",
|
||||
proto = "fd.l4proto",
|
||||
lip = "fd.lip",
|
||||
}
|
||||
for k,v in pairs(mapping) do
|
||||
fields[k] = chisel.request_field(v)
|
||||
end
|
||||
key_fields = {fields.container, fields.proc, fields.pid, fields.tid, fields.proto, fields.sip, fields.sport, fields.cip, fields.cport}
|
||||
|
||||
sysdig.set_snaplen(0)
|
||||
chisel.set_filter("evt.is_io=true and (fd.type=ipv4 or fd.type=ipv6) and fd.rip exists and fd.lip exists and container.name!=host")
|
||||
return true
|
||||
end
|
||||
|
||||
local stats = {}
|
||||
|
||||
local DEBUG = false
|
||||
|
||||
function on_event()
|
||||
-- only capture connections of servers
|
||||
local sip = evt.field(fields.sip)
|
||||
if not (evt.field(fields.lip) == sip or evt.field(fields.rip) == sip) then
|
||||
return true
|
||||
end
|
||||
|
||||
local dir
|
||||
if evt.field(fields.isread) then
|
||||
dir = "rx"
|
||||
else
|
||||
dir = "tx"
|
||||
end
|
||||
|
||||
if DEBUG then
|
||||
function to_s(v)
|
||||
return (evt.field(v) or "nil").." "
|
||||
end
|
||||
io.write("DEBUG: ",
|
||||
to_s(fields.container),
|
||||
to_s(fields.proc),
|
||||
to_s(fields.pid),
|
||||
to_s(fields.tid),
|
||||
to_s(fields.proto),
|
||||
to_s(fields.sip),
|
||||
to_s(fields.sport),
|
||||
to_s(fields.cip),
|
||||
to_s(fields.cport),
|
||||
to_s(fields.lip),
|
||||
dir, "\n")
|
||||
end
|
||||
|
||||
local t = { }
|
||||
for k,v in ipairs(key_fields) do
|
||||
t[#t+1] = tostring(evt.field(v))
|
||||
end
|
||||
|
||||
t[#t+1] = dir
|
||||
local key = table.concat(t, "\t")
|
||||
|
||||
stats[key] = (stats[key] or 0) + (evt.field(fields.buflen) or 0)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function string.starts(string, prefix)
|
||||
return string.sub(string, 1, string.len(start)) == prefix
|
||||
end
|
||||
|
||||
function on_capture_start()
|
||||
hostname = sysdig.get_machine_info().hostname
|
||||
return true
|
||||
end
|
||||
|
||||
function on_capture_end()
|
||||
for k, v in pairs(stats) do
|
||||
io.write(hostname, "\t", k, "\t", v, "\n")
|
||||
end
|
||||
end
|
86
captured-connections.tsv
Normal file
86
captured-connections.tsv
Normal file
@ -0,0 +1,86 @@
|
||||
macmini4 rancher-agent python 32703 32703 tcp 192.168.8.17 8080 192.168.8.34 59245 rx 3176
|
||||
macmini4 rancher-agent host-api 526 619 tcp 192.168.8.17 8080 192.168.8.34 59244 rx 12
|
||||
macmini4 rancher-agent python 32703 32703 tcp 192.168.8.17 8080 192.168.8.34 59247 tx 3304
|
||||
macmini4 rancher-agent host-api 526 526 tcp 192.168.8.17 8080 192.168.8.34 59244 tx 6
|
||||
macmini4 rancher-agent python 32703 32703 tcp 192.168.8.17 8080 192.168.8.34 59247 rx 1044
|
||||
macmini4 rancher-agent host-api 526 531 tcp 192.168.8.17 8080 192.168.8.34 59244 tx 6
|
||||
macmini4 rancher-agent host-api 526 528 tcp 192.168.8.17 8080 192.168.8.34 59244 tx 6
|
||||
macmini4 rancher-agent host-api 526 619 tcp 192.168.8.17 8080 192.168.8.34 59244 tx 18
|
||||
macmini6 rancher-agent host-api 1034 1034 tcp 192.168.8.17 8080 192.168.8.36 40729 tx 6
|
||||
macmini6 rancher-agent python 753 753 tcp 192.168.8.17 8080 192.168.8.36 40732 tx 3299
|
||||
macmini6 rancher-agent host-api 1034 1037 tcp 192.168.8.17 8080 192.168.8.36 40729 tx 6
|
||||
macmini6 rancher-agent host-api 1034 1041 tcp 192.168.8.17 8080 192.168.8.36 40729 tx 12
|
||||
macmini6 rancher-agent host-api 1034 1039 tcp 192.168.8.17 8080 192.168.8.36 40729 tx 24
|
||||
macmini6 rancher-agent host-api 1034 1039 tcp 192.168.8.17 8080 192.168.8.36 40729 rx 16
|
||||
macmini6 rancher-agent python 753 753 tcp 192.168.8.17 8080 192.168.8.36 40730 rx 2795
|
||||
macmini6 rancher-agent python 753 753 tcp 192.168.8.17 8080 192.168.8.36 40732 rx 1044
|
||||
macmini3 rancher-agent host-api 31449 31449 tcp 192.168.8.17 8080 192.168.8.33 52136 tx 24
|
||||
macmini3 rancher-agent host-api 31449 31454 tcp 192.168.8.17 8080 192.168.8.33 52136 tx 6
|
||||
macmini3 rancher-agent host-api 31449 31506 tcp 192.168.8.17 8080 192.168.8.33 52136 tx 12
|
||||
macmini3 rancher-agent host-api 31449 31455 tcp 192.168.8.17 8080 192.168.8.33 52136 tx 6
|
||||
macmini3 rancher-agent python 31167 31167 tcp 192.168.8.17 8080 192.168.8.33 52133 rx 3175
|
||||
macmini3 rancher-agent python 31167 31167 tcp 192.168.8.17 8080 192.168.8.33 52138 tx 3295
|
||||
macmini3 rancher-agent host-api 31449 31449 tcp 192.168.8.17 8080 192.168.8.33 52136 rx 16
|
||||
macmini3 rancher-agent python 31167 31167 tcp 192.168.8.17 8080 192.168.8.33 52138 rx 1044
|
||||
macmini5 rancher-agent host-api 404 412 tcp 192.168.8.17 8080 192.168.8.35 50962 rx 16
|
||||
macmini5 rancher-agent host-api 404 418 tcp 192.168.8.17 8080 192.168.8.35 50962 tx 6
|
||||
macmini5 rancher-agent host-api 404 412 tcp 192.168.8.17 8080 192.168.8.35 50962 tx 24
|
||||
macmini5 rancher-agent python 32581 32581 tcp 192.168.8.17 8080 192.168.8.35 50964 tx 3299
|
||||
macmini5 rancher-agent python 32581 32581 tcp 192.168.8.17 8080 192.168.8.35 50964 rx 1040
|
||||
macmini5 rancher-agent python 32581 32581 tcp 192.168.8.17 8080 192.168.8.35 50958 rx 2794
|
||||
macmini5 rancher-agent host-api 404 417 tcp 192.168.8.17 8080 192.168.8.35 50962 tx 18
|
||||
macmini7 rancher-agent python 5674 5674 tcp 192.168.8.17 8080 192.168.8.37 35624 tx 3296
|
||||
macmini7 rancher-agent host-api 5963 5965 tcp 192.168.8.17 8080 192.168.8.37 35618 tx 12
|
||||
macmini7 rancher-agent python 5674 5674 tcp 192.168.8.17 8080 192.168.8.37 35624 rx 1044
|
||||
macmini7 rancher-agent host-api 5963 5963 tcp 192.168.8.17 8080 192.168.8.37 35618 tx 12
|
||||
macmini7 rancher-agent host-api 5963 6026 tcp 192.168.8.17 8080 192.168.8.37 35618 rx 16
|
||||
macmini7 rancher-agent host-api 5963 6026 tcp 192.168.8.17 8080 192.168.8.37 35618 tx 24
|
||||
macmini7 rancher-agent python 5674 5674 tcp 192.168.8.17 8080 192.168.8.37 35619 rx 2795
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60579 tx 493799
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16401 tcp 172.17.0.2 8080 192.168.8.17 50504 rx 553
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60574 tx 800550
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16402 tcp 172.17.0.2 8080 192.168.8.17 50500 tx 2
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16402 tcp 172.17.0.2 8080 192.168.8.17 42695 tx 381
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60580 rx 18573
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16398 tcp 172.17.0.2 8080 192.168.8.17 42695 tx 381
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16398 tcp 172.17.0.2 8080 192.168.8.17 50500 rx 12
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16401 tcp 172.17.0.2 8080 192.168.8.17 50500 tx 4
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60595 tx 311083
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16400 tcp 172.17.0.2 8080 192.168.8.17 42695 tx 381
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60579 rx 23733
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60574 rx 32808
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16403 tcp 172.17.0.2 8080 192.168.8.17 50500 rx 6
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16403 tcp 172.17.0.2 8080 192.168.8.17 42695 tx 381
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60872 tx 1882
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16400 tcp 172.17.0.2 8080 192.168.8.17 50500 tx 2
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60872 rx 920
|
||||
slfy79 7461f018-c62e-4b02-9e1b-af6547e9a1e2 charon 13331 13335 udp 192.168.8.18 4500 192.168.8.17 4500 tx 1
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16400 tcp 172.17.0.2 8080 192.168.8.17 50504 tx 522
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16403 tcp 172.17.0.2 8080 192.168.8.17 50504 rx 8573
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60578 rx 30589
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60580 tx 265880
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60530 tx 6328
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16398 tcp 172.17.0.2 8080 192.168.8.17 50495 tx 1171
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16398 tcp 172.17.0.2 8080 192.168.8.17 50500 tx 8
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16402 tcp 172.17.0.2 8080 192.168.8.17 50500 rx 12
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60595 rx 21729
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16403 tcp 172.17.0.2 8080 192.168.8.17 50504 tx 522
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60530 rx 102
|
||||
slfy79 7461f018-c62e-4b02-9e1b-af6547e9a1e2 charon 13331 13335 udp 192.168.8.19 4500 192.168.8.17 4500 tx 2
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16403 tcp 172.17.0.2 8080 192.168.8.17 50495 tx 408
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16398 tcp 172.17.0.2 8080 192.168.8.17 50504 rx 1108
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60592 tx 524911
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16401 tcp 172.17.0.2 8080 192.168.8.17 50500 rx 12
|
||||
slfy79 rancher-agent haproxy 21608 21608 tcp 172.17.0.11 80 192.168.8.17 60592 rx 24020
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16400 tcp 172.17.0.2 8080 192.168.8.17 50500 rx 6
|
||||
slfy79 rangerserver-v1 websocket-proxy 16398 16402 tcp 172.17.0.2 8080 192.168.8.17 50495 tx 1596
|
||||
slfy85 rancher-agent host-api 26530 26536 tcp 192.168.8.17 8080 192.168.8.19 52183 rx 16
|
||||
slfy85 rancher-agent host-api 26530 26550 tcp 192.168.8.17 8080 192.168.8.19 52183 tx 12
|
||||
slfy85 rancher-agent python 26248 26248 tcp 192.168.8.17 8080 192.168.8.19 52185 tx 9748
|
||||
slfy85 rancher-agent python 26248 26248 tcp 192.168.8.17 8080 192.168.8.19 52179 rx 19204
|
||||
slfy85 rancher-agent host-api 26530 26544 tcp 192.168.8.17 8080 192.168.8.19 52183 tx 6
|
||||
slfy85 9d011c95-bfcd-4880-adc4-2215175de5da charon 3236 3242 udp 192.168.8.17 4500 192.168.8.19 4500 tx 2
|
||||
slfy85 rancher-agent python 26248 26248 tcp 192.168.8.17 8080 192.168.8.19 52185 rx 1044
|
||||
slfy85 rancher-agent host-api 26530 26535 tcp 192.168.8.17 8080 192.168.8.19 52183 tx 6
|
||||
slfy85 rancher-agent host-api 26530 26536 tcp 192.168.8.17 8080 192.168.8.19 52183 tx 24
|
||||
slfy85 9d011c95-bfcd-4880-adc4-2215175de5da charon 3236 3242 udp 192.168.8.18 4500 192.168.8.19 4500 tx 2
|
|
122
main.go
Normal file
122
main.go
Normal file
@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mic92/callgraph/rancher"
|
||||
"github.com/mic92/callgraph/sharelatex"
|
||||
"github.com/mic92/callgraph/sysdig"
|
||||
)
|
||||
|
||||
var (
|
||||
rancherHost = flag.String("rancher-host", "localhost:8080", "host of rancher server")
|
||||
rancherAccessKey = flag.String("rancher-access-key", os.Getenv("RANCHER_ACCESS_KEY"), "api access key")
|
||||
rancherSecretKey = flag.String("rancher-secret-key", os.Getenv("RANCHER_SECRET_KEY"), "api secret key")
|
||||
)
|
||||
|
||||
func parseArgs() (sharelatexUrl string, hosts []string) {
|
||||
flag.Parse()
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintf(os.Stderr, "USAGE: %s SHARELATEXURL HOSTS...", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
if *rancherAccessKey == "" {
|
||||
fmt.Fprintf(os.Stderr, "Set RANCHER_ACCESS_KEY environment variable")
|
||||
os.Exit(1)
|
||||
}
|
||||
if *rancherSecretKey == "" {
|
||||
fmt.Fprintf(os.Stderr, "Set RANCHER_SECRET_KEY environment variable")
|
||||
os.Exit(1)
|
||||
}
|
||||
return os.Args[1], os.Args[2:]
|
||||
}
|
||||
func rancherServices() ([]sysdig.Service, error) {
|
||||
c := rancher.New(*rancherHost, *rancherAccessKey, *rancherSecretKey)
|
||||
var api rancher.Api
|
||||
err := c.Get(fmt.Sprintf("http://%s/v1", c.Host), &api)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to request rancher api: %v", err)
|
||||
}
|
||||
if api.Links == nil {
|
||||
return nil, fmt.Errorf("no links object found in api response")
|
||||
}
|
||||
val, ok := api.Links["containers"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no container link found in api response")
|
||||
}
|
||||
var containers rancher.Containers
|
||||
err = c.Get(val, &containers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get container list from api: %s", err)
|
||||
}
|
||||
serviceMap := make(map[string][]net.IP)
|
||||
for _, container := range containers.Data {
|
||||
var name string
|
||||
if container.Labels.ProjectServiceName == "" {
|
||||
name = container.Name
|
||||
} else {
|
||||
name = container.Labels.ProjectServiceName
|
||||
}
|
||||
ip := net.ParseIP(container.PrimaryIpAddress)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("Got invalid IP from api for '%s' container: %s", container.Name, ip)
|
||||
}
|
||||
serviceMap[name] = append(serviceMap[name], ip)
|
||||
}
|
||||
var services []sysdig.Service
|
||||
for name, ips := range serviceMap {
|
||||
services = append(services, sysdig.Service{name, ips})
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func die(message string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, message, args...)
|
||||
fmt.Fprint(os.Stderr, "\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
url, hosts := parseArgs()
|
||||
|
||||
cmd, err := sysdig.Start(hosts)
|
||||
if err != nil {
|
||||
die("failed to start sysdig: %v", err)
|
||||
}
|
||||
time.Sleep(4 * time.Second)
|
||||
err = sharelatex.IntegrationTest(url)
|
||||
if err != nil {
|
||||
die("selenium not running?: %v", err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
cmd.SendStopSignal()
|
||||
|
||||
var records []sysdig.Record
|
||||
for range hosts {
|
||||
select {
|
||||
case result := <-cmd.Results:
|
||||
if exitError, ok := result.Error.(*exec.ExitError); ok {
|
||||
waitStatus := exitError.Sys().(syscall.WaitStatus)
|
||||
if waitStatus != 130 { // Interrupt
|
||||
fmt.Printf("[%s] sysdig failed with %v\n", result.Host.Name, result.Error, result.Output)
|
||||
}
|
||||
}
|
||||
r, err := sysdig.ParseOutput(result.Output)
|
||||
if err != nil {
|
||||
die("Error parsing results from %s", result.Host.Name)
|
||||
}
|
||||
records = append(records, r...)
|
||||
}
|
||||
}
|
||||
services, err := rancherServices()
|
||||
if err != nil {
|
||||
die("getting rancher services failed: %s", err)
|
||||
}
|
||||
(sysdig.Records(records)).PrintGraph(services)
|
||||
}
|
34
rancher/client.go
Normal file
34
rancher/client.go
Normal file
@ -0,0 +1,34 @@
|
||||
package rancher
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Host, SecretKey, AccessKey string
|
||||
client http.Client
|
||||
}
|
||||
|
||||
func New(host, secretkey, accesskey string) Client {
|
||||
return Client{host, secretkey, accesskey, http.Client{}}
|
||||
}
|
||||
|
||||
func (c *Client) Get(url string, result interface{}) error {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.SetBasicAuth(c.AccessKey, c.SecretKey)
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
json.Unmarshal(body, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
18
rancher/types.go
Normal file
18
rancher/types.go
Normal file
@ -0,0 +1,18 @@
|
||||
package rancher
|
||||
|
||||
type Api struct {
|
||||
Links map[string]string `json:"links"`
|
||||
Data []interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type Containers struct {
|
||||
Data []Container `json:"data"`
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Name string `json:"name"`
|
||||
PrimaryIpAddress string `json:"primaryIpAddress"`
|
||||
Labels struct {
|
||||
ProjectServiceName string `json:"io.rancher.project_service.name"`
|
||||
}
|
||||
}
|
82
sharelatex/client.go
Normal file
82
sharelatex/client.go
Normal file
@ -0,0 +1,82 @@
|
||||
package sharelatex
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
func IntegrationTest(url string) error {
|
||||
_, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to get '%s': %s", url, err)
|
||||
}
|
||||
caps := selenium.Capabilities{"browserName": "firefox"}
|
||||
wd, err := selenium.NewRemote(caps, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wd.Quit()
|
||||
|
||||
// Get simple playground interface
|
||||
wd.Get(url)
|
||||
|
||||
e, _ := wd.FindElement(selenium.ByXPATH, "//input[@type='email']")
|
||||
e.Clear()
|
||||
e.SendKeys("joerg@higgsboson.tk")
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByXPATH, "//input[@type='password']")
|
||||
e.Clear()
|
||||
e.SendKeys("password")
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByXPATH, "//button[@type='submit']")
|
||||
e.Click()
|
||||
|
||||
for {
|
||||
_, err = wd.FindElement(selenium.ByLinkText, "Account")
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 4000)
|
||||
e, _ = wd.FindElement(selenium.ByLinkText, "New Project")
|
||||
e.Click()
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByLinkText, "Blank Project")
|
||||
e.Click()
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByXPATH, "//input[@type='text']")
|
||||
e.Clear()
|
||||
e.SendKeys(fmt.Sprintf("p %d", rand.Int63()))
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByCSSSelector, "button.btn-primary") // Create
|
||||
e.Click()
|
||||
time.Sleep(time.Millisecond * 3000)
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByCSSSelector, `i.fa-comment`)
|
||||
e.Click()
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByCSSSelector, `div.new-message textarea`)
|
||||
e.SendKeys("hello world" + selenium.EnterKey)
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByCSSSelector, "i.fa.fa-upload") // Upload
|
||||
e.Click()
|
||||
|
||||
e, _ = wd.FindElement(selenium.ByName, "file") // Create
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
e.SendKeys(filepath.Join(dir, "./test.txt"))
|
||||
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
e, _ = wd.FindElement(selenium.ByCSSSelector, "textarea") // Latex Doc
|
||||
e.Clear()
|
||||
e.SendKeys(`\documentclass{article} \begin{document} ello \end{document}`)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
return nil
|
||||
}
|
5
sysdig.sh
Normal file
5
sysdig.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash -e
|
||||
t="$(mktemp)"; f() {
|
||||
rm "$t"
|
||||
}
|
||||
trap f EXIT; cat > "$t"; sudo sysdig -c "$t"
|
41
sysdig/graph.go
Normal file
41
sysdig/graph.go
Normal file
@ -0,0 +1,41 @@
|
||||
package sysdig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Records []Record
|
||||
|
||||
type Service struct {
|
||||
Name string
|
||||
Ips []net.IP
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
func (records Records) PrintGraph(services []Service) {
|
||||
serviceMap := make(map[string]string)
|
||||
for _, service := range services {
|
||||
for _, ip := range service.Ips {
|
||||
serviceMap[ip.String()] = service.Name
|
||||
}
|
||||
}
|
||||
|
||||
connections := make(map[string]Connection)
|
||||
for _, r := range records {
|
||||
if clientService, ok := serviceMap[r.ClientIp.String()]; ok {
|
||||
if serverService, ok := serviceMap[r.ServerIp.String()]; ok {
|
||||
connections[clientService+":"+serverService] = Connection{clientService, serverService}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("digraph G {\n")
|
||||
for _, connection := range connections {
|
||||
fmt.Printf("\"%s\" -> \"%s\"\n", connection.From, connection.To)
|
||||
}
|
||||
fmt.Printf("}\n")
|
||||
}
|
62
sysdig/graph_test.go
Normal file
62
sysdig/graph_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
package sysdig_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/mic92/callgraph/sysdig"
|
||||
)
|
||||
|
||||
func assert(tb testing.TB, condition bool, msg string, v ...interface{}) {
|
||||
if !condition {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func ok(tb testing.TB, err error) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func equals(tb testing.TB, exp, act interface{}) {
|
||||
if !reflect.DeepEqual(exp, act) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act)
|
||||
tb.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
cwd_arg = flag.String("cwd", "", "set cwd")
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Parse()
|
||||
if *cwd_arg != "" {
|
||||
if err := os.Chdir(*cwd_arg); err != nil {
|
||||
fmt.Println("Chdir error:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintGraph(t *testing.T) {
|
||||
f, e := os.Open("./captured-connections.tsv")
|
||||
ok(t, e)
|
||||
c, e := ioutil.ReadAll(f)
|
||||
ok(t, e)
|
||||
_, e = sysdig.ParseOutput(string(c))
|
||||
ok(t, e)
|
||||
//(sysdig.Records(r)).PrintGraph()
|
||||
//t.Fatal()
|
||||
}
|
97
sysdig/records.go
Normal file
97
sysdig/records.go
Normal file
@ -0,0 +1,97 @@
|
||||
package sysdig
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
RX = iota
|
||||
TX
|
||||
)
|
||||
|
||||
type Record struct {
|
||||
Host, Container, Process string
|
||||
Pid, Tid uint64
|
||||
Proto string
|
||||
ServerIp net.IP
|
||||
ServerPort uint16
|
||||
ClientIp net.IP
|
||||
ClientPort uint16
|
||||
Direction int
|
||||
Bytes uint64
|
||||
}
|
||||
|
||||
func ParseOutput(result string) ([]Record, error) {
|
||||
r := csv.NewReader(strings.NewReader(result))
|
||||
r.Comma = '\t'
|
||||
|
||||
var records []Record
|
||||
for {
|
||||
record, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if record[0] == "host" {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.ParseUint(record[3], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid pid %s: %v", record[2], err)
|
||||
}
|
||||
tid, err := strconv.ParseUint(record[4], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid tid %s: %v", record[3], err)
|
||||
}
|
||||
sip := net.ParseIP(record[6])
|
||||
if sip == nil {
|
||||
return nil, fmt.Errorf("invalid ip '%s'", record[6])
|
||||
}
|
||||
sport, err := strconv.ParseUint(record[7], 10, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port %s: %v", record[7], err)
|
||||
}
|
||||
cip := net.ParseIP(record[8])
|
||||
if cip == nil {
|
||||
return nil, fmt.Errorf("invalid ip '%s'", record[8])
|
||||
}
|
||||
cport, err := strconv.ParseUint(record[9], 10, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port %s: %v", record[9], err)
|
||||
}
|
||||
var direction int
|
||||
if record[10] == "rx" {
|
||||
direction = RX
|
||||
} else {
|
||||
direction = TX
|
||||
}
|
||||
|
||||
bytes, err := strconv.ParseUint(record[11], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid transmission size %s: %v", record[11], err)
|
||||
}
|
||||
records = append(records, Record{
|
||||
record[0],
|
||||
record[1],
|
||||
record[2],
|
||||
pid,
|
||||
tid,
|
||||
record[5],
|
||||
sip,
|
||||
uint16(sport),
|
||||
cip,
|
||||
uint16(cport),
|
||||
direction,
|
||||
bytes,
|
||||
})
|
||||
}
|
||||
return records, nil
|
||||
}
|
42
sysdig/start.go
Normal file
42
sysdig/start.go
Normal file
@ -0,0 +1,42 @@
|
||||
package sysdig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mic92/clusterssh"
|
||||
)
|
||||
|
||||
func Start(hostnames []string) (*clusterssh.Command, error) {
|
||||
hosts := make([]clusterssh.Host, len(hostnames))
|
||||
for i, arg := range hostnames {
|
||||
host, err := clusterssh.ParseHost(arg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid host '%s': %v", arg, err)
|
||||
}
|
||||
hosts[i] = *host
|
||||
}
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
f1, err := os.Open(filepath.Join(dir, "capture-connections.lua"))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open sysdig plugin")
|
||||
}
|
||||
sysdigPlugin, err := ioutil.ReadAll(f1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read sysdig plugin: %v", err)
|
||||
}
|
||||
f2, err := os.Open(filepath.Join(dir, "sysdig.sh"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open sysdig plugin: %v", err)
|
||||
}
|
||||
sysdigCommand, err := ioutil.ReadAll(f2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read sysdig command: %v", err)
|
||||
}
|
||||
cluster := clusterssh.Cluster{hosts}
|
||||
cmd := cluster.Run(string(sysdigCommand), sysdigPlugin)
|
||||
return &cmd, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user