Skip to content

Commit 04aedda

Browse files
feat: refactor API and add application orchestrator (#24)
1 parent 4e8508d commit 04aedda

File tree

7 files changed

+205
-69
lines changed

7 files changed

+205
-69
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package application
2+
3+
import (
4+
"apploader/internal/config"
5+
"apploader/internal/cvm"
6+
"apploader/internal/secret"
7+
"context"
8+
"log"
9+
"net"
10+
"net/http"
11+
"os"
12+
"os/signal"
13+
"syscall"
14+
"time"
15+
16+
"github.com/gin-gonic/gin"
17+
)
18+
19+
// Application orchestrates CVM boot sequence, secrets, and HTTP server.
20+
type Application struct {
21+
config *config.Config
22+
cvmBootManager cvm.CvmBootManager
23+
secretService secret.SecretService
24+
server *http.Server
25+
}
26+
27+
// New creates a new application
28+
func New(cfg *config.Config) *Application {
29+
secretService := secret.NewSecretService()
30+
cvmBootManager, err := cvm.NewCvmBootManager(&cfg.Cvm, secretService)
31+
if err != nil {
32+
log.Fatalf("Failed to create CVM boot manager: %v", err)
33+
}
34+
return &Application{
35+
config: cfg,
36+
secretService: secretService,
37+
cvmBootManager: cvmBootManager,
38+
}
39+
}
40+
41+
// Start starts the application
42+
func (app *Application) Start() {
43+
// Channel to signal when API is ready
44+
apiReady := make(chan struct{})
45+
go app.startHTTP(apiReady)
46+
<-apiReady
47+
log.Printf("API started successfully")
48+
49+
// Channel to signal when CVM boot sequence is done
50+
cvmDone := make(chan bool, 1)
51+
52+
// Start CVM boot sequence in background
53+
go func() {
54+
log.Printf("Starting CVM boot sequence")
55+
app.cvmBootManager.Start()
56+
log.Printf("CVM boot sequence completed successfully")
57+
cvmDone <- true
58+
}()
59+
60+
// Setup graceful shutdown
61+
quit := make(chan os.Signal, 1)
62+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
63+
64+
select {
65+
case <-cvmDone:
66+
log.Printf("CVM boot sequence completed. Application will exit.")
67+
case <-quit:
68+
log.Printf("Shutting down application...")
69+
}
70+
71+
// Graceful shutdown of HTTP server
72+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
73+
defer cancel()
74+
75+
if err := app.server.Shutdown(ctx); err != nil {
76+
log.Printf("Server forced to shutdown: %v", err)
77+
}
78+
}
79+
80+
// StartHTTP starts the HTTP server
81+
func (app *Application) startHTTP(ready chan<- struct{}) {
82+
log.Printf("Starting HTTP server on port %s", app.config.Server.Port)
83+
84+
router := gin.New()
85+
// Custom logging middleware with log.Printf format
86+
router.Use(func(c *gin.Context) {
87+
c.Next()
88+
log.Printf("API Request: %s %s %d", c.Request.Method, c.Request.URL.Path, c.Writer.Status())
89+
})
90+
91+
router.GET("/health", func(c *gin.Context) {
92+
c.JSON(http.StatusOK, gin.H{"status": "ok"})
93+
})
94+
secret.NewSecretHandler(app.secretService).RegisterHandler(router)
95+
96+
app.server = &http.Server{
97+
Addr: app.config.Server.Port,
98+
ReadTimeout: 10 * time.Second,
99+
WriteTimeout: 10 * time.Second,
100+
IdleTimeout: 10 * time.Second,
101+
102+
Handler: router,
103+
}
104+
listener, err := net.Listen("tcp", app.config.Server.Port)
105+
if err != nil {
106+
log.Fatalf("Failed to start server: %v", err)
107+
}
108+
close(ready)
109+
110+
app.server.Serve(listener)
111+
}

apploader/internal/cvm/cvm.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ type CvmBootManager interface {
2828
type cvmBootManager struct {
2929
config *config.CvmConfig
3030
cvmBootSequence *CvmBootSequence
31+
secretService secret.SecretService
3132
}
3233

3334
// NewCvmBootManager creates a new cvm service
34-
func NewCvmBootManager(config *config.CvmConfig) (CvmBootManager, error) {
35-
service := &cvmBootManager{config: config}
35+
func NewCvmBootManager(config *config.CvmConfig, secretService secret.SecretService) (CvmBootManager, error) {
36+
service := &cvmBootManager{config: config, secretService: secretService}
3637
cvmBootSequence, err := service.loadConfig()
3738
if err != nil {
3839
return nil, err
@@ -71,7 +72,7 @@ func (cbm *cvmBootManager) executeTask(taskInfo *TaskInfo) error {
7172
}
7273

7374
envs := make([]string, 0)
74-
for k, v := range secret.Secret {
75+
for k, v := range cbm.secretService.GetAllSecrets() {
7576
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
7677
}
7778

@@ -86,7 +87,6 @@ func (cbm *cvmBootManager) executeTask(taskInfo *TaskInfo) error {
8687
}
8788
}
8889

89-
log.Printf("entrypoint is %s", taskInfo.Entrypoint)
9090
return command.RunCommand(taskInfo.Entrypoint, envs, taskInfo.Args...)
9191

9292
}
@@ -148,30 +148,33 @@ func (cbm *cvmBootManager) processTasks(tasks []*TaskInfo) {
148148
for i, t := range tasks {
149149
switch t.Type {
150150
case JOB:
151-
log.Printf("begin to do job %s\n", t.Name)
151+
log.Printf("Executing job: %s (entrypoint: %s)", t.Name, t.Entrypoint)
152152
err := cbm.executeTask(t)
153153
if err != nil {
154-
log.Fatalf("do job %s failed, error: %s\n", t.Name, err.Error())
154+
log.Fatalf("Failed to execute job %s: %v", t.Name, err)
155155
}
156-
log.Printf("end to do job %s\n", t.Name)
156+
log.Printf("Job completed: %s", t.Name)
157157
case SERVER:
158-
log.Printf("begin to deploy server %s\n", t.Name)
158+
log.Printf("Deploying service: %s (entrypoint: %s)", t.Name, t.Entrypoint)
159159
t.Priority = i + 2
160160
err := cbm.deployService(t)
161161
if err != nil {
162-
log.Fatalf("deploy server %s failed, error: %s\n", t.Name, err)
162+
log.Fatalf("Failed to deploy service %s: %v", t.Name, err)
163163
}
164+
log.Printf("Service deployed: %s", t.Name)
165+
log.Printf("Updating supervisor configuration for service: %s", t.Name)
164166
err = command.RunCommand("supervisorctl", nil, "update")
165167
if err != nil {
166-
log.Fatalf("update supervisor conf failed, error: %s", err.Error())
168+
log.Fatalf("Failed to update supervisor configuration: %v", err)
167169
}
170+
log.Printf("Starting service: %s with supervisor configuration", t.Name)
168171
err = command.RunCommand("supervisorctl", nil, "start", t.Name)
169172
if err != nil {
170-
log.Fatalf("start %s failed, error: %s", t.Name, err.Error())
173+
log.Fatalf("Failed to start service %s: %v", t.Name, err)
171174
}
172-
log.Printf("end to deply server %s\n", t.Name)
175+
log.Printf("Service started: %s", t.Name)
173176
default:
174-
log.Fatalf("task type: %s does not support", t.Type)
177+
log.Fatalf("Task type: %s is not supported", t.Type)
175178
}
176179
}
177180
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package secret
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
)
8+
9+
// SecretHandler is the handler for the secret service
10+
type SecretHandler struct {
11+
service SecretService
12+
}
13+
14+
// NewSecretHandler creates a new secret handler
15+
func NewSecretHandler(service SecretService) *SecretHandler {
16+
return &SecretHandler{service: service}
17+
}
18+
19+
// RegisterHandler registers the secret handler
20+
func (h *SecretHandler) RegisterHandler(router *gin.Engine) {
21+
router.POST("/secret", h.saveSecret)
22+
}
23+
24+
// SaveSecret saves a secret
25+
func (h *SecretHandler) saveSecret(c *gin.Context) {
26+
h.service.SaveSecret(c.PostForm("key"), c.PostForm("value"))
27+
c.JSON(http.StatusOK, gin.H{
28+
"code": 200,
29+
"message": "update secret successful",
30+
})
31+
}

apploader/internal/secret/secret.go

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package secret
2+
3+
import "sync"
4+
5+
// SecretService is the interface for the secret service
6+
type SecretService interface {
7+
SaveSecret(key string, value string)
8+
GetAllSecrets() map[string]string
9+
}
10+
11+
// secretService is the implementation of the SecretService interface
12+
type secretService struct {
13+
secrets map[string]string
14+
secretsMutex sync.RWMutex
15+
}
16+
17+
// NewSecretService creates a new secret service
18+
func NewSecretService() SecretService {
19+
return &secretService{
20+
secrets: make(map[string]string),
21+
}
22+
}
23+
24+
// SaveSecret saves a secret
25+
func (s *secretService) SaveSecret(key string, value string) {
26+
s.secretsMutex.Lock()
27+
defer s.secretsMutex.Unlock()
28+
s.secrets[key] = value
29+
}
30+
31+
// GetAllSecrets gets all secrets
32+
func (s *secretService) GetAllSecrets() map[string]string {
33+
s.secretsMutex.RLock()
34+
defer s.secretsMutex.RUnlock()
35+
return s.secrets
36+
}

apploader/main.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
package main
22

33
import (
4+
"apploader/internal/application"
45
"apploader/internal/config"
5-
"apploader/internal/cvm"
6-
"apploader/internal/secret"
76
"log"
87
"os"
9-
"time"
108
)
119

1210
func main() {
1311
log.SetFlags(log.Lshortfile | log.LstdFlags)
14-
15-
cfg := config.Load()
16-
cvm, err := cvm.NewCvmBootManager(&cfg.Cvm)
17-
if err != nil {
18-
log.Fatalf("failed to create cvm boot manager: %v", err)
19-
}
20-
go secret.StartSecretServer()
21-
time.Sleep(2 * time.Second) // wait for secret server to start
22-
cvm.Start()
12+
application.New(config.Load()).Start()
2313
os.Exit(0)
2414
}

apploader/pkg/command/command.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package command
22

33
import (
4-
"fmt"
4+
"bufio"
55
"log"
66
"os/exec"
77
"path"
@@ -23,20 +23,19 @@ func RunCommand(name string, envs []string, arg ...string) error {
2323
if err = cmd.Start(); err != nil {
2424
return err
2525
}
26-
for {
27-
tmp := make([]byte, 128)
28-
_, err := stdout.Read(tmp)
29-
fmt.Print(string(tmp))
30-
if err != nil {
31-
break
32-
}
26+
scanner := bufio.NewScanner(stdout)
27+
for scanner.Scan() {
28+
log.Printf("Command output: %s", scanner.Text())
29+
}
30+
if err := scanner.Err(); err != nil {
31+
log.Printf("Error reading command output: %v", err)
3332
}
3433
if err = cmd.Wait(); err != nil {
3534
if ex, ok := err.(*exec.ExitError); ok {
3635
cmdExitStatus := ex.Sys().(syscall.WaitStatus).ExitStatus()
37-
log.Println(cmdExitStatus)
36+
log.Printf("Command execution failed with exit status: %d", cmdExitStatus)
3837
}
39-
log.Println(err)
38+
log.Printf("Command execution failed: %v", err)
4039
return err
4140
}
4241
return nil

0 commit comments

Comments
 (0)