1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

Merge branch 'master' into add_digitalclock_module

This commit is contained in:
Narendra L
2019-09-09 10:11:51 +02:00
committed by GitHub
22 changed files with 780 additions and 153 deletions

163
modules/docker/client.go Normal file
View File

@@ -0,0 +1,163 @@
package docker
import (
"context"
"fmt"
"sort"
"strings"
"github.com/docker/docker/api/types"
"github.com/dustin/go-humanize"
"github.com/pkg/errors"
)
func (widget *Widget) getSystemInfo() string {
info, err := widget.cli.Info(context.Background())
if err != nil {
return errors.Wrap(err, "could not get docker system info").Error()
}
diskUsage, err := widget.cli.DiskUsage(context.Background())
if err != nil {
return errors.Wrap(err, "could not get disk usage").Error()
}
var duContainer int64
for _, c := range diskUsage.Containers {
duContainer += c.SizeRw
}
var duImg int64
for _, im := range diskUsage.Images {
duImg += im.Size
}
var duVol int64
for _, v := range diskUsage.Volumes {
duVol += v.UsageData.Size
}
sysInfo := []struct {
name string
value string
}{
{
name: "name:",
value: fmt.Sprintf("[%s]%s", widget.settings.common.Foreground, info.Name),
}, {
name: "version:",
value: fmt.Sprintf("[%s]%s", widget.settings.common.Foreground, info.ServerVersion),
}, {
name: "root:",
value: fmt.Sprintf("[%s]%s", widget.settings.common.Foreground, info.DockerRootDir),
},
{
name: "containers:",
value: fmt.Sprintf("[lime]%d[white]/[yellow]%d[white]/[red]%d",
info.ContainersRunning,
info.ContainersPaused, info.ContainersStopped),
},
{
name: "images:",
value: fmt.Sprintf("[%s]%d", widget.settings.common.Foreground, info.Images),
},
{
name: "volumes:",
value: fmt.Sprintf("[%s]%v", widget.settings.common.Foreground, len(diskUsage.Volumes)),
},
{
name: "memory limit:",
value: fmt.Sprintf("[%s]%s", widget.settings.common.Foreground, humanize.Bytes(uint64(info.MemTotal))),
},
{
name: "disk usage:",
value: fmt.Sprintf(`
[%s]* containers: [%s]%s
[%s]* images: [%s]%s
[%s]* volumes: [%s]%s
[%s]* [::b]total: [%s]%s[::-]
`,
widget.settings.labelColor,
widget.settings.common.Foreground,
humanize.Bytes(uint64(duContainer)),
widget.settings.labelColor,
widget.settings.common.Foreground,
humanize.Bytes(uint64(duImg)),
widget.settings.labelColor,
widget.settings.common.Foreground,
humanize.Bytes(uint64(duVol)),
widget.settings.labelColor,
widget.settings.common.Foreground,
humanize.Bytes(uint64(duContainer+duImg+duVol))),
},
}
padSlice(true, sysInfo, func(i int) string {
return sysInfo[i].name
}, func(i int, newVal string) {
sysInfo[i].name = newVal
})
result := ""
for _, info := range sysInfo {
result += fmt.Sprintf("[%s]%s %s\n", widget.settings.labelColor, info.name, info.value)
}
return result
}
func (widget *Widget) getContainerStates() string {
cntrs, err := widget.cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
if err != nil {
return errors.Wrapf(err, "could not get container list").Error()
}
if len(cntrs) == 0 {
return "no containers"
}
colorMap := map[string]string{
"created": "green",
"running": "lime",
"paused": "yellow",
"restarting": "yellow",
"removing": "yellow",
"exited": "red",
"dead": "red",
}
containers := []struct {
name string
state string
}{}
for _, c := range cntrs {
container := struct {
name string
state string
}{
name: c.Names[0],
state: c.State,
}
container.name = strings.Replace(container.name, "/", "", -1)
containers = append(containers, container)
}
sort.Slice(containers, func(i, j int) bool {
return containers[i].name < containers[j].name
})
padSlice(false, containers, func(i int) string {
return containers[i].name
}, func(i int, val string) {
containers[i].name = val
})
result := ""
for _, c := range containers {
result += fmt.Sprintf("[white]%s [%s]%s\n", c.name, colorMap[c.state], c.state)
}
return result
}

View File

@@ -0,0 +1,37 @@
wtf:
colors:
# background: black
# foreground: blue
border:
focusable: darkslateblue
focused: orange
normal: gray
checked: yellow
highlight:
fore: black
back: gray
rows:
even: yellow
odd: white
grid:
# How _wide_ the columns are, in terminal characters. In this case we have
# four columns, each of which are 35 characters wide.
# columns: [50, ]
# How _high_ the rows are, in terminal lines. In this case we have four rows
# that support ten line of text and one of four.
# rows: [50]
refreshInterval: 1
openFileUtil: "open"
mods:
docker:
type: docker
title: "💻"
enabled: true
position:
top: 0
left: 0
height: 3
width: 3
refreshInterval: 1
labelColor: lightblue

