Merge pull request #4301 from dokku/4299-vector-fixes

Ensure vector container starts correctly
This commit is contained in:
Jose Diaz-Gonzalez
2021-01-07 03:07:40 -05:00
committed by GitHub
23 changed files with 372 additions and 64 deletions

View File

@@ -4,8 +4,8 @@
logs <app> [-h] [-t] [-n num] [-q] [-p process] # Display recent log output
logs:failed [--all|<app>] # Shows the last failed deploy logs
logs:report [<app>] [<flag>] # Displays a logs report for one or more apps
logs:set [--global|<app>] <key> <value> # Set or clear a logs property for an app
logs:vector-logs # Tail the logs of the vector container
logs:set [--global|<app>] <key> <value> # Set or clear a logs property for an app
logs:vector-logs [--num num] [--tail] # Display vector log output
logs:vector-start # Start the vector logging container
logs:vector-stop # Stop the vector logging container
```
@@ -101,7 +101,7 @@ The `vector` container will be stopped and removed from the system. If the conta
#### Configuring a log sink
Vector uses the concept of log "sinks" to send logs to a given endpoint. Log sinks may be configured globally or on a per-app basis by specifying a `sink` in DSN form with the `logs:set` command. Specifying a sink value will reload any running vector container.
Vector uses the concept of log "sinks" to send logs to a given endpoint. Log sinks may be configured globally or on a per-app basis by specifying a `vector-sink` in DSN form with the `logs:set` command. Specifying a sink value will reload any running vector container.
```shell
# setting the sink value in quotes is encouraged to avoid
@@ -112,7 +112,7 @@ dokku logs:set node-js-app sink "console://?encoding[codec]=json"
A sink may be removed by setting an empty value, which will also reload the running vector container.
```shell
dokku logs:set node-js-app sink
dokku logs:set node-js-app vector-sink
```
Only one sink may be specified on a per-app basis at a given time.
@@ -120,13 +120,13 @@ Only one sink may be specified on a per-app basis at a given time.
Log sinks can also be specified globally by specifying the `--global` flag to `logs:set` with no app name specified:
```shell
dokku logs:set --global sink "console://?encoding[codec]=json"
dokku logs:set --global vector-sink "console://?encoding[codec]=json"
```
As with app-specific sink settings, the global value may also be cleared by setting no value.
```shell
dokku logs:set --global sink
dokku logs:set --global vector-sink
```
##### Log Sink DSN Format

View File

