From a250c8630496f1573b53410a922aa6661e84fc98 Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Wed, 24 Jan 2024 21:23:53 +0300 Subject: [PATCH] feat: Auto reconfiguration on server side --- README.md | 10 ++++++++++ client/main.go | 2 +- server/conf.go | 22 +++++++++++----------- server/main.go | 38 +++++++++++++++++++++++++++++--------- server/pot.go | 2 +- 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0181194..5ae9f44 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,22 @@ Default configuration file structure: `brew-time-sec` is the approximate time it takes to brew coffee in seconds. After this amount is passed, status of the pot changes to `Pouring` and will stay it until `max-pour-time-sec` seconds are passed or an incoming request with `WHEN` method is presented. +Configuration is initialized on program start and is responsive to change of the configuration file. If the configuration file is changed - the changes will take place in server program as well. Configuration file is reopened and applied once on each incoming request. This behaviour can be turned off via `-no-autoreconf` flag. If flag is present - initial configuration will stay constant. + By default port 80 is used, but can be changed with a `-port` flag: `HTCPCP-server -port 8000` Server will listen and handle incoming requests. +Examples: + +- `HTCPCP-server -port 8000` - starts a server on port 8000 +- `HTCPCP-server` - starts a server on port 80 +- `HTCPCP-server -port 8000 -no-autoreconf` - starts a server on port 8000, configuration is constant +- `HTCPCP-server -version` - prints a version information + + ### Client Client is used to interact with the server and has such syntax: diff --git a/client/main.go b/client/main.go index 9e367cf..fb0546c 100644 --- a/client/main.go +++ b/client/main.go @@ -71,7 +71,7 @@ func main() { flag.Parse() if *version { - fmt.Printf("HTCPCP-client %s\nKasianov Nikolai Alekseevich (Unbewohnte)\n", Version) + fmt.Printf("HTCPCP-client %s\n(C) 2024 Kasianov Nikolai Alekseevich (Unbewohnte)\n", Version) return } diff --git a/server/conf.go b/server/conf.go index 26f9db3..f519544 100644 --- a/server/conf.go +++ b/server/conf.go @@ -43,44 +43,44 @@ func DefaultConf() Conf { } // Tries to retrieve configuration structure from given json file -func ConfFromFile(path string) (Conf, error) { +func ConfFromFile(path string) (*Conf, error) { confFile, err := os.Open(path) if err != nil { - return DefaultConf(), err + return nil, err } defer confFile.Close() confBytes, err := io.ReadAll(confFile) if err != nil { - return DefaultConf(), err + return nil, err } - var conf Conf + var conf *Conf err = json.Unmarshal(confBytes, &conf) if err != nil { - return DefaultConf(), err + return nil, err } return conf, nil } // Create a new configuration file -func CreateConf(path string, conf Conf) (Conf, error) { +func CreateConf(path string, conf Conf) (*Conf, error) { confFile, err := os.Create(path) if err != nil { - return DefaultConf(), err + return nil, err } defer confFile.Close() - confJsonBytes, err := json.MarshalIndent(conf, "", " ") + confJsonBytes, err := json.MarshalIndent(&conf, "", " ") if err != nil { - return conf, err + return nil, err } _, err = confFile.Write(confJsonBytes) if err != nil { - return conf, nil + return nil, nil } - return conf, nil + return &conf, nil } diff --git a/server/main.go b/server/main.go index 4734c74..4ddd9b7 100644 --- a/server/main.go +++ b/server/main.go @@ -24,11 +24,12 @@ import ( const ConfName string = "conf.json" -const Version string = "0.1-server" +const Version string = "0.2-server" var ( - port *uint = flag.Uint("port", 80, "Set server port") - version *bool = flag.Bool("version", false, "Print version information") + port *uint = flag.Uint("port", 80, "Set server port") + version *bool = flag.Bool("version", false, "Print version information") + noAutoReconf *bool = flag.Bool("no-autoreconf", false, "Do NOT reopen configuration file and reload stored configuration on each new request") ) func main() { @@ -36,7 +37,7 @@ func main() { flag.Parse() if *version { - fmt.Printf("HTCPCP-server %s\nKasianov Nikolai Alekseevich (Unbewohnte)\n", Version) + fmt.Printf("HTCPCP-server %s\n(C) 2024 Kasianov Nikolai Alekseevich (Unbewohnte)\n", Version) return } @@ -47,14 +48,15 @@ func main() { } wDir := filepath.Dir(exePath) - // Open commands file, create if does not exist - conf, err := ConfFromFile(filepath.Join(wDir, ConfName)) + confPath := filepath.Join(wDir, ConfName) + // Open configuration file, create if does not exist + conf, err := ConfFromFile(confPath) if err != nil { - _, err = CreateConf(filepath.Join(wDir, ConfName), DefaultConf()) + _, err = CreateConf(confPath, DefaultConf()) if err != nil { - log.Fatalf("Failed to create a new commands file: %s", err) + log.Fatalf("Failed to create a new configuration file: %s", err) } - log.Printf("Created a new commands file") + log.Printf("Created a new configuration file") os.Exit(0) } @@ -62,6 +64,22 @@ func main() { handler := http.NewServeMux() handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // Reload configuration options in case something's changed + if !*noAutoReconf { + conf, err = ConfFromFile(confPath) + if err != nil { + log.Fatalf("Could not reopen configuration file: %s", err) + } + + // If not ready - wait for this iteration to end, not now + if pot.State == PotStatusReady { + pot.commands = conf.Commands + pot.CoffeeType = conf.CoffeeType + pot.BrewTimeSec = conf.BrewTimeSec + pot.MaxPourTimeSec = conf.MaxPourTimeSec + } + } + if r.Method == "BREW" || r.Method == "POST" { // Brew some coffee if r.Header.Get("Content-Type") != "application/coffee-pot-command" { @@ -102,6 +120,8 @@ func main() { err := pot.StopPouring() if err != nil { log.Printf("Failed to stop pouring milk: %s\n", err) + http.Error(w, "Coffee is not brewed yet", http.StatusBadRequest) + return } } else { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) diff --git a/server/pot.go b/server/pot.go index 3678336..2370b8c 100644 --- a/server/pot.go +++ b/server/pot.go @@ -34,7 +34,7 @@ type Pot struct { commands Commands } -func NewPot(conf Conf) *Pot { +func NewPot(conf *Conf) *Pot { return &Pot{ State: PotStatusReady, CoffeeType: conf.CoffeeType,