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"` }