added docker templating and introducted possbility to change the baseimage name

This commit is contained in:
2023-08-23 22:45:46 +01:00
parent dfa8713aea
commit 6b287f2cf1
9 changed files with 1061 additions and 990 deletions

2
.gitignore vendored
View File

@@ -21,6 +21,8 @@ generate/Test
#ignore windows exe files
*.exe
dist/
# Ignore any sort of logs
logs/
# ignore docker image files
server/docker/containers/

View File

@@ -19,7 +19,7 @@ var (
// From the selected server IP address
// TODO: Test cases for this function
// Calls URL ex: http://0.0.0.0:8088/startcontainer?ports=0&GPU=false&ContainerName=docker-ubuntu-sshd
func StartContainer(IP string, NumPorts int, GPU bool, ContainerName string) (*docker.DockerVM, error) {
func StartContainer(IP string, NumPorts int, GPU bool, ContainerName string, baseImage string) (*docker.DockerVM, error) {
// Passes URL with number of TCP ports to allocated and to give GPU access to the docker container
var URL string
//version := p2p.Ip4or6(IP)
@@ -33,7 +33,7 @@ func StartContainer(IP string, NumPorts int, GPU bool, ContainerName string) (*d
//if version == "version 6" {
// URL = "http://[" + IP + "]:" + serverPort + "/startcontainer?ports=" + fmt.Sprint(NumPorts) + "&GPU=" + strconv.FormatBool(GPU) + "&ContainerName=" + ContainerName
//} else {
URL = "http://" + IP + "/startcontainer?ports=" + fmt.Sprint(NumPorts) + "&GPU=" + strconv.FormatBool(GPU) + "&ContainerName=" + ContainerName
URL = "http://" + IP + "/startcontainer?ports=" + fmt.Sprint(NumPorts) + "&GPU=" + strconv.FormatBool(GPU) + "&ContainerName=" + ContainerName + "&BaseImage=" + baseImage
//}
resp, err := http.Get(URL)

View File

@@ -117,7 +117,7 @@ var CliAction = func(ctx *cli.Context) error {
}
// Calls function to do Api call to start the container on the server side
imageRes, err := client.StartContainer(CreateVM, PortsInt, GPU, ContainerName)
imageRes, err := client.StartContainer(CreateVM, PortsInt, GPU, ContainerName, BaseImage)
if err != nil {
fmt.Print(err)

View File

@@ -10,6 +10,7 @@ var (
ViewImages string
CreateVM string
ContainerName string
BaseImage string
Ports string
Server bool
RemoveVM string
@@ -89,6 +90,13 @@ var AppConfigFlags = []cli.Flag{
EnvVars: []string{"CONTAINER_NAME"},
Destination: &ContainerName,
},
&cli.StringFlag{
Name: "BaseImage",
Aliases: []string{"bi"},
Usage: "Specifying the docker base image to template the dockerfile",
EnvVars: []string{"CONTAINER_NAME"},
Destination: &BaseImage,
},
&cli.StringFlag{
Name: "RemoveVM",
Aliases: []string{"rm"},

View File

@@ -21,6 +21,7 @@ type Config struct {
IPTable string
DockerContainers string
DefaultDockerFile string
DockerRunLogs string
SpeedTestFile string
IPV6Address string
PluginPath string

View File

@@ -84,6 +84,7 @@ func SetDefaults(envName string, forceDefault bool, CustomConfig interface{}, No
Defaults.FRPServerPort = "True"
Defaults.CustomConfig = CustomConfig
Defaults.BehindNAT = "True"
Defaults.DockerRunLogs = "/tmp/"
// Random name generator
hostname, err := os.Hostname()
if err != nil {

View File

@@ -3,22 +3,20 @@ package docker
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/Akilan1999/p2p-rendering-computation/config"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/archive"
"github.com/docker/go-connections/nat"
"github.com/google/uuid"
"github.com/lithammer/shortuuid"
"github.com/otiai10/copy"
"github.com/phayes/freeport"
"io"
"io/ioutil"
"os"
"os/exec"
"time"
"text/template"
)
type DockerVM struct {
@@ -29,6 +27,9 @@ type DockerVM struct {
ImagePath string `json:"ImagePath"`
Ports Ports `json:"Ports"`
GPU string `json:"GPU"`
TempPath string
BaseImage string
LogsPath string
}
type DockerContainers struct {
@@ -65,7 +66,7 @@ var dockerRegistryUserID = ""
// BuildRunContainer Function is incharge to invoke building and running contianer and also allocating external
// ports
func BuildRunContainer(NumPorts int, GPU string, ContainerName string) (*DockerVM, error) {
func BuildRunContainer(NumPorts int, GPU string, ContainerName string, baseImage string) (*DockerVM, error) {
//Docker Struct Variable
var RespDocker *DockerVM = new(DockerVM)
@@ -79,6 +80,7 @@ func BuildRunContainer(NumPorts int, GPU string, ContainerName string) (*DockerV
// variables in the struct
RespDocker.SSHUsername = "master"
RespDocker.SSHPassword = "password"
//RespDocker.BaseImage = "ubuntu:20.04"
//RespDocker.VNCPassword = "vncpassword"
//Default parameters
@@ -89,6 +91,7 @@ func BuildRunContainer(NumPorts int, GPU string, ContainerName string) (*DockerV
return nil, err
}
RespDocker.ImagePath = config.DefaultDockerFile
RespDocker.LogsPath = config.DockerRunLogs
// We are checking if the container name is not nil and not equal to the default one used
// which is docker-ubuntu-sshd
@@ -110,7 +113,22 @@ func BuildRunContainer(NumPorts int, GPU string, ContainerName string) (*DockerV
}
}
PortsInformation, err := OpenPortsFile(RespDocker.ImagePath + "/ports.json")
// Checking if the base image is provided
if baseImage != "" {
RespDocker.BaseImage = baseImage
} else {
RespDocker.BaseImage = "ubuntu:20.04"
}
// Template docker with the base image provided
err = RespDocker.TemplateDockerContainer()
if err != nil {
return nil, err
}
// Template the DockerFile and point to the temp location
PortsInformation, err := OpenPortsFile(RespDocker.ImagePath + "/" + RespDocker.TagName + "/ports.json")
if err != nil {
return nil, err
}
@@ -171,30 +189,40 @@ func BuildRunContainer(NumPorts int, GPU string, ContainerName string) (*DockerV
// Builds docker image (TODO: relative path for Dockerfile deploy)
func (d *DockerVM) imageBuild(dockerClient *client.Client) error {
ctx, _ := context.WithTimeout(context.Background(), time.Second*2000)
//ctx, _ := context.WithTimeout(context.Background(), time.Second*2000)
//defer cancel()
tar, err := archive.TarWithOptions(d.ImagePath, &archive.TarOptions{})
var cmd bytes.Buffer
cmd.WriteString("docker build -t " + d.TagName + " " + d.ImagePath + "/" + d.TagName)
//"-v=/opt/data:/data p2p-ubuntu /start > /dev/null"
cmdStr := cmd.String()
_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
return err
}
opts := types.ImageBuildOptions{
Dockerfile: "Dockerfile",
Tags: []string{d.TagName},
Remove: true,
}
res, err := dockerClient.ImageBuild(ctx, tar, opts)
if err != nil {
return err
}
defer res.Body.Close()
err = print(res.Body)
if err != nil {
return err
}
//tar, err := archive.TarWithOptions(d.ImagePath+"/"+d.TagName, &archive.TarOptions{})
//if err != nil {
// return err
//}
//
//opts := types.ImageBuildOptions{
// Dockerfile: "Dockerfile",
// Tags: []string{d.TagName},
// Remove: true,
//}
//res, err := dockerClient.ImageBuild(ctx, tar, opts)
//if err != nil {
// return err
//}
//
//defer res.Body.Close()
//
//err = print(res.Body)
//if err != nil {
// return err
//}
return nil
}
@@ -205,97 +233,101 @@ func (d *DockerVM) imageBuild(dockerClient *client.Client) error {
// -p 3443:6901 -p 3453:22 -p 3434:3434 -p 3245:3245 -v=/opt/data:/data
// p2p-ubuntu /start > /dev/null
func (d *DockerVM) runContainer(dockerClient *client.Client) error {
ctx, _ := context.WithTimeout(context.Background(), time.Second*2000)
//ctx, _ := context.WithTimeout(context.Background(), time.Second*2000)
// The first mode runs using the Docker Api. As the API supports using
// CPU and uses a shell script for GPU call because till this point of
// implementation docker api does not support the flag "--gpu all"
if d.GPU != "true" {
//Exposed ports for docker config file
var ExposedPort nat.PortSet
ExposedPort = nat.PortSet{
"22/tcp": struct{}{},
//"6901/tcp": struct{}{},
}
// Port forwarding for VNC and SSH ports
PortForwarding := nat.PortMap{
//"22/tcp": []nat.PortBinding{
//if d.GPU != "true" {
// //Exposed ports for docker config file
// var ExposedPort nat.PortSet
//
// ExposedPort = nat.PortSet{
// "22/tcp": struct{}{},
// //"6901/tcp": struct{}{},
// }
//
// // Port forwarding for VNC and SSH ports
// PortForwarding := nat.PortMap{
// //"22/tcp": []nat.PortBinding{
// // {
// // HostIP: "0.0.0.0",
// // HostPort: fmt.Sprint(d.SSHPort),
// // },
// //},
// //"6901/tcp": []nat.PortBinding{
// // {
// // HostIP: "0.0.0.0",
// // HostPort: fmt.Sprint(d.VNCPort),
// // },
// //},
// }
//
// for i := range d.Ports.PortSet {
// // Parameters "tcp or udp", external port
// Port, err := nat.NewPort(d.Ports.PortSet[i].Type, fmt.Sprint(d.Ports.PortSet[i].InternalPort))
// if err != nil {
// return err
// }
//
// // Exposed Ports
// ExposedPort[Port] = struct{}{}
//
// PortForwarding[Port] = []nat.PortBinding{
// {
// HostIP: "0.0.0.0",
// HostPort: fmt.Sprint(d.SSHPort),
// HostPort: fmt.Sprint(d.Ports.PortSet[i].ExternalPort),
// },
//},
//"6901/tcp": []nat.PortBinding{
// {
// HostIP: "0.0.0.0",
// HostPort: fmt.Sprint(d.VNCPort),
// },
//},
}
for i := range d.Ports.PortSet {
// Parameters "tcp or udp", external port
Port, err := nat.NewPort(d.Ports.PortSet[i].Type, fmt.Sprint(d.Ports.PortSet[i].InternalPort))
if err != nil {
return err
}
// Exposed Ports
ExposedPort[Port] = struct{}{}
PortForwarding[Port] = []nat.PortBinding{
{
HostIP: "0.0.0.0",
HostPort: fmt.Sprint(d.Ports.PortSet[i].ExternalPort),
},
}
}
config := &container.Config{
Image: d.TagName,
Entrypoint: []string{"/start"},
Volumes: map[string]struct{}{"/opt/data:/data": {}},
ExposedPorts: ExposedPort,
}
hostConfig := &container.HostConfig{
PortBindings: PortForwarding,
}
res, err := dockerClient.ContainerCreate(ctx, config, hostConfig,
nil, nil, "")
// Set response ID
d.ID = res.ID
if err != nil {
return err
}
err = dockerClient.ContainerStart(ctx, res.ID, types.ContainerStartOptions{})
if err != nil {
return err
}
} else {
// }
// }
//
// config := &container.Config{
// Image: d.TagName,
// Entrypoint: []string{"/start"},
// Volumes: map[string]struct{}{"/opt/data:/data": {}},
// ExposedPorts: ExposedPort,
// }
// hostConfig := &container.HostConfig{
// PortBindings: PortForwarding,
// }
//
// res, err := dockerClient.ContainerCreate(ctx, config, hostConfig,
// nil, nil, "")
//
// // Set response ID
// d.ID = res.ID
//
// if err != nil {
// return err
// }
//
// err = dockerClient.ContainerStart(ctx, res.ID, types.ContainerStartOptions{})
//
// if err != nil {
// return err
// }
//} else {
// Generate Random ID
id := shortuuid.New()
d.ID = id
var cmd bytes.Buffer
cmd.WriteString("docker run -d=true --name=" + id + " --restart=always --gpus all ")
cmd.WriteString("docker run -d=true --name=" + id + " --restart=always ")
if d.GPU == "true" {
cmd.WriteString("--gpus all ")
}
for i := range d.Ports.PortSet {
cmd.WriteString("-p " + fmt.Sprint(d.Ports.PortSet[i].ExternalPort) + ":" + fmt.Sprint(d.Ports.PortSet[i].InternalPort) + " ")
}
cmd.WriteString("-v=/opt/data:/data " + d.TagName + " /start > /dev/null")
cmd.WriteString("-v=/tmp:/data " + d.TagName + " > /dev/null")
//"-v=/opt/data:/data p2p-ubuntu /start > /dev/null"
cmdStr := cmd.String()
_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
return err
}
}
//}
return nil
}
@@ -303,24 +335,45 @@ func (d *DockerVM) runContainer(dockerClient *client.Client) error {
// Stop and remove a container
// Reference (https://gist.github.com/frikky/e2efcea6c733ea8d8d015b7fe8a91bf6)
func StopAndRemoveContainer(containername string) error {
ctx := context.Background()
//ctx := context.Background()
//
//// Gets docker information from env variables
//client, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
//if err != nil {
// return err
//}
//
//if err = client.ContainerStop(ctx, containername, nil); err != nil {
// return err
//}
//
//removeOptions := types.ContainerRemoveOptions{
// RemoveVolumes: true,
// Force: true,
//}
//
//if err = client.ContainerRemove(ctx, containername, removeOptions); err != nil {
// return err
//}
// Gets docker information from env variables
client, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
// stop docker container
var stop bytes.Buffer
stop.WriteString("docker stop " + containername)
cmdStr := stop.String()
_, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
return err
}
if err = client.ContainerStop(ctx, containername, nil); err != nil {
return err
}
// remove docker container
var remove bytes.Buffer
remove.WriteString("docker remove " + containername)
removeOptions := types.ContainerRemoveOptions{
RemoveVolumes: true,
Force: true,
}
cmdStr = remove.String()
if err = client.ContainerRemove(ctx, containername, removeOptions); err != nil {
_, err = exec.Command("/bin/sh", "-c", cmdStr).Output()
if err != nil {
return err
}
@@ -402,3 +455,51 @@ func OpenPortsFile(filename string) (*Ports, error) {
return c, nil
}
// TemplateDockerContainer This function templates the docker container
// with the base docker image to use
func (d *DockerVM) TemplateDockerContainer() error {
err := d.CopyToTmpContainer()
if err != nil {
return err
}
// parses the site.yml file in the tmp directory
t, err := template.ParseFiles(d.ImagePath + "/" + d.TagName + "/Dockerfile")
if err != nil {
return err
}
// opens the output file
f, err := os.Create(d.ImagePath + "/" + d.TagName + "/Dockerfile")
if err != nil {
return err
}
image := d.BaseImage
// Pass in Docker Base Image
err = t.Execute(f, image)
if err != nil {
return err
}
return nil
}
// CopyToTmpContainer Creates a copy of the docker folder
func (d *DockerVM) CopyToTmpContainer() error {
// generate rand to UUID this is debug the ansible file if needed
id := uuid.New()
// copies the plugin to the tmp directory
err := copy.Copy(d.ImagePath+"/", d.LogsPath+id.String()+"_"+d.TagName)
if err != nil {
return err
}
// Set the plugin execution to the tmp location
d.TagName = id.String() + "_" + d.TagName
// removing slash
d.ImagePath = d.LogsPath[:len(d.LogsPath)-1]
return nil
}

View File

@@ -1,43 +0,0 @@
package server
import (
"fmt"
"github.com/Akilan1999/p2p-rendering-computation/server/docker"
"net"
"net/rpc"
)
const (
port = "8089"
)
type Listener int
type Docker struct {
docker *docker.DockerVM
}
// Starts container using RPC calls
func (l *Listener) StartContainer(reply *Docker) error {
vm, err := docker.BuildRunContainer(3, "false", "")
if err != nil {
return err
}
fmt.Printf("Receive: %v\n", vm)
*reply = Docker{vm}
return nil
}
func Rpc() {
rpcServer, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+port)
if err != nil {
fmt.Print(err)
}
inbound, err := net.ListenTCP("tcp", rpcServer)
if err != nil {
fmt.Print(err)
}
listener := new(Listener)
rpc.Register(listener)
rpc.Accept(inbound)
}

View File

@@ -107,6 +107,7 @@ func Server() (*gin.Engine, error) {
Ports := c.DefaultQuery("ports", "0")
GPU := c.DefaultQuery("GPU", "false")
ContainerName := c.DefaultQuery("ContainerName", "")
BaseImage := c.DefaultQuery("BaseImage", "")
var PortsInt int
// Convert Get Request value to int
@@ -114,7 +115,7 @@ func Server() (*gin.Engine, error) {
// Creates container and returns-back result to
// access container
resp, err := docker.BuildRunContainer(PortsInt, GPU, ContainerName)
resp, err := docker.BuildRunContainer(PortsInt, GPU, ContainerName, BaseImage)
if err != nil {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))