Remove crontab integration
This commit is contained in:
parent
76353c4029
commit
1be261f54d
@ -57,10 +57,6 @@ A minimally functional source requires a `fetch` action that returns items.
|
|||||||
TTL, TTD, and TTS can be configured at the source level by setting the environment variables `INTAKE_TTL`, `INTAKE_TTS`, or `INTAKE_TTS` to an integer value.
|
TTL, TTD, and TTS can be configured at the source level by setting the environment variables `INTAKE_TTL`, `INTAKE_TTS`, or `INTAKE_TTS` to an integer value.
|
||||||
These values override any `ttl`, `ttd`, or `tts` value returned by a fetch or action.
|
These values override any `ttl`, `ttd`, or `tts` value returned by a fetch or action.
|
||||||
|
|
||||||
Intake provides integration with `cron`.
|
|
||||||
To create a cron job for a source, set the `INTAKE_CRON` environment variable to a five-element crontab spec (e.g. `0 0 * * *`).
|
|
||||||
The `intake crontab` command will synchronize source cron jobs to your crontab.
|
|
||||||
|
|
||||||
Automatic fetching can be configured by setting the `INTAKE_FETCH` environment variable to a fetch schedule.
|
Automatic fetching can be configured by setting the `INTAKE_FETCH` environment variable to a fetch schedule.
|
||||||
A fetch schedule may be:
|
A fetch schedule may be:
|
||||||
- `every <duration>`, where `<duration>` is a Go duration string
|
- `every <duration>`, where `<duration>` is a Go duration string
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/Jaculabilis/intake/core"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var crontabCmd = &cobra.Command{
|
|
||||||
Use: "crontab",
|
|
||||||
Short: "Update crontab entries",
|
|
||||||
Long: `Update crontab entries.
|
|
||||||
|
|
||||||
A source's cron job is defined by its INTAKE_CRON environment variable.
|
|
||||||
`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
crontab(boolArg(cmd, "list"))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(crontabCmd)
|
|
||||||
|
|
||||||
crontabCmd.Flags().BoolP("list", "l", false, "List crontab entries")
|
|
||||||
}
|
|
||||||
|
|
||||||
func crontab(list bool) {
|
|
||||||
db := openAndMigrateDb()
|
|
||||||
|
|
||||||
specs, err := core.GetCronSources(db)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error: failed to get crontab sources: %v", err)
|
|
||||||
}
|
|
||||||
if list {
|
|
||||||
var sources []string
|
|
||||||
for source := range specs {
|
|
||||||
sources = append(sources, source)
|
|
||||||
}
|
|
||||||
sort.Strings(sources)
|
|
||||||
for _, source := range sources {
|
|
||||||
fmt.Println(specs[source])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := core.UpdateCrontab(db, specs); err != nil {
|
|
||||||
log.Fatalf("error: failed to update crontab: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Jaculabilis/intake/core"
|
"github.com/Jaculabilis/intake/core"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -51,16 +50,4 @@ func sourceEnv(source string, env []string) {
|
|||||||
if err := core.SetEnvs(db, source, env); err != nil {
|
if err := core.SetEnvs(db, source, env); err != nil {
|
||||||
log.Fatalf("failed to set envs: %v", err)
|
log.Fatalf("failed to set envs: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, envval := range env {
|
|
||||||
if strings.HasPrefix(envval, "INTAKE_CRON=") {
|
|
||||||
specs, err := core.GetCronSources(db)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to get cron specs: %v", err)
|
|
||||||
}
|
|
||||||
if err = core.UpdateCrontab(db, specs); err != nil {
|
|
||||||
log.Fatalf("failed to update crontab: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
121
core/crontab.go
121
core/crontab.go
@ -1,121 +0,0 @@
|
|||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var IntakeCronBegin = "### begin intake-managed crontab entries"
|
|
||||||
var IntakeCronEnd = "### end intake-managed crontab entries"
|
|
||||||
|
|
||||||
func makeCrontabEntry(source string, spec string) string {
|
|
||||||
// TODO the /etc/profile setup is NixOS-specific, maybe there's another way to do this
|
|
||||||
return fmt.Sprintf("%-20s . /etc/profile; intake source fetch -s %s", spec, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCronSources(db DB) (specs map[string]string, err error) {
|
|
||||||
res, err := db.Query(`
|
|
||||||
select source, value
|
|
||||||
from envs
|
|
||||||
where name = 'INTAKE_CRON'
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get source crontabs: %v", err)
|
|
||||||
}
|
|
||||||
specs = make(map[string]string)
|
|
||||||
for res.Next() {
|
|
||||||
var source string
|
|
||||||
var value string
|
|
||||||
if err = res.Scan(&source, &value); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to scan source crontab: %v", err)
|
|
||||||
}
|
|
||||||
specs[source] = makeCrontabEntry(source, value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the intake-managed section of the user's crontab.
|
|
||||||
func UpdateCrontab(db DB, specs map[string]string) (err error) {
|
|
||||||
// If there is no crontab command available, quit early.
|
|
||||||
crontabPath, err := exec.LookPath("crontab")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("no crontab found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current crontab without extra header lines via `EDITOR=cat crontab -e`
|
|
||||||
cmdLoad := exec.Command(crontabPath, "-e")
|
|
||||||
cmdLoad.Env = append(os.Environ(), "EDITOR=cat")
|
|
||||||
output, err := cmdLoad.Output()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error: failed to get current crontab: %v", err)
|
|
||||||
}
|
|
||||||
lines := strings.Split(string(output), "\n")
|
|
||||||
|
|
||||||
// Sort the new intake crons
|
|
||||||
var sources []string
|
|
||||||
for source := range specs {
|
|
||||||
sources = append(sources, source)
|
|
||||||
}
|
|
||||||
sort.Strings(sources)
|
|
||||||
|
|
||||||
// Splice the intake crons into the crontab
|
|
||||||
var newCrontab []string
|
|
||||||
headerFound := false
|
|
||||||
inSection := false
|
|
||||||
for i := range lines {
|
|
||||||
switch {
|
|
||||||
case !headerFound && lines[i] == IntakeCronBegin:
|
|
||||||
headerFound = true
|
|
||||||
inSection = true
|
|
||||||
newCrontab = append(newCrontab, IntakeCronBegin)
|
|
||||||
for _, source := range sources {
|
|
||||||
newCrontab = append(newCrontab, specs[source])
|
|
||||||
}
|
|
||||||
|
|
||||||
case lines[i] == IntakeCronEnd:
|
|
||||||
newCrontab = append(newCrontab, IntakeCronEnd)
|
|
||||||
inSection = false
|
|
||||||
|
|
||||||
case !inSection:
|
|
||||||
newCrontab = append(newCrontab, lines[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the splice mark was never found, append the whole section to the end
|
|
||||||
if !headerFound {
|
|
||||||
newCrontab = append(newCrontab, IntakeCronBegin)
|
|
||||||
for _, source := range sources {
|
|
||||||
newCrontab = append(newCrontab, specs[source])
|
|
||||||
}
|
|
||||||
newCrontab = append(newCrontab, IntakeCronEnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Updating %d crontab entries", len(specs))
|
|
||||||
|
|
||||||
// Save the updated crontab
|
|
||||||
cmdSave := exec.Command(crontabPath, "-")
|
|
||||||
stdin, err := cmdSave.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open stdin: %v", err)
|
|
||||||
}
|
|
||||||
if _, err := io.WriteString(stdin, strings.Join(newCrontab, "\n")); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to crontab: %v", err)
|
|
||||||
}
|
|
||||||
if err := stdin.Close(); err != nil {
|
|
||||||
return fmt.Errorf("failed to close stdin: %v", err)
|
|
||||||
}
|
|
||||||
output, err = cmdSave.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to read crontab output: %v", err)
|
|
||||||
}
|
|
||||||
if len(output) > 0 {
|
|
||||||
log.Printf("crontab output: %s", string(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -5,9 +5,6 @@ if [ -f /home/alice/.intake-setup-done ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# intake service gets a crontab wrapper, cheat here
|
|
||||||
export PATH="/run/wrappers/bin:$PATH"
|
|
||||||
|
|
||||||
mkdir -p $INTAKE_DATA_DIR
|
mkdir -p $INTAKE_DATA_DIR
|
||||||
|
|
||||||
intake source add -s echo
|
intake source add -s echo
|
||||||
@ -18,7 +15,7 @@ intake channel add -s echo -c home
|
|||||||
|
|
||||||
intake source add -s currenttime
|
intake source add -s currenttime
|
||||||
intake action add -s currenttime -a fetch -- sh -c "date +%Y-%m-%d-%H-%M | jq -cR '{id: .}'"
|
intake action add -s currenttime -a fetch -- sh -c "date +%Y-%m-%d-%H-%M | jq -cR '{id: .}'"
|
||||||
intake source env -s currenttime --set "INTAKE_CRON=* * * * *"
|
intake source env -s currenttime --set "INTAKE_FETCH=every 1m"
|
||||||
intake channel add -s currenttime -c home
|
intake channel add -s currenttime -c home
|
||||||
|
|
||||||
touch /home/alice/.intake-setup-done
|
touch /home/alice/.intake-setup-done
|
||||||
|
@ -5,9 +5,6 @@ if [ -f /home/bob/.intake-setup-done ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# intake service gets a crontab wrapper, cheat here
|
|
||||||
export PATH="/run/wrappers/bin:$PATH"
|
|
||||||
|
|
||||||
mkdir -p $INTAKE_DATA_DIR
|
mkdir -p $INTAKE_DATA_DIR
|
||||||
|
|
||||||
intake source add -s echo
|
intake source add -s echo
|
||||||
|
10
module.nix
10
module.nix
@ -75,9 +75,6 @@ in
|
|||||||
enabledUsers = filterAttrs (userName: userCfg: userCfg.enable) intakeCfg.users;
|
enabledUsers = filterAttrs (userName: userCfg: userCfg.enable) intakeCfg.users;
|
||||||
enabledUserNames = mapAttrsToList (userName: userCfg: userName) enabledUsers;
|
enabledUserNames = mapAttrsToList (userName: userCfg: userName) enabledUsers;
|
||||||
userPackages = userName: [ intakeCfg.package ] ++ intakeCfg.extraPackages ++ intakeCfg.users.${userName}.extraPackages;
|
userPackages = userName: [ intakeCfg.package ] ++ intakeCfg.extraPackages ++ intakeCfg.users.${userName}.extraPackages;
|
||||||
crontabWrapper = pkgs.writeShellScriptBin "crontab" ''
|
|
||||||
exec ${config.security.wrapperDir}/crontab "$@"
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Apply the overlay so intake is included in pkgs.
|
# Apply the overlay so intake is included in pkgs.
|
||||||
@ -92,9 +89,6 @@ in
|
|||||||
in
|
in
|
||||||
mkMerge (map addPackagesToUser enabledUserNames);
|
mkMerge (map addPackagesToUser enabledUserNames);
|
||||||
|
|
||||||
# Enable cron
|
|
||||||
services.cron.enable = true;
|
|
||||||
|
|
||||||
# Define a user service for each configured user
|
# Define a user service for each configured user
|
||||||
systemd.services =
|
systemd.services =
|
||||||
let
|
let
|
||||||
@ -102,8 +96,6 @@ in
|
|||||||
userName:
|
userName:
|
||||||
pkgs.writeShellScript "intake-run.sh" ''
|
pkgs.writeShellScript "intake-run.sh" ''
|
||||||
mkdir -p $INTAKE_DATA_DIR
|
mkdir -p $INTAKE_DATA_DIR
|
||||||
# Add the setuid wrapper directory so `crontab` is accessible
|
|
||||||
export PATH="${config.security.wrapperDir}:$PATH"
|
|
||||||
${intakeCfg.package}/bin/intake serve --addr ${enabledUsers.${userName}.listen.addr} --port ${toString enabledUsers.${userName}.listen.port}
|
${intakeCfg.package}/bin/intake serve --addr ${enabledUsers.${userName}.listen.addr} --port ${toString enabledUsers.${userName}.listen.port}
|
||||||
'';
|
'';
|
||||||
# systemd service definition for a single user, given `services.intake.users.userName` = `userCfg`
|
# systemd service definition for a single user, given `services.intake.users.userName` = `userCfg`
|
||||||
@ -111,7 +103,7 @@ in
|
|||||||
"intake-${userName}" = {
|
"intake-${userName}" = {
|
||||||
description = "Intake service for user ${userName}";
|
description = "Intake service for user ${userName}";
|
||||||
script = "${runScript userName}";
|
script = "${runScript userName}";
|
||||||
path = [ crontabWrapper ] ++ intakeCfg.extraPackages ++ userCfg.extraPackages;
|
path = intakeCfg.extraPackages ++ userCfg.extraPackages;
|
||||||
environment = {
|
environment = {
|
||||||
INTAKE_DATA_DIR = "/home/${userName}/.local/share/intake";
|
INTAKE_DATA_DIR = "/home/${userName}/.local/share/intake";
|
||||||
};
|
};
|
||||||
|
@ -135,17 +135,6 @@ func (env *Env) editSource(writer http.ResponseWriter, req *http.Request) {
|
|||||||
http.Error(writer, err.Error(), 500)
|
http.Error(writer, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if envName == "INTAKE_CRON" {
|
|
||||||
specs, err := core.GetCronSources(env.db)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error: failed to get cron specs: %v", err)
|
|
||||||
} else {
|
|
||||||
err = core.UpdateCrontab(env.db, specs)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("error: failed to update crontab: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
actionName := req.PostForm.Get("actionName")
|
actionName := req.PostForm.Get("actionName")
|
||||||
|
Loading…
Reference in New Issue
Block a user