node-api/apps/types.go

179 lines
5.7 KiB
Go

package apps
import (
"regexp"
"strings"
"time"
"github.com/rosti-cz/node-api/detector"
"github.com/rosti-cz/node-api/jsonmap"
)
// ValidationError is error that holds multiple validation error messages
type ValidationError struct {
// List of validation errors
// Example: ["phone number is too short", "email address is wrongly formated"]
Errors []string
}
func (v ValidationError) Error() string {
return strings.Join(v.Errors, "\n")
}
// Label holds metadata about the application
type Label struct {
// Value of the label
// Example: userid:1
// Required: true
Value string `json:"value"`
AppID uint `json:"-" gorm:"not null"`
}
// AppState contains info about runnint application, it's not saved in the database
type AppState struct {
State string `json:"state"`
OOMKilled bool `json:"oom_killed"`
CPUUsage float64 `json:"cpu_usage"` // in percents
MemoryUsage int `json:"memory_usage"` // in MB
DiskUsageBytes int `json:"disk_usage_bytes"`
DiskUsageInodes int `json:"disk_usage_inodes"`
Flags detector.Flags `json:"flags"`
}
// Apps is list of applications
type Apps []App
// App keeps info about hosted application
type App struct {
// ID of the applicaton
// Unique: true
ID uint `gorm:"primary_key" json:"id"`
// Datetime of creation
CreatedAt time.Time `json:"created_at"`
// Datetime of last update
UpdatedAt time.Time `json:"updated_at"`
// Datetime of deletion
DeletedAt *time.Time `sql:"index" json:"deleted_at"`
// ####################################################
// This part is used in app template
// Name of the application
// Example: test_1234
Name string `json:"name" gorm:"unique,index,not_null"`
// SSH port where the application will be available
// Unique: true
// Example: 10001
SSHPort int `json:"ssh_port"`
// HTTP port where the application will be available
// Unique: true
// Example: 10002
HTTPPort int `json:"http_port"`
// Port of the application inside the container. Default is 0 which means default by the image.
// But it has to be between 1024 and 65536 with exception of 8000.
AppPort int `json:"app_port"`
// Runtime image
Image string `json:"image"`
// Number of CPUs ticks assigned, 100 means one CPU, 200 are two
CPU int `json:"cpu"` // percentage, 200 means two CPU
// Memory limit in MB
Memory int `json:"memory"` // Limit in MB
// Custom labels
Labels []Label `json:"labels" gorm:"foreignkey:AppID"` // username:cx or user_id:1
// ####################################################
// Current status of the application (underlaying container)
State string `json:"state"`
// True if the current container has been killed by OOM killer
OOMKilled bool `json:"oom_killed"`
// CPU usage in percents
CPUUsage float64 `json:"cpu_usage"` // in percents
// Memory usage in bytes
MemoryUsage int `json:"memory_usage"` // in B
// Disk usage in bytes
DiskUsageBytes int `json:"disk_usage_bytes"`
// Disk usage in inodes
DiskUsageInodes int `json:"disk_usage_inodes"`
// Flags from detector of problems in the container
Flags string `json:"flags"` // flags are separated by comma
// this is gathered in docker package and has to be assembled externally
Techs AppTechs `json:"techs,omitempty" gorm:"-"` // list of available technologies in the image
PrimaryTech AppTech `json:"primary_tech,omitempty" gorm:"-"` // Technology that was selected as primary in the environment
// This is not store in the database but used in create request when the app suppose to be created from an existing snapshot
Snapshot string `json:"snapshot" gorm:"-"`
EnvRaw jsonmap.JSONMap `json:"env" sql:"type:json"`
// Fields to setup during creating of the app, this is not stored in the database
Setup struct {
SSHKeys string `json:"ssh_keys"`
Tech string `json:"tech"`
TechVersion string `json:"tech_version"`
Password string `json:"password"`
ArchiveURL string `json:"archive_url"` // Archive with content of /srv
} `json:"setup,omitempty" gorm:"-"`
}
// Return env as map[string]string
func (a *App) GetEnv() map[string]string {
env := make(map[string]string)
for key, value := range a.EnvRaw {
env[key] = value.(string)
}
return env
}
// SetEnv sets env from map[string]string
func (a *App) SetEnv(env map[string]string) {
a.EnvRaw = make(jsonmap.JSONMap)
for key, value := range env {
a.EnvRaw[key] = value
}
}
// 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.AppPort != 0 && ((a.AppPort < 1024 && a.AppPort > 65536) || a.AppPort == 8000) {
errors = append(errors, "App port has to be between 1024 and 65536 with exception of 8000")
}
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
}
// AppTechs is list of technologies available in the app
type AppTechs []AppTech
// AppTech holds info about one technology in the app
type AppTech struct {
Name string `json:"name"`
Version string `json:"version"`
}