Compare commits

..

No commits in common. "master" and "fd5c5b570090fe9dfd286a7df869cae604f4be61" have entirely different histories.

3 changed files with 37 additions and 86 deletions

21
LICENSE
View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Augusto Dwenger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,57 +1,32 @@
# whook # whook
A simple and naive implementation of a web server for the usage with webhooks. Simple Server for Webhook usage.
## Installation
**Requirements:**
- Go >= 1.17
```sh
git clone https://git.hhhammer.de/hamburghammer/whook.git
cd whook
go build
```
You should now have a `whook` executable in the directory.
## Usage ## Usage
The server is build to run behind a proxy that provides `https` and some kind of
`auth` and it is not designed for long running processes.
```sh ```sh
./whook -c ./test.sh ./whook -c ./test.sh -d $(pwd)
``` ```
The `test.sh` script contains only `echo "hellow"` so that the output after The `test.sh` script contains only `echo "hellow"` so that the output after starting
starting the application and opening the given URL in side the browser we see the application and opening the given URL in side the browser we see following output:
following output:
```plain ```plain
2022/01/17 21:48:43 The HTTP server is running: http://localhost:8080/ 2022/01/17 19:14:28 The HTTP server is running: http://localhost:8080/
2022/01/17 21:48:49 Executing a command... Executing a command...
2022/01/17 21:48:49 hellow hallow
``` ```
## Options ## Options
```plain ```plain
A small server to listen for requests to execute some particular code. A small server to listen for requests to execute some particular code.
USAGE: USAGE:
whook [FLAGS] --cmd [COMMAND [--args [ARGS]]] whook [FLAGS]
FLAGS: FLAGS:
-a, --args strings Arguments for the command. Can be provided multiple times or as comma-separated string. -c, --cmd string REQUIRED: The command to execute.
-c, --cmd string REQUIRED: The command to execute. -d, --dir string REQUIRED: The Directory to execute the command.
-d, --dir string The Directory to execute the command. -h, --help Prints this help message and exits.
Defaults to the directory the tool was called on. -p, --port string Port to listen for incoming connections. (default "8080")
-h, --help Prints this help message and exits.
-p, --port string Port to listen for incoming connections. (default "8080")
``` ```
## License
This project is licensed under the [MIT](LICENSE) license.

53
main.go
View file

@ -19,8 +19,7 @@ import (
var ( var (
servePort string servePort string
isHelp bool isHelp bool
cmd string cmdPath string
cmdArgs []string
commandExecDir string commandExecDir string
) )
@ -29,41 +28,35 @@ func init() {
flags.StringVarP(&servePort, "port", "p", "8080", "Port to listen for incoming connections.") flags.StringVarP(&servePort, "port", "p", "8080", "Port to listen for incoming connections.")
flags.BoolVarP(&isHelp, "help", "h", false, "Prints this help message and exits.") flags.BoolVarP(&isHelp, "help", "h", false, "Prints this help message and exits.")
flags.StringVarP(&cmd, "cmd", "c", "", "REQUIRED: The command to execute.") flags.StringVarP(&cmdPath, "cmd", "c", "", "REQUIRED: The command to execute.")
flags.StringSliceVarP(&cmdArgs, "args", "a", []string{}, "Arguments for the command. Can be provided multiple times or as comma-separated string.") flags.StringVarP(&commandExecDir, "dir", "d", "", "REQUIRED: The Directory to execute the command.")
flags.StringVarP(&commandExecDir, "dir", "d", "", "The Directory to execute the command.\nDefaults to the directory the tool was called on.")
flags.Parse() flags.Parse()
} }
// handles the webhook requests // handles the webhook requests
func handler(w http.ResponseWriter, r *http.Request) { func handler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() // so I don't forget to close it -> eventhough it's not read. defer r.Body.Close()
cmd := exec.CommandContext(r.Context(), cmd, cmdArgs...) fmt.Println("Executing a command...")
// set directory only if it's present. cmd := exec.Command(cmdPath)
if commandExecDir != "" { cmd.Dir = commandExecDir
cmd.Dir = commandExecDir out, err := cmd.Output()
}
out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
fmt.Fprint(w, err.Error())
w.WriteHeader(http.StatusBadGateway) w.WriteHeader(http.StatusBadGateway)
} }
output := string(out) fmt.Println(string(out))
log.Println("Command output:\n" + output)
fmt.Fprint(w, output)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func main() { func main() {
if isHelp { if isHelp {
printHelp() printHelp()
// printing help is treated as a successful execution.
os.Exit(0) os.Exit(0)
} }
err := validateParams() err := validate()
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
@ -71,7 +64,7 @@ func main() {
// use the default mux because it implements the Handler interface // use the default mux because it implements the Handler interface
// which we need for the server struct. // which we need for the server struct.
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/", handler) // only add one handler on root path mux.HandleFunc("/", handler) // only add out handler on root path
// custom server struct to set custom timeouts for better performance. // custom server struct to set custom timeouts for better performance.
server := &http.Server{ server := &http.Server{
@ -98,19 +91,20 @@ func startHTTPServer(server *http.Server, wg *sync.WaitGroup) {
log.Println("Shutting down the server...") log.Println("Shutting down the server...")
return return
} }
log.Fatalf("An unexpected error happened while running the HTTP server: %v\n", err) log.Fatalf("An unexpected error happend while running the HTTP server: %v\n", err)
} }
} }
func listenToStopHTTPServer(server *http.Server, wg *sync.WaitGroup) { func listenToStopHTTPServer(server *http.Server, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
stop := make(chan os.Signal, 1) stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt) signal.Notify(stop, os.Interrupt, os.Kill)
<-stop // block til signal is captured.
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
if err := server.Shutdown(ctx); err != nil { if err := server.Shutdown(ctx); err != nil {
log.Fatalf("An error happened on the shutdown of the server: %v", err) log.Fatalf("An error happened on the shutdown of the server: %v", err)
} }
@ -120,16 +114,19 @@ func printHelp() {
fmt.Println(`A small server to listen for requests to execute some particular code. fmt.Println(`A small server to listen for requests to execute some particular code.
USAGE: USAGE:
whook [FLAGS] --cmd [COMMAND [--args [ARGS]] whook [FLAGS]
FLAGS:`) FLAGS:`)
flags.PrintDefaults() flags.PrintDefaults()
} }
// validateParams if required params are present. // validate if required params are presend.
func validateParams() error { func validate() error {
if cmd == "" { if cmdPath == "" {
return errors.New("missing required 'cmd' parameter") return errors.New("Missing required 'cmd' parameter")
}
if commandExecDir == "" {
return errors.New("Missing required 'dir' parameter")
} }
return nil return nil