This commit is contained in:
parent
2a34177ab8
commit
a213fac34a
4
Makefile
4
Makefile
@ -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:
|
||||||
|
10
README.md
10
README.md
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
@ -36,6 +37,7 @@ type AppState struct {
|
|||||||
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
37
detector/main.go
Normal 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
35
detector/main_test.go
Normal 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
8
detector/patterns.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package detector
|
||||||
|
|
||||||
|
var patterns map[string][]string = map[string][]string{
|
||||||
|
"miner": {
|
||||||
|
`verus\-solve`,
|
||||||
|
`hellminer`,
|
||||||
|
},
|
||||||
|
}
|
@ -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
28
docker/docker_test.go
Normal 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")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user