Merge pull request #137 from cloud-native-application/gin-route

RESTful framework
This commit is contained in:
Zheng Xi Zhou
2020-08-12 12:10:02 +08:00
committed by GitHub
18 changed files with 635 additions and 6 deletions
+1
View File
@@ -32,3 +32,4 @@ vendor/
.vscode
pkg/test/vela
tmp/
+60
View File
@@ -0,0 +1,60 @@
package main
import (
"context"
"flag"
"io"
"os"
"os/signal"
"syscall"
"time"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"github.com/cloud-native-application/rudrx/pkg/server"
)
func main() {
var logFilePath string
var logRetainDate int
var logCompress, development bool
flag.StringVar(&logFilePath, "log-file-path", "", "The log file path.")
flag.IntVar(&logRetainDate, "log-retain-date", 7, "The number of days of logs history to retain.")
flag.BoolVar(&logCompress, "log-compress", true, "Enable compression on the rotated logs.")
flag.BoolVar(&development, "development", true, "Development mode.")
flag.Parse()
// setup logging
var w io.Writer
if len(logFilePath) > 0 {
w = zapcore.AddSync(&lumberjack.Logger{
Filename: logFilePath,
MaxAge: logRetainDate, // days
Compress: logCompress,
})
} else {
w = os.Stdout
}
ctrl.SetLogger(zap.New(func(o *zap.Options) {
o.Development = development
o.DestWritter = w
}))
//Setup RESTful server
server := server.ApiServer{}
server.Launch()
// handle signal: SIGTERM(15), SIGKILL(9)
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGTERM)
signal.Notify(sc, syscall.SIGKILL)
select {
case <-sc:
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
server.Shutdown(ctx)
}
}
-1
View File
@@ -51,7 +51,6 @@ func main() {
rand.Seed(time.Now().UnixNano())
command := newCommand()
logs.InitLogs()
defer logs.FlushLogs()
+5 -3
View File
@@ -313,11 +313,13 @@ type definitionName struct {
## Repo related API
These API operate on definition files stored in a git repo. It could be a git repo on the local file system.
The only valid `categoryName` are `workload`,`trait` and `scope`
They all have a `repoURL` query parameter that stores the git repo's URL so that the server is stateless.
### Update
**URL** : `/api/category/${definitionType}/${definitionName}`
**URL** : `/api/category/${categoryName}/${definitionName}`
**Method** : `PUT`
@@ -336,7 +338,7 @@ type oamDefinition struct {
### GET
**URL** : `/api/category/${definitionType}/${definitionName}`
**URL** : `/api/category/${categoryName}/${definitionName}`
**Method** : `Get`
@@ -353,7 +355,7 @@ type definition struct {
### List
**URL** : `/api/category/${definitionType}`
**URL** : `/api/category/${categoryName}`
**Method** : `Get`
+5 -2
View File
@@ -7,13 +7,16 @@ require (
github.com/crossplane/crossplane-runtime v0.8.0
github.com/crossplane/oam-kubernetes-runtime v0.0.8
github.com/ghodss/yaml v1.0.0
github.com/gin-gonic/gin v1.6.3
github.com/gosuri/uitable v0.0.4
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1
github.com/pkg/errors v0.9.1
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
gopkg.in/yaml.v2 v2.2.8
go.uber.org/zap v1.10.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gotest.tools v2.2.0+incompatible
helm.sh/helm/v3 v3.2.4
@@ -23,7 +26,7 @@ require (
k8s.io/cli-runtime v0.18.6
k8s.io/client-go v0.18.6
k8s.io/klog v1.0.0
k8s.io/kubectl v0.18.6
k8s.io/kubectl v0.18.6 // indirect
rsc.io/letsencrypt v0.0.3 // indirect
sigs.k8s.io/controller-runtime v0.6.0
)
+28
View File
@@ -209,6 +209,10 @@ github.com/gertd/go-pluralize v0.1.7/go.mod h1:O4eNeeIf91MHh1GJ2I47DNtaesm66NYvj
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -269,6 +273,14 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@@ -308,6 +320,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
@@ -397,6 +411,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -419,6 +435,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
@@ -442,6 +460,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -573,6 +593,7 @@ github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlp
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -622,7 +643,11 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@@ -770,6 +795,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -868,6 +895,7 @@ gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+36
View File
@@ -0,0 +1,36 @@
package server
import (
"context"
"net/http"
"time"
ctrl "sigs.k8s.io/controller-runtime"
)
type ApiServer struct {
server *http.Server
}
func (s *ApiServer) Launch() {
s.server = &http.Server{
Addr: ":8080",
Handler: setupRoute(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
s.server.SetKeepAlivesEnabled(true)
go (func() error {
err := s.server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
ctrl.Log.Error(err, "failed to start the server")
}
return err
})()
}
func (s *ApiServer) Shutdown(ctx context.Context) error {
ctrl.Log.Info("sever shutting down")
return s.server.Shutdown(ctx)
}
+15
View File
@@ -0,0 +1,15 @@
package apis
import "k8s.io/apimachinery/pkg/runtime"
type Environment struct {
EnvironmentName string `json:"environmentName" binding:"required,min=1,max=32"`
Namespace string `json:"namespace" binding:"required,min=1,max=32"`
}
type AppConfig struct {
AppConfigName string `json:"appName" binding:"required,max=64"`
Definition runtime.RawExtension `json:"definition" binding:"required"`
DefinitionType string `json:"definitionType" binding:"required,max=32"`
DefinitionName string `json:"definitionName" binding:"required,max=64"`
}
+32
View File
@@ -0,0 +1,32 @@
package handler
import (
"github.com/gin-gonic/gin"
ctrl "sigs.k8s.io/controller-runtime"
)
const querymodeKey = "appQuerymode"
// Apps related handlers
func CreateApps(c *gin.Context) {
}
func UpdateApps(c *gin.Context) {
}
func GetApps(c *gin.Context) {
envName := c.Param("envName")
appName := c.Param("appName")
queryMode, found := c.GetQuery(querymodeKey)
if !found {
panic("no repoUrl in update")
}
ctrl.Log.Info("Get an application request for", "envName", envName, "appName", appName, "queryMdoe", queryMode)
}
func ListApps(c *gin.Context) {
}
func DeleteApps(c *gin.Context) {
}
+42
View File
@@ -0,0 +1,42 @@
package handler
import (
"net/http"
"github.com/gin-gonic/gin"
ctrl "sigs.k8s.io/controller-runtime"
"github.com/cloud-native-application/rudrx/pkg/server/apis"
"github.com/cloud-native-application/rudrx/pkg/server/util"
)
// ENV related handlers
func CreateEnv(c *gin.Context) {
var envConfig apis.Environment
if err := c.ShouldBindJSON(&envConfig); err != nil {
util.HandleError(c, util.InvalidArgument, "the create environment request body is invalid")
return
}
ctrl.Log.Info("Get a create environment request", "env", envConfig)
// TODO: implement this
c.Status(http.StatusOK)
}
func GetEnv(c *gin.Context) {
envName := c.Param("envName")
ctrl.Log.Info("Get a get environment request", "envName", envName)
// TODO: implement this
c.JSON(http.StatusOK, apis.Environment{
EnvironmentName: envName,
Namespace: "test",
})
}
func ListEnv(c *gin.Context) {
}
func DeleteEnv(c *gin.Context) {
}
+6
View File
@@ -0,0 +1,6 @@
package handler
import "github.com/gin-gonic/gin"
func GetVersion(c *gin.Context) {
}
+41
View File
@@ -0,0 +1,41 @@
package handler
import (
"github.com/gin-gonic/gin"
ctrl "sigs.k8s.io/controller-runtime"
)
// repo related handlers
const (
repoUrlKey = "repoUrl"
)
func UpdateDefinition(c *gin.Context) {
//ctx := util.GetContext(c)
categoryName := c.Param("categoryName")
definitionName := c.Param("definitionName")
repoUrl, found := c.GetQuery(repoUrlKey)
if !found {
panic("no repoUrl in update")
}
ctrl.Log.Info("Get Update Repo Definition request for", "categoryName", categoryName, "definitionName", definitionName, "repoUrl", repoUrl)
}
func GetDefinition(c *gin.Context) {
categoryName := c.Param("categoryName")
definitionName := c.Param("definitionName")
repoUrl, found := c.GetQuery(repoUrlKey)
if !found {
panic("no repoUrl in update")
}
ctrl.Log.Info("Get Repo Definition request for", "categoryName", categoryName, "definitionName", definitionName, "repoUrl", repoUrl)
}
func ListDefinition(c *gin.Context) {
categoryName := c.Param("categoryName")
repoUrl, found := c.GetQuery(repoUrlKey)
if !found {
panic("no repoUrl in update")
}
ctrl.Log.Info("Get Repo Definition request for", "categoryName", categoryName, repoUrl)
}
+19
View File
@@ -0,0 +1,19 @@
package handler
import "github.com/gin-gonic/gin"
// Scope related handlers
func CreateScope(c *gin.Context) {
}
func UpdateScope(c *gin.Context) {
}
func GetScope(c *gin.Context) {
}
func ListScope(c *gin.Context) {
}
func DeleteScope(c *gin.Context) {
}
+19
View File
@@ -0,0 +1,19 @@
package handler
import "github.com/gin-gonic/gin"
// Trait related handlers
func CreateTrait(c *gin.Context) {
}
func UpdateTrait(c *gin.Context) {
}
func GetTrait(c *gin.Context) {
}
func ListTrait(c *gin.Context) {
}
func DeleteTrait(c *gin.Context) {
}
+19
View File
@@ -0,0 +1,19 @@
package handler
import "github.com/gin-gonic/gin"
// Workload related handlers
func CreateWorkload(c *gin.Context) {
}
func UpdateWorkload(c *gin.Context) {
}
func GetWorkload(c *gin.Context) {
}
func ListWorkload(c *gin.Context) {
}
func DeleteWorkload(c *gin.Context) {
}
+98
View File
@@ -0,0 +1,98 @@
package server
import (
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/cloud-native-application/rudrx/pkg/server/handler"
"github.com/cloud-native-application/rudrx/pkg/server/util"
)
// setup the gin http server handler
func setupRoute() http.Handler {
// create the router
router := gin.New()
loggerConfig := gin.LoggerConfig{
Output: os.Stdout,
Formatter: func(param gin.LogFormatterParams) string {
return fmt.Sprintf("%v | %3d | %13v | %15s | %-7s %s | %s\n",
param.TimeStamp.Format("2006/01/02 - 15:04:05"),
param.StatusCode,
param.Latency,
param.ClientIP,
param.Method,
param.Path,
param.ErrorMessage,
)
},
}
router.Use(gin.LoggerWithConfig(loggerConfig))
router.Use(util.SetRequestID())
router.Use(util.SetContext())
router.Use(gin.Recovery())
router.Use(util.ValidateHeaders())
// all requests start with /api
api := router.Group(util.RootPath)
// env related operation
envs := api.Group(util.EnvironmentPath)
{
envs.POST("/", handler.CreateEnv)
envs.GET("/:envName", handler.GetEnv)
envs.GET("/", handler.ListEnv)
envs.DELETE("/:envName", handler.DeleteEnv)
// app related operation
apps := envs.Group("/:envName/apps")
{
apps.POST("/", handler.CreateApps)
apps.GET("/:appName", handler.GetApps)
apps.PUT("/:appName", handler.UpdateApps)
apps.GET("/", handler.ListApps)
apps.DELETE("/:appName", handler.DeleteApps)
}
}
// workload related api
workload := api.Group(util.WorkloadDefinitionPath)
{
workload.POST("/", handler.CreateWorkload)
workload.GET("/:workloadName", handler.GetWorkload)
workload.PUT("/:workloadName", handler.UpdateWorkload)
workload.GET("/", handler.ListWorkload)
workload.DELETE("/:workloadName", handler.DeleteWorkload)
}
// trait related api
trait := api.Group(util.TraitDefinitionPath)
{
trait.POST("/", handler.CreateTrait)
trait.GET("/:traitName", handler.GetTrait)
trait.PUT("/:traitName", handler.UpdateTrait)
trait.GET("/", handler.ListTrait)
trait.DELETE("/:traitName", handler.DeleteTrait)
}
// scope related api
scopes := api.Group(util.ScopeDefinitionPath)
{
scopes.POST("/", handler.CreateScope)
scopes.GET("/:scopeName", handler.GetScope)
scopes.PUT("/:scopeName", handler.UpdateScope)
scopes.GET("/", handler.ListScope)
scopes.DELETE("/:scopeName", handler.DeleteScope)
}
// scope related api
repo := api.Group(util.RepoPath)
{
repo.GET("/:categoryName/:definitionName", handler.GetDefinition)
repo.PUT("/:categoryName/:definitionName", handler.UpdateDefinition)
repo.GET("/:categoryName", handler.ListDefinition)
}
// version
api.GET(util.VersionPath, handler.GetVersion)
// default
router.NoRoute(util.NoRoute())
return router
}
+81
View File
@@ -0,0 +1,81 @@
package util
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
ctrl "sigs.k8s.io/controller-runtime"
)
// Code defines the error code type.
type Code int
// Be careful, below constants must be added to the errorDetails map.
// All code should be defined between StartMarker and EndMarker
const (
startMarker Code = iota
PathNotSupported
InvalidArgument
UnsupportedMediaType
// End marker
endMarker
)
type errorDetail struct {
ID string
StatusCode int
Message string
}
var errorDetails = map[Code]errorDetail{
PathNotSupported: {"PathNotSupported", http.StatusNotFound, "'%s' against '%s' is not supported"},
InvalidArgument: {"InvalidArgument", http.StatusBadRequest, "%s"},
UnsupportedMediaType: {"UnsupportedMediaType", http.StatusUnsupportedMediaType, "content type should be 'application/json' or 'application/octet-stream'"},
}
// ID returns the error ID.
func (c Code) ID() string {
return errorDetails[c].ID
}
// StatusCode returns the http status code.
func (c Code) StatusCode() int {
return errorDetails[c].StatusCode
}
// Message returns the detailed error message.
func (c Code) Message() string {
return errorDetails[c].Message
}
// ConstructError returns a new OpError.
func ConstructError(ec Code, a ...interface{}) error {
msg := ""
// the number of keys should be equal to the number of placeholders defined in ErrorCode.Message.
c := strings.Count(ec.Message(), "%")
if a == nil && c > 0 ||
a != nil && (c != len(a) || a[0] == nil) {
ctrl.Log.Error(fmt.Errorf("Args '%v' do not match placeholders in the msg '%s'", a, ec.Message()),
"Invalid error message argument")
} else if a == nil || len(a) == 0 || a[0] == nil {
msg = ec.Message()
} else {
msg = fmt.Sprintf(ec.Message(), a...)
}
return errors.New(msg)
}
// - use setErrorAndAbort to abort the rest of the handlers, mostly called in middleware
func SetErrorAndAbort(c *gin.Context, code Code, msg ...interface{}) {
// Calling abort so no handlers and middlewares will be executed.
c.AbortWithStatusJSON(code.StatusCode(), gin.H{"error": ConstructError(code, msg...).Error()} )
}
func HandleError(c *gin.Context, code Code, msg ...interface{}) {
c.JSON(code.StatusCode(), gin.H{"error": ConstructError(code, msg...).Error()} )
}
+128
View File
@@ -0,0 +1,128 @@
package util
import (
"context"
"mime"
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
"go.uber.org/zap/zapcore"
)
// Header Keys
const (
// ContextKey is used as key to set/get context.
ContextKey = "context"
// HeaderRequestID is used as key to set/get request id.
HeaderRequestID = "x-fc-request-id"
// ContentTypeJSON : json
ContentTypeJSON = "application/json"
// ContentTypeOctetStream: octet stream
ContentTypeOctetStream = "application/octet-stream"
// HeaderTraceID is header name for trace id.
HeaderTraceID = "x-fc-trace-id"
HeaderContentType = "content-Type"
HeaderContentLength = "content-Length"
// ServiceLogFields shared key service log fields
ServiceLogFields = "ServiceLogFields"
// HeaderClientIP is the real IP of the remote client
HeaderClientIP = "clientIP"
)
const (
// RESTful API paths
RootPath = "/api"
EnvironmentPath = "/envs"
ApplicationPath = "/apps"
WorkloadDefinitionPath = "/workloads"
ScopeDefinitionPath = "/scopes"
TraitDefinitionPath = "/traits"
RepoPath = "/category"
VersionPath = "/version"
)
const contextLoggerKey = "logger"
//NoRoute is a handler which is invoked when there is no route matches.
func NoRoute() gin.HandlerFunc {
return func(c *gin.Context) {
SetErrorAndAbort(c, PathNotSupported, c.Request.Method, c.Request.URL.Path)
}
}
//generateRequestID :Get request id
func generateRequestID() string {
return uuid.NewV4().String()
}
// SetRequestID ...
func SetRequestID() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := ""
traceID := ""
if traceID = c.Request.Header.Get(HeaderTraceID); traceID != "" {
requestID = traceID
} else if requestID = c.Request.Header.Get(HeaderRequestID); requestID != "" {
traceID = requestID
} else {
requestID = generateRequestID()
traceID = requestID
}
c.Set(HeaderRequestID, requestID)
c.Set(HeaderClientIP, c.ClientIP())
c.Set(HeaderTraceID, traceID)
}
}
// SetContext :Set context metadata for request
// Before get request
func SetContext() gin.HandlerFunc {
return func(c *gin.Context) {
reqID := c.MustGet(HeaderRequestID).(string)
ctx, cancel := context.WithCancel(c.Request.Context())
fields := make(map[string]zapcore.Field)
mctx := context.WithValue(ctx, ServiceLogFields, fields)
fctx := context.WithValue(mctx, HeaderRequestID, reqID)
c.Set(ContextKey, fctx)
c.Next()
cancel()
}
}
// get the context from the gin context
func GetContext(c *gin.Context) context.Context {
return c.MustGet(ContextKey).(context.Context)
}
// ValidateHeaders validates the common headers.
//
// It reports one problem at a time.
func ValidateHeaders() gin.HandlerFunc {
return func(c *gin.Context) {
// It's ok to not specify Content-Type header, but it should be correct if it's specified.
contentType := c.Request.Header.Get(HeaderContentType)
if len(contentType) != 0 {
mType, _, err := mime.ParseMediaType(contentType)
if err != nil {
SetErrorAndAbort(c, UnsupportedMediaType)
return
}
switch mType {
case ContentTypeJSON, ContentTypeOctetStream:
// Passes.
default:
SetErrorAndAbort(c, UnsupportedMediaType)
return
}
}
}
}