diff --git a/Makefile b/Makefile index 135b7f6d7..3541c18b1 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,10 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst go_source_dirs=cli fs internal repo snapshot all_go_sources=$(foreach d,$(go_source_dirs),$(call rwildcard,$d/,*.go)) $(wildcard *.go) -all: test lint vet integration-tests +all: + $(MAKE) test + $(MAKE) lint + $(MAKE) integration-tests include tools/tools.mk diff --git a/cli/command_policy_set_logging_test.go b/cli/command_policy_set_logging_test.go index b313c29ea..da744241c 100644 --- a/cli/command_policy_set_logging_test.go +++ b/cli/command_policy_set_logging_test.go @@ -1,6 +1,7 @@ package cli_test import ( + "strings" "testing" "github.com/stretchr/testify/require" @@ -16,23 +17,25 @@ func TestSetLoggingPolicy(t *testing.T) { e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir) lines := e.RunAndExpectSuccess(t, "policy", "show", "--global") - require.Contains(t, lines, " Directory snapshotted: 5 (defined for this target)") - require.Contains(t, lines, " Directory ignored: 5 (defined for this target)") - require.Contains(t, lines, " Entry snapshotted: 0 (defined for this target)") - require.Contains(t, lines, " Entry ignored: 5 (defined for this target)") - require.Contains(t, lines, " Entry cache hit 0 (defined for this target)") - require.Contains(t, lines, " Entry cache miss 0 (defined for this target)") + lines = compressSpaces(lines) + require.Contains(t, lines, " Directory snapshotted: 5 (defined for this target)") + require.Contains(t, lines, " Directory ignored: 5 (defined for this target)") + require.Contains(t, lines, " Entry snapshotted: 0 (defined for this target)") + require.Contains(t, lines, " Entry ignored: 5 (defined for this target)") + require.Contains(t, lines, " Entry cache hit: 0 (defined for this target)") + require.Contains(t, lines, " Entry cache miss: 0 (defined for this target)") // make some directory we'll be setting policy on td := testutil.TempDirectory(t) lines = e.RunAndExpectSuccess(t, "policy", "show", td) - require.Contains(t, lines, " Directory snapshotted: 5 inherited from (global)") - require.Contains(t, lines, " Directory ignored: 5 inherited from (global)") - require.Contains(t, lines, " Entry snapshotted: 0 inherited from (global)") - require.Contains(t, lines, " Entry ignored: 5 inherited from (global)") - require.Contains(t, lines, " Entry cache hit 0 inherited from (global)") - require.Contains(t, lines, " Entry cache miss 0 inherited from (global)") + lines = compressSpaces(lines) + require.Contains(t, lines, " Directory snapshotted: 5 inherited from (global)") + require.Contains(t, lines, " Directory ignored: 5 inherited from (global)") + require.Contains(t, lines, " Entry snapshotted: 0 inherited from (global)") + require.Contains(t, lines, " Entry ignored: 5 inherited from (global)") + require.Contains(t, lines, " Entry cache hit: 0 inherited from (global)") + require.Contains(t, lines, " Entry cache miss: 0 inherited from (global)") e.RunAndExpectSuccess(t, "policy", "set", td, "--log-dir-snapshotted=1", @@ -43,17 +46,19 @@ func TestSetLoggingPolicy(t *testing.T) { "--log-entry-cache-miss=6") lines = e.RunAndExpectSuccess(t, "policy", "show", td) - require.Contains(t, lines, " Directory snapshotted: 1 (defined for this target)") - require.Contains(t, lines, " Directory ignored: 2 (defined for this target)") - require.Contains(t, lines, " Entry snapshotted: 3 (defined for this target)") - require.Contains(t, lines, " Entry ignored: 4 (defined for this target)") - require.Contains(t, lines, " Entry cache hit 5 (defined for this target)") - require.Contains(t, lines, " Entry cache miss 6 (defined for this target)") + lines = compressSpaces(lines) + require.Contains(t, lines, " Directory snapshotted: 1 (defined for this target)") + require.Contains(t, lines, " Directory ignored: 2 (defined for this target)") + require.Contains(t, lines, " Entry snapshotted: 3 (defined for this target)") + require.Contains(t, lines, " Entry ignored: 4 (defined for this target)") + require.Contains(t, lines, " Entry cache hit: 5 (defined for this target)") + require.Contains(t, lines, " Entry cache miss: 6 (defined for this target)") e.RunAndExpectSuccess(t, "policy", "set", td, "--log-entry-ignored=inherit") lines = e.RunAndExpectSuccess(t, "policy", "show", td) - require.Contains(t, lines, " Entry ignored: 5 inherited from (global)") + lines = compressSpaces(lines) + require.Contains(t, lines, " Entry ignored: 5 inherited from (global)") e.RunAndExpectFailure(t, "policy", "set", td, "--log-dir-snapshotted=-1") e.RunAndExpectFailure(t, "policy", "set", td, "--log-dir-ignored=11") @@ -62,3 +67,17 @@ func TestSetLoggingPolicy(t *testing.T) { e.RunAndExpectFailure(t, "policy", "set", td, "--log-entry-cache-hit=-1") e.RunAndExpectFailure(t, "policy", "set", td, "--log-entry-cache-miss=-1") } + +func compressSpaces(lines []string) []string { + var result []string + + for _, l := range lines { + for l2 := strings.ReplaceAll(l, " ", " "); l != l2; l2 = strings.ReplaceAll(l, " ", " ") { + l = l2 + } + + result = append(result, l) + } + + return result +} diff --git a/cli/command_policy_show.go b/cli/command_policy_show.go index 73122a3c5..8de5b13f3 100644 --- a/cli/command_policy_show.go +++ b/cli/command_policy_show.go @@ -4,6 +4,7 @@ "context" "fmt" "strings" + "time" "github.com/pkg/errors" @@ -51,6 +52,59 @@ func (c *commandPolicyShow) run(ctx context.Context, rep repo.Repository) error return nil } +type policyTableRow struct { + name string + value string + def string +} + +func alignedPolicyTableRows(v []policyTableRow) string { + var nameValueLen int + + const ( + nameValueSpace = " " + defSpace = " " + ) + + for _, it := range v { + if it.value == "" { + continue + } + + t := it.name + if it.value != "" { + t += nameValueSpace + it.value + } + + if len(t) > nameValueLen { + nameValueLen = len(t) + } + } + + var lines []string + + for _, it := range v { + l := it.name + + if it.value != "" || it.def != "" { + if spaces := nameValueLen - len(l) - len(it.value); spaces > 0 { + l += strings.Repeat(" ", spaces) + } + + l += it.value + } + + if it.def != "" { + l += defSpace + l += it.def + } + + lines = append(lines, l) + } + + return strings.Join(lines, "\n") +} + func definitionPointToString(target, src snapshot.SourceInfo) string { if src == target { return "(defined for this target)" @@ -60,253 +114,309 @@ func definitionPointToString(target, src snapshot.SourceInfo) string { } func printPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Policy for %v:\n\n", p.Target()) + var rows []policyTableRow - printRetentionPolicy(out, p, def) - out.printStdout("\n") - printFilesPolicy(out, p, def) - out.printStdout("\n") - printErrorHandlingPolicy(out, p, def) - out.printStdout("\n") - printSchedulingPolicy(out, p, def) - out.printStdout("\n") - printCompressionPolicy(out, p, def) - out.printStdout("\n") - printActions(out, p, def) - out.printStdout("\n") - printLoggingPolicy(out, p, def) + rows = appendRetentionPolicyRows(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendFilesPolicyValue(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendErrorHandlingPolicyRows(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendSchedulingPolicyRows(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendCompressionPolicyRows(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendActionsPolicyRows(rows, p, def) + rows = append(rows, policyTableRow{}) + rows = appendLoggingPolicyRows(rows, p, def) + + out.printStdout("Policy for %v:\n\n%v\n", p.Target(), alignedPolicyTableRows(rows)) } -func printRetentionPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Retention:\n") - out.printStdout(" Annual snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepAnnual), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepAnnual)) - out.printStdout(" Monthly snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepMonthly), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepMonthly)) - out.printStdout(" Weekly snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepWeekly), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepWeekly)) - out.printStdout(" Daily snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepDaily), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepDaily)) - out.printStdout(" Hourly snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepHourly), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepHourly)) - out.printStdout(" Latest snapshots: %3v %v\n", - valueOrNotSet(p.RetentionPolicy.KeepLatest), - definitionPointToString(p.Target(), def.RetentionPolicy.KeepLatest)) +func appendRetentionPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + return append(rows, + policyTableRow{"Retention:", "", ""}, + policyTableRow{" Annual snapshots:", valueOrNotSet(p.RetentionPolicy.KeepAnnual), definitionPointToString(p.Target(), def.RetentionPolicy.KeepAnnual)}, + policyTableRow{" Monthly snapshots:", valueOrNotSet(p.RetentionPolicy.KeepMonthly), definitionPointToString(p.Target(), def.RetentionPolicy.KeepMonthly)}, + policyTableRow{" Weekly snapshots:", valueOrNotSet(p.RetentionPolicy.KeepWeekly), definitionPointToString(p.Target(), def.RetentionPolicy.KeepWeekly)}, + policyTableRow{" Daily snapshots:", valueOrNotSet(p.RetentionPolicy.KeepDaily), definitionPointToString(p.Target(), def.RetentionPolicy.KeepDaily)}, + policyTableRow{" Hourly snapshots:", valueOrNotSet(p.RetentionPolicy.KeepHourly), definitionPointToString(p.Target(), def.RetentionPolicy.KeepHourly)}, + policyTableRow{" Latest snapshots:", valueOrNotSet(p.RetentionPolicy.KeepLatest), definitionPointToString(p.Target(), def.RetentionPolicy.KeepLatest)}, + ) } -func printFilesPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Files policy:\n") - - out.printStdout(" Ignore cache directories: %5v %v\n", - p.FilesPolicy.IgnoreCacheDirectories.OrDefault(true), - definitionPointToString(p.Target(), def.FilesPolicy.IgnoreCacheDirectories)) - - if len(p.FilesPolicy.IgnoreRules) > 0 { - out.printStdout(" Ignore rules: %v\n", - definitionPointToString(p.Target(), def.FilesPolicy.IgnoreRules)) - } else { - out.printStdout(" No ignore rules.\n") +func boolToString(v bool) string { + if v { + return "true" } - for _, rule := range p.FilesPolicy.IgnoreRules { - out.printStdout(" %-30v\n", rule) + return "false" +} + +func logDetailToString(v policy.LogDetail) string { + return fmt.Sprintf("%v", v) +} + +func appendFilesPolicyValue(items []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + items = append(items, + policyTableRow{"Files policy:", "", ""}, + policyTableRow{ + " Ignore cache directories:", + boolToString(p.FilesPolicy.IgnoreCacheDirectories.OrDefault(true)), + definitionPointToString(p.Target(), def.FilesPolicy.IgnoreCacheDirectories), + }) + + if len(p.FilesPolicy.IgnoreRules) > 0 { + items = append(items, policyTableRow{ + " Ignore rules:", "", definitionPointToString(p.Target(), def.FilesPolicy.IgnoreRules), + }) + for _, rule := range p.FilesPolicy.IgnoreRules { + items = append(items, policyTableRow{" " + rule, "", ""}) + } + } else { + items = append(items, policyTableRow{" No ignore rules:", "", ""}) } if len(p.FilesPolicy.DotIgnoreFiles) > 0 { - out.printStdout(" Read ignore rules from files: %v\n", - definitionPointToString(p.Target(), def.FilesPolicy.DotIgnoreFiles)) - } + items = append(items, policyTableRow{ + " Read ignore rules from files:", "", + definitionPointToString(p.Target(), def.FilesPolicy.DotIgnoreFiles), + }) - for _, dotFile := range p.FilesPolicy.DotIgnoreFiles { - out.printStdout(" %-30v\n", dotFile) + for _, dotFile := range p.FilesPolicy.DotIgnoreFiles { + items = append(items, policyTableRow{" " + dotFile, "", ""}) + } } if maxSize := p.FilesPolicy.MaxFileSize; maxSize > 0 { - out.printStdout(" Ignore files above: %10v %v\n", + items = append(items, policyTableRow{ + " Ignore files above:", units.BytesStringBase2(maxSize), - definitionPointToString(p.Target(), def.FilesPolicy.MaxFileSize)) + definitionPointToString(p.Target(), def.FilesPolicy.MaxFileSize), + }) } - out.printStdout(" Scan one filesystem only: %5v %v\n", - p.FilesPolicy.OneFileSystem.OrDefault(false), - definitionPointToString(p.Target(), def.FilesPolicy.OneFileSystem)) + items = append(items, policyTableRow{ + " Scan one filesystem only:", + boolToString(p.FilesPolicy.OneFileSystem.OrDefault(false)), + definitionPointToString(p.Target(), def.FilesPolicy.OneFileSystem), + }) + + return items } -func printErrorHandlingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Error handling policy:\n") - - out.printStdout(" Ignore file read errors: %5v %v\n", - p.ErrorHandlingPolicy.IgnoreFileErrors.OrDefault(false), - definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreFileErrors)) - - out.printStdout(" Ignore directory read errors: %5v %v\n", - p.ErrorHandlingPolicy.IgnoreDirectoryErrors.OrDefault(false), - definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreDirectoryErrors)) - - out.printStdout(" Ignore unknown types: %5v %v\n", - p.ErrorHandlingPolicy.IgnoreUnknownTypes.OrDefault(true), - definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreUnknownTypes)) +func appendErrorHandlingPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + return append(rows, + policyTableRow{"Error handling policy:", "", ""}, + policyTableRow{ + " Ignore file read errors:", + boolToString(p.ErrorHandlingPolicy.IgnoreFileErrors.OrDefault(false)), + definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreFileErrors), + }, + policyTableRow{ + " Ignore directory read errors:", + boolToString(p.ErrorHandlingPolicy.IgnoreDirectoryErrors.OrDefault(false)), + definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreDirectoryErrors), + }, + policyTableRow{ + " Ignore unknown types:", + boolToString(p.ErrorHandlingPolicy.IgnoreUnknownTypes.OrDefault(true)), + definitionPointToString(p.Target(), def.ErrorHandlingPolicy.IgnoreUnknownTypes), + }, + ) } -func printLoggingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Logging details (%v-none, %v-maximum):\n", policy.LogDetailNone, policy.LogDetailMax) - - out.printStdout(" Directory snapshotted: %5v %v\n", - p.LoggingPolicy.Directories.Snapshotted.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Snapshotted)) - - out.printStdout(" Directory ignored: %5v %v\n", - p.LoggingPolicy.Directories.Ignored.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Ignored)) - - out.printStdout(" Entry snapshotted: %5v %v\n", - p.LoggingPolicy.Entries.Snapshotted.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Snapshotted)) - - out.printStdout(" Entry ignored: %5v %v\n", - p.LoggingPolicy.Entries.Ignored.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Ignored)) - - out.printStdout(" Entry cache hit %5v %v\n", - p.LoggingPolicy.Entries.CacheHit.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheHit)) - - out.printStdout(" Entry cache miss %5v %v\n", - p.LoggingPolicy.Entries.CacheMiss.OrDefault(policy.LogDetailNone), - definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheMiss)) +func appendLoggingPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + return append(rows, + policyTableRow{ + fmt.Sprintf("Logging details (%v-none, %v-maximum):", policy.LogDetailNone, policy.LogDetailMax), "", "", + }, + policyTableRow{ + " Directory snapshotted:", + logDetailToString(p.LoggingPolicy.Directories.Snapshotted.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Snapshotted), + }, + policyTableRow{ + " Directory ignored:", + logDetailToString(p.LoggingPolicy.Directories.Ignored.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Directories.Ignored), + }, + policyTableRow{ + " Entry snapshotted:", + logDetailToString(p.LoggingPolicy.Entries.Snapshotted.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Snapshotted), + }, + policyTableRow{ + " Entry ignored:", + logDetailToString(p.LoggingPolicy.Entries.Ignored.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Entries.Ignored), + }, + policyTableRow{ + " Entry cache hit:", + logDetailToString(p.LoggingPolicy.Entries.CacheHit.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheHit), + }, + policyTableRow{ + " Entry cache miss:", + logDetailToString(p.LoggingPolicy.Entries.CacheMiss.OrDefault(policy.LogDetailNone)), + definitionPointToString(p.Target(), def.LoggingPolicy.Entries.CacheMiss), + }, + ) } -func printSchedulingPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - out.printStdout("Scheduling policy:\n") +func appendSchedulingPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + rows = append(rows, policyTableRow{"Scheduling policy:", "", ""}) any := false - out.printStdout(" Scheduled snapshots:\n") + rows = append(rows, policyTableRow{" Scheduled snapshots:", "", ""}) if p.SchedulingPolicy.Interval() != 0 { - out.printStdout(" Snapshot interval: %10v %v\n", p.SchedulingPolicy.Interval(), - definitionPointToString(p.Target(), def.SchedulingPolicy.IntervalSeconds)) + rows = append(rows, policyTableRow{ + " Snapshot interval:", + p.SchedulingPolicy.Interval().String(), + definitionPointToString(p.Target(), def.SchedulingPolicy.IntervalSeconds), + }) any = true } if len(p.SchedulingPolicy.TimesOfDay) > 0 { - out.printStdout(" Snapshot times: %v\n", definitionPointToString(p.Target(), def.SchedulingPolicy.TimesOfDay)) + rows = append(rows, policyTableRow{" Snapshot times:", "", definitionPointToString(p.Target(), def.SchedulingPolicy.TimesOfDay)}) for _, tod := range p.SchedulingPolicy.TimesOfDay { - out.printStdout(" %9v\n", tod) + rows = append(rows, policyTableRow{" " + tod.String(), "", ""}) } any = true } if !any { - out.printStdout(" None\n") + rows = append(rows, policyTableRow{" None.", "", ""}) } - out.printStdout(" Manual snapshot: %5v %v\n", - p.SchedulingPolicy.Manual, - definitionPointToString(p.Target(), def.SchedulingPolicy.Manual)) + rows = append(rows, policyTableRow{" Manual snapshot:", boolToString(p.SchedulingPolicy.Manual), definitionPointToString(p.Target(), def.SchedulingPolicy.Manual)}) + + return rows } -func printCompressionPolicy(out *textOutput, p *policy.Policy, def *policy.Definition) { - if p.CompressionPolicy.CompressorName != "" && p.CompressionPolicy.CompressorName != "none" { - out.printStdout("Compression:\n") - out.printStdout(" Compressor: %q %v\n", p.CompressionPolicy.CompressorName, - definitionPointToString(p.Target(), def.CompressionPolicy.CompressorName)) - } else { - out.printStdout("Compression disabled.\n") - return +func appendCompressionPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { + if p.CompressionPolicy.CompressorName == "" || p.CompressionPolicy.CompressorName == "none" { + rows = append(rows, policyTableRow{"Compression disabled.", "", ""}) + return rows } + rows = append(rows, + policyTableRow{"Compression:", "", ""}, + policyTableRow{" Compressor:", string(p.CompressionPolicy.CompressorName), definitionPointToString(p.Target(), def.CompressionPolicy.CompressorName)}) + switch { case len(p.CompressionPolicy.OnlyCompress) > 0: - out.printStdout(" Only compress files with the following extensions: %v\n", - definitionPointToString(p.Target(), def.CompressionPolicy.OnlyCompress)) + rows = append(rows, policyTableRow{ + " Only compress files with the following extensions:", "", + definitionPointToString(p.Target(), def.CompressionPolicy.OnlyCompress), + }) for _, rule := range p.CompressionPolicy.OnlyCompress { - out.printStdout(" %-30v\n", rule) + rows = append(rows, policyTableRow{" - " + rule, "", ""}) } case len(p.CompressionPolicy.NeverCompress) > 0: - out.printStdout(" Compress all files except the following extensions: %v\n", - definitionPointToString(p.Target(), def.CompressionPolicy.NeverCompress)) + rows = append(rows, policyTableRow{ + " Compress all files except the following extensions:", "", + definitionPointToString(p.Target(), def.CompressionPolicy.NeverCompress), + }) for _, rule := range p.CompressionPolicy.NeverCompress { - out.printStdout(" %-30v\n", rule) + rows = append(rows, policyTableRow{" " + rule, "", ""}) } default: - out.printStdout(" Compress files regardless of extensions.\n") + rows = append(rows, policyTableRow{" Compress files regardless of extensions.", "", ""}) } switch { case p.CompressionPolicy.MaxSize > 0: - out.printStdout(" Only compress files between %v and %v.\n", units.BytesStringBase10(p.CompressionPolicy.MinSize), units.BytesStringBase10(p.CompressionPolicy.MaxSize)) + rows = append(rows, policyTableRow{fmt.Sprintf( + " Only compress files between %v and %v.", + units.BytesStringBase10(p.CompressionPolicy.MinSize), + units.BytesStringBase10(p.CompressionPolicy.MaxSize)), "", ""}) case p.CompressionPolicy.MinSize > 0: - out.printStdout(" Only compress files bigger than %v.\n", units.BytesStringBase10(p.CompressionPolicy.MinSize)) + rows = append(rows, policyTableRow{fmt.Sprintf( + " Only compress files bigger than %v.", + units.BytesStringBase10(p.CompressionPolicy.MinSize)), "", ""}) default: - out.printStdout(" Compress files of all sizes.\n") + rows = append(rows, policyTableRow{" Compress files of all sizes.", "", ""}) } + + return rows } -func printActions(out *textOutput, p *policy.Policy, def *policy.Definition) { +func appendActionsPolicyRows(rows []policyTableRow, p *policy.Policy, def *policy.Definition) []policyTableRow { var anyActions bool if h := p.Actions.BeforeSnapshotRoot; h != nil { - out.printStdout("Run command before snapshot root: %v\n", - definitionPointToString(p.Target(), def.Actions.BeforeSnapshotRoot)) - - printActionCommand(out, h) + rows = append(rows, + policyTableRow{"Run command before snapshot root:", "", definitionPointToString(p.Target(), def.Actions.BeforeSnapshotRoot)}) + rows = appendActionCommandRows(rows, h) anyActions = true } if h := p.Actions.AfterSnapshotRoot; h != nil { - out.printStdout("Run command after snapshot root: %v\n", - definitionPointToString(p.Target(), def.Actions.AfterSnapshotRoot)) - printActionCommand(out, h) + rows = append(rows, policyTableRow{"Run command after snapshot root:", "", definitionPointToString(p.Target(), def.Actions.AfterSnapshotRoot)}) + rows = appendActionCommandRows(rows, h) anyActions = true } if h := p.Actions.BeforeFolder; h != nil { - out.printStdout("Run command before this folder: (non-inheritable)\n") - - printActionCommand(out, h) + rows = append(rows, policyTableRow{"Run command before this folder:", "", "(non-inheritable)"}) + rows = appendActionCommandRows(rows, h) anyActions = true } if h := p.Actions.AfterFolder; h != nil { - out.printStdout("Run command after this folder: (non-inheritable)\n") - printActionCommand(out, h) + rows = append(rows, policyTableRow{"Run command after this folder:", "", "(non-inheritable)"}) + rows = appendActionCommandRows(rows, h) anyActions = true } if !anyActions { - out.printStdout("No actions defined.\n") + rows = append(rows, policyTableRow{"No actions defined.", "", ""}) } + + return rows } -func printActionCommand(out *textOutput, h *policy.ActionCommand) { +func appendActionCommandRows(rows []policyTableRow, h *policy.ActionCommand) []policyTableRow { if h.Script != "" { - out.printStdout(" Embedded Script: %q\n", h.Script) + rows = append(rows, + policyTableRow{" Embedded script (stored in repository):", "", ""}, + policyTableRow{indentMultilineString(h.Script, " "), "", ""}, + ) } else { - out.printStdout(" Command: %v %v\n", h.Command, strings.Join(h.Arguments, " ")) + rows = append(rows, + policyTableRow{" Command:", "", ""}, + policyTableRow{" " + h.Command + " " + strings.Join(h.Arguments, " "), "", ""}) } - out.printStdout(" Mode: %v\n", h.Mode) - out.printStdout(" Timeout: %v\n", h.TimeoutSeconds) - out.printStdout("\n") + actualMode := h.Mode + if actualMode == "" { + actualMode = "sync" + } + + rows = append(rows, + policyTableRow{" Mode:", actualMode, ""}, + policyTableRow{" Timeout:", (time.Second * time.Duration(h.TimeoutSeconds)).String(), ""}, + policyTableRow{"", "", ""}, + ) + + return rows } func valueOrNotSet(p *policy.OptionalInt) string { diff --git a/cli/command_repository_create.go b/cli/command_repository_create.go index e9cfa6a1b..e06f10989 100644 --- a/cli/command_repository_create.go +++ b/cli/command_repository_create.go @@ -165,8 +165,12 @@ func (c *commandRepositoryCreate) populateRepository(ctx context.Context, passwo return errors.Wrap(err, "unable to set global policy") } - printRetentionPolicy(&c.out, policy.DefaultPolicy, &policy.Definition{}) - printCompressionPolicy(&c.out, policy.DefaultPolicy, &policy.Definition{}) + var rows []policyTableRow + + rows = appendRetentionPolicyRows(rows, policy.DefaultPolicy, &policy.Definition{}) + rows = appendCompressionPolicyRows(rows, policy.DefaultPolicy, &policy.Definition{}) + + c.out.printStdout("%v\n", alignedPolicyTableRows(rows)) c.out.printStderr("\nTo find more information about default policy run 'kopia policy get'.\nTo change the policy use 'kopia policy set' command.\n") diff --git a/cli/show_utils.go b/cli/show_utils.go index 30a9d2665..5dc7d1c48 100644 --- a/cli/show_utils.go +++ b/cli/show_utils.go @@ -1,11 +1,13 @@ package cli import ( + "bufio" "bytes" "compress/gzip" "encoding/json" "fmt" "io" + "strings" "time" "github.com/pkg/errors" @@ -103,3 +105,14 @@ func formatCompressionPercentage(original, compressed int64) string { return fmt.Sprintf("%.1f%%", oneHundredPercent*(1-float64(compressed)/float64(original))) } + +func indentMultilineString(l, prefix string) string { + var lines []string + + s := bufio.NewScanner(strings.NewReader(l)) + for s.Scan() { + lines = append(lines, prefix+s.Text()) + } + + return strings.Join(lines, "\n") +}