223 lines
4.6 KiB
Go
223 lines
4.6 KiB
Go
package docker
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"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/phayes/freeport"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
type DockerVM struct {
|
|
SSHPort int `json:"SSHPort"`
|
|
SSHUsername string `json:"SSHUsername"`
|
|
SSHPassword string `json:"SSHPassword"`
|
|
VNCPort int `json:"VNCPort"`
|
|
VNCPassword string `json:"VNCPassword"`
|
|
ID string `json:"ID"`
|
|
TagName string `json:"TagName"`
|
|
ImagePath string `json:"ImagePath"`
|
|
Ports []int `json:"OpenPorts"`
|
|
}
|
|
|
|
type ErrorLine struct {
|
|
Error string `json:"error"`
|
|
ErrorDetail ErrorDetail `json:"errorDetail"`
|
|
}
|
|
|
|
type ErrorDetail struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
var dockerRegistryUserID = ""
|
|
|
|
func BuildRunContainer(NumPorts int) (*DockerVM,error) {
|
|
//Docker Struct Variable
|
|
var RespDocker *DockerVM = new(DockerVM)
|
|
|
|
// Count: 2 allocated ports for VNC and SSH + other ports
|
|
// to open
|
|
count := NumPorts + 2
|
|
|
|
// Gets 2 TCP ports empty
|
|
Ports, err := freeport.GetFreePorts(count)
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
for i := 2; i < count; i++ {
|
|
RespDocker.Ports = append(RespDocker.Ports, Ports[i])
|
|
}
|
|
|
|
// Sets Free port to Struct
|
|
RespDocker.SSHPort = Ports[0]
|
|
RespDocker.VNCPort = Ports[1]
|
|
// Sets appropriate username and password to the
|
|
// variables in the struct
|
|
RespDocker.SSHUsername = "master"
|
|
RespDocker.SSHPassword = "password"
|
|
RespDocker.VNCPassword = "vncpassword"
|
|
|
|
//Default parameters
|
|
RespDocker.TagName = "p2p-ubuntu"
|
|
RespDocker.ImagePath = "server/docker/containers/docker-ubuntu-sshd/"
|
|
|
|
// Gets docker information from env variables
|
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
// Builds docker image
|
|
err = RespDocker.imageBuild(cli)
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
// Runs docker contianer
|
|
err = RespDocker.runContainer(cli)
|
|
|
|
if err != nil {
|
|
return nil,err
|
|
}
|
|
|
|
|
|
return RespDocker,nil
|
|
|
|
}
|
|
|
|
//Builds docker image (TODO: relative path for Dockerfile folder)
|
|
func (d *DockerVM)imageBuild(dockerClient *client.Client) error {
|
|
ctx, _ := context.WithTimeout(context.Background(), time.Second*2000)
|
|
//defer cancel()
|
|
|
|
tar, err := archive.TarWithOptions(d.ImagePath, &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
|
|
}
|
|
|
|
// Starts container and assigns port numbers
|
|
// Sample Docker run Command
|
|
// docker run -d=true --name=Test123 --restart=always -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)
|
|
|
|
//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 {
|
|
|
|
Port, err := nat.NewPort("tcp",fmt.Sprint(d.Ports[i]))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Exposed Ports
|
|
ExposedPort[Port] = struct{}{}
|
|
|
|
PortForwarding[Port] = []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: fmt.Sprint(d.Ports[i]),
|
|
},
|
|
}
|
|
}
|
|
|
|
|
|
config := &container.Config{
|
|
Image : d.TagName,
|
|
Entrypoint: [] string {"/dockerstartup/vnc_startup.sh","/start"},
|
|
Volumes: map[string]struct{}{"/opt/data:/data":{}},
|
|
ExposedPorts: ExposedPort,
|
|
}
|
|
hostConfig := &container.HostConfig{
|
|
PortBindings: PortForwarding,
|
|
}
|
|
|
|
res, err := dockerClient.ContainerCreate(ctx,config,hostConfig,
|
|
nil,nil,"")
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = dockerClient.ContainerStart(ctx, res.ID, types.ContainerStartOptions{})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
|
|
func print(rd io.Reader) error {
|
|
var lastLine string
|
|
|
|
scanner := bufio.NewScanner(rd)
|
|
for scanner.Scan() {
|
|
lastLine = scanner.Text()
|
|
}
|
|
|
|
errLine := &ErrorLine{}
|
|
json.Unmarshal([]byte(lastLine), errLine)
|
|
if errLine.Error != "" {
|
|
return errors.New(errLine.Error)
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
} |