diff --git a/config.hcl b/config.hcl new file mode 100644 index 0000000..e69de29 diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..9dabc77 --- /dev/null +++ b/config/config.go @@ -0,0 +1,51 @@ +package config + +import ( + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsimple" +) + +type OpaRule struct { + Query string `hcl:"query"` + File string `hcl:"file"` + Module string `hcl:"module"` + Binding string `hcl:"binding"` +} + +type Validator struct { + Type string `hcl:"type,label"` + Name string `hcl:"name,label"` + OpaRules []OpaRule `hcl:"rule,block"` +} + +type NomadServer struct { + Address string `hcl:"address"` +} +type Config struct { + Port int `hcl:"port,optional"` + Bind string `hcl:"bind,optional"` + Nomad *NomadServer `hcl:"nomad,block"` + Validators []Validator `hcl:"validator,block"` +} + +func DefaultConfig() *Config { + c := &Config{ + Port: 6464, + Bind: "0.0.0.0", + Nomad: &NomadServer{ + Address: "http://localhost:4646", + }, + Validators: []Validator{}, + } + return c +} +func LoadConfig(name string) (*Config, error) { + + c := DefaultConfig() + evalContext := &hcl.EvalContext{} + err := hclsimple.DecodeFile(name, evalContext, c) + if err != nil { + return nil, err + } + return c, nil +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 0000000..189fd83 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,54 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadConfig(t *testing.T) { + type args struct { + name string + } + port := 6464 + bind := "0.0.0.0" + nomadAddr := "http://localhost:4646" + tests := []struct { + name string + args args + want *Config + wantErr bool + }{ + { + name: "default config", + args: args{name: "testdata/simple.hcl"}, + want: &Config{ + Port: port, + Bind: bind, + Nomad: &NomadServer{ + Address: nomadAddr, + }, + Validators: []Validator{}, + }, + }, + { + name: "fail if no config file", + args: args{name: "testdata/doesnotexist.hcl"}, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config, err := LoadConfig(tt.args.name) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.want, config) + } + + }) + } +} diff --git a/config/testdata/simple.hcl b/config/testdata/simple.hcl new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod index 7a5daa9..5f15475 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ require ( require ( github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/agext/levenshtein v1.2.1 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect @@ -21,6 +23,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.0 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/hashicorp/consul/api v1.15.3 // indirect github.com/hashicorp/cronexpr v1.1.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -52,6 +55,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -65,6 +69,7 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect + github.com/zclconf/go-cty v1.12.1 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/exp v0.0.0-20220921164117-439092de6870 // indirect @@ -84,6 +89,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/hashicorp/hcl/v2 v2.16.1 github.com/hashicorp/nomad v1.4.3 github.com/open-policy-agent/opa v0.49.0 github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index f29f2ed..c4bf9ed 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,16 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -63,7 +67,7 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -93,6 +97,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/hashicorp/consul/api v1.15.3 h1:WYONYL2rxTXtlekAqblR2SCdJsizMDIj/uXb5wNy9zU= github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= @@ -157,6 +162,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.1-vault-3 h1:V95v5KSTu6DB5huDSKiq4uAfILEuNigK/+qPET6H/Mg= github.com/hashicorp/hcl v1.0.1-vault-3/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/hashicorp/hcl/v2 v2.16.1 h1:BwuxEMD/tsYgbhIW7UuI3crjovf3MzuFWiVgiv57iHg= +github.com/hashicorp/hcl/v2 v2.16.1/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= @@ -197,6 +204,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -228,6 +236,8 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -287,6 +297,7 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/shoenig/test v0.4.3 h1:3+CjrpqCwtL08S0wZQilu9WWR/S2CdsLKhHjbJqPj/I= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -316,6 +327,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= diff --git a/main.go b/main.go index 3069798..c04f95e 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "encoding/json" + "fmt" "io" "net/http" "net/http/httputil" @@ -14,23 +15,15 @@ import ( "github.com/hashicorp/nomad/nomad/structs" "github.com/mxab/nacp/admissionctrl" "github.com/mxab/nacp/admissionctrl/mutator" + "github.com/mxab/nacp/config" ) -type NomadServer struct { - Address string -} -type Config struct { - Port int - Bind string - nomad NomadServer -} - -func NewServer(c Config, appLogger hclog.Logger) *httputil.ReverseProxy { +func NewServer(c *config.Config, appLogger hclog.Logger) func(http.ResponseWriter, *http.Request) { // create a reverse proxy that catches "/v1/jobs" post calls // and forwards them to the jobs service // create a new reverse proxy - backend, err := url.Parse(c.nomad.Address) + backend, err := url.Parse(c.Nomad.Address) if err != nil { panic(err) } @@ -43,10 +36,14 @@ func NewServer(c Config, appLogger hclog.Logger) *httputil.ReverseProxy { ) proxy := httputil.NewSingleHostReverseProxy(backend) + originalDirector := proxy.Director proxy.Director = func(r *http.Request) { originalDirector(r) + } + + return func(w http.ResponseWriter, r *http.Request) { // only check for the header if it is a POST request and path is /v1/jobs if isCreate(r) || isUpdate(r) { @@ -80,9 +77,9 @@ func NewServer(c Config, appLogger hclog.Logger) *httputil.ReverseProxy { r.Body = io.NopCloser(bytes.NewBuffer(data)) } - + proxy.ServeHTTP(w, r) } - return proxy + } func isCreate(r *http.Request) bool { return r.Method == "POST" && r.URL.Path == "/v1/jobs" @@ -92,6 +89,7 @@ func isUpdate(r *http.Request) bool { } // https://www.codedodle.com/go-reverse-proxy-example.html +// https://joshsoftware.wordpress.com/2021/05/25/simple-and-powerful-reverseproxy-in-go/ func main() { appLogger := hclog.New(&hclog.LoggerOptions{ @@ -105,13 +103,15 @@ func main() { // and forwards them to the jobs service // create a new reverse proxy - c := Config{ - Port: 8080, - Bind: "0.0.0.0", - nomad: NomadServer{ - Address: "http://localhost:4646", - }, + c, err := config.LoadConfig("config.hcl") + if err != nil { + appLogger.Error("Failed to load config", "error", err) + os.Exit(1) } - NewServer(c, appLogger) + proxy := NewServer(c, appLogger) + + http.HandleFunc("/", proxy) + appLogger.Info("Started Nomad Admission Control Proxy", "bind", c.Bind, "port", c.Port) + appLogger.Error("%s", http.ListenAndServe(fmt.Sprintf("%s:%d", c.Bind, c.Port), nil)) } diff --git a/main_test.go b/main_test.go index 3915b41..7bcf0a7 100644 --- a/main_test.go +++ b/main_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/nomad/nomad/structs" + "github.com/mxab/nacp/config" "github.com/mxab/nacp/testutil" "github.com/stretchr/testify/assert" ) @@ -69,18 +70,17 @@ func TestProxyTableDriven(t *testing.T) { defer nomadDummy.Close() // Use Client & URL from our local test server - c := Config{ + c := &config.Config{ Port: 8080, Bind: "0.0.0.0", - nomad: NomadServer{ + Nomad: &config.NomadServer{ Address: nomadDummy.URL, }, + Validators: []config.Validator{}, } proxy := NewServer(c, hclog.NewNullLogger()) - //http.Handle("/", proxy) - - proxyServer := httptest.NewServer(proxy) + proxyServer := httptest.NewServer(http.HandlerFunc(proxy)) defer proxyServer.Close() jobRequest := structs.JobRegisterRequest{