callgraph/main.go

139 lines
3.6 KiB
Go

package main
import (
"flag"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"./rancher"
"./sharelatex"
"./sysdig"
)
var (
skipTest = flag.Bool("skip-test", false, "skip selenium integration test")
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()
args := flag.Args()
if len(args) < 1 {
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 args[0], args[1:]
}
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
}
if name == "Network Agent" {
continue
}
ip := net.ParseIP(container.PrimaryIpAddress)
if ip == nil {
return nil, fmt.Errorf("Got invalid IP from api for '%s' container: %s", container.Name, container.PrimaryIpAddress)
}
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)
}
if *skipTest {
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
signal.Notify(signalChan, syscall.SIGTERM)
<-signalChan
} else {
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)
}