added docker templating and introducted possbility to change the baseimage name
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -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/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -21,6 +21,7 @@ type Config struct {
|
||||
IPTable string
|
||||
DockerContainers string
|
||||
DefaultDockerFile string
|
||||
DockerRunLogs string
|
||||
SpeedTestFile string
|
||||
IPV6Address string
|
||||
PluginPath string
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user