View File

@@ -0,0 +1,24 @@
package docker
import (
"github.com/olebedev/config"
"github.com/wtfutil/wtf/cfg"
)
const defaultTitle = "docker"
// Settings defines the configuration options for this module
type Settings struct {
common *cfg.Common
labelColor string
}
// NewSettingsFromYAML creates and returns an instance of Settings with configuration options populated
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
settings := Settings{
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig),
labelColor: ymlConfig.UString("labelColor", "white"),
}
return &settings
}

29
modules/docker/utils.go Normal file
View File

@@ -0,0 +1,29 @@
package docker
import (
"fmt"
"math"
"reflect"
"strconv"
)
func padSlice(padLeft bool, slice interface{}, getter func(i int) string, setter func(i int, newVal string)) {
rv := reflect.ValueOf(slice)
length := rv.Len()
maxLen := 0
for i := 0; i < length; i++ {
val := getter(i)
maxLen = int(math.Max(float64(len(val)), float64(maxLen)))
}
sign := "-"
if padLeft {
sign = ""
}
for i := 0; i < length; i++ {
val := getter(i)
val = fmt.Sprintf("%"+sign+strconv.Itoa(maxLen)+"s", val)
setter(i, val)
}
}

64
modules/docker/widget.go Normal file
View File

@@ -0,0 +1,64 @@
package docker
import (
"github.com/docker/docker/client"
"github.com/pkg/errors"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/view"
)
type Widget struct {
view.TextWidget
cli *client.Client
settings *Settings
displayBuffer string
}
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
widget := Widget{
TextWidget: view.NewTextWidget(app, settings.common, false),
settings: settings,
}
widget.View.SetScrollable(true)
cli, err := client.NewEnvClient()
if err != nil {
widget.displayBuffer = errors.Wrap(err, "could not create client").Error()
} else {
widget.cli = cli
}
widget.refreshDisplayBuffer()
return &widget
}
/* -------------------- Exported Functions -------------------- */
func (widget *Widget) Refresh() {
widget.refreshDisplayBuffer()
widget.Redraw(widget.display)
}
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) display() (string, string, bool) {
return widget.CommonSettings().Title, widget.displayBuffer, true
}
func (widget *Widget) refreshDisplayBuffer() {
if widget.cli == nil {
return
}
widget.displayBuffer = ""
widget.displayBuffer += "[" + widget.settings.labelColor + "::bul]system\n"
widget.displayBuffer += widget.getSystemInfo()
widget.displayBuffer += "\n"
widget.displayBuffer += "[" + widget.settings.labelColor + "::bul]containers\n"
widget.displayBuffer += widget.getContainerStates()
}

View File

