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 }