converge/cmd/converge/server.go

99 lines
3.2 KiB
Go

package main
import (
"converge/pkg/converge"
"converge/pkg/websocketutil"
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"regexp"
)
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 main() {
downloadOption := flag.String("d", "downloads",
"directory where documentation is located, either relative to current directory or an absolute path")
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "Usage: converge [options]")
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr, "Converge server is a rendez-vous server for debugging continuous integration")
fmt.Fprintln(os.Stderr, "jobs be providing the capability to log into the agents where jobs are running.")
fmt.Fprintln(os.Stderr, "This is achieve by starting an agent in the continuous integration job")
fmt.Fprintln(os.Stderr, "which connects to Converge using a websocket connection. The end user also connects")
fmt.Fprintln(os.Stderr, "to Converge using ssh over websockets. The server then matches the end-user with")
fmt.Fprintln(os.Stderr, "the agent running in the continous integration job (the rendez-vous) and sets up")
fmt.Fprintln(os.Stderr, "an end-to-end SSH connection between end-user and agent, with the agent providing")
fmt.Fprintln(os.Stderr, "an embedded SSH server to provide interactive access to the end-user. This works")
fmt.Fprintln(os.Stderr, "both on linux and on windows.")
fmt.Fprintln(os.Stderr)
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() != 0 {
flag.Usage()
os.Exit(1)
}
downloadDir := *downloadOption
log.Println("Download directory", downloadDir)
admin := converge.NewAdmin()
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)
if err != nil {
log.Printf("Error %v\n", err)
}
},
}
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
}
log.Printf("Got client connection: '%s'\n", publicId)
err = admin.Connect(publicId, conn)
if err != nil {
log.Printf("Error %v\n", err)
}
},
}
// websocket endpoints
http.HandleFunc("/agent/", registrationService.Handle)
http.HandleFunc("/client/", clientService.Handle)
// create filehandler with templating for html files.
fileHandler := NewFileHandler(downloadDir)
http.Handle("/docs/", http.StripPrefix("/docs/", fileHandler))
http.HandleFunc("/", catchAllHandler)
// Start HTTP server
fmt.Println("Rendez-vous server listening on :8000")
log.Fatal(http.ListenAndServe(":8000", nil))
}