github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/cmd/otsd/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/leg100/ots/agent"
    10  	"github.com/leg100/ots/app"
    11  	cmdutil "github.com/leg100/ots/cmd"
    12  	"github.com/leg100/ots/filestore"
    13  	"github.com/leg100/ots/http"
    14  	"github.com/leg100/ots/inmem"
    15  	"github.com/leg100/ots/sqlite"
    16  	"github.com/leg100/zerologr"
    17  	"github.com/mitchellh/go-homedir"
    18  	"github.com/rs/zerolog"
    19  	"github.com/spf13/cobra"
    20  )
    21  
    22  const (
    23  	DefaultAddress  = ":8080"
    24  	DefaultHostname = "localhost:8080"
    25  	DefaultDBPath   = "ots.db"
    26  	DefaultDataDir  = "~/.ots-data"
    27  	DefaultLogLevel = "info"
    28  )
    29  
    30  var (
    31  	// DBPath is the path to the sqlite database file
    32  	DBPath string
    33  	// DataDir is the path to the directory used for storing OTS-related data
    34  	DataDir string
    35  )
    36  
    37  func main() {
    38  	// Configure ^C to terminate program
    39  	ctx, cancel := context.WithCancel(context.Background())
    40  	cmdutil.CatchCtrlC(cancel)
    41  
    42  	server := http.NewServer()
    43  
    44  	cmd := &cobra.Command{
    45  		Use:           "otsd",
    46  		SilenceUsage:  true,
    47  		SilenceErrors: true,
    48  	}
    49  
    50  	cmd.Flags().StringVar(&server.Addr, "address", DefaultAddress, "Listening address")
    51  	cmd.Flags().BoolVar(&server.SSL, "ssl", false, "Toggle SSL")
    52  	cmd.Flags().StringVar(&server.CertFile, "cert-file", "", "Path to SSL certificate (required if enabling SSL)")
    53  	cmd.Flags().StringVar(&server.KeyFile, "key-file", "", "Path to SSL key (required if enabling SSL)")
    54  	cmd.Flags().StringVar(&DBPath, "db-path", DefaultDBPath, "Path to SQLite database file")
    55  	cmd.Flags().StringVar(&server.Hostname, "hostname", DefaultHostname, "Hostname used within absolute URL links")
    56  	cmd.Flags().StringVar(&DataDir, "data-dir", DefaultDataDir, "Path to directory for storing OTS related data")
    57  	logLevel := cmd.Flags().StringP("log-level", "l", DefaultLogLevel, "Logging level")
    58  
    59  	cmdutil.SetFlagsFromEnvVariables(cmd.Flags())
    60  
    61  	if err := cmd.ParseFlags(os.Args[1:]); err != nil {
    62  		panic(err.Error())
    63  	}
    64  
    65  	// DataDir: Expand ~ to home dir
    66  	var err error
    67  	DataDir, err = homedir.Expand(DataDir)
    68  	if err != nil {
    69  		panic(err.Error())
    70  	}
    71  
    72  	// Setup logger
    73  	zerologger, err := newLogger(*logLevel)
    74  	if err != nil {
    75  		panic(err.Error())
    76  	}
    77  	logger := zerologr.NewLogger(zerologger)
    78  	server.Logger = logger
    79  
    80  	// Validate SSL params
    81  	if server.SSL {
    82  		if server.CertFile == "" || server.KeyFile == "" {
    83  			fmt.Fprintf(os.Stderr, "must provide both --cert-file and --key-file")
    84  			os.Exit(1)
    85  		}
    86  	}
    87  
    88  	// Setup filestore
    89  	fs, err := filestore.NewFilestore(DataDir)
    90  	if err != nil {
    91  		panic(err.Error())
    92  	}
    93  	logger.Info("filestore started", "path", fs.Path)
    94  
    95  	// Setup sqlite db
    96  	db, err := sqlite.New(DBPath, sqlite.WithZeroLogger(zerologger))
    97  	if err != nil {
    98  		panic(err.Error())
    99  	}
   100  
   101  	organizationStore := sqlite.NewOrganizationDB(db)
   102  	workspaceStore := sqlite.NewWorkspaceDB(db)
   103  	stateVersionStore := sqlite.NewStateVersionDB(db)
   104  	runStore := sqlite.NewRunDB(db)
   105  	configurationVersionStore := sqlite.NewConfigurationVersionDB(db)
   106  
   107  	eventService := inmem.NewEventService(logger)
   108  
   109  	server.OrganizationService = app.NewOrganizationService(organizationStore, eventService)
   110  	server.WorkspaceService = app.NewWorkspaceService(workspaceStore, server.OrganizationService, eventService)
   111  	server.StateVersionService = app.NewStateVersionService(stateVersionStore, server.WorkspaceService, fs)
   112  	server.ConfigurationVersionService = app.NewConfigurationVersionService(configurationVersionStore, server.WorkspaceService, fs)
   113  	server.RunService = app.NewRunService(runStore, server.WorkspaceService, server.ConfigurationVersionService, fs, eventService)
   114  	server.PlanService = app.NewPlanService(runStore, fs)
   115  	server.ApplyService = app.NewApplyService(runStore)
   116  
   117  	scheduler, err := inmem.NewScheduler(server.WorkspaceService, server.RunService, eventService, logger)
   118  	if err != nil {
   119  		panic(fmt.Sprintf("unable to start scheduler: %s", err.Error()))
   120  	}
   121  
   122  	// Run scheduler in background
   123  	go scheduler.Start(ctx)
   124  
   125  	// Run poller in background
   126  	agent, err := agent.NewAgent(
   127  		logger,
   128  		server.ConfigurationVersionService,
   129  		server.StateVersionService,
   130  		server.RunService,
   131  		eventService,
   132  	)
   133  	if err != nil {
   134  		panic(fmt.Sprintf("unable to start agent: %s", err.Error()))
   135  	}
   136  	go agent.Start(ctx)
   137  
   138  	if err := server.Open(); err != nil {
   139  		server.Close()
   140  		fmt.Fprintln(os.Stderr, err)
   141  		os.Exit(1)
   142  	}
   143  
   144  	logger.Info("server started", "address", server.Addr, "ssl", server.SSL)
   145  
   146  	// Block until Ctrl-C received.
   147  	if err := server.Wait(ctx); err != nil {
   148  		fmt.Fprintln(os.Stderr, err)
   149  		os.Exit(1)
   150  	}
   151  }
   152  
   153  func newLogger(lvl string) (*zerolog.Logger, error) {
   154  	zlvl, err := zerolog.ParseLevel(lvl)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	// Setup logger
   160  	consoleWriter := zerolog.ConsoleWriter{
   161  		Out:        os.Stdout,
   162  		TimeFormat: time.RFC3339,
   163  	}
   164  	zerolog.DurationFieldInteger = true
   165  
   166  	logger := zerolog.New(consoleWriter).Level(zlvl).With().Timestamp().Logger()
   167  
   168  	if logger.GetLevel() < zerolog.InfoLevel {
   169  		// Inform the user that logging lower than INFO threshold has been
   170  		// enabled
   171  		logger.WithLevel(logger.GetLevel()).Msg("custom log level enabled")
   172  	}
   173  
   174  	return &logger, nil
   175  }