@@ -1,12 +1,10 @@
package github
import (
"fmt"
"os"
"github.com/olebedev/config"
"github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/utils"
)
const defaultTitle = "GitHub"
@@ -42,26 +40,13 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
uploadURL: ymlConfig.UString("uploadURL", os.Getenv("WTF_GITHUB_UPLOAD_URL")),
username: ymlConfig.UString("username"),
}
settings.repositories = parseRepositories(ymlConfig)
settings.repositories = cfg.ParseAsMapOrList(ymlConfig, "repositories")
settings.customQueries = parseCustomQueries(ymlConfig)
return &settings
}
func parseRepositories(ymlConfig *config.Config) []string {
result := []string{}
repositories, err := ymlConfig.Map("repositories")
if err == nil {
for key, value := range repositories {
result = append(result, fmt.Sprintf("%s/%s", value, key))
}
return result
}
result = utils.ToStrs(ymlConfig.UList("repositories"))
return result
}
/* -------------------- Unexported Functions -------------------- */
func parseCustomQueries(ymlConfig *config.Config) []customQuery {
result := []customQuery{}

View File

@@ -73,5 +73,5 @@ func (widget *Widget) displayStats(project *GitlabProject) string {
}
func (widget *Widget) title(project *GitlabProject) string {
return fmt.Sprintf("[green]%s [white]", project.Path)
return fmt.Sprintf("[green]%s [white]", project.path)
}

View File

@@ -5,18 +5,17 @@ import (
)
type GitlabProject struct {
gitlab *glb.Client
Path string
client *glb.Client
path string
MergeRequests []*glb.MergeRequest
RemoteProject *glb.Project
}
func NewGitlabProject(name string, namespace string, gitlab *glb.Client) *GitlabProject {
path := namespace + "/" + name
func NewGitlabProject(projectPath string, client *glb.Client) *GitlabProject {
project := GitlabProject{
gitlab: gitlab,
Path: path,
client: client,
path: projectPath,
}
return &project
@@ -73,7 +72,7 @@ func (project *GitlabProject) myApprovalRequests(username string) []*glb.MergeRe
mrs := []*glb.MergeRequest{}
for _, mr := range project.MergeRequests {
approvers, _, err := project.gitlab.MergeRequests.GetMergeRequestApprovals(project.Path, mr.IID)
approvers, _, err := project.client.MergeRequests.GetMergeRequestApprovals(project.path, mr.IID)
if err != nil {
continue
}
@@ -93,7 +92,7 @@ func (project *GitlabProject) loadMergeRequests() ([]*glb.MergeRequest, error) {
State: &state,
}
mrs, _, err := project.gitlab.MergeRequests.ListProjectMergeRequests(project.Path, &opts)
mrs, _, err := project.client.MergeRequests.ListProjectMergeRequests(project.path, &opts)
if err != nil {
return nil, err
@@ -103,7 +102,7 @@ func (project *GitlabProject) loadMergeRequests() ([]*glb.MergeRequest, error) {
}
func (project *GitlabProject) loadRemoteProject() (*glb.Project, error) {
projectsitory, _, err := project.gitlab.Projects.GetProject(project.Path, nil)
projectsitory, _, err := project.client.Projects.GetProject(project.path, nil)
if err != nil {
return nil, err

View File

@@ -9,25 +9,27 @@ import (
const defaultTitle = "GitLab"
// Settings defines the configuration properties for this module
type Settings struct {
common *cfg.Common
apiKey string `help:"A GitLab personal access token. Requires at least api access."`
domain string `help:"Your GitLab corporate domain."`
projects map[string]interface{} `help:"A list of key/value pairs each describing a GitLab project to fetch data for." values:"Key: The name of the project. Value: The namespace of the project."`
username string `help:"Your GitLab username. Used to figure out which requests require your approval"`
apiKey string `help:"A GitLab personal access token. Requires at least api access."`
domain string `help:"Your GitLab corporate domain."`
projects []string `help:"A list of key/value pairs each describing a GitLab project to fetch data for." values:"Key: The name of the project. Value: The namespace of the project."`
username string `help:"Your GitLab username. Used to figure out which requests require your approval"`
}
// NewSettingsFromYAML creates a new settings instance from a YAML config block
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
settings := Settings{
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig),
apiKey: ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_GITLAB_TOKEN"))),
domain: ymlConfig.UString("domain"),
projects: ymlConfig.UMap("projects"),
username: ymlConfig.UString("username"),
}
settings.projects = cfg.ParseAsMapOrList(ymlConfig, "projects")
return &settings
}

View File

@@ -61,11 +61,11 @@ func (widget *Widget) HelpText() string {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) buildProjectCollection(projectData map[string]interface{}) []*GitlabProject {
func (widget *Widget) buildProjectCollection(projectData []string) []*GitlabProject {
gitlabProjects := []*GitlabProject{}
for name, namespace := range projectData {
project := NewGitlabProject(name, namespace.(string), widget.gitlab)
for _, projectPath := range projectData {
project := NewGitlabProject(projectPath, widget.gitlab)
gitlabProjects = append(gitlabProjects, project)
}

View File

@@ -0,0 +1,74 @@
package newrelic
import (
"fmt"
"github.com/wtfutil/wtf/utils"
"github.com/wtfutil/wtf/wtf"
nr "github.com/yfronto/newrelic"
)
func (widget *Widget) content() (string, string, bool) {
client := widget.currentData()
if client == nil {
return widget.CommonSettings().Title, " NewRelic data unavailable ", false
}
app, appErr := client.Application()
deploys, depErr := client.Deployments()
appName := "error"
if appErr == nil {
appName = app.Name
}
var content string
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings().Title, appName)
wrap := false
if depErr != nil {
wrap = true
content = depErr.Error()
} else {
content = widget.contentFrom(deploys)
}
return title, content, wrap
}
func (widget *Widget) contentFrom(deploys []nr.ApplicationDeployment) string {
str := fmt.Sprintf(
" %s\n",
"[red]Latest Deploys[white]",
)
revisions := []string{}
for _, deploy := range deploys {
if (deploy.Revision != "") && utils.DoesNotInclude(revisions, deploy.Revision) {
lineColor := "white"
if wtf.IsToday(deploy.Timestamp) {
lineColor = "lightblue"
}
revLen := 8
if revLen > len(deploy.Revision) {
revLen = len(deploy.Revision)
}
str += fmt.Sprintf(
" [green]%s[%s] %s %-.16s[white]\n",
deploy.Revision[0:revLen],
lineColor,
deploy.Timestamp.Format("Jan 02 15:04 MST"),
utils.NameFromEmail(deploy.User),
)
revisions = append(revisions, deploy.Revision)
if len(revisions) == widget.settings.deployCount {
break
}
}
}
return str
}

View File

@@ -0,0 +1,9 @@
package newrelic
import "github.com/gdamore/tcell"
func (widget *Widget) initializeKeyboardControls() {
widget.SetKeyboardChar("/", widget.ShowHelp, "Show/hide this help window")
widget.SetKeyboardKey(tcell.KeyLeft, widget.PrevSource, "Select previous application")
widget.SetKeyboardKey(tcell.KeyRight, widget.NextSource, "Select next application")
}

View File

@@ -12,9 +12,9 @@ const defaultTitle = "NewRelic"
type Settings struct {
common *cfg.Common
apiKey string `help:"Your New Relic API token."`
applicationID int `help:"The integer ID of the New Relic application you wish to report on."`
deployCount int `help:"The number of past deploys to display on screen." optional:"true"`
apiKey string `help:"Your New Relic API token."`
deployCount int `help:"The number of past deploys to display on screen." optional:"true"`
applicationIDs []interface{} `help:"The integer ID of the New Relic application you wish to report on."`
}
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
@@ -22,9 +22,9 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co
settings := Settings{
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig),
apiKey: ymlConfig.UString("apiKey", ymlConfig.UString("apikey", os.Getenv("WTF_NEW_RELIC_API_KEY"))),
applicationID: ymlConfig.UInt("applicationID"),
deployCount: ymlConfig.UInt("deployCount", 5),
apiKey: ymlConfig.UString("apiKey", os.Getenv("WTF_NEW_RELIC_API_KEY")),
deployCount: ymlConfig.UInt("deployCount", 5),
applicationIDs: ymlConfig.UList("applicationIDs"),
}
return &settings

View File

@@ -1,29 +1,46 @@
package newrelic
import (
"fmt"
"sort"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/utils"
"github.com/wtfutil/wtf/view"
"github.com/wtfutil/wtf/wtf"
)
type Widget struct {
view.KeyboardWidget
view.MultiSourceWidget
view.TextWidget
client *Client
Clients []*Client
settings *Settings
}
func NewWidget(app *tview.Application, settings *Settings) *Widget {
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
widget := Widget{
TextWidget: view.NewTextWidget(app, settings.common, false),
KeyboardWidget: view.NewKeyboardWidget(app, pages, settings.common),
MultiSourceWidget: view.NewMultiSourceWidget(settings.common, "applicationID", "applicationIDs"),
TextWidget: view.NewTextWidget(app, settings.common, true),
settings: settings,
}
widget.client = NewClient(widget.settings.apiKey, widget.settings.applicationID)
widget.initializeKeyboardControls()
widget.View.SetInputCapture(widget.InputCapture)
for _, id := range utils.ToInts(widget.settings.applicationIDs) {
widget.Clients = append(widget.Clients, NewClient(widget.settings.apiKey, id))
}
sort.Slice(widget.Clients, func(i, j int) bool {
return widget.Clients[i].applicationId < widget.Clients[j].applicationId
})
widget.SetDisplayFunction(widget.Refresh)
widget.KeyboardWidget.SetView(widget.View)
return &widget
}
@@ -36,57 +53,20 @@ func (widget *Widget) Refresh() {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) content() (string, string, bool) {
app, appErr := widget.client.Application()
deploys, depErr := widget.client.Deployments()
appName := "error"
if appErr == nil {
appName = app.Name
}
var content string
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings().Title, appName)
wrap := false
if depErr != nil {
wrap = true
content = depErr.Error()
} else {
content += fmt.Sprintf(
" %s\n",
"[red]Latest Deploys[white]",
)
revisions := []string{}
for _, deploy := range deploys {
if (deploy.Revision != "") && utils.DoesNotInclude(revisions, deploy.Revision) {
lineColor := "white"
if wtf.IsToday(deploy.Timestamp) {
lineColor = "lightblue"
}
revLen := 8
if revLen > len(deploy.Revision) {
revLen = len(deploy.Revision)
}
content += fmt.Sprintf(
" [green]%s[%s] %s %-.16s[white]\n",
deploy.Revision[0:revLen],
lineColor,
deploy.Timestamp.Format("Jan 02 15:04 MST"),
utils.NameFromEmail(deploy.User),
)
revisions = append(revisions, deploy.Revision)
if len(revisions) == widget.settings.deployCount {
break
}
}
}
}
return title, content, wrap
func (widget *Widget) HelpText() string {
return widget.KeyboardWidget.HelpText()
}
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) currentData() *Client {
if len(widget.Clients) == 0 {
return nil
}
if widget.Idx < 0 || widget.Idx >= len(widget.Clients) {
return nil
}
return widget.Clients[widget.Idx]
}