diff --git a/sandbox/main.go b/sandbox/main.go index 05ba138..270fcf4 100644 --- a/sandbox/main.go +++ b/sandbox/main.go @@ -94,8 +94,9 @@ func main() { } log.Printf("Go playground sandbox starting...") - - codenireManager = NewCodenireOrchestrator() + // storage := &Storage{} + // codenireManager = NewCodenireOrchestrator(storage) + //Not possible to use storage in this case as it is not implemented codenireManager.KillAll() runSem = make(chan struct{}, *numWorkers) @@ -114,6 +115,11 @@ func main() { h.Post("/run", runHandler) h.Get("/templates", listTemplatesHandler) + h.Get("/templates/{id}", getTemplateByIDHandler) + h.Post("/templates", AddTemplateHandler) + h.Post("/templates/{id}", runTemplateHandler) + h.Delete("/templates/{id}", deleteTemplateHandler) + h.Put("/templates/{id}", updateTemplateHandler) httpServer := &http.Server{ Addr: ":" + *listenAddr, diff --git a/sandbox/manager.go b/sandbox/manager.go index bad0ff0..be3ab85 100644 --- a/sandbox/manager.go +++ b/sandbox/manager.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" + "errors" contract "sandbox/api/gen" "sandbox/internal" ) @@ -58,10 +59,17 @@ type ContainerOrchestrator interface { Boot() error GetTemplates() []BuiltImage GetContainer(ctx context.Context, id string) (*StartedContainer, error) + AddTemplate(cfg contract.ImageConfig) error KillAll() KillContainer(StartedContainer) error } +type Storage interface { + SaveTemplates([]BuiltImage) error + LoadTemplates() ([]BuiltImage, error) + DeleteTemplate(id string) error +} + type CodenireOrchestrator struct { sync.Mutex numSysWorkers int @@ -75,9 +83,10 @@ type CodenireOrchestrator struct { isolated bool dockerFilesPath string + storage Storage } -func NewCodenireOrchestrator() *CodenireOrchestrator { +func NewCodenireOrchestrator(storage Storage) *CodenireOrchestrator { c, err := client.NewClientWithOpts(client.WithVersion("1.41")) if err != nil { panic("fail on createDB docker client") @@ -92,11 +101,17 @@ func NewCodenireOrchestrator() *CodenireOrchestrator { idleContainersCount: *replicaContainerCnt, dockerFilesPath: *dockerFilesPath, isolated: *isolated, + storage: storage, } } func (m *CodenireOrchestrator) Prepare() error { - templates := parseConfigFiles(m.dockerFilesPath) + loadedTemplates, err := m.storage.LoadTemplates() //loads the template from storage + if err == nil && len(loadedTemplates) > 0 { + m.imgs = loadedTemplates + return nil + } + templates := parseConfigFiles(m.dockerFilesPath) //if no templates are loaded from storage, parse the config files for _, t := range templates { err := m.prebuildImage(t, m.dockerFilesPath) @@ -105,7 +120,7 @@ func (m *CodenireOrchestrator) Prepare() error { continue } } - + m.storage.SaveTemplates(m.imgs) return nil } @@ -129,7 +144,29 @@ func (m *CodenireOrchestrator) Boot() (err error) { } func (m *CodenireOrchestrator) GetTemplates() []BuiltImage { - return m.imgs + templates, err := m.storage.LoadTemplates() + if err != nil { + log.Println("Failed to load templates:", err) + return nil + } + return templates //returns empty list on error. hence returns maximum one value ie []BuildImages +} + +func (m *CodenireOrchestrator) GetTemplateByID(id string) (*BuiltImage, error) { + templates, err := m.storage.LoadTemplates() + if err != nil { + return nil, err + } + + for _, t := range templates { + if t.imageID != nil && *t.imageID == id { // Convert pointer to string for comparison + return &t, nil + } + } + return nil, errors.New("template not found") +} +func (m *CodenireOrchestrator) DeleteTemplate(id string) error { + return m.storage.DeleteTemplate(id) } func (m *CodenireOrchestrator) GetContainer(ctx context.Context, id string) (*StartedContainer, error) { @@ -141,6 +178,54 @@ func (m *CodenireOrchestrator) GetContainer(ctx context.Context, id string) (*St } } +func (m *CodenireOrchestrator) AddTemplate(cfg contract.ImageConfig) error { + templates, err := m.storage.LoadTemplates() + if err != nil { + log.Println("Failed to load templates:", err) + return err + } + for _, t := range templates { + if t.Template == cfg.Template { + return errors.New("template already exists") + } + } + newImage := BuiltImage{ + ImageConfig: cfg, + } + templates = append(templates, newImage) + return m.storage.SaveTemplates(templates) + +} + +func (m *CodenireOrchestrator) runTemplate(id string) (*StartedContainer, error) { + template, err := m.GetTemplateByID(id) + if err != nil { + return nil, err + } + return m.runSndContainer(*template) +} + +func (m *CodenireOrchestrator) updateTemplate(id string, newConfig contract.ImageConfig) error { + template, err := m.storage.LoadTemplates() + if err != nil { + log.Println("Failed to load templates:", err) + return err + } + + found := false + for i, t := range template { + if t.Template == id { + template[i].ImageConfig = newConfig + found = true + break + } + } + if !found { + return errors.New("template not found") + } + return m.storage.SaveTemplates(template) +} + func (m *CodenireOrchestrator) KillAll() { m.Lock() defer m.Unlock() diff --git a/sandbox/sandbox.go b/sandbox/sandbox.go index 438eda9..b4c25f5 100644 --- a/sandbox/sandbox.go +++ b/sandbox/sandbox.go @@ -44,6 +44,8 @@ import ( contract "sandbox/api/gen" "sandbox/internal" + + chi "github.com/go-chi/chi/v5" ) const ( @@ -304,7 +306,7 @@ func replacePlaceholders(input string, values map[string]string) string { } func listTemplatesHandler(w http.ResponseWriter, _ *http.Request) { - body, err := json.MarshalIndent(codenireManager.GetTemplates(), "", " ") + body, err := json.MarshalIndent((*CodenireOrchestrator).GetTemplates(&CodenireOrchestrator{}), "", " ") if err != nil { http.Error(w, "error encoding JSON", http.StatusInternalServerError) log.Printf("json marshal: %v", err) @@ -313,3 +315,72 @@ func listTemplatesHandler(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write(body) } +func getTemplateByIDHandler(w http.ResponseWriter, r *http.Request) { + id := chi.URLParam(r, "id") + + template, err := (*CodenireOrchestrator).GetTemplateByID(&CodenireOrchestrator{}, id) + if err != nil { + http.Error(w, "Template not found", http.StatusNotFound) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(template) +} + +func AddTemplateHandler(w http.ResponseWriter, r *http.Request) { + var template contract.ImageConfig + if err := json.NewDecoder(r.Body).Decode(&template); err != nil { + http.Error(w, "Invalid JSON format", http.StatusBadRequest) + return + } + + if err := (*CodenireOrchestrator).AddTemplate(&CodenireOrchestrator{}, template); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusCreated) +} + +func runTemplateHandler(w http.ResponseWriter, r *http.Request) { + id := chi.URLParam(r, "id") + + container, err := (*CodenireOrchestrator).runTemplate(&CodenireOrchestrator{}, id) + if err != nil { + http.Error(w, "Failed to run template", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(container) +} + +func deleteTemplateHandler(w http.ResponseWriter, r *http.Request) { + id := chi.URLParam(r, "id") + + err := (*CodenireOrchestrator).DeleteTemplate(&CodenireOrchestrator{}, id) + if err != nil { + http.Error(w, "Failed to delete template", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func updateTemplateHandler(w http.ResponseWriter, r *http.Request) { + id := chi.URLParam(r, "id") + + var template contract.ImageConfig + if err := json.NewDecoder(r.Body).Decode(&template); err != nil { + http.Error(w, "Invalid JSON format", http.StatusBadRequest) + return + } + + if err := (*CodenireOrchestrator).updateTemplate(&CodenireOrchestrator{}, id, template); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusNoContent) +}