/
webapp.go
110 lines (86 loc) · 3.14 KB
/
webapp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
Licensed under the BSD 3-Clause License (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://opensource.org/licenses/BSD-3-Clause
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package app
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/alron/ginlogr"
"github.com/gin-gonic/gin"
"github.com/go-logr/logr"
"github.com/kubemod/kubemod/core"
)
// KubeModWebApp is the DI container of kubemod web application state.
type KubeModWebApp struct {
log logr.Logger
modRuleStoreItemFactory *core.ModRuleStoreItemFactory
clusterModRulesNamespace core.ClusterModRulesNamespace
}
// NewKubeModWebApp instantiates a kubemod web application.
func NewKubeModWebApp(
webAppAddr string,
enableDevModeLog EnableDevModeLog,
clusterModRulesNamespace core.ClusterModRulesNamespace,
log logr.Logger,
modRuleStoreItemFactory *core.ModRuleStoreItemFactory,
) (*KubeModWebApp, error) {
setupLog := log.WithName("webapp-setup")
setupLog.Info("web app server is starting to listen", "addr", webAppAddr)
if enableDevModeLog {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
r := gin.New()
r.Use(ginlogr.RecoveryWithLogr(log, time.RFC3339, false, true))
app := &KubeModWebApp{
log: log.WithName("webapp"),
modRuleStoreItemFactory: modRuleStoreItemFactory,
clusterModRulesNamespace: clusterModRulesNamespace,
}
// Set up the API routes.
app.setupRoutes(r)
// Run the server - this will block until the process is terminated through a SIGTERM or SIGINT.
run(r, webAppAddr, log)
return app, nil
}
// run starts a web server with the given router as a handler and blocks until the process is terminated.
func run(router *gin.Engine, webAppAddr string, log logr.Logger) {
srv := &http.Server{
Addr: webAppAddr,
Handler: router,
}
// Initializing the server in a goroutine so that it won't block the graceful shutdown handling below.
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error(err, "KubeMod web app failed to listen and serve", "addr", webAppAddr)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Info("shutting down KubeMod web app")
// The context is used to inform the server it has 10 seconds to finish the request it is currently handling.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Error(err, "KubeMod web app forced to shutdown")
}
log.Info("KubeMod web app exited")
}