The contextpath parameter in converge.go is temporary and should be removed later. What is needed is autodetectio of the context path for the usage page and passing on the context for rendering.
192 lines
5.5 KiB
Go
192 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"converge/pkg/comms"
|
|
"converge/pkg/models"
|
|
"converge/pkg/server/converge"
|
|
"converge/pkg/support/websocketutil"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"net/http"
|
|
_ "net/http/pprof"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
_ "time/tzdata"
|
|
)
|
|
|
|
func parsePublicId(path string) (publicId string, _ error) {
|
|
pattern := regexp.MustCompile("/([^/]+)$")
|
|
matches := pattern.FindStringSubmatch(path)
|
|
if len(matches) != 2 {
|
|
return "", fmt.Errorf("Invalid URL path '%s'", path)
|
|
}
|
|
return matches[1], nil
|
|
}
|
|
|
|
func catchAllHandler(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, "/docs", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
func printHelp(msg string) {
|
|
if msg != "" {
|
|
fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", msg)
|
|
}
|
|
helpText := "Usage: converge [options]\n" +
|
|
"\n" +
|
|
"Converge server is a rendez-vous server for debugging continuous integration\n" +
|
|
"jobs be providing the capability to log into the agents where jobs are running.\n" +
|
|
"This is achieve by starting an agent in the continuous integration job\n" +
|
|
"which connects to Converge using a websocket connection. The end user also connects\n" +
|
|
"to Converge using ssh over websockets. The server then matches the end-user with\n" +
|
|
"the agent running in the continous integration job (the rendez-vous) and sets up\n" +
|
|
"an end-to-end SSH connection between end-user and agent, with the agent providing\n" +
|
|
"an embedded SSH server to provide interactive access to the end-user. This works\n" +
|
|
"both on linux and on windows.\n" +
|
|
"\n" +
|
|
"-s <contentdir>: directory where static content of converge is placed\n" +
|
|
"-d <downloaddir>: directory where downloads of converge are placed\n"
|
|
fmt.Fprintln(os.Stderr, helpText)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func main() {
|
|
|
|
downloaddir := "."
|
|
staticdir := "../static"
|
|
|
|
args := os.Args[1:]
|
|
for len(args) > 0 && strings.HasPrefix(args[0], "-") {
|
|
switch args[0] {
|
|
case "-d":
|
|
if len(args) <= 1 {
|
|
printHelp("The -d option expects an argument")
|
|
}
|
|
downloaddir = args[1]
|
|
args = args[1:]
|
|
case "-s":
|
|
if len(args) <= 1 {
|
|
printHelp("The -s option expects an argument")
|
|
}
|
|
staticdir = args[1]
|
|
args = args[1:]
|
|
default:
|
|
printHelp("Unknown option " + args[0])
|
|
}
|
|
args = args[1:]
|
|
}
|
|
log.Println("Content directory", staticdir)
|
|
|
|
if len(args) != 0 {
|
|
printHelp("")
|
|
}
|
|
|
|
userPassword := comms.UserPassword{
|
|
Username: strconv.Itoa(rand.Int()),
|
|
Password: strconv.Itoa(rand.Int()),
|
|
}
|
|
|
|
username, ok := os.LookupEnv("CONVERGE_USERNAME")
|
|
if ok {
|
|
userPassword.Username = username
|
|
} else {
|
|
os.Setenv("CONVERGE_USERNAME", userPassword.Username)
|
|
}
|
|
|
|
password, ok := os.LookupEnv("CONVERGE_PASSWORD")
|
|
if ok {
|
|
userPassword.Password = password
|
|
} else {
|
|
os.Setenv("CONVERGE_PASSWORD", userPassword.Password)
|
|
}
|
|
|
|
log.Printf("Using username '%s' and password '%s'", userPassword.Username, userPassword.Password)
|
|
|
|
notifications := make(chan *models.State, 10)
|
|
admin := converge.NewAdmin(notifications)
|
|
websessions := converge.NewWebSessions(notifications)
|
|
|
|
// For agents connecting
|
|
registrationService := websocketutil.WebSocketService{
|
|
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
|
|
publicId, err := parsePublicId(r.URL.Path)
|
|
if err != nil {
|
|
log.Printf("Cannot parse public id from url: '%v'\n", err)
|
|
return
|
|
}
|
|
log.Printf("Got registration connection: '%s'\n", publicId)
|
|
err = admin.Register(publicId, conn,
|
|
userPassword)
|
|
if err != nil {
|
|
log.Printf("Error %v\n", err)
|
|
}
|
|
},
|
|
}
|
|
|
|
// For users connecting with ssh
|
|
clientService := websocketutil.WebSocketService{
|
|
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
|
|
publicId, err := parsePublicId(r.URL.Path)
|
|
if err != nil {
|
|
log.Printf("Cannot parse public id from url: '%v'\n", err)
|
|
return
|
|
}
|
|
_, wsProxyMode := r.URL.Query()["wsproxy"]
|
|
log.Printf("Got client connection: '%s'\n", publicId)
|
|
err = admin.Connect(wsProxyMode, publicId, conn)
|
|
if err != nil {
|
|
log.Printf("Error %v\n", err)
|
|
}
|
|
},
|
|
}
|
|
|
|
// for the web browser getting live status updates.
|
|
sessionService := websocketutil.WebSocketService{
|
|
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
|
|
websession := websessions.NewSession(conn)
|
|
defer websessions.SessionClosed(websession)
|
|
location, err := converge.GetUserLocation(r)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
websession.WriteNotifications(location)
|
|
},
|
|
Text: true,
|
|
}
|
|
|
|
// websocket endpoints
|
|
|
|
// TODO remove, simulate contextpath
|
|
|
|
contextpath := ""
|
|
http.HandleFunc(contextpath+"/agent/", registrationService.Handle)
|
|
http.HandleFunc(contextpath+"/client/", clientService.Handle)
|
|
http.HandleFunc(contextpath+"/ws/sessions", sessionService.Handle)
|
|
|
|
// create filehandler with templating for html files.
|
|
http.Handle(contextpath+"/docs/", http.StripPrefix(contextpath+"/docs/", http.HandlerFunc(pageHandler)))
|
|
http.Handle(contextpath+"/static/", http.StripPrefix(contextpath+"/static/",
|
|
http.FileServer(http.Dir(staticdir))))
|
|
http.Handle(contextpath+"/downloads/", http.StripPrefix(contextpath+"/downloads/",
|
|
http.FileServer(http.Dir(downloaddir))))
|
|
|
|
// TODO remove for testing contextpath
|
|
|
|
catchAllHandler2 := func(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, contextpath+"/docs", http.StatusFound)
|
|
return
|
|
}
|
|
http.HandleFunc(contextpath+"/", catchAllHandler2)
|
|
|
|
// create usage generator
|
|
http.HandleFunc(contextpath+"/usage", generateCLIExammple)
|
|
|
|
// Start HTTP server
|
|
fmt.Println("Rendez-vous server listening on :8000")
|
|
log.Fatal(http.ListenAndServe(":8000", nil))
|
|
}
|