@ -8,6 +8,7 @@ package main
import (
"context"
"encoding/json"
"errors"
"expvar"
"flag"
@ -23,6 +24,7 @@ import (
"time"
"github.com/gaissmai/bart"
"github.com/hashicorp/raft"
"github.com/inetaf/tcpproxy"
"github.com/peterbourgon/ff/v3"
"go4.org/netipx"
@ -63,6 +65,7 @@ func main() {
server = fs . String ( "login-server" , ipn . DefaultControlURL , "the base URL of control server" )
stateDir = fs . String ( "state-dir" , "" , "path to directory in which to store app state" )
clusterFollowOnly = fs . Bool ( "follow-only" , false , "Try to find a leader with the cluster tag or exit." )
clusterAdminPort = fs . Int ( "cluster-admin-port" , 8081 , "Port on localhost for the cluster admin HTTP API" )
)
ff . Parse ( fs , os . Args [ 1 : ] , ff . WithEnvVarPrefix ( "TS_NATC" ) )
@ -179,6 +182,12 @@ func main() {
}
} ( )
ipp = cipp
go func ( ) {
// This listens on localhost only, so that only those with access to the host machine
// can remove servers from the cluster config.
log . Print ( http . ListenAndServe ( fmt . Sprintf ( "127.0.0.1:%d" , * clusterAdminPort ) , httpClusterAdmin ( cipp ) ) )
} ( )
} else {
ipp = & ippool . SingleMachineIPPool { IPSet : addrPool }
}
@ -633,3 +642,32 @@ func getClusterStatePath(stateDirFlag string) (string, error) {
return dirPath , nil
}
func httpClusterAdmin ( ipp * ippool . ConsensusIPPool ) http . Handler {
mux := http . NewServeMux ( )
mux . HandleFunc ( "GET /{$}" , func ( w http . ResponseWriter , r * http . Request ) {
c , err := ipp . GetClusterConfiguration ( )
if err != nil {
log . Printf ( "cluster admin http: error getClusterConfig: %v" , err )
http . Error ( w , "" , http . StatusInternalServerError )
return
}
if err := json . NewEncoder ( w ) . Encode ( c ) ; err != nil {
log . Printf ( "cluster admin http: error encoding raft configuration: %v" , err )
}
} )
mux . HandleFunc ( "DELETE /{id}" , func ( w http . ResponseWriter , r * http . Request ) {
idString := r . PathValue ( "id" )
id := raft . ServerID ( idString )
idx , err := ipp . DeleteClusterServer ( id )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
if err := json . NewEncoder ( w ) . Encode ( idx ) ; err != nil {
log . Printf ( "cluster admin http: error encoding delete index: %v" , err )
return
}
} )
return mux
}