Flags support
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Adam Štrauch 2022-02-03 01:31:47 +01:00
parent 2a34177ab8
commit a213fac34a
Signed by: cx
GPG Key ID: 018304FFA8988F8D
10 changed files with 181 additions and 9 deletions

View File

@ -2,6 +2,8 @@
test: test:
go test -v apps/*.go go test -v apps/*.go
go test -v apps/drivers/*.go go test -v apps/drivers/*.go
go test -v detector/*.go
go test -v docker/*.go
build: build:
#podman run --rm --privileged -ti -v ${shell pwd}:/srv docker.io/library/golang:1.14-stretch /bin/sh -c "cd /srv && go build" #podman run --rm --privileged -ti -v ${shell pwd}:/srv docker.io/library/golang:1.14-stretch /bin/sh -c "cd /srv && go build"
@ -18,7 +20,7 @@ minio:
-p 9001:9001 \ -p 9001:9001 \
-e MINIO_ROOT_USER=test \ -e MINIO_ROOT_USER=test \
-e MINIO_ROOT_PASSWORD=testtest \ -e MINIO_ROOT_PASSWORD=testtest \
minio/minio server /data --console-address ":9001" docker.io/minio/minio:latest server /data --console-address ":9001"
.PHONY: clean .PHONY: clean
clean: clean:

View File

@ -4,3 +4,13 @@
Node API is an microservice that runs on node servers. It provides interface between Node API is an microservice that runs on node servers. It provides interface between
Docker and the admin site. Docker and the admin site.
## Test
On Fedora run podman API:
Root: sudo systemctl enable --now podman.socket
Rootless: podman system service -t 0 --log-level=debug

View File

@ -3,6 +3,7 @@ package apps
import ( import (
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/rosti-cz/node-api/detector"
) )
// AppsProcessor encapsulates functions for apps manipulation // AppsProcessor encapsulates functions for apps manipulation
@ -109,13 +110,14 @@ func (a *AppsProcessor) Update(name string, SSHPort int, HTTPPort int, image str
} }
// UpdateResources updates various metrics saved in the database // UpdateResources updates various metrics saved in the database
func (a *AppsProcessor) UpdateResources(name string, state string, CPUUsage float64, memory int, diskUsageBytes int, diskUsageInodes int) error { func (a *AppsProcessor) UpdateResources(name string, state string, CPUUsage float64, memory int, diskUsageBytes int, diskUsageInodes int, flags detector.Flags) error {
err := a.DB.Model(&App{}).Where("name = ?", name).Updates(App{ err := a.DB.Model(&App{}).Where("name = ?", name).Updates(App{
State: state, State: state,
CPUUsage: CPUUsage, CPUUsage: CPUUsage,
MemoryUsage: memory, MemoryUsage: memory,
DiskUsageBytes: diskUsageBytes, DiskUsageBytes: diskUsageBytes,
DiskUsageInodes: diskUsageInodes, DiskUsageInodes: diskUsageInodes,
Flags: flags,
}).Error }).Error
return err return err
} }

View File

@ -7,6 +7,7 @@ import (
// This is line from GORM documentation that imports database dialect // This is line from GORM documentation that imports database dialect
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/rosti-cz/node-api/detector"
) )
// ValidationError is error that holds multiple validation error messages // ValidationError is error that holds multiple validation error messages
@ -31,11 +32,12 @@ type Label struct {
// AppState contains info about runnint application, it's not saved in the database // AppState contains info about runnint application, it's not saved in the database
type AppState struct { type AppState struct {
State string `json:"state"` State string `json:"state"`
CPUUsage float64 `json:"cpu_usage"` // in percents CPUUsage float64 `json:"cpu_usage"` // in percents
MemoryUsage int `json:"memory_usage"` // in MB MemoryUsage int `json:"memory_usage"` // in MB
DiskUsageBytes int `json:"disk_usage_bytes"` DiskUsageBytes int `json:"disk_usage_bytes"`
DiskUsageInodes int `json:"disk_usage_inodes"` DiskUsageInodes int `json:"disk_usage_inodes"`
Flags detector.Flags `json:"flags"`
} }
// Apps is list of applications // Apps is list of applications
@ -83,6 +85,8 @@ type App struct {
DiskUsageBytes int `json:"disk_usage_bytes"` DiskUsageBytes int `json:"disk_usage_bytes"`
// Disk usage in inodes // Disk usage in inodes
DiskUsageInodes int `json:"disk_usage_inodes"` DiskUsageInodes int `json:"disk_usage_inodes"`
// Flags from detector of problems in the container
Flags detector.Flags `json:"flags"`
// this is gathered in docker package and has to be assembled externally // 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 Techs AppTechs `json:"techs,omitempty" gorm:"-"` // list of available technologies in the image

37
detector/main.go Normal file
View File

@ -0,0 +1,37 @@
package detector
import (
"fmt"
"regexp"
)
// Flags is list of strings describing problems found among the processes
type Flags []string
// Check goes over patterns and tries to flag given list of processes with flags.
func Check(processes []string) (Flags, error) {
flags := Flags{}
tmpFlags := make(map[string]bool)
for _, process := range processes {
for flag, patternSet := range patterns {
for _, pattern := range patternSet {
matched, err := regexp.MatchString(".*"+pattern+".*", process)
if err != nil {
return flags, err
}
if matched {
tmpFlags[flag] = true
break
}
fmt.Println(process, pattern, flag)
}
}
}
for flag, _ := range tmpFlags {
flags = append(flags, flag)
}
return flags, nil
}

35
detector/main_test.go Normal file
View File

@ -0,0 +1,35 @@
package detector
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCheck(t *testing.T) {
flags, err := Check([]string{
"sleep",
"apache2",
"miner",
"verus-solve",
})
assert.Nil(t, err)
assert.Contains(t, flags, "miner")
flags, err = Check([]string{
"sleep",
"apache2",
"hellminer",
})
assert.Nil(t, err)
assert.Contains(t, flags, "miner")
flags, err = Check([]string{
"sleep",
"apache2",
"miner", // This is not among patterns map
})
assert.Nil(t, err)
assert.NotContains(t, flags, "miner")
}

8
detector/patterns.go Normal file
View File

@ -0,0 +1,8 @@
package detector
var patterns map[string][]string = map[string][]string{
"miner": {
`verus\-solve`,
`hellminer`,
},
}

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -17,6 +18,7 @@ import (
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
dockerClient "github.com/docker/docker/client" dockerClient "github.com/docker/docker/client"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
"github.com/rosti-cz/node-api/detector"
) )
// Stats delay in seconds // Stats delay in seconds
@ -27,7 +29,7 @@ const dockerTimeout = 10
// DOCKER_SOCK tells where to connect to docker, it will be always local sock // DOCKER_SOCK tells where to connect to docker, it will be always local sock
const dockerSock = "/var/run/docker.sock" const dockerSock = "/var/run/docker.sock"
const podmanSock = "/run/podman/podman.sock" const podmanSock = "/run/user/1000/podman/podman.sock"
// DOCKER_API_VERSION set API version of Docker, 1.40 belongs to Docker 19.03.11 // DOCKER_API_VERSION set API version of Docker, 1.40 belongs to Docker 19.03.11
const dockerAPIVersion = "1.38" const dockerAPIVersion = "1.38"
@ -47,7 +49,10 @@ func (d *Driver) getClient() (*dockerClient.Client, error) {
} }
cli, err := dockerClient.NewClient("unix://"+connectTo, dockerAPIVersion, nil, nil) cli, err := dockerClient.NewClient("unix://"+connectTo, dockerAPIVersion, nil, nil)
return cli, err if err != nil {
return cli, fmt.Errorf("get docker client error: %v", err)
}
return cli, nil
} }
// ConnectionStatus checks connection to the Docker daemon // ConnectionStatus checks connection to the Docker daemon
@ -448,3 +453,43 @@ func (d *Driver) Exec(name string, cmd []string, stdin string, env []string, att
return &stdouterr, err return &stdouterr, err
} }
// GetProcesses return list of processes running under this container
func (d *Driver) GetProcesses(name string) ([]string, error) {
processes := []string{}
ctx := context.Background()
cli, err := d.getClient()
if err != nil {
return processes, err
}
defer cli.Close()
processList, err := cli.ContainerTop(ctx, name, []string{"-eo", "args"})
if err != nil {
return processes, fmt.Errorf("docker container top call error: %v", err)
}
for _, process := range processList.Processes {
if len(process) > 0 {
processes = append(processes, process[0])
}
}
return processes, nil
}
// GetFlags returns list of flags with problems found in the container, mainly used to detect miners or viruses
func (d *Driver) GetFlags(name string) (detector.Flags, error) {
processes, err := d.GetProcesses(name)
if err != nil {
return detector.Flags{}, err
}
flags, err := detector.Check(processes)
if err != nil {
return flags, err
}
return flags, nil
}

28
docker/docker_test.go Normal file
View File

@ -0,0 +1,28 @@
package docker
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetProcesses(t *testing.T) {
driver := Driver{
BindIPHTTP: "127.0.0.1",
BindIPSSH: "127.0.0.1",
}
driver.Remove("test")
_, err := driver.Create("test", "docker.io/library/busybox", "/tmp", 8990, 8922, 1, 128, []string{"sleep", "3600"})
assert.Nil(t, err)
err = driver.Start("test")
assert.Nil(t, err)
processes, err := driver.GetProcesses("test")
assert.Nil(t, err)
assert.Contains(t, processes, "sleep 3600")
driver.Remove("test")
}

View File

@ -35,6 +35,7 @@ func updateUsage(name string) error {
state.MemoryUsage, state.MemoryUsage,
state.DiskUsageBytes, state.DiskUsageBytes,
state.DiskUsageInodes, state.DiskUsageInodes,
state.Flags,
) )
return err return err