diff --git a/api.http b/api.http index 2000617..0be6992 100644 --- a/api.http +++ b/api.http @@ -74,3 +74,12 @@ Content-type: application/json GET http://localhost:1323/v1/apps Content-type: application/json Authorization: Token 333ff32b-6c9e-4794-adab-c289447e66b0 + +### + +POST http://localhost:1323/v1/apps/test_1234/labels +Content-type: application/json + +{ + "Value": "userid:cx" +} diff --git a/apps/main.go b/apps/main.go index 4686865..eba8183 100644 --- a/apps/main.go +++ b/apps/main.go @@ -16,7 +16,7 @@ func Get(name string) (*App, error) { db := common.GetDBConnection() - err := db.First(&app).Where("name = ?", name).Error + err := db.Preload("Labels").First(&app).Where("name = ?", name).Error if err != nil { return nil, err } @@ -30,7 +30,7 @@ func List() (*[]App, error) { db := common.GetDBConnection() - err := db.Find(&apps).Error + err := db.Preload("Labels").Find(&apps).Error if err != nil { return nil, err } @@ -132,3 +132,43 @@ func Delete(name string) error { err := db.Delete(App{}).Where("name = ?", name).Error return err } + +// AddLabel adds label to existing application +func AddLabel(appName string, label string) error { + var app App + + db := common.GetDBConnection() + + err := db.First(&app).Where("name = ?", appName).Error + if err != nil { + return err + } + + // Check if there is such label already + var count int + err = db.Model(&Label{}).Where("value = ? AND app_id = ?", label, app.ID).Count(&count).Error + if err != nil { + return err + } + + if count > 0 { + return nil + } + + // And create the label + return db.Create(&Label{AppID: app.ID, Value: label}).Error +} + +// RemoveLabel removes label from existing application +func RemoveLabel(appName string, label string) error { + var app App + + db := common.GetDBConnection() + + err := db.First(&app).Where("name = ?", appName).Error + if err != nil { + return err + } + + return db.Delete(&Label{}).Where("label = ? AND app_id = ?", label, app.ID).Error +} diff --git a/apps/types.go b/apps/types.go index fe4533e..2c79260 100644 --- a/apps/types.go +++ b/apps/types.go @@ -21,6 +21,7 @@ func (v ValidationError) Error() string { // Label holds metadata about the application type Label struct { Value string `json:"value"` + AppID uint `json:"-" gorm:"not null"` } // AppState contains info about runnint application, it's not saved in the database @@ -39,13 +40,13 @@ type App struct { UpdatedAt time.Time `json:"updated_at"` DeletedAt *time.Time `sql:"index" json:"deleted_at"` - Name string `json:"name" gorm:"primary_key"` - SSHPort int `json:"ssh_port"` - HTTPPort int `json:"http_port"` - Image string `json:"image"` - 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 + Name string `json:"name" gorm:"unique,index,not_null"` + SSHPort int `json:"ssh_port"` + HTTPPort int `json:"http_port"` + Image string `json:"image"` + CPU int `json:"cpu"` // percentage, 200 means two CPU + Memory int `json:"memory"` // Limit in MB + Labels []Label `json:"labels" gorm:"foreignkey:AppID"` // username:cx or user_id:1 State string `json:"state"` CPUUsage float64 `json:"cpu_usage"` // in percents diff --git a/docker/tools.go b/docker/tools.go index 5bf0e0b..fb9f1c8 100644 --- a/docker/tools.go +++ b/docker/tools.go @@ -17,13 +17,12 @@ func du(path string) (int, int, error) { // Occupied space var out bytes.Buffer var errOut bytes.Buffer - command := exec.Command("/usr/bin/du", "-m", "-s", path) + command := exec.Command("/usr/bin/du", "-b", "-s", path) command.Stdout = &out command.Stderr = &errOut err := command.Run() if err != nil { log.Println(errOut.String()) - log.Println("/usr/bin/du -m -s " + path) return space, inodes, err } fields := strings.Fields(strings.TrimSpace(out.String())) @@ -44,7 +43,6 @@ func du(path string) (int, int, error) { err = command.Run() if err != nil { log.Println(errOut.String()) - log.Println("/usr/bin/du --inodes -s " + path) return space, inodes, err } fields = strings.Fields(strings.TrimSpace(out.String())) diff --git a/docker/types.go b/docker/types.go index 7babff7..a184900 100644 --- a/docker/types.go +++ b/docker/types.go @@ -78,7 +78,7 @@ func (c *Container) DiskUsage() (int, int, error) { return du(c.volumeHostPath()) } -// ResourceUsage returns amount of memory in MB and CPU in % that the app occupies +// ResourceUsage returns amount of memory in B and CPU in % that the app occupies func (c *Container) ResourceUsage() (float64, int, error) { driver := c.getDriver() diff --git a/main.go b/main.go index f839442..c19ea75 100644 --- a/main.go +++ b/main.go @@ -255,6 +255,37 @@ func main() { return c.JSON(http.StatusOK, Message{Message: "ok"}) }) + // Adds new label + e.POST("/v1/apps/:name/labels", func(c echo.Context) error { + name := c.Param("name") + label := apps.Label{} + + err := c.Bind(&label) + if err != nil { + return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent) + } + + err = apps.AddLabel(name, label.Value) + if err != nil { + return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) + } + + return c.JSON(http.StatusOK, Message{Message: "ok"}) + }) + + // Removes existing label + e.DELETE("/v1/apps/:name/labels/:label", func(c echo.Context) error { + name := c.Param("name") + label := c.Param("name") + + err := apps.RemoveLabel(name, label) + if err != nil { + return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) + } + + return c.JSON(http.StatusOK, Message{Message: "ok"}) + }) + // Delete one app e.DELETE("/v1/apps/:name", func(c echo.Context) error { name := c.Param("name") diff --git a/ui/index.html b/ui/index.html index bcfe0ec..2e9c6e8 100644 --- a/ui/index.html +++ b/ui/index.html @@ -69,11 +69,13 @@ {( app.name )} {( app.state.state )} {( app.image.replace("docker.io/", "") )} - - / {( app.cpu )} % - - / {( app.memory )} MB - - GB - - - - + {( app.cpu_usage )} % / {( app.cpu )} % + {( app.memory_usage )} MB / {( app.memory )} MB + {( (app.disk_usage_bytes / 1000 / 1000 / 1000).toFixed(2) )} GB + {( app.disk_usage_inodes )} + + {( label.value )} +