Skip to content

Commit

Permalink
Implement spawning docker containers
Browse files Browse the repository at this point in the history
  • Loading branch information
semanser committed Mar 20, 2024
1 parent a83d175 commit f4df00c
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
.DS_Store
.env
87 changes: 87 additions & 0 deletions backend/executor/executor.go
@@ -0,0 +1,87 @@
package executor

import (
"context"
"log"

"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)

var (
dockerClient *client.Client
containers []string
)

const imageName = "alpine"

func InitDockerClient() error {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
dockerClient = cli
info, err := dockerClient.Info(context.Background())

if err != nil {
return err
}

log.Printf("Docker client initialized: %s", info.Name)

return nil
}

func SpawnContainer(name string) (containerID string, err error) {
log.Printf("Spawning container %s\n", name)

resp, err := dockerClient.ContainerCreate(context.Background(), &container.Config{
Image: imageName,
Cmd: []string{"tail", "-f", "/dev/null"},
}, nil, nil, nil, name)

if err != nil {
return "", err
}
log.Printf("Container %s created\n", name)

containerID = resp.ID
if err := dockerClient.ContainerStart(context.Background(), containerID, container.StartOptions{}); err != nil {
return "", err
}
log.Printf("Container %s started\n", name)

containers = append(containers, containerID)
return containerID, nil
}

func StopContainer(containerID string) error {
if err := dockerClient.ContainerStop(context.Background(), containerID, container.StopOptions{}); err != nil {
return err
}
log.Printf("Container %s stopped\n", containerID)
return nil
}

func DeleteContainer(containerID string) error {
if err := StopContainer(containerID); err != nil {
return err
}

if err := dockerClient.ContainerRemove(context.Background(), containerID, container.RemoveOptions{}); err != nil {
return err
}
log.Printf("Container %s removed\n", containerID)
return nil
}

func Cleanup() error {
log.Println("Cleaning up containers")

for _, containerID := range containers {
if err := DeleteContainer(containerID); err != nil {
return err
}
}
return nil
}
7 changes: 7 additions & 0 deletions backend/graph/schema.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 30 additions & 4 deletions backend/main.go
Expand Up @@ -4,20 +4,26 @@ import (
"log"
"net/http"
"os"
"os/signal"
"syscall"

"gorm.io/driver/postgres"
"gorm.io/gorm"

"github.com/semanser/ai-coder/executor"
"github.com/semanser/ai-coder/models"
)

const defaultPort = "8080"

func main() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)

dsn := "postgresql://postgres@localhost/ai-coder"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
log.Fatalf("failed to connect database: %v", err)
}

// Migrate the schema
Expand All @@ -30,7 +36,27 @@ func main() {

r := newRouter(db)

// Run the server
log.Printf("connect to http://localhost:%s/playground for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, r))
err = executor.InitDockerClient()
if err != nil {
log.Fatalf("failed to initialize Docker client: %v", err)
}

// Run the server in a separate goroutine
go func() {
log.Printf("connect to http://localhost:%s/playground for GraphQL playground", port)
if err := http.ListenAndServe(":"+port, r); err != nil {
log.Fatalf("HTTP server error: %v", err)
}
}()

// Wait for termination signal
<-sigChan
log.Println("Shutting down...")

// Cleanup resources
if err := executor.Cleanup(); err != nil {
log.Printf("Error during cleanup: %v", err)
}

log.Println("Shutdown complete")
}
2 changes: 1 addition & 1 deletion backend/router.go
Expand Up @@ -22,7 +22,7 @@ func newRouter(db *gorm.DB) *gin.Engine {
// Configure CORS middleware
config := cors.DefaultConfig()
// TODO change to only allow specific origins
config.AllowOrigins = []string{"*"}
config.AllowAllOrigins = true
config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
r.Use(cors.New(config))

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/graphql.ts
Expand Up @@ -36,7 +36,7 @@ const wsClient = createWSClient({
});

export const graphqlClient = createClient({
url: "http://" + import.meta.env.VITE_API_URL + "/graphql",
url: window.location.origin + "/graphql",
fetchOptions: {},
exchanges: [
devtoolsExchange,
Expand Down
5 changes: 5 additions & 0 deletions frontend/vite.config.ts
Expand Up @@ -6,6 +6,11 @@ import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), vanillaExtractPlugin()],
server: {
proxy: {
"/graphql": "http://localhost:8080",
},
},
resolve: {
alias: {
"@/generated": path.resolve(__dirname, "./generated"),
Expand Down

0 comments on commit f4df00c

Please sign in to comment.