Skip to content

Commit a71f46a

Browse files
authored
cos-csi-mounter server code refactoring and UT implementation (#187)
* minor fixes Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * fix linter Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * publish dev tag Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> * UTs for server.go Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com> --------- Signed-off-by: Ashima-Ashima1 <Ashima.Ashima1@ibm.com>
1 parent eee0596 commit a71f46a

File tree

7 files changed

+454
-23
lines changed

7 files changed

+454
-23
lines changed

.secrets.baseline

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "go.sum|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2025-06-03T12:41:51Z",
6+
"generated_at": "2025-06-05T09:08:12Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"

cos-csi-mounter/server/fake_server.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//go:build !unit_test
2+
// +build !unit_test
3+
4+
package main
5+
6+
import (
7+
"errors"
8+
"net"
9+
"net/http"
10+
"time"
11+
12+
"github.com/stretchr/testify/mock"
13+
)
14+
15+
// Mock implementations
16+
type MockMounterUtils struct {
17+
mock.Mock
18+
}
19+
20+
func (m *MockMounterUtils) FuseMount(path string, mounter string, args []string) error {
21+
argsCalled := m.Called(path, mounter, args)
22+
return argsCalled.Error(0)
23+
}
24+
25+
func (m *MockMounterUtils) FuseUnmount(path string) error {
26+
argsCalled := m.Called(path)
27+
return argsCalled.Error(0)
28+
}
29+
30+
type fakeListener struct{}
31+
32+
func (d *fakeListener) Accept() (net.Conn, error) {
33+
time.Sleep(100 * time.Millisecond)
34+
return nil, errors.New("simulated shutdown")
35+
}
36+
func (d *fakeListener) Close() error { return nil }
37+
func (d *fakeListener) Addr() net.Addr { return &net.UnixAddr{Name: "fake", Net: "unix"} }
38+
39+
func fakeSetupSocketSuccess() (net.Listener, error) {
40+
return &fakeListener{}, nil
41+
}
42+
43+
func fakeRouter() http.Handler {
44+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
45+
}
46+
47+
func fakeHandleSignals() {}
48+
49+
func fakeSetupSocketFail() (net.Listener, error) {
50+
return nil, errors.New("socket creation error")
51+
}
52+
53+
type MockMounterArgsParser struct {
54+
mock.Mock
55+
}
56+
57+
func (m *MockMounterArgsParser) Parse(request MountRequest) ([]string, error) {
58+
args := m.Called(request)
59+
return args.Get(0).([]string), args.Error(1)
60+
}

cos-csi-mounter/server/server.go

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ import (
1818
"go.uber.org/zap/zapcore"
1919
)
2020

21-
var logger *zap.Logger
21+
var (
22+
logger *zap.Logger
23+
MakeDir = os.MkdirAll
24+
RemoveFile = os.Remove
25+
UnixSocketListener = func(network, address string) (net.Listener, error) {
26+
return net.Listen(network, address)
27+
}
28+
)
2229

2330
func init() {
2431
_ = flag.Set("logtostderr", "true") // #nosec G104: Attempt to set flags for logging to stderr only on best-effort basis.Error cannot be usefully handled.
@@ -50,24 +57,24 @@ func setupSocket() (net.Listener, error) {
5057
socketPath := filepath.Join(constants.SocketDir, constants.SocketFile)
5158

5259
// Ensure the socket directory exists
53-
if err := os.MkdirAll(constants.SocketDir, 0750); err != nil {
54-
logger.Fatal("Failed to create socket directory", zap.String("dir", constants.SocketDir), zap.Error(err))
60+
if err := MakeDir(constants.SocketDir, 0750); err != nil {
61+
logger.Error("Failed to create socket directory", zap.String("dir", constants.SocketDir), zap.Error(err))
5562
return nil, err
5663
}
5764

5865
// Check for socket file
5966
if _, err := os.Stat(socketPath); err == nil {
60-
if err := os.Remove(socketPath); err != nil {
67+
if err := RemoveFile(socketPath); err != nil {
6168
logger.Warn("Failed to remove existing socket file", zap.String("path", socketPath), zap.Error(err))
6269
}
6370
} else if !os.IsNotExist(err) {
6471
logger.Warn("Could not stat socket file", zap.String("path", socketPath), zap.Error(err))
6572
}
6673

6774
logger.Info("Creating unix socket listener...", zap.String("path", socketPath))
68-
listener, err := net.Listen("unix", socketPath)
75+
listener, err := UnixSocketListener("unix", socketPath)
6976
if err != nil {
70-
logger.Fatal("Failed to create unix socket listener", zap.String("path", socketPath), zap.Error(err))
77+
logger.Error("Failed to create unix socket listener", zap.String("path", socketPath), zap.Error(err))
7178
return nil, err
7279
}
7380
return listener, nil
@@ -90,17 +97,21 @@ func handleSignals() {
9097
}
9198

9299
func newRouter() *gin.Engine {
100+
utils := &mounterUtils.MounterOptsUtils{}
101+
parser := &DefaultMounterArgsParser{}
102+
93103
// Create gin router
94104
router := gin.Default()
95-
router.POST("/api/cos/mount", handleCosMount())
96-
router.POST("/api/cos/unmount", handleCosUnmount())
105+
router.POST("/api/cos/mount", handleCosMount(utils, parser))
106+
router.POST("/api/cos/unmount", handleCosUnmount(utils))
97107
return router
98108
}
99109

100-
func main() {
101-
listener, err := setupSocket()
110+
func startService(setupSocketFunc func() (net.Listener, error), router http.Handler, handleSignalsFunc func()) error {
111+
listener, err := setupSocketFunc()
102112
if err != nil {
103-
logger.Fatal("Failed to create socket")
113+
logger.Error("Failed to create socket", zap.Error(err))
114+
return err
104115
}
105116
// Close the listener at the end
106117
defer func() {
@@ -109,23 +120,31 @@ func main() {
109120
}
110121
}()
111122

112-
handleSignals()
123+
handleSignalsFunc()
113124

114125
logger.Info("Starting cos-csi-mounter service...")
115126

116-
router := newRouter()
117-
118127
// Serve HTTP requests over Unix socket
119128
server := &http.Server{
120129
Handler: router,
121130
ReadHeaderTimeout: 3 * time.Second,
122131
}
123132
if err := server.Serve(listener); err != nil {
124-
logger.Fatal("Error while serving HTTP requests:", zap.Error(err))
133+
logger.Error("Error while serving HTTP requests:", zap.Error(err))
134+
return err
135+
}
136+
return nil
137+
}
138+
139+
func main() {
140+
err := startService(setupSocket, newRouter(), handleSignals)
141+
if err != nil {
142+
logger.Error("cos-csi-mounter exited with error", zap.Error(err))
143+
os.Exit(1)
125144
}
126145
}
127146

128-
func handleCosMount() gin.HandlerFunc {
147+
func handleCosMount(mounter mounterUtils.MounterUtils, parser MounterArgsParser) gin.HandlerFunc {
129148
return func(c *gin.Context) {
130149
var request MountRequest
131150

@@ -150,16 +169,15 @@ func handleCosMount() gin.HandlerFunc {
150169
}
151170

152171
// validate mounter args
153-
args, err := request.ParseMounterArgs()
172+
args, err := parser.Parse(request)
154173
if err != nil {
155174
logger.Error("failed to parse mounter args", zap.Any("mounter", request.Mounter), zap.Error(err))
156175

157176
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("invalid args for mounter: %v", err)})
158177
return
159178
}
160179

161-
utils := mounterUtils.MounterOptsUtils{}
162-
err = utils.FuseMount(request.Path, request.Mounter, args)
180+
err = mounter.FuseMount(request.Path, request.Mounter, args)
163181
if err != nil {
164182
logger.Error("mount failed: ", zap.Error(err))
165183
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("mount failed: %v", err)})
@@ -171,7 +189,7 @@ func handleCosMount() gin.HandlerFunc {
171189
}
172190
}
173191

174-
func handleCosUnmount() gin.HandlerFunc {
192+
func handleCosUnmount(mounter mounterUtils.MounterUtils) gin.HandlerFunc {
175193
return func(c *gin.Context) {
176194
var request struct {
177195
Path string `json:"path"`
@@ -185,8 +203,7 @@ func handleCosUnmount() gin.HandlerFunc {
185203

186204
logger.Info("New unmount request with values: ", zap.String("Path", request.Path))
187205

188-
utils := mounterUtils.MounterOptsUtils{}
189-
err := utils.FuseUnmount(request.Path)
206+
err := mounter.FuseUnmount(request.Path)
190207
if err != nil {
191208
logger.Error("unmount failed: ", zap.Error(err))
192209
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("unmount failed :%v", err)})

0 commit comments

Comments
 (0)