Skip to content

Commit 27c6067

Browse files
Implemented scheduler, session and started fcfs
1 parent 0990b0e commit 27c6067

File tree

10 files changed

+256
-76
lines changed

10 files changed

+256
-76
lines changed

api/routes/routes.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ import (
66
"github.com/PythonHacker24/linux-acl-management-backend/api/middleware"
77
"github.com/PythonHacker24/linux-acl-management-backend/internal/auth"
88
"github.com/PythonHacker24/linux-acl-management-backend/internal/health"
9+
"github.com/PythonHacker24/linux-acl-management-backend/internal/session"
910
"github.com/PythonHacker24/linux-acl-management-backend/internal/traversal"
1011
)
1112

1213
/* all routes for all features are registered here */
13-
func RegisterRoutes(mux *http.ServeMux) {
14+
func RegisterRoutes(mux *http.ServeMux, sessionManager *session.Manager) {
1415

1516
/* for logging into the backend and creating a session */
16-
mux.Handle("POST /login", http.HandlerFunc(
17-
middleware.LoggingMiddleware(auth.LoginHandler),
18-
))
17+
mux.HandleFunc("POST /login",
18+
middleware.LoggingMiddleware(auth.LoginHandler(sessionManager)),
19+
)
1920

2021
/* for monitoring the state of overall server and laclm backend */
2122
mux.Handle("GET /health", http.HandlerFunc(

cmd/laclm/main.go

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"os"
88
"os/signal"
9+
"sync"
910
"syscall"
1011
"time"
1112

@@ -16,6 +17,9 @@ import (
1617

1718
"github.com/PythonHacker24/linux-acl-management-backend/api/routes"
1819
"github.com/PythonHacker24/linux-acl-management-backend/config"
20+
"github.com/PythonHacker24/linux-acl-management-backend/internal/scheduler"
21+
"github.com/PythonHacker24/linux-acl-management-backend/internal/scheduler/fcfs"
22+
"github.com/PythonHacker24/linux-acl-management-backend/internal/session"
1923
"github.com/PythonHacker24/linux-acl-management-backend/internal/utils"
2024
)
2125

@@ -101,15 +105,30 @@ func exec() error {
101105
}
102106

103107
func run(ctx context.Context) error {
104-
var err error
108+
var (
109+
err error
110+
wg sync.WaitGroup
111+
)
112+
113+
/* RULE: complete backend system must initiate before http server starts */
105114

106-
/* complete backend system must initiate before http server starts */
115+
/*
116+
initializing schedular
117+
scheduler uses context to quit - part of waitgroup
118+
propogates error through error channel
119+
*/
120+
errCh := make(chan error, 1)
121+
sessionManager := session.NewManager()
107122

123+
/* currently FCFS scheduler */
124+
transSched := fcfs.NewFCFSScheduler(sessionManager)
125+
scheduler.InitSchedular(ctx, transSched, &wg, errCh)
126+
108127
/* setting up http mux and routes */
109128
mux := http.NewServeMux()
110129

111-
/* routes declared in /api/routes */
112-
routes.RegisterRoutes(mux)
130+
/* routes declared in /api/routes.go */
131+
routes.RegisterRoutes(mux, sessionManager)
113132

114133
server := &http.Server{
115134
Addr: fmt.Sprintf("%s:%d",
@@ -121,7 +140,10 @@ func run(ctx context.Context) error {
121140

122141
/* starting http server as a goroutine */
123142
go func() {
124-
zap.L().Info("HTTP REST API server starting on :8080")
143+
zap.L().Info("HTTP REST API server starting",
144+
zap.String("Host", config.BackendConfig.Server.Host),
145+
zap.Int("Port", config.BackendConfig.Server.Port),
146+
)
125147
if err = server.ListenAndServe(); err != http.ErrServerClosed {
126148
zap.L().Error("ListenAndServe error",
127149
zap.Error(err),
@@ -134,7 +156,20 @@ func run(ctx context.Context) error {
134156
all the functions called must be async here and ready for graceful shutdowns
135157
*/
136158

137-
<-ctx.Done()
159+
select {
160+
case <-ctx.Done():
161+
zap.L().Info("Shutdown process initiated")
162+
case err = <-errCh:
163+
164+
/* context done can be called here (optional for now) */
165+
166+
zap.L().Error("Fatal Error from schedular",
167+
zap.Error(err),
168+
)
169+
return err
170+
}
171+
172+
// <-ctx.Done()
138173

139174
/*
140175
after this, exit signal is triggered
@@ -157,5 +192,9 @@ func run(ctx context.Context) error {
157192

158193
/* after the http server is stopped, rest of the components can be shutdown */
159194

195+
wg.Wait()
196+
197+
zap.L().Info("All background processes closed gracefully")
198+
160199
return err
161200
}

internal/auth/handler.go

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,58 +11,67 @@ import (
1111
)
1212

1313
/* Handles user login and creates a session */
14-
func LoginHandler(w http.ResponseWriter, r *http.Request) {
14+
func LoginHandler(sessionManager *session.Manager) http.HandlerFunc {
15+
return func(w http.ResponseWriter, r *http.Request) {
1516

16-
/* POST Request only - specified in routes */
17+
/* POST Request only - specified in routes */
1718

18-
/* decode the request body */
19-
var user User
20-
err := json.NewDecoder(r.Body).Decode(&user)
21-
if err != nil {
22-
http.Error(w, "Invalid request body", http.StatusBadRequest)
23-
return
24-
}
19+
/* decode the request body */
20+
var user User
21+
err := json.NewDecoder(r.Body).Decode(&user)
22+
if err != nil {
23+
http.Error(w, "Invalid request body", http.StatusBadRequest)
24+
return
25+
}
2526

26-
/* check if username and password are specified */
27-
if user.Username == "" || user.Password == "" {
28-
http.Error(w, "Username and password are required", http.StatusBadRequest)
29-
return
30-
}
27+
/* check if username and password are specified */
28+
if user.Username == "" || user.Password == "" {
29+
http.Error(w, "Username and password are required", http.StatusBadRequest)
30+
return
31+
}
3132

32-
/* authenticate the user */
33-
authStatus := AuthenticateUser(user.Username,
34-
user.Password,
35-
config.BackendConfig.Authentication.LDAPConfig.SearchBase,
36-
)
33+
/* authenticate the user */
34+
authStatus := AuthenticateUser(user.Username,
35+
user.Password,
36+
config.BackendConfig.Authentication.LDAPConfig.SearchBase,
37+
)
3738

38-
/* check if authentication is successful */
39-
if !authStatus {
40-
zap.L().Warn("User with invalid credentials attempted to log in")
41-
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
42-
return
43-
}
39+
/* check if authentication is successful */
40+
if !authStatus {
41+
zap.L().Warn("User with invalid credentials attempted to log in")
42+
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
43+
return
44+
}
4445

45-
/* after building session manager */
46-
session.CreateSession(user.Username)
46+
/* after building session manager */
47+
err = sessionManager.CreateSession(user.Username)
48+
if err != nil {
49+
zap.L().Error("Error creating session",
50+
zap.Error(err),
51+
)
52+
http.Error(w, "Error creating session", http.StatusInternalServerError)
53+
return
54+
}
4755

48-
/* generate JWT for user interaction */
49-
token, err := GenerateJWT(user.Username)
50-
if err != nil {
51-
zap.L().Error("Error generating token",
52-
zap.Error(err),
53-
)
54-
http.Error(w, "Error generating token", http.StatusInternalServerError)
55-
return
56-
}
56+
/* generate JWT for user interaction */
57+
token, err := GenerateJWT(user.Username)
58+
if err != nil {
59+
zap.L().Error("Error generating token",
60+
zap.Error(err),
61+
)
62+
http.Error(w, "Error generating token", http.StatusInternalServerError)
63+
return
64+
}
5765

58-
/* create auth successful response */
59-
response := map[string]string{"token": token}
60-
w.Header().Set("Content-Type", "application/json")
61-
if err := json.NewEncoder(w).Encode(response); err != nil {
62-
zap.L().Error("Failed to encode response for login request",
63-
zap.Error(err),
64-
)
65-
http.Error(w, "Failed to encode response for login request", http.StatusInternalServerError)
66-
return
66+
/* create auth successful response */
67+
response := map[string]string{"token": token}
68+
w.Header().Set("Content-Type", "application/json")
69+
if err := json.NewEncoder(w).Encode(response); err != nil {
70+
zap.L().Error("Failed to encode response for login request",
71+
zap.Error(err),
72+
)
73+
http.Error(w, "Failed to encode response for login request", http.StatusInternalServerError)
74+
return
75+
}
6776
}
6877
}

internal/scheduler/fcfs/fcfs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package fcfs
2+
3+
import "github.com/PythonHacker24/linux-acl-management-backend/internal/session"
4+
5+
func NewFCFSScheduler(sm *session.Manager) *FCFSScheduler {
6+
return &FCFSScheduler{
7+
sessionManager: sm,
8+
}
9+
}

internal/scheduler/fcfs/model.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package fcfs
2+
3+
import (
4+
"context"
5+
6+
"github.com/PythonHacker24/linux-acl-management-backend/internal/session"
7+
)
8+
9+
/* FCFS Scheduler attached with session.Manager */
10+
type FCFSScheduler struct {
11+
sessionManager *session.Manager
12+
}
13+
14+
/* run the fcfs scheduler with context */
15+
func (f *FCFSScheduler) Run(ctx context.Context) error {
16+
for {
17+
select {
18+
19+
/* check if ctx is done - catchable if default is not working hard (ideal scheduler) */
20+
case <-ctx.Done():
21+
return nil
22+
23+
/* in case default is working hard - ctx is passed here so it must attempt to quit */
24+
default:
25+
/* RULE: ctx is propogates all over the coming functions */
26+
27+
}
28+
}
29+
}

internal/scheduler/interface.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scheduler
2+
3+
import "context"
4+
5+
/* schedular interface */
6+
type Scheduler interface {
7+
Run(ctx context.Context) error
8+
}

internal/scheduler/scheduler.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,35 @@ package scheduler
22

33
/*
44
laclm uses FCFS scheduling algorithm
5-
the schedular module is highly modular and can be attached/detached/replaced quickly on-demand
5+
the scheduler module is highly modular and can be attached/detached/replaced quickly on-demand
66
*/
7+
8+
import (
9+
"context"
10+
"sync"
11+
12+
"go.uber.org/zap"
13+
)
14+
15+
/*
16+
initialized a scheduler of Schedular type as a goroutine with context
17+
when ctx.Done() is recieved, scheduler starts shutting down
18+
the main function waits till wg.Done() is not called ensuring complete shutdown of scheduler
19+
in case of any error, the errCh is used to propogate it back to main function where it's handled
20+
*/
21+
func InitSchedular(ctx context.Context, sched Scheduler, wg *sync.WaitGroup, errCh chan<-error) {
22+
wg.Add(1)
23+
go func(ctx context.Context) {
24+
defer wg.Done()
25+
zap.L().Info("Scheduler Initialization Started")
26+
27+
/* the context is used here for gracefully stopping the scheduler */
28+
if err := sched.Run(ctx); err != nil {
29+
zap.L().Error("Scheduler running error",
30+
zap.Error(err),
31+
)
32+
} else {
33+
zap.L().Info("Schedular Stopped Gracefully")
34+
}
35+
}(ctx)
36+
}

internal/session/interact.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package session
2+
3+
import (
4+
"container/list"
5+
)
6+
7+
/* create a new session manager */
8+
func NewManager() *Manager {
9+
return &Manager{
10+
sessionsMap: make(map[string]*Session),
11+
sessionOrder: list.New(),
12+
}
13+
}
14+
15+
/* get next session for round robin */
16+
func (m *Manager) GetNextSession() *Session {
17+
m.mutex.Lock()
18+
defer m.mutex.Unlock()
19+
20+
/* check if sessionOrder is empty */
21+
if m.sessionOrder.Len() == 0 {
22+
return nil
23+
}
24+
25+
element := m.sessionOrder.Front()
26+
session := element.Value.(*Session)
27+
28+
m.sessionOrder.MoveToBack(element)
29+
return session
30+
}

0 commit comments

Comments
 (0)