123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- //TODO: Harden. Add failover for every method and agent calls
- //TODO: Figure out why mongodb keeps failing (log rotation?)
- package main
- import (
- "log"
- "flag"
- "github.com/gravitl/netmaker/models"
- "github.com/gravitl/netmaker/controllers"
- "github.com/gravitl/netmaker/serverctl"
- "github.com/gravitl/netmaker/functions"
- "github.com/gravitl/netmaker/mongoconn"
- "github.com/gravitl/netmaker/config"
- "go.mongodb.org/mongo-driver/bson"
- "fmt"
- "time"
- "net/http"
- "strings"
- "errors"
- "io/ioutil"
- "os"
- "os/exec"
- "net"
- "context"
- "strconv"
- "sync"
- "os/signal"
- "go.mongodb.org/mongo-driver/mongo"
- service "github.com/gravitl/netmaker/controllers"
- nodepb "github.com/gravitl/netmaker/grpc"
- "google.golang.org/grpc"
- )
- var ServerGRPC string
- var PortGRPC string
- //Start MongoDB Connection and start API Request Handler
- func main() {
- var clientmode string
- var defaultnet string
- flag.StringVar(&clientmode, "clientmode", "on", "Have a client on the server")
- flag.StringVar(&defaultnet, "defaultnet", "on", "Create a default network")
- flag.Parse()
- if clientmode == "on" {
- cmd := exec.Command("id", "-u")
- output, err := cmd.Output()
- if err != nil {
- log.Fatal(err)
- }
- i, err := strconv.Atoi(string(output[:len(output)-1]))
- if err != nil {
- log.Fatal(err)
- }
- if i != 0 {
- log.Fatal("To run in client mode requires root privileges. Either turn off client mode with the --clientmode=off flag, or run with sudo.")
- }
- }
- log.Println("Server starting...")
- mongoconn.ConnectDatabase()
- installserver := false
- if !(defaultnet == "off") {
- if config.Config.Server.CreateDefault {
- created, err := createDefaultNetwork()
- if err != nil {
- fmt.Printf("Error creating default network: %v", err)
- }
- if created && clientmode != "off" {
- installserver = true
- }
- }
- }
- var waitnetwork sync.WaitGroup
- if config.Config.Server.AgentBackend {
- waitnetwork.Add(1)
- go runGRPC(&waitnetwork, installserver)
- }
- if config.Config.Server.RestBackend {
- waitnetwork.Add(1)
- controller.HandleRESTRequests(&waitnetwork)
- }
- if !config.Config.Server.RestBackend && !config.Config.Server.AgentBackend {
- fmt.Println("Oops! No Server Mode selected. Nothing being served.")
- }
- waitnetwork.Wait()
- fmt.Println("Exiting now.")
- }
- func runGRPC(wg *sync.WaitGroup, installserver bool) {
- defer wg.Done()
- // Configure 'log' package to give file name and line number on eg. log.Fatal
- // Pipe flags to one another (log.LstdFLags = log.Ldate | log.Ltime)
- log.SetFlags(log.LstdFlags | log.Lshortfile)
- // Start our listener, 50051 is the default gRPC port
- grpcport := ":50051"
- if config.Config.Server.GrpcPort != "" {
- grpcport = ":" + config.Config.Server.GrpcPort
- }
- if os.Getenv("GRPC_PORT") != "" {
- grpcport = ":" + os.Getenv("GRPC_PORT")
- }
- PortGRPC = grpcport
- if os.Getenv("BACKEND_URL") == "" {
- if config.Config.Server.Host == "" {
- ServerGRPC, _ = getPublicIP()
- } else {
- ServerGRPC = config.Config.Server.Host
- }
- } else {
- ServerGRPC = os.Getenv("BACKEND_URL")
- }
- fmt.Println("GRPC Server set to: " + ServerGRPC)
- fmt.Println("GRPC Port set to: " + PortGRPC)
- var gconf models.GlobalConfig
- gconf.ServerGRPC = ServerGRPC
- gconf.PortGRPC = PortGRPC
- gconf.Name = "netmaker"
- err := setGlobalConfig(gconf)
- if err != nil && err != mongo.ErrNoDocuments{
- log.Fatalf("Unable to set global config: %v", err)
- }
- listener, err := net.Listen("tcp", grpcport)
- // Handle errors if any
- if err != nil {
- log.Fatalf("Unable to listen on port" + grpcport + ": %v", err)
- }
- s := grpc.NewServer(
- authServerUnaryInterceptor(),
- authServerStreamInterceptor(),
- )
- // Create NodeService type
- srv := &service.NodeServiceServer{}
- // Register the service with the server
- nodepb.RegisterNodeServiceServer(s, srv)
- srv.NodeDB = mongoconn.NodeDB
- // Start the server in a child routine
- go func() {
- if err := s.Serve(listener); err != nil {
- log.Fatalf("Failed to serve: %v", err)
- }
- }()
- fmt.Println("Agent Server succesfully started on port " + grpcport + " (gRPC)")
- if installserver {
- fmt.Println("Adding server to " + config.Config.Server.DefaultNetName)
- success, err := serverctl.AddNetwork(config.Config.Server.DefaultNetName)
- if err != nil {
- fmt.Printf("Error adding to default network: %v", err)
- fmt.Println("")
- fmt.Println("Unable to add server to network. Continuing.")
- fmt.Println("Please investigate client installation on server.")
- } else if !success {
- fmt.Println("Unable to add server to network. Continuing.")
- fmt.Println("Please investigate client installation on server.")
- } else{
- fmt.Println("Server successfully added to default network.")
- }
- }
- fmt.Println("Setup complete. You are ready to begin using netmaker.")
- // Right way to stop the server using a SHUTDOWN HOOK
- // Create a channel to receive OS signals
- c := make(chan os.Signal)
- // Relay os.Interrupt to our channel (os.Interrupt = CTRL+C)
- // Ignore other incoming signals
- signal.Notify(c, os.Interrupt)
- // Block main routine until a signal is received
- // As long as user doesn't press CTRL+C a message is not passed and our main routine keeps running
- <-c
- // After receiving CTRL+C Properly stop the server
- fmt.Println("Stopping the Agent server...")
- s.Stop()
- listener.Close()
- fmt.Println("Agent server closed..")
- fmt.Println("Closing MongoDB connection")
- mongoconn.Client.Disconnect(context.TODO())
- fmt.Println("MongoDB connection closed.")
- }
- func setGlobalConfig(globalconf models.GlobalConfig) (error) {
- collection := mongoconn.Client.Database("netmaker").Collection("config")
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- create, _, err := functions.GetGlobalConfig()
- if create {
- _, err := collection.InsertOne(ctx, globalconf)
- defer cancel()
- if err != nil {
- if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
- return nil
- } else {
- return err
- }
- }
- } else {
- filter := bson.M{"name": "netmaker"}
- update := bson.D{
- {"$set", bson.D{
- {"servergrpc", globalconf.ServerGRPC},
- {"portgrpc", globalconf.PortGRPC},
- }},
- }
- err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&globalconf)
- if err == mongo.ErrNoDocuments {
- //if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
- return nil
- }
- }
- return err
- }
- func createDefaultNetwork() (bool, error) {
- iscreated := false
- exists, err := functions.NetworkExists(config.Config.Server.DefaultNetName)
- if exists || err != nil {
- fmt.Println("Default network already exists")
- fmt.Println("Skipping default network create")
- return iscreated, err
- } else {
- var network models.Network
- network.NetID = config.Config.Server.DefaultNetName
- network.AddressRange = config.Config.Server.DefaultNetRange
- network.DisplayName = config.Config.Server.DefaultNetName
- network.SetDefaults()
- network.SetNodesLastModified()
- network.SetNetworkLastModified()
- network.KeyUpdateTimeStamp = time.Now().Unix()
- priv := false
- network.IsLocal = &priv
- network.KeyUpdateTimeStamp = time.Now().Unix()
- allow := true
- network.AllowManualSignUp = &allow
- fmt.Println("Creating default network.")
- collection := mongoconn.Client.Database("netmaker").Collection("networks")
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- // insert our network into the network table
- _, err = collection.InsertOne(ctx, network)
- defer cancel()
- }
- if err == nil {
- iscreated = true
- }
- return iscreated, err
- }
- func getPublicIP() (string, error) {
- iplist := []string{"https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
- endpoint := ""
- var err error
- for _, ipserver := range iplist {
- resp, err := http.Get(ipserver)
- if err != nil {
- continue
- }
- defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- bodyBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- continue
- }
- endpoint = string(bodyBytes)
- break
- }
- }
- if err == nil && endpoint == "" {
- err = errors.New("Public Address Not Found.")
- }
- return endpoint, err
- }
- func authServerUnaryInterceptor() grpc.ServerOption {
- return grpc.UnaryInterceptor(controller.AuthServerUnaryInterceptor)
- }
- func authServerStreamInterceptor() grpc.ServerOption {
- return grpc.StreamInterceptor(controller.AuthServerStreamInterceptor)
- }
|