Skip to content

Commit

Permalink
Merge pull request #7 from makeopensource/cli-docker-controls
Browse files Browse the repository at this point in the history
Implemented Api Cli
  • Loading branch information
RA341 authored Sep 30, 2024
2 parents 6588092 + bc59f1e commit 1f323dc
Show file tree
Hide file tree
Showing 10 changed files with 716 additions and 189 deletions.
510 changes: 506 additions & 4 deletions .example/hydra/package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion .example/hydra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"license": "ISC",
"dependencies": {
"commander": "^12.1.0",
"leviathan-client": "file:../../spec/client"
"inquirer": "^11.1.0",
"leviathan-client": "file:../../.spec/client"
},
"devDependencies": {
"@types/node": "^22.7.2",
Expand Down
89 changes: 76 additions & 13 deletions .example/hydra/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,87 @@
#!/usr/bin/env node

import {Command} from 'commander';
import {CoursesApi, DockerApi} from 'leviathan-client'
import {DockerApi} from 'leviathan-client';
import inquirer from 'inquirer';

const program = new Command();

program
.version('1.0.0')
.description('A simple TypeScript CLI application')
.option('-n, --name <name>', 'Your name')
.option('-g, --greeting <greeting>', 'Custom greeting', 'Hello')
.action((options) => {
const name = options.name || 'World';
console.log(`${options.greeting}, ${name}!`);
});
.description('A CLI to interact with the Leviathan API');

program.parse(process.argv);
const baseUrl = "http://localhost:9221"

const courses = new DockerApi(undefined ,"http://localhost:9221");
// const coursesApi = new CoursesApi(undefined, "http://localhost:9221");
const dockerApi = new DockerApi(undefined, baseUrl);
const dockerEndpoints = {
"Get Container info": async () => {
const {containerId} = await inquirer.prompt([
{type: 'input', name: 'containerId', message: 'Enter the container ID:'}
]);

courses.dockerContainerIdDelete(94882).then(value => {
console.log(value)
})
const result = await dockerApi.dockerContainerIdGet(containerId as string);
console.log(`Status: ${result.status}, message: ${result.statusText}`);
},
'Delete Container': async () => {
const {containerId} = await inquirer.prompt([
{type: 'input', name: 'containerId', message: 'Enter the container ID:'}
]);

const result = await dockerApi.dockerContainerIdDelete(parseInt(containerId));
console.log(`Status: ${result.status}, message: ${result.statusText}`);
},
'Start Docker Container': async () => {
const {containerId} = await inquirer.prompt([
{type: 'input', name: 'containerId', message: 'Enter the container ID:'}
]);
const result = await dockerApi.dockerContainerIdStartGet(parseInt(containerId));
console.log(`Status: ${result.status}, message: ${result.statusText}`);
},
'Stop Docker Container': async () => {
const {containerId} = await inquirer.prompt([
{type: 'input', name: 'containerId', message: 'Enter the container ID:'}
]);
const result = await dockerApi.dockerContainerIdStopGet(parseInt(containerId));
console.log(`Status: ${result.status}, message: ${result.statusText}`);
},
// todo
// 'Create Docker image': async () => {
// const file = fs.readFileSync('../ex-Dockerfile', 'utf8');
// // const result = await dockerApi.dockerImagesCreatePost(file ,"test:latest");
// // console.log(JSON.stringify(result, null, 2));
// },
};

async function main() {
while (true) {
const {action} = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: 'Choose an endpoint to call:',
choices: [...Object.keys(dockerEndpoints), 'Exit']
}
]);

if (action === 'Exit') {
console.log('Goodbye!');
break;
}

try {
const act = action as string
// @ts-ignore
await dockerEndpoints[act]();
} catch (error) {
// @ts-ignore
console.error('An error occurred:', error.message);
}

console.log('\n');
}
}

program.action(main);

program.parse(process.argv);
18 changes: 16 additions & 2 deletions .spec/readme.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# OpenApi Spec

This is the directory where the spec is defined and the code is generated.
This is where the spec is defined and the code is generated.

# What is this

