Initial version

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2020-10-02 01:02:41 +02:00
commit 89916ea9b5
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
6 changed files with 158 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
scripts

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
Copyright (c) [year] [fullname]
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.

11
README.md Normal file
View file

@ -0,0 +1,11 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/Luzifer/local-functions)](https://goreportcard.com/report/github.com/Luzifer/local-functions)
![](https://badges.fyi/github/license/Luzifer/local-functions)
![](https://badges.fyi/github/downloads/Luzifer/local-functions)
![](https://badges.fyi/github/latest-release/Luzifer/local-functions)
![](https://knut.in/project-status/local-functions)
# Luzifer / local-functions
`local-functions` is intended as the opposite of Cloud-Functions: Run scripts on the local machine through HTTP calls.
**Be aware:** This will expose scripts in a certain folder on your machine. This might cause trouble for you! So you really should only expose the server on **localhost** and ensure nobody else is able to access the API.

10
go.mod Normal file
View file

@ -0,0 +1,10 @@
module github.com/Luzifer/local-functions
go 1.15
require (
github.com/Luzifer/go_helpers/v2 v2.11.0
github.com/Luzifer/rconfig/v2 v2.2.1
github.com/gorilla/mux v1.8.0
github.com/sirupsen/logrus v1.7.0
)

24
go.sum Normal file
View file

@ -0,0 +1,24 @@
github.com/Luzifer/go_helpers v1.4.0 h1:Pmm058SbYewfnpP1CHda/zERoAqYoZFiBHF4l8k03Ko=
github.com/Luzifer/go_helpers/v2 v2.11.0 h1:IEVuDEAq2st1sjQNaaTX8TxZ2LsXP0qGeqb2uzYZCIo=
github.com/Luzifer/go_helpers/v2 v2.11.0/go.mod h1:ZnWxPjyCdQ4rZP3kNiMSUW/7FigU1X9Rz8XopdJ5ZCU=
github.com/Luzifer/rconfig v1.2.0 h1:waD1sqasGVSQSrExpLrQ9Q1JmMaltrS391VdOjWXP/I=
github.com/Luzifer/rconfig/v2 v2.2.1 h1:zcDdLQlnlzwcBJ8E0WFzOkQE1pCMn3EbX0dFYkeTczg=
github.com/Luzifer/rconfig/v2 v2.2.1/go.mod h1:OKIX0/JRZrPJ/ZXXWklQEFXA6tBfWaljZbW37w+sqBw=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
github.com/leekchan/gtf v0.0.0-20190214083521-5fba33c5b00b/go.mod h1:thNruaSwydMhkQ8dXzapABF9Sc1Tz08ZBcDdgott9RA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19 h1:WB265cn5OpO+hK3pikC9hpP1zI/KTwmyMFKloW9eOVc=
gopkg.in/validator.v2 v2.0.0-20180514200540-135c24b11c19/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

90
main.go Normal file
View file

@ -0,0 +1,90 @@
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/Luzifer/go_helpers/v2/env"
httpHelper "github.com/Luzifer/go_helpers/v2/http"
"github.com/Luzifer/rconfig/v2"
)
var (
cfg = struct {
Listen string `flag:"listen" default:"127.0.0.1:3000" description:"Port/IP to listen on"`
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
ScriptDir string `flag:"script-dir" default:"./scripts" description:"Directory to execute the script / binary from"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
version = "dev"
)
func init() {
rconfig.AutoEnv(true)
if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err)
}
if cfg.VersionAndExit {
fmt.Printf("local-functions %s\n", version)
os.Exit(0)
}
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
log.WithError(err).Fatal("Unable to parse log level")
} else {
log.SetLevel(l)
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/{script}", handleScriptCall)
var h http.Handler = r
h = httpHelper.NewHTTPLogHandler(h)
http.ListenAndServe(cfg.Listen, h)
}
func handleScriptCall(w http.ResponseWriter, r *http.Request) {
var (
vars = mux.Vars(r)
script = path.Join(cfg.ScriptDir, vars["script"])
)
if _, err := os.Stat(script); vars["script"] == "" || os.IsNotExist(err) {
http.Error(w, "Not found", http.StatusNotFound)
return
}
var (
stdout = new(bytes.Buffer)
cmd = exec.Command(script)
)
cmd.Stdout = stdout
cmd.Stderr = os.Stderr
cmd.Stdin = r.Body
cmd.Env = env.MapToList(map[string]string{
"ACCEPT": r.Header.Get("Accept"),
"CONTENT_TYPE": r.Header.Get("Content-Type"),
"METHOD": r.Method,
})
if err := cmd.Run(); err != nil {
http.Error(w, "Script execution failed, see log", http.StatusInternalServerError)
return
}
io.Copy(w, stdout)
}