Skip to content

Commit f29a996

Browse files
refactor: abstract sandbox interfaces for #13 (#15)
1 parent 535f9e4 commit f29a996

File tree

8 files changed

+926
-98
lines changed

8 files changed

+926
-98
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ repos:
66
rev: v5.0.0
77
hooks:
88
- id: trailing-whitespace
9+
exclude: \.md$|\.rst$
910
- id: end-of-file-fixer
1011
- id: check-yaml
1112
args: [--allow-multiple-documents]

api-service/controller/env_instance.go

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ limitations under the License.
1717
package controller
1818

1919
import (
20+
"api-service/models"
2021
backendmodels "envhub/models"
2122

22-
"api-service/models"
2323
"api-service/service"
2424
"api-service/util"
2525

@@ -29,17 +29,21 @@ import (
2929

3030
// EnvInstanceController handles EnvInstance operations
3131
type EnvInstanceController struct {
32-
scheduleClient *service.ScheduleClient
33-
backendClient *service.BackendClient
34-
redisClient *service.RedisClient
32+
envInstanceService service.EnvInstanceService // use interface
33+
backendClient *service.BackendClient
34+
redisClient *service.RedisClient
3535
}
3636

3737
// NewEnvInstanceController creates a new EnvInstance controller instance
38-
func NewEnvInstanceController(scheduleClient *service.ScheduleClient, backendClient *service.BackendClient, redisClient *service.RedisClient) *EnvInstanceController {
38+
func NewEnvInstanceController(
39+
envInstanceService service.EnvInstanceService,
40+
backendClient *service.BackendClient,
41+
redisClient *service.RedisClient,
42+
) *EnvInstanceController {
3943
return &EnvInstanceController{
40-
scheduleClient: scheduleClient,
41-
backendClient: backendClient,
42-
redisClient: redisClient,
44+
envInstanceService: envInstanceService,
45+
backendClient: backendClient,
46+
redisClient: redisClient,
4347
}
4448
}
4549

@@ -52,14 +56,6 @@ type CreateEnvInstanceRequest struct {
5256
TTL string `json:"ttl"`
5357
}
5458

55-
// EnvInstanceResponse represents the response body for EnvInstance operations
56-
type EnvInstanceResponse struct {
57-
ID string `json:"id"`
58-
Env string `json:"env"`
59-
CreatedAt string `json:"createdAt"`
60-
Status string `json:"status"`
61-
}
62-
6359
// CreateEnvInstance creates a new EnvInstance
6460
// POST /env-instance/
6561
func (ctrl *EnvInstanceController) CreateEnvInstance(c *gin.Context) {
@@ -107,7 +103,7 @@ func (ctrl *EnvInstanceController) CreateEnvInstance(c *gin.Context) {
107103
// Set TTL for environment
108104
backendEnv.DeployConfig["ttl"] = req.TTL
109105
// Call ScheduleClient to create Pod
110-
envInstance, err := ctrl.scheduleClient.CreatePod(backendEnv)
106+
envInstance, err := ctrl.envInstanceService.CreateEnvInstance(backendEnv)
111107
if err != nil {
112108
backendmodels.JSONErrorWithMessage(c, 500, "Failed to create: "+err.Error())
113109
return
@@ -124,30 +120,6 @@ func (ctrl *EnvInstanceController) CreateEnvInstance(c *gin.Context) {
124120
backendmodels.JSONSuccess(c, envInstance)
125121
}
126122

127-
func (ctrl *EnvInstanceController) ListEnvInstances(c *gin.Context) {
128-
token := util.GetCurrentToken(c)
129-
if token == nil {
130-
backendmodels.JSONErrorWithMessage(c, 403, "token required")
131-
}
132-
var query = models.EnvInstance{Env: &backendmodels.Env{}}
133-
id := c.Param("id")
134-
if id != "" {
135-
// Split name and version using SplitEnvNameVersionStrict function
136-
name, version := util.SplitEnvNameVersion(id)
137-
query.Env.Name = name
138-
query.Env.Version = version
139-
}
140-
if ctrl.redisClient == nil {
141-
backendmodels.JSONErrorWithMessage(c, 500, "no redis is declared for supporting listEnvInstance api, please specify `redis-addr` flag")
142-
return
143-
}
144-
instances, err := ctrl.redisClient.ListEnvInstancesFromRedis(token.Token, &query)
145-
if err != nil {
146-
backendmodels.JSONErrorWithMessage(c, 500, err.Error())
147-
}
148-
backendmodels.JSONSuccess(c, instances)
149-
}
150-
151123
// GetEnvInstance retrieves a single EnvInstance
152124
// GET /env-instance/:id
153125
func (ctrl *EnvInstanceController) GetEnvInstance(c *gin.Context) {
@@ -157,7 +129,7 @@ func (ctrl *EnvInstanceController) GetEnvInstance(c *gin.Context) {
157129
return
158130
}
159131
// Call ScheduleClient to query Pod
160-
envInstance, err := ctrl.scheduleClient.GetPod(id)
132+
envInstance, err := ctrl.envInstanceService.GetEnvInstance(id)
161133
if err != nil {
162134
backendmodels.JSONErrorWithMessage(c, 500, "Failed to query: "+err.Error())
163135
return
@@ -175,16 +147,11 @@ func (ctrl *EnvInstanceController) DeleteEnvInstance(c *gin.Context) {
175147
}
176148

177149
// Call ScheduleClient to delete Pod
178-
success, err := ctrl.scheduleClient.DeletePod(id)
150+
err := ctrl.envInstanceService.DeleteEnvInstance(id)
179151
if err != nil {
180152
backendmodels.JSONErrorWithMessage(c, 500, "Failed to delete: "+err.Error())
181153
return
182154
}
183-
184-
if !success {
185-
backendmodels.JSONErrorWithMessage(c, 500, "Failed to delete")
186-
return
187-
}
188155
backendmodels.JSONSuccess(c, "Deleted successfully")
189156
token := util.GetCurrentToken(c)
190157
if token != nil && ctrl.redisClient != nil {
@@ -193,3 +160,62 @@ func (ctrl *EnvInstanceController) DeleteEnvInstance(c *gin.Context) {
193160
}
194161
}
195162
}
163+
164+
func (ctrl *EnvInstanceController) ListEnvInstances(c *gin.Context) {
165+
token := util.GetCurrentToken(c)
166+
if token == nil {
167+
backendmodels.JSONErrorWithMessage(c, 403, "token required")
168+
return
169+
}
170+
if ctrl.redisClient != nil {
171+
var query = models.EnvInstance{Env: &backendmodels.Env{}}
172+
id := c.Param("id")
173+
if id != "" {
174+
name, version := util.SplitEnvNameVersion(id)
175+
query.Env.Name = name
176+
query.Env.Version = version
177+
}
178+
instances, err := ctrl.redisClient.ListEnvInstancesFromRedis(token.Token, &query)
179+
if err == nil {
180+
backendmodels.JSONSuccess(c, instances)
181+
return
182+
}
183+
log.Warnf("failed to list from redis: %v", err)
184+
}
185+
envName := c.Query("envName")
186+
instances, err := ctrl.envInstanceService.ListEnvInstances(envName)
187+
if err != nil {
188+
backendmodels.JSONErrorWithMessage(c, 500, err.Error())
189+
return
190+
}
191+
backendmodels.JSONSuccess(c, instances)
192+
}
193+
194+
func (ctrl *EnvInstanceController) Warmup(c *gin.Context) {
195+
id := c.Param("id")
196+
if id == "" {
197+
backendmodels.JSONErrorWithMessage(c, 400, "env id required")
198+
return
199+
}
200+
name, version, err := util.SplitEnvNameVersionStrict(id)
201+
if err != nil {
202+
backendmodels.JSONErrorWithMessage(c, 400, "invalid env format, should be name@version")
203+
return
204+
}
205+
backendEnv, err := ctrl.backendClient.GetEnvByVersion(name, version)
206+
if err != nil {
207+
backendmodels.JSONErrorWithMessage(c, 500, "failed to warm up env instance: "+err.Error())
208+
return
209+
}
210+
if backendEnv == nil {
211+
backendmodels.JSONErrorWithMessage(c, 404, "can not find env by id: "+id)
212+
return
213+
}
214+
215+
err = ctrl.envInstanceService.Warmup(backendEnv)
216+
if err != nil {
217+
backendmodels.JSONErrorWithMessage(c, 500, err.Error())
218+
return
219+
}
220+
backendmodels.JSONSuccess(c, backendEnv)
221+
}

api-service/main.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434

3535
var (
3636
scheduleAddr string
37+
scheduleType string
3738
backendAddr string
3839
redisAddr string
3940
redisPassword string
@@ -47,6 +48,7 @@ var (
4748

4849
func init() {
4950
pflag.StringVar(&scheduleAddr, "schedule-addr", "", "Meta service address (host:port)")
51+
pflag.StringVar(&scheduleType, "schedule-type", "k8s", "sandbox service schedule type, currently only 'k8s', 'standard' support")
5052
pflag.StringVar(&backendAddr, "backend-addr", "", "backend service address (host:port)")
5153

5254
pflag.Int64Var(&qps, "qps", int64(100), "total qps limit")
@@ -93,7 +95,15 @@ func main() {
9395
log.Fatalf("Failed to create backend client: %v", err)
9496
}
9597

96-
scheduleClient := service.NewScheduleClient(scheduleAddr)
98+
var scheduleClient service.EnvInstanceService
99+
if scheduleType == "k8s" {
100+
scheduleClient = service.NewScheduleClient(scheduleAddr)
101+
} else if scheduleType == "standard" {
102+
scheduleClient = service.NewEnvInstanceClient(scheduleAddr)
103+
} else {
104+
log.Fatalf("unsupported schedule type: %v", scheduleType)
105+
}
106+
97107
envInstanceController := controller.NewEnvInstanceController(scheduleClient, backendClient, redisClient)
98108
// Main route configuration
99109
mainRouter.POST("/env-instance",

api-service/service/cleanup_service.go

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -75,48 +75,17 @@ func (cm *AEnvCleanManager) Stop() {
7575

7676
// KubeCleaner cleanup service responsible for periodically cleaning expired EnvInstances
7777
type KubeCleaner struct {
78-
scheduleClient *ScheduleClient
78+
scheduleClient EnvInstanceService
7979
}
8080

8181
// NewCleanupService
82-
func NewKubeCleaner(scheduleClient *ScheduleClient) *KubeCleaner {
82+
func NewKubeCleaner(scheduleClient EnvInstanceService) *KubeCleaner {
8383
return &KubeCleaner{
8484
scheduleClient: scheduleClient,
8585
}
8686
}
8787

8888
// cleanup executes cleanup task
8989
func (cs *KubeCleaner) cleanup() {
90-
log.Println("Starting cleanup task...")
91-
// Get all EnvInstances
92-
envInstances, err := cs.scheduleClient.FilterPods()
93-
if err != nil {
94-
log.Printf("Failed to get env instances: %v", err)
95-
return
96-
}
97-
if envInstances == nil || len(*envInstances) == 0 {
98-
log.Println("No env instances found")
99-
return
100-
}
101-
102-
var deletedCount int
103-
104-
for _, instance := range *envInstances {
105-
// Skip terminated instances
106-
if instance.Status == "Terminated" {
107-
continue
108-
}
109-
deleted, err := cs.scheduleClient.DeletePod(instance.ID)
110-
if err != nil {
111-
log.Printf("Failed to delete instance %s: %v", instance.ID, err)
112-
continue
113-
}
114-
if deleted {
115-
deletedCount++
116-
log.Printf("Successfully deleted instance %s", instance.ID)
117-
} else {
118-
log.Printf("Instance %s was not deleted (may already be deleted)", instance.ID)
119-
}
120-
}
121-
log.Printf("Cleanup task completed. Deleted %d expired instances", deletedCount)
90+
_ = cs.scheduleClient.Cleanup()
12291
}

0 commit comments

Comments
 (0)