Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions sandbox/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
93 changes: 89 additions & 4 deletions sandbox/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"

"errors"
contract "sandbox/api/gen"
"sandbox/internal"
)
Expand Down Expand Up @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this and other API code to separate file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By other API code, do you mean moving the handlers to a separate file too, or just the logic related functions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean move code with API and storage from manager which control Docker images, networks and containers

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I'll move the API handlers and storage logic into separate files

SaveTemplates([]BuiltImage) error
LoadTemplates() ([]BuiltImage, error)
DeleteTemplate(id string) error
}

type CodenireOrchestrator struct {
sync.Mutex
numSysWorkers int
Expand All @@ -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")
Expand All @@ -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)
Expand All @@ -105,7 +120,7 @@ func (m *CodenireOrchestrator) Prepare() error {
continue
}
}

m.storage.SaveTemplates(m.imgs)
return nil
}

Expand All @@ -129,7 +144,29 @@ func (m *CodenireOrchestrator) Boot() (err error) {
}

func (m *CodenireOrchestrator) GetTemplates() []BuiltImage {
return m.imgs
templates, err := m.storage.LoadTemplates()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currentrly, GetTemplates() need for work Playground and should be connect to running images. So we can do it lately, when all logic with storage wiil be done

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense. We'll update it once the storage logic is complete.

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) {
Expand All @@ -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()
Expand Down
73 changes: 72 additions & 1 deletion sandbox/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import (

contract "sandbox/api/gen"
"sandbox/internal"

chi "github.com/go-chi/chi/v5"
)

const (
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}