about summary refs log tree commit diff
path: root/fun/clbot/clbot.go
diff options
context:
space:
mode:
Diffstat (limited to 'fun/clbot/clbot.go')
-rw-r--r--fun/clbot/clbot.go101
1 files changed, 101 insertions, 0 deletions
diff --git a/fun/clbot/clbot.go b/fun/clbot/clbot.go
new file mode 100644
index 000000000000..7fa12f2c3bc3
--- /dev/null
+++ b/fun/clbot/clbot.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"io/ioutil"
+	"os"
+	"os/signal"
+	"time"
+
+	"code.tvl.fyi/fun/clbot/gerrit"
+	"github.com/davecgh/go-spew/spew"
+	log "github.com/golang/glog"
+	"golang.org/x/crypto/ssh"
+)
+
+var (
+	gerritAddr       = flag.String("gerrit_host", "cl.tvl.fyi:29418", "Gerrit SSH host:port")
+	gerritSSHHostKey = flag.String("gerrit_ssh_pubkey", "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIUNYBYPCCBNDFSd0BuCR+8kgeuJ7IA5S2nTNQmkQUYNyXK+ot5os7rHtCk96+grd5+J8jFCuFBWisUe8h8NC0Q=", "Gerrit SSH public key")
+	gerritSSHTimeout = flag.Duration("gerrit_tcp_timeout", 5*time.Second, "Gerrit SSH TCP connect timeout")
+
+	required           = flag.NewFlagSet("required flags", flag.ExitOnError)
+	gerritAuthUsername = required.String("gerrit_ssh_auth_username", "", "Gerrit SSH username")
+	gerritAuthKeyPath  = required.String("gerrit_ssh_auth_key", "", "Gerrit SSH private key path")
+)
+
+func mustFixedHostKey(f string) ssh.HostKeyCallback {
+	pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(f))
+	if err != nil {
+		log.Exitf("ParseAuthorizedKey(%q): %v", f, err)
+	}
+	return ssh.FixedHostKey(pk)
+}
+
+func mustPrivateKey(p string) ssh.AuthMethod {
+	pkBytes, err := ioutil.ReadFile(p)
+	if err != nil {
+		log.Exitf("reading SSH private key from %q: %v", p, err)
+	}
+	pk, err := ssh.ParsePrivateKey(pkBytes)
+	if err != nil {
+		log.Exitf("parsing private key from %q: %v", p, err)
+	}
+	return ssh.PublicKeys(pk)
+}
+
+func checkRequired(fs *flag.FlagSet) {
+	missing := map[string]bool{}
+	fs.VisitAll(func(f *flag.Flag) { missing[f.Name] = true })
+	fs.Visit(func(f *flag.Flag) { delete(missing, f.Name) })
+	for f := range missing {
+		log.Errorf("flag %q was unset but is required", f)
+	}
+	if len(missing) > 0 {
+		os.Exit(1)
+	}
+}
+
+var shutdownFuncs []func()
+
+func callOnShutdown(f func()) {
+	shutdownFuncs = append(shutdownFuncs, f)
+}
+
+func main() {
+	flag.Parse()
+	checkRequired(required)
+
+	shutdownCh := make(chan os.Signal)
+	signal.Notify(shutdownCh, os.Interrupt)
+	go func() {
+		<-shutdownCh
+		for n := len(shutdownFuncs); n >= 0; n-- {
+			shutdownFuncs[n]()
+		}
+	}()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	callOnShutdown(cancel)
+	cfg := &ssh.ClientConfig{
+		User:            *gerritAuthUsername,
+		Auth:            []ssh.AuthMethod{mustPrivateKey(*gerritAuthKeyPath)},
+		HostKeyCallback: mustFixedHostKey(*gerritSSHHostKey),
+		Timeout:         *gerritSSHTimeout,
+	}
+	cfg.SetDefaults()
+
+	gw, err := gerrit.New(ctx, "tcp", *gerritAddr, cfg)
+	if err != nil {
+		log.Errorf("gerrit.New(%q): %v", *gerritAddr, err)
+	}
+	callOnShutdown(func() {
+		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+		defer cancel()
+		gw.Close(ctx)
+	})
+
+	for e := range gw.Events() {
+		log.Infof("hello: %v", spew.Sdump(e))
+	}
+}