package schedulerdockerlocal import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "text/template" "github.com/dokku/dokku/plugins/common" "github.com/dokku/dokku/plugins/cron" "golang.org/x/sync/errgroup" base36 "github.com/multiformats/go-base36" ) func deleteCrontab() error { command := common.NewShellCmd("sudo /usr/bin/crontab -l -u dokku") command.ShowOutput = false if !command.Execute() { return nil } command = common.NewShellCmd("sudo /usr/bin/crontab -r -u dokku") command.ShowOutput = false out, err := command.CombinedOutput() if err != nil { return fmt.Errorf("Unable to remove schedule file: %v", string(out)) } common.LogInfo1("Removed") return nil } func generateCronEntries() ([]cron.TemplateCommand, error) { apps, _ := common.UnfilteredDokkuApps() g := new(errgroup.Group) results := make(chan []cron.TemplateCommand, len(apps)+1) for _, appName := range apps { appName := appName g.Go(func() error { scheduler := common.GetAppScheduler(appName) if scheduler != "docker-local" { results <- []cron.TemplateCommand{} return nil } c, err := cron.FetchCronEntries(appName) if err != nil { results <- []cron.TemplateCommand{} return err } results <- c return nil }) } g.Go(func() error { commands := []cron.TemplateCommand{} b, _ := common.PlugnTriggerOutput("cron-entries", "docker-local") for _, line := range strings.Split(strings.TrimSpace(string(b[:])), "\n") { if strings.TrimSpace(line) == "" { results <- []cron.TemplateCommand{} return nil } parts := strings.Split(line, ";") if len(parts) != 2 && len(parts) != 3 { results <- []cron.TemplateCommand{} return fmt.Errorf("Invalid injected cron task: %v", line) } id := base36.EncodeToStringLc([]byte(strings.Join(parts, ";;;"))) command := cron.TemplateCommand{ ID: id, Schedule: parts[0], AltCommand: parts[1], } if len(parts) == 3 { command.LogFile = parts[2] } commands = append(commands, command) } results <- commands return nil }) err := g.Wait() close(results) commands := []cron.TemplateCommand{} if err != nil { return commands, err } for result := range results { c := result if len(c) > 0 { commands = append(commands, c...) } } return commands, nil } func writeCronEntries() error { commands, err := generateCronEntries() if err != nil { return err } if len(commands) == 0 { return deleteCrontab() } data := map[string]interface{}{ "Commands": commands, } t, err := getCronTemplate() if err != nil { return err } tmpFile, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("dokku-%s-%s", common.MustGetEnv("DOKKU_PID"), "WriteCronEntries")) if err != nil { return fmt.Errorf("Cannot create temporary schedule file: %v", err) } defer tmpFile.Close() defer os.Remove(tmpFile.Name()) if err := t.Execute(tmpFile, data); err != nil { return fmt.Errorf("Unable to template out schedule file: %v", err) } command := common.NewShellCmd(fmt.Sprintf("sudo /usr/bin/crontab -u dokku %s", tmpFile.Name())) command.ShowOutput = false out, err := command.CombinedOutput() if err != nil { return fmt.Errorf("Unable to update schedule file: %s", string(out)) } common.LogInfo1("Updated schedule file") return nil } func getCronTemplate() (*template.Template, error) { t := template.New("cron") templatePath := filepath.Join(common.MustGetEnv("PLUGIN_ENABLED_PATH"), "cron", "templates", "cron.tmpl") b, err := ioutil.ReadFile(templatePath) if err != nil { return t, fmt.Errorf("Cannot read template file: %v", err) } s := strings.TrimSpace(string(b)) return t.Parse(s) }