diff --git a/.gitignore b/.gitignore index b003483..227b35d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,11 +32,14 @@ # .idea/artifacts # .idea/compiler.xml # .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml # .idea/modules # *.iml # *.ipr +.idea/.gitignore +.idea/modules.xml +.idea/*.iml +.idea/*.xml +.idea/vcs.xml # CMake cmake-build-*/ @@ -85,6 +88,11 @@ fabric.properties # Output of the go coverage tool, specifically when used with LiteIDE *.out +# Project binary file +proksi-http +proksi-sql +proksi-redis + # Dependency directories (remove the comment below to include it) vendor/ diff --git a/Makefile b/Makefile index 12c724d..304e2fb 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,12 @@ build-http: build-linux-http: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -installsuffix cgo -o proksi-http ./http +build-redis: + CGO_ENABLED=0 go build -a -installsuffix cgo -o proksi-redis ./redis + +build-linux-redis: + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -installsuffix cgo -o proksi-redis ./redis + test: go test ./... diff --git a/go.mod b/go.mod index 08376b0..0a8db63 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,18 @@ go 1.18 require ( github.com/elastic/go-elasticsearch/v8 v8.3.0 + github.com/go-redis/redis/v8 v8.11.5 github.com/knadh/koanf v1.4.3 github.com/prometheus/client_golang v1.11.1 + github.com/tidwall/redcon v1.6.0 github.com/tidwall/sjson v1.2.5 go.uber.org/zap v1.22.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/elastic/elastic-transport-go/v8 v8.0.0-20211216131617-bbee439d559c // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect @@ -24,12 +27,13 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect + github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/gjson v1.14.3 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect google.golang.org/protobuf v1.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 53fedab..bcc021c 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -38,6 +39,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elastic/elastic-transport-go/v8 v8.0.0-20211216131617-bbee439d559c h1:onA2RpIyeCPvYAj1LFYiiMTrSpqVINWMfYFRS7lofJs= github.com/elastic/elastic-transport-go/v8 v8.0.0-20211216131617-bbee439d559c/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= @@ -62,6 +65,8 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp 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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -194,7 +199,10 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= @@ -246,6 +254,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= +github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -254,6 +265,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/redcon v1.6.0 h1:ekkYf2xwk1+VmyTVrefZElJC71EK/1JOLwlGSllmPIk= +github.com/tidwall/redcon v1.6.0/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -301,6 +314,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -342,14 +356,16 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -402,6 +418,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -409,6 +426,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/config/redis.go b/internal/config/redis.go new file mode 100644 index 0000000..b477317 --- /dev/null +++ b/internal/config/redis.go @@ -0,0 +1,66 @@ +package config + +import ( + "github.com/knadh/koanf/parsers/yaml" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/providers/structs" + "go.uber.org/zap" + + "github.com/snapp-incubator/proksi/internal/logging" +) + +var ( + // Redis is the config for ProksiRedis + Redis *RedisConfig +) + +var defaultRedis = RedisConfig{ + MainFrontend: Frontend{ + Bind: "127.0.0.1:6380", + }, + TestFrontend: Frontend{ + Bind: "127.0.0.1:6381", + }, + Backend: Backend{ + Address: "127.0.0.1:6379", + }, +} + +// RedisConfig represent config of the ProksiRedis. +type RedisConfig struct { + MainFrontend Frontend `koanf:"main_frontend"` + TestFrontend Frontend `koanf:"test_frontend"` + Backend Backend `koanf:"backend"` +} + +type Frontend struct { + Bind string `koanf:"bind"` +} + +type Backend struct { + Address string `koanf:"address"` +} + +// LoadRedis function will load the file located in path and return the parsed config. This function will panic on errors +func LoadRedis(path string) *RedisConfig { + // Load default config in the beginning + err := k.Load(structs.Provider(defaultRedis, "koanf"), nil) + if err != nil { + logging.L.Fatal("error in loading the default config", zap.Error(err)) + } + + // Load YAML config and merge into the previously loaded config. + err = k.Load(file.Provider(path), yaml.Parser()) + if err != nil { + logging.L.Fatal("error in loading the config file", zap.Error(err)) + } + + var c RedisConfig + err = k.Unmarshal("", &c) + if err != nil { + logging.L.Fatal("error in unmarshalling the config file", zap.Error(err)) + } + + Redis = &c + return &c +} diff --git a/redis/config.example.yaml b/redis/config.example.yaml new file mode 100644 index 0000000..d101d0e --- /dev/null +++ b/redis/config.example.yaml @@ -0,0 +1,8 @@ +main_frontend: + bind: "127.0.0.1:6380" + +test_frontend: + bind: "127.0.0.1:6381" + +backend: + address: "127.0.0.1:6379" diff --git a/redis/handler.go b/redis/handler.go new file mode 100644 index 0000000..73299e1 --- /dev/null +++ b/redis/handler.go @@ -0,0 +1,80 @@ +package main + +import ( + "context" + "strconv" + "sync" + "time" + + "github.com/go-redis/redis/v8" + "github.com/tidwall/redcon" +) + +type Proxy struct { + ctx context.Context + mux sync.RWMutex + mainClient *redis.Client + cache map[string]string + errSig chan bool +} + +func NewProxy() *Proxy { + return &Proxy{ + ctx: context.Background(), + cache: make(map[string]string), + errSig: make(chan bool), + } +} + +func (p *Proxy) ConnectToServer(address string) { + if p.mainClient != nil { + return + } + + opts := &redis.Options{Addr: address, DB: 0} + p.mainClient = redis.NewClient(opts) +} + +func (p *Proxy) ping(conn redcon.Conn, cmd redcon.Command) { + result, _ := p.mainClient.Ping(p.ctx).Result() + p.cache = map[string]string{string(cmd.Args[0]): result} + + conn.WriteString(result) +} + +func (p *Proxy) set(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) < 3 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + expiration, _ := strconv.ParseInt(string(cmd.Args[3]), 10, 64) + result, _ := p.mainClient.Set(p.ctx, string(cmd.Args[1]), string(cmd.Args[2]), time.Duration(expiration)).Result() + p.cache = map[string]string{string(cmd.Args[0]): result} + + conn.WriteString(result) +} + +func (p *Proxy) get(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + result, _ := p.mainClient.Get(p.ctx, string(cmd.Args[1])).Result() + p.cache = map[string]string{string(cmd.Args[0]): result} + + conn.WriteString(result) +} + +func (p *Proxy) delete(conn redcon.Conn, cmd redcon.Command) { + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + + result, _ := p.mainClient.Del(p.ctx, string(cmd.Args[1])).Result() + p.cache = map[string]string{string(cmd.Args[0]): strconv.Itoa(int(result))} + + conn.WriteInt(int(result)) +} diff --git a/redis/redis.go b/redis/redis.go new file mode 100644 index 0000000..05a4204 --- /dev/null +++ b/redis/redis.go @@ -0,0 +1,125 @@ +package main + +import ( + "flag" + "log" + "strconv" + + "github.com/tidwall/redcon" + + "github.com/snapp-incubator/proksi/internal/config" +) + +type ServerType string + +const ( + Main ServerType = "main" + Test ServerType = "test" +) + +type FunctionType string + +const ( + Del FunctionType = "del" +) + +var ( + help bool // Indicates whether to show the help or not + configPath string // Path of config file +) + +func init() { + flag.BoolVar(&help, "help", false, "Show help") + flag.StringVar(&configPath, "config", "", "The path of config file") + + // Parse the terminal flags + flag.Parse() +} + +func main() { + // Usage Demo + if help { + flag.Usage() + return + } + + c := config.LoadRedis(configPath) + + proxy := NewProxy() + + proxy.ConnectToServer(c.Backend.Address) + + errSig := make(chan bool) + + log.Printf("proxing from %v to %v\n", c.TestFrontend.Bind, c.Backend.Address) + + go proxy.serve(Main, c.MainFrontend.Bind, errSig) + go proxy.serve(Test, c.TestFrontend.Bind, errSig) + <-proxy.errSig +} + +// serve implements the Redis server. +func (p *Proxy) serve(serverType ServerType, address string, errSig chan bool) { + var err error + + for { + log.Printf("%s server started at %s", serverType, address) + + if serverType == Main { + err = redcon.ListenAndServe(address, p.mainHandler(), accept, p.closed()) + if err != nil { + log.Print(err) + errSig <- true + return + } + } else { + err = redcon.ListenAndServe(address, p.testHandler(), accept, p.closed()) + if err != nil { + log.Print(err) + errSig <- true + return + } + } + } +} + +// mainHandler is an RESP handler for the main Redis server that responds to command and fills a cache. +func (p *Proxy) mainHandler() func(conn redcon.Conn, cmd redcon.Command) { + mux := redcon.NewServeMux() + mux.HandleFunc("ping", p.ping) + mux.HandleFunc("set", p.set) + mux.HandleFunc("get", p.get) + mux.HandleFunc("del", p.delete) + + return mux.ServeRESP +} + +// testHandler is an RESP handler for the test Redis server that lookup the cache and sends responses. +func (p *Proxy) testHandler() func(conn redcon.Conn, cmd redcon.Command) { + return func(conn redcon.Conn, cmd redcon.Command) { + result, found := p.cache[string(cmd.Args[0])] + + if found { + if string(cmd.Args[0]) == string(Del) { + i, _ := strconv.Atoi(result) + conn.WriteInt(i) + } else { + conn.WriteString(result) + } + } + } +} + +// accept is called to accept or deny the connection. +func accept(conn redcon.Conn) bool { + log.Printf("The connection between %s and %s haas been accepted", conn.NetConn().LocalAddr().String(), conn.RemoteAddr()) + return true +} + +// closed is called when the connection has been closed. +func (p *Proxy) closed() func(conn redcon.Conn, err error) { + return func(conn redcon.Conn, err error) { + log.Printf("The connection between %s, %s has been closed, err: %v", conn.NetConn().LocalAddr().String(), conn.RemoteAddr(), err) + <-p.errSig + } +}