@@ -18,10 +18,15 @@ func ReportSingleApp(appName, infoFlag string) error {
"--app-locked": reportLocked,
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("app", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("app", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func reportDir(appName string) string {

View File

@@ -65,8 +65,8 @@ func main() {
err = apps.CommandRename(oldAppName, newAppName, *skipDeploy)
case "report":
args := flag.NewFlagSet("apps:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("apps", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("apps", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = apps.CommandReport(appName, infoFlag)

View File

@@ -16,10 +16,15 @@ func ReportSingleApp(appName, infoFlag string) error {
"--buildpacks-list": reportList,
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("buildpacks", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("buildpacks", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func reportList(appName string) string {

View File

@@ -44,8 +44,8 @@ func main() {
err = buildpacks.CommandRemove(appName, buildpack, *index)
case "report":
args := flag.NewFlagSet("buildpacks:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("buildpacks", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("buildpacks", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = buildpacks.CommandReport(appName, infoFlag)

View File

@@ -7,7 +7,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
@@ -324,7 +323,7 @@ func ParseReportArgs(pluginName string, arguments []string) ([]string, string, e
}
// ReportSingleApp is an internal function that displays a report for an app
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, trimPrefix bool, uppercaseFirstCharacter bool) error {
func ReportSingleApp(reportType string, appName string, infoFlag string, infoFlags map[string]string, infoFlagKeys []string, trimPrefix bool, uppercaseFirstCharacter bool) error {
flags := []string{}
for key := range infoFlags {
flags = append(flags, key)
@@ -334,7 +333,11 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
if len(infoFlag) == 0 {
LogInfo2Quiet(fmt.Sprintf("%s %v information", appName, reportType))
for _, k := range flags {
v := infoFlags[k]
v, ok := infoFlags[k]
if !ok {
continue
}
prefix := "--"
if trimPrefix {
prefix = fmt.Sprintf("--%v-", reportType)
@@ -353,19 +356,17 @@ func ReportSingleApp(reportType string, appName string, infoFlag string, infoFla
for _, k := range flags {
if infoFlag == k {
v := infoFlags[k]
v, ok := infoFlags[k]
if !ok {
continue
}
fmt.Println(v)
return nil
}
}
keys := reflect.ValueOf(infoFlags).MapKeys()
strkeys := make([]string, len(keys))
for i := 0; i < len(keys); i++ {
strkeys[i] = keys[i].String()
}
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))
sort.Strings(infoFlagKeys)
return fmt.Errorf("Invalid flag passed, valid flags: %s", strings.Join(infoFlagKeys, ", "))
}
// RightPad right-pads the string with pad up to len runes

View File

@@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"strings"
"time"
"github.com/codeskyblue/go-sh"
)
@@ -45,6 +46,18 @@ func ContainerExists(containerID string) bool {
return true
}
// ContainerWaitTilReady will wait timeout seconds and then check if a container is running
// returning an error if it is not running at the end of the timeout
func ContainerWaitTilReady(containerID string, timeout time.Duration) error {
time.Sleep(timeout)
if !ContainerIsRunning(containerID) {
return fmt.Errorf("Container %s is not running", containerID)
}
return nil
}
// CopyFromImage copies a file from named image to destination
func CopyFromImage(appName string, image string, source string, destination string) error {
if !VerifyImage(image) {

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
@@ -95,8 +96,15 @@ func LogVerboseQuietContainerLogs(containerID string) {
}
// LogVerboseQuietContainerLogsTail is the verbose log formatter for container logs with tail mode enabled
func LogVerboseQuietContainerLogsTail(containerID string) {
sc := NewShellCmdWithArgs(DockerBin(), "container", "logs", containerID, "--follow", "--tail", "10")
func LogVerboseQuietContainerLogsTail(containerID string, lines int, tail bool) {
args := []string{"container", "logs", containerID}
if lines > 0 {
args = append(args, "--tail", strconv.Itoa(lines))
}
if tail {
args = append(args, "--follow")
}
sc := NewShellCmdWithArgs(DockerBin(), args...)
stdout, _ := sc.Command.StdoutPipe()
sc.Command.Start()
@@ -104,7 +112,7 @@ func LogVerboseQuietContainerLogsTail(containerID string) {
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
m := scanner.Text()
fmt.Println(m)
LogVerboseQuiet(m)
}
sc.Command.Wait()
}

View File

@@ -74,7 +74,8 @@ func startVectorContainer(vectorImage string) error {
"--volume", "/var/run/docker.sock:/var/run/docker.sock",
"--volume", common.MustGetEnv("DOKKU_LOGS_HOST_DIR") + ":/var/logs/dokku/apps",
vectorImage,
"--config", "/etc/vector/vector.json", "--watch-config"}, " "))
"--config", "/etc/vector/vector.json", "--watch-config", "1"}, " "))
cmd.ShowOutput = false
if !cmd.Execute() {
return errors.New("Unable to start vector container")
@@ -151,11 +152,7 @@ func valueToConfig(appName string, value string) (vectorSink, error) {
}
func writeVectorConfig() error {
apps, err := common.DokkuApps()
if err != nil {
return err
}
apps, _ := common.DokkuApps()
data := vectorConfig{
Sources: map[string]vectorSource{},
Sinks: map[string]vectorSink{},

View File

@@ -15,10 +15,15 @@ func ReportSingleApp(appName, infoFlag string) error {
"--logs-global-vector-sink": reportGlobalVectorSink,
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("logs", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("logs", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func reportVectorSink(appName string) string {

View File

@@ -25,8 +25,8 @@ Additional commands:`
logs [-h] [-t|--tail] [-n|--num num] [-q|--quiet] [-p|--ps process] <app>, Display recent log output
logs:failed [--all|<app>], Shows the last failed deploy logs
logs:report [<app>] [<flag>], Displays a logs report for one or more apps
logs:set <app> <key> <value>, Set or clear a logs property for an app
logs:vector-logs, Tail the logs of the vector container
logs:set [--global|<app>] <key> <value>, Set or clear a logs property for an app
logs:vector-logs [--num num] [--tail], Display vector log output
logs:vector-start, Start the vector logging container
logs:vector-stop, Stop the vector logging container
`

View File

@@ -26,8 +26,8 @@ func main() {
err = logs.CommandFailed(appName, *allApps)
case "report":
args := flag.NewFlagSet("logs:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("logs", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("logs", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = logs.CommandReport(appName, infoFlag)
@@ -47,8 +47,10 @@ func main() {
err = logs.CommandSet(appName, property, value)
case "vector-logs":
args := flag.NewFlagSet("logs:vector-logs", flag.ExitOnError)
num := args.Int("num", 100, "the number of lines to display")
tail := args.Bool("tail", false, "continually stream logs")
args.Parse(os.Args[2:])
err = logs.CommandVectorLogs()
err = logs.CommandVectorLogs(*num, *tail)
case "vector-start":
args := flag.NewFlagSet("logs:vector-start", flag.ExitOnError)
vectorImage := args.String("vector-image", logs.VectorImage, "--vector-image: the name of the docker image to run for vector")

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"path/filepath"
"strconv"
"time"
"github.com/dokku/dokku/plugins/common"
)
@@ -84,7 +85,7 @@ func CommandSet(appName string, property string, value string) error {
}
// CommandVectorLogs tails the log output for the vector container
func CommandVectorLogs() error {
func CommandVectorLogs(lines int, tail bool) error {
if !common.ContainerExists(vectorContainerName) {
return errors.New("Vector container does not exist")
}
@@ -93,8 +94,8 @@ func CommandVectorLogs() error {
return errors.New("Vector container is not running")
}
common.LogInfo1Quiet("Tailing vector container logs")
common.LogVerboseQuietContainerLogsTail(vectorContainerName)
common.LogInfo1Quiet("Vector container logs")
common.LogVerboseQuietContainerLogsTail(vectorContainerName, lines, tail)
return nil
}
@@ -108,19 +109,28 @@ func CommandVectorStart(vectorImage string) error {
return err
}
if !common.ContainerExists(vectorContainerName) {
return startVectorContainer(vectorImage)
if common.ContainerExists(vectorContainerName) {
if common.ContainerIsRunning(vectorContainerName) {
common.LogVerbose("Vector container is running")
return nil
}
common.LogVerbose("Starting vector container")
if !common.ContainerStart(vectorContainerName) {
return errors.New("Unable to start vector container")
}
} else {
if err := startVectorContainer(vectorImage); err != nil {
return err
}
}
if common.ContainerIsRunning(vectorContainerName) {
common.LogVerbose("Container already running")
return nil
}
if !common.ContainerStart(vectorContainerName) {
return errors.New("Unable to start vector container")
common.LogVerbose("Waiting for 10 seconds")
if err := common.ContainerWaitTilReady(vectorContainerName, 10*time.Second); err != nil {
return errors.New("Vector container did not start properly, run logs:vector-logs for more details")
}
common.LogVerbose("Vector container is running")
return nil
}

View File

@@ -19,10 +19,15 @@ func ReportSingleApp(appName, infoFlag string) error {
"--network-web-listeners": reportWebListeners,
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("network", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("network", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func reportBindAllInterfaces(appName string) string {

View File

@@ -53,8 +53,8 @@ func main() {
err = network.CommandRebuildall()
case "report":
args := flag.NewFlagSet("network:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("network", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("network", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = network.CommandReport(appName, infoFlag)

View File

@@ -18,10 +18,15 @@ func ReportSingleApp(appName string, infoFlag string) error {
"--proxy-port-map": reportPortMap,
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("proxy", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("proxy", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func reportEnabled(appName string) string {

View File

@@ -64,8 +64,8 @@ func main() {
err = proxy.CommandPortsSet(appName, portMaps)
case "report":
args := flag.NewFlagSet("proxy:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("proxy", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("proxy", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = proxy.CommandReport(appName, infoFlag)

View File

@@ -28,10 +28,15 @@ func ReportSingleApp(appName, infoFlag string) error {
flags[flag] = fn
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("ps", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("ps", appName, infoFlag, infoFlags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
func addStatusFlags(appName string, infoFlag string) map[string]common.ReportFunc {

View File

@@ -32,8 +32,8 @@ func main() {
err = ps.CommandRebuild(appName, *allApps, *parallelCount)
case "report":
args := flag.NewFlagSet("ps:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("ps", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("ps", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = ps.CommandReport(appName, infoFlag)

View File

@@ -28,15 +28,20 @@ func ReportSingleApp(appName, infoFlag string) error {
return nil
}
infoFlags := map[string]string{}
flags := map[string]string{}
for key, value := range resources {
flag := fmt.Sprintf("--resource-%v", key)
infoFlags[flag] = value
flags[flag] = value
}
flagKeys := []string{}
for flagKey := range flags {
flagKeys = append(flagKeys, flagKey)
}
trimPrefix := true
uppercaseFirstCharacter := false
return common.ReportSingleApp("resource", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return common.ReportSingleApp("resource", appName, infoFlag, flags, flagKeys, trimPrefix, uppercaseFirstCharacter)
}
// GetResourceValue fetches a single value for a given app/process/request/key combination

View File

@@ -50,8 +50,8 @@ func main() {
err = resource.CommandLimitClear(appName, *processType)
case "report":
args := flag.NewFlagSet("resource:report", flag.ExitOnError)
osArgs, infoFlag, err := common.ParseReportArgs("resource", os.Args[2:])
if err == nil {
osArgs, infoFlag, flagErr := common.ParseReportArgs("resource", os.Args[2:])
if flagErr == nil {
args.Parse(osArgs)
appName := args.Arg(0)
err = resource.CommandReport(appName, infoFlag)

234
tests/unit/logs.bats Normal file
View File

@@ -0,0 +1,234 @@
#!/usr/bin/env bats
load test_helper
setup() {
global_setup
}
teardown() {
destroy_app
global_teardown
}
@test "(logs) logs:help" {
run /bin/bash -c "dokku logs:help"
echo "output: $output"
echo "status: $status"
assert_output_contains "Manage log integration for an app"
}
@test "(logs) logs:report" {
run /bin/bash -c "dokku logs:report"
echo "output: $output"
echo "status: $status"
assert_output_contains "You haven't deployed any applications yet"
run create_app
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku logs:report 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "$TEST_APP logs information"
}
@test "(logs) logs:report app" {
run create_app
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku logs:report $TEST_APP 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "$TEST_APP logs information"
run /bin/bash -c "dokku logs:report $TEST_APP --invalid-flag 2>&1"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "$TEST_APP logs information" 0
assert_output_contains "Invalid flag passed, valid flags: --logs-global-vector-sink, --logs-vector-sink"
run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "$TEST_APP logs information" 0
assert_output_contains "Invalid flag passed" 0
run /bin/bash -c "dokku logs:report $TEST_APP --logs-global-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "$TEST_APP logs information" 0
assert_output_contains "Invalid flag passed" 0
}
@test "(logs) logs:set [error]" {
run /bin/bash -c "dokku logs:set 2>&1"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "Please specify an app to run the command on"
run /bin/bash -c "dokku logs:set ${TEST_APP}-non-existent" 2>&1
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "App $TEST_APP-non-existent does not exist"
run create_app
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku logs:set $TEST_APP" 2>&1
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "No property specified"
run /bin/bash -c "dokku logs:set $TEST_APP invalid" 2>&1
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "Invalid property specified, valid properties include: vector-sink"
run /bin/bash -c "dokku logs:set $TEST_APP invalid value" 2>&1
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "Invalid property specified, valid properties include: vector-sink"
}
@test "(logs) logs:set app" {
run create_app
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
run /bin/bash -c "dokku logs:set $TEST_APP vector-sink" 2>&1
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Unsetting vector-sink"
assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json"
run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
run /bin/bash -c "dokku logs:set $TEST_APP vector-sink console://?encoding[codec]=json" 2>&1
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Setting vector-sink"
assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json"
run /bin/bash -c "dokku logs:report $TEST_APP --logs-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "console://?encoding[codec]=json"
}
@test "(logs) logs:set global" {
run create_app
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku logs:report $TEST_APP --logs-global-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
run /bin/bash -c "dokku logs:set --global vector-sink" 2>&1
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Unsetting vector-sink"
assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json"
run /bin/bash -c "dokku logs:report $TEST_APP --logs-global-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_not_exists
run /bin/bash -c "dokku logs:set --global vector-sink console://?encoding[codec]=json" 2>&1
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Setting vector-sink"
assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json"
run /bin/bash -c "dokku logs:report $TEST_APP --logs-global-vector-sink 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output "console://?encoding[codec]=json"
run /bin/bash -c "dokku logs:set --global vector-sink" 2>&1
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Unsetting vector-sink"
assert_output_contains "Writing updated vector config to /var/lib/dokku/data/logs/vector.json"
}
@test "(logs) logs:vector" {
run /bin/bash -c "dokku logs:vector-logs 2>&1"
echo "output: $output"
echo "status: $status"
assert_failure
assert_output_contains "Vector container does not exist"
run /bin/bash -c "dokku logs:vector-start 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Vector container is running"
run /bin/bash -c "dokku logs:vector-logs 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Vector container logs"
run /bin/bash -c "dokku logs:vector-logs --num 10 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Vector container logs"
assert_output_contains "vector:" 10
assert_line_count 11
run /bin/bash -c "dokku logs:vector-logs --num 5 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Vector container logs"
assert_output_contains "vector:" 5
assert_line_count 6
run /bin/bash -c "dokku logs:vector-stop 2>&1"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains "Stopping and removing vector container"
}

View File

@@ -163,6 +163,14 @@ assert_line() {
fi
}
# ShellCheck doesn't know about $lines from Bats
# shellcheck disable=SC2154
assert_line_count() {
declare EXPECTED="$1"
local num_lines="${#lines[@]}"
assert_equal "$EXPECTED" "$num_lines"
}
refute_line() {
if [[ "$1" -ge 0 ]] 2>/dev/null; then
local num_lines="${#lines[@]}"