We implement the api using the [spec](./leviathan.yaml), this contains the all paths and types defined in
the [open API spec](https://swagger.io/specification/v3/) format.
This allows us to autogenerate the client and server code, in a typesafe manner.
This allows us to autogenerate the client and server code, in a (relatively)typesafe manner, the spec only defines the
types it is up to us to follow it.

## Generated code usage

The server files are automatically moved to [internal/generated-server](../internal/generated-server), can be directly
used.

To use the client TS code install it via:

```
npm install 'https://gitpkg.vercel.app/makeopensource/leviathan/.spec/client?master'
```

This install the generated code on the ```master``` branch.

## Directory walkthrough

Expand Down
Empty file added Makefile
Empty file.
21 changes: 16 additions & 5 deletions cmd/api/docker_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,38 @@ type DockerAPI struct {

// DockerContainerIdDelete Delete /docker/:containerId
func (dk DockerAPI) DockerContainerIdDelete(c *gin.Context) {

log.Debug().Msgf("Recived Delete container request with id: %s", c.Param("containerId"))
c.Status(200)
}

// DockerContainerIdGet Get /docker/:containerId
func (dk DockerAPI) DockerContainerIdGet(c *gin.Context) {

log.Debug().Msgf("Recived container info request with id: %s", c.Param("containerId"))
c.Status(200)
}

// DockerContainerIdStartGet Get /docker/:containerId/start
func (dk DockerAPI) DockerContainerIdStartGet(c *gin.Context) {

log.Debug().Msgf("Recived start container request with id: %s", c.Param("containerId"))
c.Status(200)
}

// DockerContainerIdStopGet Get /docker/:containerId/stop
func (dk DockerAPI) DockerContainerIdStopGet(c *gin.Context) {

log.Debug().Msgf("Recived stop container request with id: %s", c.Param("containerId"))
c.Status(200)
}

// DockerImagesCreatePost Post /docker/images/create
func (dk DockerAPI) DockerImagesCreatePost(c *gin.Context) {
log.Debug().Msgf("Recived create image request")
form, err := c.MultipartForm()
if err != nil {
log.Error().Err(err).Msgf("Could not parse multipart form")
return
}
tagName := form.Value["tagName"]

log.Debug().Any("Tagname", tagName).Msgf("Recived create image request")
c.Status(200)
}

Expand Down
163 changes: 1 addition & 162 deletions cmd/leviathan-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/google/uuid"
api "github.com/makeopensource/leviathan/cmd/api"
"github.com/makeopensource/leviathan/internal/dockerclient"
store "github.com/makeopensource/leviathan/internal/messagestore"
store "github.com/makeopensource/leviathan/internal/message-store"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"net/http"
Expand All @@ -20,82 +20,6 @@ import (
const jobQueueTopicName = "jobqueue.topic"
const totalJobs = 5

// test functions
//func main() {
// log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
//
// client, err := dockerclient.NewLocalClient()
// if err != nil {
// log.Fatal().Msg("Failed to setup local docker client")
// }
//
// //client, err := dockerclient.NewSSHClient("r334@192.168.50.123")
// //if err != nil {
// // log.Fatal().Msg("Failed to setup docker client")
// //}
//
// log.Info().Msg("Connected to remote client")
//
// err = dockerclient.BuildImageFromDockerfile(client, ".example/ex-Dockerfile", "testimage:latest")
// if err != nil {
// log.Error().Err(err).Msg("Failed to build image")
// return
// }
//
// images, err := dockerclient.ListImages(client)
// if err != nil {
// log.Error().Msg("Failed to build image")
// return
// }
//
// for _, image := range images {
// log.Info().Msgf("Container names: %v", image.RepoTags)
// }
//
// newContainerId, err := dockerclient.CreateNewContainer(
// client,
// "92912992939",
// "testimage:latest",
// []string{"py", "/home/autolab/student.py"},
// container.Resources{
// Memory: 512 * 1000000,
// NanoCPUs: 2 * 1000000000,
// },
// )
// if err != nil {
// log.Error().Err(err).Msg("Failed to create container")
// return
// }
//
// err = dockerclient.CopyToContainer(client, newContainerId, ".example/student/test.py")
// if err != nil {
// log.Error().Err(err).Msg("Failed to copy to container")
// }
//
// err = dockerclient.StartContainer(client, newContainerId)
// if err != nil {
// log.Error().Err(err).Msg("Failed to start container")
// return
// }
//
// err = dockerclient.TailContainerLogs(context.Background(), client, newContainerId)
// if err != nil {
// log.Error().Err(err).Msg("Failed to tail logs")
// return
// }
//
// data, err := dockerclient.ListContainers(client)
// if err != nil {
// log.Error().Msg("Failed to build image")
// return
// }
//
// for _, info := range data {
// log.Info().Msgf("Container names: %v", info.Names)
// }
//
//}

func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

Expand Down Expand Up @@ -174,88 +98,3 @@ func handleJobStatus(w http.ResponseWriter, r *http.Request, publisher message.P
return
}
}

//func cleanup(c *client.Client, containerID string) {
// dockerclient.StopContainer(c, containerID)
// err := dockerclient.RemoveContainer(c, containerID, false, false)
// if err != nil {
// os.Exit(-1)
// }
// os.Exit(0)
//}

//func main() {
//
// log.SetLevel(log.DebugLevel)
// if log.GetLevel() == log.TraceLevel {
// log.SetReportCaller(true)
// }
// // TODO: For prod ensure logs are json formatted for ingest
// // log.SetFormatter(&log.JSONFormatter{})
//
// cli, err := client.NewEnvClient()
// // cli, err := dockerclient.NewSSHClient("yeager")
// if err != nil {
// log.Fatal("Failed to setup docker client")
// }
//
// // err = dockerclient.PullImage(cli, "ubautograding/autograding_image_2004")
//
// id, err := dockerclient.CreateNewContainer(cli, "ubautograding/autograding_image_2004")
// if err != nil {
// log.Fatal("Failed to create image")
// }
//
// err = dockerclient.CopyToContainer(cli, id, fmt.Sprintf("%s/tmp/sanitycheck/tmp/", util.UserHomeDir()))
// if err != nil {
// cleanup(cli, id)
// }
//
// err = dockerclient.StartContainer(cli, id)
// if err != nil {
// cleanup(cli, id)
// }
//
// ctx, cancel := context.WithCancel(context.Background())
// go func() {
// err := dockerclient.TailContainerLogs(ctx, cli, id)
// if err != nil {
// log.Fatal("")
// }
// }()
//
// time.Sleep(10 * time.Second)
// cancel()
//
// cleanup(cli, id)
// dockerclient.ListContainer(cli)
//}
//list, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
//if err != nil {
// return
//}
//go func() {
// logs, err := cli.ContainerLogs(context.Background(), "fe2c5534dba7d7a18cd802d2d5133cd5763f166c0464a828c6d929512744a338", types.ContainerLogsOptions{ShowStdout: true,
// ShowStderr: true,
// Follow: true,
// })
// if err != nil {
// fmt.Println(err)
// return
// }
// defer func(logs io.ReadCloser) {
// err := logs.Close()
// if err != nil {
// panic(err)
// }
// }(logs)
//
// // Copy the log stream to stdout
// _, err = io.Copy(os.Stdout, logs)
// if err != nil && err != io.EOF {
// panic(err)
// }
//}()
//for _, container := range list {
// fmt.Print(container.Names)
//}
4 changes: 3 additions & 1 deletion internal/dockerclient/dockerclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ func ListContainers(client *client.Client) ([]ContainerInfo, error) {
func CreateNewContainer(client *client.Client, jobUuid string, image string, entryPointCmd []string, machineLimits container.Resources) (string, error) {
config := &container.Config{
Image: image,

Labels: map[string]string{
"con": jobUuid,
},
Cmd: entryPointCmd,
}
hostConfig := &container.HostConfig{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package messagestore
package message_store

import (
"github.com/ThreeDotsLabs/watermill/message"
Expand Down
Loading

0 comments on commit 1f323dc

Please sign in to comment.