Implementation of Docker
This commit is contained in:
		
							parent
							
								
									837f629d8d
								
							
						
					
					
						commit
						064d6e6487
					
				
					 9 changed files with 416 additions and 15 deletions
				
			
		
							
								
								
									
										32
									
								
								apps/main.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								apps/main.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -37,7 +37,7 @@ func List() (*[]App, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// New creates new record about application in the database
 | 
			
		||||
func New(name string, SSHPort int, HTTPPort int, image string, CPU string, memory int) error {
 | 
			
		||||
func New(name string, SSHPort int, HTTPPort int, image string, CPU int, memory int) error {
 | 
			
		||||
	app := App{
 | 
			
		||||
		Name:     name,
 | 
			
		||||
		SSHPort:  SSHPort,
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +49,13 @@ func New(name string, SSHPort int, HTTPPort int, image string, CPU string, memor
 | 
			
		|||
 | 
			
		||||
	db := common.GetDBConnection()
 | 
			
		||||
 | 
			
		||||
	validationErrors := app.Validate()
 | 
			
		||||
	if len(validationErrors) != 0 {
 | 
			
		||||
		return ValidationError{
 | 
			
		||||
			Errors: validationErrors,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db.Create(app).Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +64,7 @@ func New(name string, SSHPort int, HTTPPort int, image string, CPU string, memor
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Update changes value about app in the database
 | 
			
		||||
func Update(name string, SSHPort int, HTTPPort int, image string, CPU string, memory int) error {
 | 
			
		||||
func Update(name string, SSHPort int, HTTPPort int, image string, CPU int, memory int) error {
 | 
			
		||||
	var app App
 | 
			
		||||
 | 
			
		||||
	db := common.GetDBConnection()
 | 
			
		||||
| 
						 | 
				
			
			@ -67,13 +74,20 @@ func Update(name string, SSHPort int, HTTPPort int, image string, CPU string, me
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = db.Model(&app).Updates(App{
 | 
			
		||||
		SSHPort:  SSHPort,
 | 
			
		||||
		HTTPPort: HTTPPort,
 | 
			
		||||
		Image:    image,
 | 
			
		||||
		CPU:      CPU,
 | 
			
		||||
		Memory:   memory,
 | 
			
		||||
	}).Error
 | 
			
		||||
	app.SSHPort = SSHPort
 | 
			
		||||
	app.HTTPPort = HTTPPort
 | 
			
		||||
	app.Image = image
 | 
			
		||||
	app.CPU = CPU
 | 
			
		||||
	app.Memory = memory
 | 
			
		||||
 | 
			
		||||
	validationErrors := app.Validate()
 | 
			
		||||
	if len(validationErrors) != 0 {
 | 
			
		||||
		return ValidationError{
 | 
			
		||||
			Errors: validationErrors,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = db.Update(&app).Error
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,20 @@
 | 
			
		|||
package apps
 | 
			
		||||
 | 
			
		||||
import "github.com/jinzhu/gorm"
 | 
			
		||||
import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/jinzhu/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ValidationError is error that holds multiple validation error messages
 | 
			
		||||
type ValidationError struct {
 | 
			
		||||
	Errors []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v ValidationError) Error() string {
 | 
			
		||||
	return strings.Join(v.Errors, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Label holds metadata about the application
 | 
			
		||||
type Label struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -15,12 +29,44 @@ type App struct {
 | 
			
		|||
	SSHPort  int     `json:"ssh_port"`
 | 
			
		||||
	HTTPPort int     `json:"http_port"`
 | 
			
		||||
	Image    string  `json:"image"`
 | 
			
		||||
	Status   string  `json:"status"` // running, data (no container, data only), stopped
 | 
			
		||||
	CPU      string  `json:"cpu"`
 | 
			
		||||
	CPU      int     `json:"cpu"`    // percentage, 200 means two CPU
 | 
			
		||||
	Memory   int     `json:"memory"` // Limit in MB
 | 
			
		||||
	Labels   []Label `json:"labels"` // username:cx or user_id:1
 | 
			
		||||
 | 
			
		||||
	MemoryUsage int `json:"memory_usage"` // Usage in MB
 | 
			
		||||
	DiskUsage   int `json:"disk_usage"`   // Usage in MB
 | 
			
		||||
	Status      string `json:"status"`       // running, data-only (no container, data only), stopped
 | 
			
		||||
	MemoryUsage int    `json:"memory_usage"` // Usage in MB
 | 
			
		||||
	DiskUsage   int    `json:"disk_usage"`   // Usage in MB
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate do basic checks of the struct values
 | 
			
		||||
func (a *App) Validate() []string {
 | 
			
		||||
	var errors []string
 | 
			
		||||
 | 
			
		||||
	nameRegExp := regexp.MustCompile(`^[a-z0-9_]{3,48}$`)
 | 
			
		||||
	if !nameRegExp.MatchString(a.Name) {
 | 
			
		||||
		errors = append(errors, "name can contain only characters, numbers and underscores and has to be between 3 and 48 characters")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.SSHPort < 0 && a.SSHPort > 65536 {
 | 
			
		||||
		errors = append(errors, "SSH port has to be between 0 and 65536, where 0 means disabled")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.HTTPPort < 1 && a.HTTPPort > 65536 {
 | 
			
		||||
		errors = append(errors, "HTTP port has to be between 1 and 65536")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.Image == "" {
 | 
			
		||||
		errors = append(errors, "image cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.CPU < 10 && a.CPU > 800 {
 | 
			
		||||
		errors = append(errors, "CPU value has be between 10 and 800")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.Memory < 32 && a.Memory > 16384 {
 | 
			
		||||
		errors = append(errors, "Memory value has be between 32 and 16384")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errors
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										222
									
								
								docker/docker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								docker/docker.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,222 @@
 | 
			
		|||
package docker
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/docker/docker/api/types/container"
 | 
			
		||||
	"github.com/docker/docker/api/types/network"
 | 
			
		||||
	dockerClient "github.com/docker/docker/client"
 | 
			
		||||
	"github.com/docker/go-connections/nat"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Docker timeout
 | 
			
		||||
const dockerTimeout = 10
 | 
			
		||||
 | 
			
		||||
// DOCKER_SOCK tells where to connect to docker, it will be always local sock
 | 
			
		||||
const dockerSock = "unix:///var/run/docker.sock"
 | 
			
		||||
 | 
			
		||||
// DOCKER_API_VERSION set API version of Docker, 1.40 belongs to Docker 19.03.11
 | 
			
		||||
const dockerAPIVersion = "1.40"
 | 
			
		||||
 | 
			
		||||
// Driver keeps everything for connection to Docker
 | 
			
		||||
type Driver struct{}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) getClient() (*dockerClient.Client, error) {
 | 
			
		||||
	cli, err := dockerClient.NewClient(dockerSock, dockerAPIVersion, nil, nil)
 | 
			
		||||
	return cli, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectionStatus checks connection to the Docker daemon
 | 
			
		||||
func (d *Driver) ConnectionStatus() (bool, error) {
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = cli.ServerVersion(context.TODO())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) nameToID(name string) (string, error) {
 | 
			
		||||
	containerIDs, err := d.IsExist(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(containerIDs) == 0 {
 | 
			
		||||
		return "", errors.New("no container found")
 | 
			
		||||
	}
 | 
			
		||||
	if len(containerIDs) > 1 {
 | 
			
		||||
		return "", errors.New("multiple containers with the same name")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return containerIDs[0], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Remove removes container represented by containerID
 | 
			
		||||
func (d *Driver) Remove(name string) error {
 | 
			
		||||
	log.Println("Removing container " + name)
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerID, err := d.nameToID(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.Duration(dockerTimeout * time.Second)
 | 
			
		||||
	err = cli.ContainerStop(context.TODO(), containerID, &timeout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cli.ContainerRemove(context.TODO(), containerID, types.ContainerRemoveOptions{})
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start starts container represented by containerID
 | 
			
		||||
func (d *Driver) Start(name string) error {
 | 
			
		||||
	log.Println("Starting container " + name)
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerID, err := d.nameToID(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cli.ContainerStart(context.TODO(), containerID, types.ContainerStartOptions{})
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop stops container represented by containerID
 | 
			
		||||
func (d *Driver) Stop(name string) error {
 | 
			
		||||
	log.Println("Stopping container " + name)
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerID, err := d.nameToID(name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.Duration(dockerTimeout * time.Second)
 | 
			
		||||
	err = cli.ContainerStop(context.TODO(), containerID, &timeout)
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsExist checks existence of the container based on container name
 | 
			
		||||
// Returns container IDs in case of existence. Otherwise
 | 
			
		||||
// empty slice.
 | 
			
		||||
func (d *Driver) IsExist(name string) ([]string, error) {
 | 
			
		||||
	var containerIDs = make([]string, 0)
 | 
			
		||||
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return containerIDs, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containers, err := cli.ContainerList(context.TODO(), types.ContainerListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return containerIDs, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We go through the containers and pick the ones which match the task name
 | 
			
		||||
	for _, containerObject := range containers {
 | 
			
		||||
		for _, name := range containerObject.Names {
 | 
			
		||||
			name = strings.Trim(name, "/")
 | 
			
		||||
			if strings.Split(name, ".")[0] == name {
 | 
			
		||||
				containerIDs = append(containerIDs, containerObject.ID)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return containerIDs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create creates the container
 | 
			
		||||
// image - docker image
 | 
			
		||||
// cmd - string slice of command and its arguments
 | 
			
		||||
// volumePath - host's directory to mount into the container
 | 
			
		||||
// returns container ID
 | 
			
		||||
func (d *Driver) Create(name string, image string, volumePath string, HTTPPort int, SSHPort int, CPU int, memory int, cmd []string) (string, error) {
 | 
			
		||||
	log.Println("Creating container " + name)
 | 
			
		||||
	cli, err := d.getClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	portmaps := make(nat.PortMap, 1)
 | 
			
		||||
 | 
			
		||||
	portbindingsHTTP := make([]nat.PortBinding, 1)
 | 
			
		||||
	portbindingsHTTP[0] = nat.PortBinding{
 | 
			
		||||
		HostPort: strconv.Itoa(HTTPPort) + "/tcp",
 | 
			
		||||
	}
 | 
			
		||||
	portmaps["8000/tcp"] = portbindingsHTTP
 | 
			
		||||
 | 
			
		||||
	if SSHPort != 0 {
 | 
			
		||||
		portbindingsSSH := make([]nat.PortBinding, 1)
 | 
			
		||||
		portbindingsSSH[0] = nat.PortBinding{
 | 
			
		||||
			HostPort: strconv.Itoa(SSHPort) + "/tcp",
 | 
			
		||||
		}
 | 
			
		||||
		portmaps["22/tcp"] = portbindingsSSH
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createdContainer, err := cli.ContainerCreate(
 | 
			
		||||
		context.TODO(),
 | 
			
		||||
		&container.Config{
 | 
			
		||||
			Hostname: name,
 | 
			
		||||
			Env:      []string{},
 | 
			
		||||
			Image:    image,
 | 
			
		||||
			Cmd:      cmd,
 | 
			
		||||
		},
 | 
			
		||||
		&container.HostConfig{
 | 
			
		||||
			Resources: container.Resources{
 | 
			
		||||
				CPUPeriod:         100,
 | 
			
		||||
				CPUQuota:          int64(CPU),
 | 
			
		||||
				Memory:            int64(memory*110/100)*1024 ^ 2, // Allow 10 % more memory so we have space for MemoryReservation
 | 
			
		||||
				MemoryReservation: int64(memory)*1024 ^ 2,         // This should provide softer way how to limit the memory of our containers
 | 
			
		||||
			},
 | 
			
		||||
			PortBindings: portmaps,
 | 
			
		||||
			AutoRemove:   false,
 | 
			
		||||
			RestartPolicy: container.RestartPolicy{
 | 
			
		||||
				Name:              "unless-stopped",
 | 
			
		||||
				MaximumRetryCount: 3,
 | 
			
		||||
			},
 | 
			
		||||
			Binds: []string{
 | 
			
		||||
				volumePath + ":/srv",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		&network.NetworkingConfig{},
 | 
			
		||||
		name,
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerID := createdContainer.ID
 | 
			
		||||
 | 
			
		||||
	// I dunno if we want this
 | 
			
		||||
	// err = cli.ContainerStart(context.TODO(), createdContainer.ID, types.ContainerStartOptions{})
 | 
			
		||||
 | 
			
		||||
	return containerID, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								docker/du.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								docker/du.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
package docker
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Return bytes, inodes occupied by a directory and/or error if there is any
 | 
			
		||||
func du(path string) (int, int, error) {
 | 
			
		||||
	space, inodes := 0, 0
 | 
			
		||||
 | 
			
		||||
	// Occupied space
 | 
			
		||||
	var out bytes.Buffer
 | 
			
		||||
	command := exec.Command("/usr/bin/du -m -s " + path)
 | 
			
		||||
	command.Stdout = &out
 | 
			
		||||
	err := command.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return space, inodes, err
 | 
			
		||||
	}
 | 
			
		||||
	fields := strings.Fields(strings.TrimSpace(out.String()))
 | 
			
		||||
	out.Reset()
 | 
			
		||||
 | 
			
		||||
	if len(fields) == 2 {
 | 
			
		||||
		space, err = strconv.Atoi(fields[0])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return space, inodes, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Occupied inodes
 | 
			
		||||
	command = exec.Command("/usr/bin/du --inodes -s " + path)
 | 
			
		||||
	command.Stdout = &out
 | 
			
		||||
	err = command.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return space, inodes, err
 | 
			
		||||
	}
 | 
			
		||||
	fields = strings.Fields(strings.TrimSpace(out.String()))
 | 
			
		||||
	out.Reset()
 | 
			
		||||
 | 
			
		||||
	if len(fields) == 2 {
 | 
			
		||||
		space, err = strconv.Atoi(fields[0])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return inodes, inodes, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return space, inodes, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								docker/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docker/main.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
package docker
 | 
			
		||||
							
								
								
									
										58
									
								
								docker/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								docker/types.go
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
package docker
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	"github.com/rosti-cz/apps-api/apps"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Container extends App struct from App
 | 
			
		||||
type Container struct {
 | 
			
		||||
	App apps.App
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// volumeHostPath each container has one volume mounted into it,
 | 
			
		||||
func (c *Container) volumeHostPath() string {
 | 
			
		||||
	return path.Join("/srv", c.App.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Status returns state of the container
 | 
			
		||||
// Possible values: running, stopped, data-only
 | 
			
		||||
func (c *Container) Status() (string, error) {
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DiskSpace returns number of MB and inodes used by the container in it's mounted volume
 | 
			
		||||
func (c *Container) DiskSpace() (int, int, error) {
 | 
			
		||||
	return du(c.volumeHostPath())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create creates the container
 | 
			
		||||
func (c *Container) Create() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start starts the container
 | 
			
		||||
func (c *Container) Start() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop stops the container
 | 
			
		||||
func (c *Container) Stop() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset restarts the container
 | 
			
		||||
func (c *Container) Reset() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy removes the container but keeps the data so it can be created again
 | 
			
		||||
func (c *Container) Destroy() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete removes both data and the container
 | 
			
		||||
func (c *Container) Delete() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -7,4 +7,6 @@ require (
 | 
			
		|||
	github.com/labstack/echo v3.3.10+incompatible
 | 
			
		||||
	github.com/labstack/gommon v0.3.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
 | 
			
		||||
	github.com/docker/docker v1.13.1
 | 
			
		||||
	github.com/docker/go-connections v0.4.0
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								main.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -53,6 +53,9 @@ func main() {
 | 
			
		|||
 | 
			
		||||
		err = apps.New(app.Name, app.SSHPort, app.HTTPPort, app.Image, app.CPU, app.Memory)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if validationError, ok := err.(apps.ValidationError); ok {
 | 
			
		||||
				return c.JSONPretty(http.StatusBadRequest, Message{Errors: validationError.Errors}, JSONIndent)
 | 
			
		||||
			}
 | 
			
		||||
			return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +74,9 @@ func main() {
 | 
			
		|||
 | 
			
		||||
		err = apps.Update(name, app.SSHPort, app.HTTPPort, app.Image, app.CPU, app.Memory)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if validationError, ok := err.(apps.ValidationError); ok {
 | 
			
		||||
				return c.JSONPretty(http.StatusBadRequest, Message{Errors: validationError.Errors}, JSONIndent)
 | 
			
		||||
			}
 | 
			
		||||
			return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								types.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								types.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
// Message represents response with information about results of something
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Message string `json:"message"`
 | 
			
		||||
	Message string   `json:"message,omitempty"`
 | 
			
		||||
	Errors  []string `json:"errors,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue