mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 21:33:18 +01:00
chore: logs swarm mode to beacon (#2918)
This commit is contained in:
@@ -1,73 +0,0 @@
|
||||
package analytics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SendStartEvent(se StartEvent) error {
|
||||
postBody := map[string]interface{}{
|
||||
"client_id": se.ClientId,
|
||||
"events": []map[string]interface{}{
|
||||
{
|
||||
"name": "start",
|
||||
"params": se,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doRequest(postBody)
|
||||
}
|
||||
|
||||
func SendRequestEvent(re RequestEvent) error {
|
||||
postBody := map[string]interface{}{
|
||||
"client_id": re.ClientId,
|
||||
"events": []map[string]interface{}{
|
||||
{
|
||||
"name": "request",
|
||||
"params": re,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doRequest(postBody)
|
||||
}
|
||||
|
||||
func doRequest(body map[string]interface{}) error {
|
||||
jsonValue, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "https://www.google-analytics.com/mp/collect", bytes.NewBuffer(jsonValue))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add("measurement_id", "G-S6NT05VXK9")
|
||||
q.Add("api_secret", "7FFhe65HQK-bXvujpQMquQ")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode/100 != 2 {
|
||||
dump, err := httputil.DumpResponse(response, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("%v", string(dump))
|
||||
return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func SendBeacon(e BeaconEvent) error {
|
||||
log.Tracef("sending beacon: %+v", e)
|
||||
jsonValue, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
package analytics
|
||||
|
||||
type StartEvent struct {
|
||||
ClientId string `json:"-"`
|
||||
Version string `json:"version"`
|
||||
FilterLength int `json:"filterLength"`
|
||||
RemoteHostLength int `json:"remoteHostLength"`
|
||||
CustomAddress bool `json:"customAddress"`
|
||||
CustomBase bool `json:"customBase"`
|
||||
Protected bool `json:"protected"`
|
||||
HasHostname bool `json:"hasHostname"`
|
||||
}
|
||||
|
||||
type RequestEvent struct {
|
||||
ClientId string `json:"-"`
|
||||
TotalContainers int `json:"totalContainers"`
|
||||
RunningContainers int `json:"runningContainers"`
|
||||
}
|
||||
|
||||
type BeaconEvent struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
@@ -24,10 +7,10 @@ type BeaconEvent struct {
|
||||
AuthProvider string `json:"authProvider"`
|
||||
FilterLength int `json:"filterLength"`
|
||||
Clients int `json:"clients"`
|
||||
HasDocumentation bool `json:"hasDocumentation"`
|
||||
HasCustomAddress bool `json:"hasCustomAddress"`
|
||||
HasCustomBase bool `json:"hasCustomBase"`
|
||||
HasHostname bool `json:"hasHostname"`
|
||||
RunningContainers int `json:"runningContainers"`
|
||||
HasActions bool `json:"hasActions"`
|
||||
IsSwarmMode bool `json:"isSwarmMode"`
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -67,16 +68,18 @@ type Client interface {
|
||||
Ping(context.Context) (types.Ping, error)
|
||||
Host() *Host
|
||||
ContainerActions(action string, containerID string) error
|
||||
IsSwarmMode() bool
|
||||
}
|
||||
|
||||
type _client struct {
|
||||
type httpClient struct {
|
||||
cli DockerCLI
|
||||
filters filters.Args
|
||||
host *Host
|
||||
SwarmMode bool
|
||||
}
|
||||
|
||||
func NewClient(cli DockerCLI, filters filters.Args, host *Host) Client {
|
||||
return &_client{cli, filters, host}
|
||||
func NewClient(cli DockerCLI, filters filters.Args, host *Host, swarm bool) Client {
|
||||
return &httpClient{cli, filters, host, swarm}
|
||||
}
|
||||
|
||||
// NewClientWithFilters creates a new instance of Client with docker filters
|
||||
@@ -96,7 +99,10 @@ func NewClientWithFilters(f map[string][]string) (Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewClient(cli, filterArgs, &Host{Name: "localhost", ID: "localhost"}), nil
|
||||
info, _ := cli.Info(context.Background())
|
||||
swarm := info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive
|
||||
|
||||
return NewClient(cli, filterArgs, &Host{Name: "localhost", ID: "localhost"}, swarm), nil
|
||||
}
|
||||
|
||||
func NewClientWithTlsAndFilter(f map[string][]string, host Host) (Client, error) {
|
||||
@@ -132,10 +138,13 @@ func NewClientWithTlsAndFilter(f map[string][]string, host Host) (Client, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewClient(cli, filterArgs, &host), nil
|
||||
info, _ := cli.Info(context.Background())
|
||||
swarm := info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive
|
||||
|
||||
return NewClient(cli, filterArgs, &host, swarm), nil
|
||||
}
|
||||
|
||||
func (d *_client) FindContainer(id string) (Container, error) {
|
||||
func (d *httpClient) FindContainer(id string) (Container, error) {
|
||||
var container Container
|
||||
containers, err := d.ListContainers()
|
||||
if err != nil {
|
||||
@@ -163,7 +172,7 @@ func (d *_client) FindContainer(id string) (Container, error) {
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (d *_client) ContainerActions(action string, containerID string) error {
|
||||
func (d *httpClient) ContainerActions(action string, containerID string) error {
|
||||
switch action {
|
||||
case "start":
|
||||
return d.cli.ContainerStart(context.Background(), containerID, container.StartOptions{})
|
||||
@@ -176,7 +185,7 @@ func (d *_client) ContainerActions(action string, containerID string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *_client) ListContainers() ([]Container, error) {
|
||||
func (d *httpClient) ListContainers() ([]Container, error) {
|
||||
containerListOptions := container.ListOptions{
|
||||
Filters: d.filters,
|
||||
All: true,
|
||||
@@ -217,7 +226,7 @@ func (d *_client) ListContainers() ([]Container, error) {
|
||||
return containers, nil
|
||||
}
|
||||
|
||||
func (d *_client) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error {
|
||||
func (d *httpClient) ContainerStats(ctx context.Context, id string, stats chan<- ContainerStat) error {
|
||||
response, err := d.cli.ContainerStats(ctx, id, true)
|
||||
|
||||
if err != nil {
|
||||
@@ -252,8 +261,6 @@ func (d *_client) ContainerStats(ctx context.Context, id string, stats chan<- Co
|
||||
mem = float64(v.MemoryStats.PrivateWorkingSet)
|
||||
}
|
||||
|
||||
log.Tracef("containerId = %s, cpuPercent = %f, memPercent = %f, memUsage = %f, daemonOSType = %s", id, cpuPercent, memPercent, mem, daemonOSType)
|
||||
|
||||
if cpuPercent > 0 || mem > 0 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -269,7 +276,7 @@ func (d *_client) ContainerStats(ctx context.Context, id string, stats chan<- Co
|
||||
}
|
||||
}
|
||||
|
||||
func (d *_client) ContainerLogs(ctx context.Context, id string, since string, stdType StdType) (io.ReadCloser, error) {
|
||||
func (d *httpClient) ContainerLogs(ctx context.Context, id string, since string, stdType StdType) (io.ReadCloser, error) {
|
||||
log.WithField("id", id).WithField("since", since).WithField("stdType", stdType).Debug("streaming logs for container")
|
||||
|
||||
if since != "" {
|
||||
@@ -297,7 +304,7 @@ func (d *_client) ContainerLogs(ctx context.Context, id string, since string, st
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
func (d *_client) Events(ctx context.Context, messages chan<- ContainerEvent) error {
|
||||
func (d *httpClient) Events(ctx context.Context, messages chan<- ContainerEvent) error {
|
||||
dockerMessages, err := d.cli.Events(ctx, types.EventsOptions{})
|
||||
|
||||
for {
|
||||
@@ -320,7 +327,7 @@ func (d *_client) Events(ctx context.Context, messages chan<- ContainerEvent) er
|
||||
|
||||
}
|
||||
|
||||
func (d *_client) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) {
|
||||
func (d *httpClient) ContainerLogsBetweenDates(ctx context.Context, id string, from time.Time, to time.Time, stdType StdType) (io.ReadCloser, error) {
|
||||
options := container.LogsOptions{
|
||||
ShowStdout: stdType&STDOUT != 0,
|
||||
ShowStderr: stdType&STDERR != 0,
|
||||
@@ -339,14 +346,18 @@ func (d *_client) ContainerLogsBetweenDates(ctx context.Context, id string, from
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
func (d *_client) Ping(ctx context.Context) (types.Ping, error) {
|
||||
func (d *httpClient) Ping(ctx context.Context) (types.Ping, error) {
|
||||
return d.cli.Ping(ctx)
|
||||
}
|
||||
|
||||
func (d *_client) Host() *Host {
|
||||
func (d *httpClient) Host() *Host {
|
||||
return d.host
|
||||
}
|
||||
|
||||
func (d *httpClient) IsSwarmMode() bool {
|
||||
return d.SwarmMode
|
||||
}
|
||||
|
||||
var PARENTHESIS_RE = regexp.MustCompile(`\(([a-zA-Z]+)\)`)
|
||||
|
||||
func findBetweenParentheses(s string) string {
|
||||
|
||||
@@ -89,7 +89,7 @@ func (m *mockedProxy) ContainerRestart(ctx context.Context, containerID string,
|
||||
func Test_dockerClient_ListContainers_null(t *testing.T) {
|
||||
proxy := new(mockedProxy)
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
assert.Empty(t, list, "list should be empty")
|
||||
@@ -101,7 +101,7 @@ func Test_dockerClient_ListContainers_null(t *testing.T) {
|
||||
func Test_dockerClient_ListContainers_error(t *testing.T) {
|
||||
proxy := new(mockedProxy)
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test"))
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
assert.Nil(t, list, "list should be nil")
|
||||
@@ -124,7 +124,7 @@ func Test_dockerClient_ListContainers_happy(t *testing.T) {
|
||||
|
||||
proxy := new(mockedProxy)
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
list, err := client.ListContainers()
|
||||
require.NoError(t, err, "error should not return an error.")
|
||||
@@ -151,7 +151,7 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
||||
options := container.LogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true, Since: "since"}
|
||||
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
||||
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
logReader, _ := client.ContainerLogs(context.Background(), id, "since", STDALL)
|
||||
|
||||
actual, _ := io.ReadAll(logReader)
|
||||
@@ -165,7 +165,7 @@ func Test_dockerClient_ContainerLogs_error(t *testing.T) {
|
||||
|
||||
proxy.On("ContainerLogs", mock.Anything, id, mock.Anything).Return(nil, errors.New("test"))
|
||||
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
reader, err := client.ContainerLogs(context.Background(), id, "", STDALL)
|
||||
|
||||
@@ -192,7 +192,7 @@ func Test_dockerClient_FindContainer_happy(t *testing.T) {
|
||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
container, err := client.FindContainer("abcdefghijkl")
|
||||
require.NoError(t, err, "error should not be thrown")
|
||||
@@ -215,7 +215,7 @@ func Test_dockerClient_FindContainer_error(t *testing.T) {
|
||||
|
||||
proxy := new(mockedProxy)
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
_, err := client.FindContainer("not_valid")
|
||||
require.Error(t, err, "error should be thrown")
|
||||
@@ -236,7 +236,7 @@ func Test_dockerClient_ContainerActions_happy(t *testing.T) {
|
||||
}
|
||||
|
||||
proxy := new(mockedProxy)
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||
proxy.On("ContainerInspect", mock.Anything, "abcdefghijkl").Return(json, nil)
|
||||
@@ -272,7 +272,7 @@ func Test_dockerClient_ContainerActions_error(t *testing.T) {
|
||||
}
|
||||
|
||||
proxy := new(mockedProxy)
|
||||
client := &_client{proxy, filters.NewArgs(), &Host{ID: "localhost"}}
|
||||
client := &httpClient{proxy, filters.NewArgs(), &Host{ID: "localhost"}, false}
|
||||
|
||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||
proxy.On("ContainerStart", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("test"))
|
||||
|
||||
@@ -42,6 +42,7 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
||||
allContainers := make([]docker.Container, 0)
|
||||
events := make(chan docker.ContainerEvent)
|
||||
stats := make(chan docker.ContainerStat)
|
||||
hasSwarm := false
|
||||
|
||||
for _, store := range h.stores {
|
||||
if containers, err := store.List(); err == nil {
|
||||
@@ -55,6 +56,10 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
store.SubscribeStats(ctx, stats)
|
||||
store.Subscribe(ctx, events)
|
||||
|
||||
if store.Client().IsSwarmMode() {
|
||||
hasSwarm = true
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
@@ -67,6 +72,8 @@ func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
||||
log.Errorf("error writing containers to event stream: %v", err)
|
||||
}
|
||||
b.RunningContainers = len(allContainers)
|
||||
b.IsSwarmMode = hasSwarm
|
||||
|
||||
f.Flush()
|
||||
|
||||
if !h.config.NoAnalytics {
|
||||
|
||||
@@ -59,6 +59,10 @@ func (m *MockedClient) Host() *docker.Host {
|
||||
return args.Get(0).(*docker.Host)
|
||||
}
|
||||
|
||||
func (m *MockedClient) IsSwarmMode() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func createHandler(client docker.Client, content fs.FS, config Config) *chi.Mux {
|
||||
if client == nil {
|
||||
client = new(MockedClient)
|
||||
|
||||
16
main_test.go
16
main_test.go
@@ -29,7 +29,7 @@ func Test_valid_localhost(t *testing.T) {
|
||||
fakeClientFactory := func(filter map[string][]string) (docker.Client, error) {
|
||||
return docker.NewClient(client, filters.NewArgs(), &docker.Host{
|
||||
ID: "localhost",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
args := args{}
|
||||
@@ -46,7 +46,7 @@ func Test_invalid_localhost(t *testing.T) {
|
||||
fakeClientFactory := func(filter map[string][]string) (docker.Client, error) {
|
||||
return docker.NewClient(client, filters.NewArgs(), &docker.Host{
|
||||
ID: "localhost",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
args := args{}
|
||||
@@ -63,7 +63,7 @@ func Test_valid_remote(t *testing.T) {
|
||||
fakeLocalClientFactory := func(filter map[string][]string) (docker.Client, error) {
|
||||
return docker.NewClient(local, filters.NewArgs(), &docker.Host{
|
||||
ID: "localhost",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
remote := new(fakeCLI)
|
||||
@@ -71,7 +71,7 @@ func Test_valid_remote(t *testing.T) {
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) {
|
||||
return docker.NewClient(remote, filters.NewArgs(), &docker.Host{
|
||||
ID: "test",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
args := args{
|
||||
@@ -93,7 +93,7 @@ func Test_valid_remote_and_local(t *testing.T) {
|
||||
fakeLocalClientFactory := func(filter map[string][]string) (docker.Client, error) {
|
||||
return docker.NewClient(local, filters.NewArgs(), &docker.Host{
|
||||
ID: "localhost",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
remote := new(fakeCLI)
|
||||
@@ -101,7 +101,7 @@ func Test_valid_remote_and_local(t *testing.T) {
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) {
|
||||
return docker.NewClient(remote, filters.NewArgs(), &docker.Host{
|
||||
ID: "test",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
args := args{
|
||||
RemoteHost: []string{"tcp://test:2375"},
|
||||
@@ -123,13 +123,13 @@ func Test_no_clients(t *testing.T) {
|
||||
|
||||
return docker.NewClient(local, filters.NewArgs(), &docker.Host{
|
||||
ID: "localhost",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
fakeRemoteClientFactory := func(filter map[string][]string, host docker.Host) (docker.Client, error) {
|
||||
client := new(fakeCLI)
|
||||
return docker.NewClient(client, filters.NewArgs(), &docker.Host{
|
||||
ID: "test",
|
||||
}), nil
|
||||
}, false), nil
|
||||
}
|
||||
|
||||
args := args{}
|
||||
|
||||
Reference in New Issue
Block a user