Skip to content

Commit

Permalink
Squashed commit of the following: (#259)
Browse files Browse the repository at this point in the history
*  avoid the file to be checked by the json schema validator

* commit updated mock

* create Init method

* allow run-schedule command on v2 format

* merge ProfileName and Profiles into one Profiles field

*  fix: remove schedule name from arguments

* docs: add information on run-schedule command

* update crond to support new run-schedule command line

* add a default log file on darwin

* Merge from pass-context-struct

* return a new context on each `With` method

* add tests

* move logTarget to context

* refactoring Context struct

* pass context to own commands and profile runnner
  • Loading branch information
creativeprojects committed Feb 21, 2024
1 parent e93874d commit 56f20af
Show file tree
Hide file tree
Showing 45 changed files with 1,527 additions and 866 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"cSpell.words": [
"afero",
"caffeinate",
"creativeprojects",
"crond",
"ionice",
"journalctl",
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ coverage:
threshold: "2%"
patch:
default:
target: "80%"
target: "70%"
threshold: "2%"
142 changes: 112 additions & 30 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func init() {

func getOwnCommands() []ownCommand {
return []ownCommand{
// commands that don't need loading the configuration
{
name: "help",
description: "display help (use resticprofile help [command])",
Expand All @@ -56,6 +57,29 @@ func getOwnCommands() []ownCommand {
needConfiguration: false,
flags: map[string]string{"-v, --verbose": "display detailed version information"},
},
{
name: "random-key",
description: "generate a cryptographically secure random key to use as a restic keyfile",
action: randomKey,
needConfiguration: false,
hide: true, // replaced by the generate command
},
{
name: "generate",
description: "generate resources such as random key, bash/zsh completion scripts, etc.",
longDescription: "The \"generate\" command is used to create various resources and print them to stdout",
action: generateCommand,
needConfiguration: false,
hide: false,
flags: map[string]string{
"--random-key [size]": "generate a cryptographically secure random key to use as a restic keyfile (size defaults to 1024 when omitted)",
"--config-reference [--version 0.15] [template]": "generate a config file reference from a go template (defaults to the built-in markdown template when omitted)",
"--json-schema [--version 0.15] [v1|v2]": "generate a JSON schema that validates resticprofile configuration files in YAML or JSON format",
"--bash-completion": "generate a shell completion script for bash",
"--zsh-completion": "generate a shell completion script for zsh",
},
},
// commands that need the configuration
{
name: "profiles",
description: "display profile names from the configuration file",
Expand All @@ -70,13 +94,6 @@ func getOwnCommands() []ownCommand {
action: showProfile,
needConfiguration: true,
},
{
name: "random-key",
description: "generate a cryptographically secure random key to use as a restic keyfile",
action: randomKey,
needConfiguration: false,
hide: true,
},
{
name: "schedule",
description: "schedule jobs from a profile (or of all profiles)",
Expand Down Expand Up @@ -108,19 +125,15 @@ func getOwnCommands() []ownCommand {
flags: map[string]string{"--all": "display the status of all scheduled jobs of all profiles"},
},
{
name: "generate",
description: "generate resources such as random key, bash/zsh completion scripts, etc.",
longDescription: "The \"generate\" command is used to create various resources and print them to stdout",
action: generateCommand,
needConfiguration: false,
name: "run-schedule",
description: "runs a scheduled job. This command should only be called by the scheduling service",
longDescription: "The \"run-schedule\" command loads the scheduled job configuration from the name in parameter and runs the restic command with the arguments defined in the profile. The name in parameter is <command>@<profile-name> for the configuration file v1, and the schedule name for the configuration file v2+.",
pre: preRunSchedule,
action: runSchedule,
needConfiguration: true,
hide: false,
flags: map[string]string{
"--random-key [size]": "generate a cryptographically secure random key to use as a restic keyfile (size defaults to 1024 when omitted)",
"--config-reference [--version 0.15] [template]": "generate a config file reference from a go template (defaults to the built-in markdown template when omitted)",
"--json-schema [--version 0.15] [v1|v2]": "generate a JSON schema that validates resticprofile configuration files in YAML or JSON format",
"--bash-completion": "generate a shell completion script for bash",
"--zsh-completion": "generate a shell completion script for zsh",
},
hideInCompletion: true,
noProfile: true,
},
// hidden commands
{
Expand Down Expand Up @@ -328,10 +341,9 @@ func showProfile(output io.Writer, ctx commandContext) error {
return nil
}

func showSchedules(output io.Writer, schedulesConfig []*config.ScheduleConfig) {
for _, schedule := range schedulesConfig {
export := schedule.Export()
err := config.ShowStruct(output, export, "schedule "+export.Profiles[0]+"-"+export.Command)
func showSchedules(output io.Writer, schedules []*config.Schedule) {
for _, schedule := range schedules {
err := config.ShowStruct(output, schedule, "schedule "+schedule.CommandName+"@"+schedule.Profiles[0])
if err != nil {
fmt.Fprintln(output, err)
}
Expand Down Expand Up @@ -408,7 +420,7 @@ func createSchedule(_ io.Writer, ctx commandContext) error {
type profileJobs struct {
scheduler schedule.SchedulerConfig
profile string
jobs []*config.ScheduleConfig
jobs []*config.Schedule
}

allJobs := make([]profileJobs, 0, 1)
Expand Down Expand Up @@ -516,7 +528,7 @@ func statusSchedule(w io.Writer, ctx commandContext) error {
return nil
}

func statusScheduleProfile(scheduler schedule.SchedulerConfig, profile *config.Profile, schedules []*config.ScheduleConfig, flags commandLineFlags) error {
func statusScheduleProfile(scheduler schedule.SchedulerConfig, profile *config.Profile, schedules []*config.Schedule, flags commandLineFlags) error {
displayProfileDeprecationNotices(profile)

err := statusJobs(schedule.NewHandler(scheduler), flags.name, schedules)
Expand All @@ -526,7 +538,7 @@ func statusScheduleProfile(scheduler schedule.SchedulerConfig, profile *config.P
return nil
}

func getScheduleJobs(c *config.Config, flags commandLineFlags) (schedule.SchedulerConfig, *config.Profile, []*config.ScheduleConfig, error) {
func getScheduleJobs(c *config.Config, flags commandLineFlags) (schedule.SchedulerConfig, *config.Profile, []*config.Schedule, error) {
global, err := c.GetGlobalSection()
if err != nil {
return nil, nil, nil, fmt.Errorf("cannot load global section: %w", err)
Expand All @@ -543,14 +555,14 @@ func getScheduleJobs(c *config.Config, flags commandLineFlags) (schedule.Schedul
return schedule.NewSchedulerConfig(global), profile, profile.Schedules(), nil
}

func requireScheduleJobs(schedules []*config.ScheduleConfig, flags commandLineFlags) error {
func requireScheduleJobs(schedules []*config.Schedule, flags commandLineFlags) error {
if len(schedules) == 0 {
return fmt.Errorf("no schedule found for profile '%s'", flags.name)
}
return nil
}

func getRemovableScheduleJobs(c *config.Config, flags commandLineFlags) (schedule.SchedulerConfig, *config.Profile, []*config.ScheduleConfig, error) {
func getRemovableScheduleJobs(c *config.Config, flags commandLineFlags) (schedule.SchedulerConfig, *config.Profile, []*config.Schedule, error) {
scheduler, profile, schedules, err := getScheduleJobs(c, flags)
if err != nil {
return nil, nil, nil, err
Expand All @@ -560,18 +572,88 @@ func getRemovableScheduleJobs(c *config.Config, flags commandLineFlags) (schedul
for _, command := range profile.SchedulableCommands() {
declared := false
for _, s := range schedules {
if declared = s.SubTitle == command; declared {
if declared = s.CommandName == command; declared {
break
}
}
if !declared {
schedules = append(schedules, config.NewRemoveOnlyConfig(profile.Name, command))
schedules = append(schedules, config.NewEmptySchedule(profile.Name, command))
}
}

return scheduler, profile, schedules, nil
}

func preRunSchedule(ctx *Context) error {
if len(ctx.request.arguments) < 1 {
return errors.New("run-schedule command expects one argument: schedule name")
}
scheduleName := ctx.request.arguments[0]
// temporarily allow v2 configuration to run v1 schedules
// if ctx.config.GetVersion() < config.Version02
{
// schedule name is in the form "command@profile"
commandName, profileName, ok := strings.Cut(scheduleName, "@")
if !ok {
return errors.New("the expected format of the schedule name is <command>@<profile-name>")
}
ctx.request.profile = profileName
ctx.request.schedule = scheduleName
ctx.command = commandName
// remove the parameter from the arguments
ctx.request.arguments = ctx.request.arguments[1:]

// don't save the profile in the context now, it's only loaded but not prepared
profile, err := ctx.config.GetProfile(profileName)
if err != nil || profile == nil {
return fmt.Errorf("cannot load profile '%s': %w", profileName, err)
}
// get the list of all scheduled commands to find the current command
schedules := profile.Schedules()
for _, schedule := range schedules {
if schedule.CommandName == ctx.command {
ctx.schedule = schedule
prepareScheduledProfile(ctx)
break
}
}
}
return nil
}

func prepareScheduledProfile(ctx *Context) {
clog.Debugf("preparing scheduled profile %q", ctx.request.schedule)
// log file
if len(ctx.schedule.Log) > 0 {
ctx.logTarget = ctx.schedule.Log
}
// battery
if ctx.schedule.IgnoreOnBatteryLessThan > 0 {
ctx.stopOnBattery = ctx.schedule.IgnoreOnBatteryLessThan
} else if ctx.schedule.IgnoreOnBattery {
ctx.stopOnBattery = 100
}
// lock
if ctx.schedule.GetLockWait() > 0 {
ctx.lockWait = ctx.schedule.LockWait
}
if ctx.schedule.GetLockMode() == config.ScheduleLockModeDefault {
if ctx.schedule.GetLockWait() > 0 {
ctx.lockWait = ctx.schedule.GetLockWait()
}
} else if ctx.schedule.GetLockMode() == config.ScheduleLockModeIgnore {
ctx.noLock = true
}
}

func runSchedule(_ io.Writer, cmdCtx commandContext) error {
err := startProfileOrGroup(&cmdCtx.Context)
if err != nil {
return err
}
return nil
}

func testElevationCommand(_ io.Writer, ctx commandContext) error {
if ctx.flags.isChild {
client := remote.NewClient(ctx.flags.parentPort)
Expand Down
4 changes: 2 additions & 2 deletions commands_display.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ func displayOwnCommandHelp(output io.Writer, commandName string, ctx commandCont
commandFlags = "[command specific flags]"
}
out("Usage:\n")
out("\t%s %s\n\n", getCommonUsageHelpLine(command.name, command.needConfiguration), commandFlags)
out("\t%s %s\n\n", getCommonUsageHelpLine(command.name, command.needConfiguration && !command.noProfile), commandFlags)

var flags []string
var flags = make([]string, 0, len(command.flags))
for f, _ := range command.flags {
flags = append(flags, f)
}
Expand Down
Loading

0 comments on commit 56f20af

Please sign in to comment.