From 71180f12e0bd5d324fda3b39f8ee02201ab18b11 Mon Sep 17 00:00:00 2001 From: Bibhu20031 Date: Fri, 14 Feb 2025 14:38:34 +0530 Subject: [PATCH] Implemented template APIs --- sandbox/main.go | 10 ++++- sandbox/manager.go | 93 ++++++++++++++++++++++++++++++++++++++++++++-- sandbox/sandbox.go | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 6 deletions(-) diff --git a/sandbox/main.go b/sandbox/main.go index 445960c..c40f8a1 100644 --- a/sandbox/main.go +++ b/sandbox/main.go @@ -51,7 +51,7 @@ import ( "sandbox/internal" ) -var codenireManager ContainerManager +var codenireManager *CodenireManager var ( listenAddr = flag.String("port", "80", "HTTP server listen address") @@ -93,7 +93,8 @@ func main() { log.Printf("Go playground sandbox starting...") - codenireManager = NewCodenireManager() + codenireManager = NewCodenireManager(codenireManager.storage) //wrong. As storage should be passed first and it needs the implementation of the storage interface + // codenireManager = NewCodenireManager(&storage{}) //will fail to compile as storage is not implemented codenireManager.KillAll() runSem = make(chan struct{}, *numWorkers) @@ -112,6 +113,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 6837c79..ba04569 100644 --- a/sandbox/manager.go +++ b/sandbox/manager.go @@ -27,6 +27,7 @@ import ( docker "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "errors" contract "sandbox/api/gen" "sandbox/internal" ) @@ -55,10 +56,17 @@ type ContainerManager 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 CodenireManager struct { sync.Mutex numSysWorkers int @@ -72,9 +80,10 @@ type CodenireManager struct { isolated bool dockerFilesPath string + storage Storage } -func NewCodenireManager() *CodenireManager { +func NewCodenireManager(storage Storage) *CodenireManager { c, err := client.NewClientWithOpts(client.WithVersion("1.41")) if err != nil { panic("fail on create docker client") @@ -89,11 +98,17 @@ func NewCodenireManager() *CodenireManager { idleContainersCount: *replicaContainerCnt, dockerFilesPath: *dockerFilesPath, isolated: *isolated, + storage: storage, } } func (m *CodenireManager) 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.prebuildImages(t, m.dockerFilesPath) @@ -102,7 +117,7 @@ func (m *CodenireManager) Prepare() error { continue } } - + m.storage.SaveTemplates(m.imgs) return nil } @@ -126,7 +141,29 @@ func (m *CodenireManager) Boot() (err error) { } func (m *CodenireManager) 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 *CodenireManager) 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 *CodenireManager) DeleteTemplate(id string) error { + return m.storage.DeleteTemplate(id) } func (m *CodenireManager) GetContainer(ctx context.Context, id string) (*StartedContainer, error) { @@ -138,6 +175,54 @@ func (m *CodenireManager) GetContainer(ctx context.Context, id string) (*Started } } +func (m *CodenireManager) 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 *CodenireManager) runTemplate(id string) (*StartedContainer, error) { + template, err := m.GetTemplateByID(id) + if err != nil { + return nil, err + } + return m.runSndContainer(*template) +} + +func (m *CodenireManager) 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 *CodenireManager) KillAll() { m.Lock() defer m.Unlock() diff --git a/sandbox/sandbox.go b/sandbox/sandbox.go index a12f375..5424c9b 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 ( @@ -311,3 +313,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 := codenireManager.GetTemplateByID(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 := codenireManager.AddTemplate(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 := codenireManager.runTemplate(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 := codenireManager.DeleteTemplate(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 := codenireManager.updateTemplate(id, template); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + w.WriteHeader(http.StatusNoContent) +}