From c94edd8bb0df572d08d1dc59c5579c655172634f Mon Sep 17 00:00:00 2001 From: b5 Date: Thu, 2 May 2019 08:40:53 -0400 Subject: [PATCH] feat(update): support updates via shell scripts --- base/cron.go | 3 ++- cmd/factory_test.go | 1 + lib/lib.go | 9 +++++++++ lib/update.go | 34 ++++++++++++++++++++++++++-------- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/base/cron.go b/base/cron.go index 00022bcf8..b6a0fdb6a 100644 --- a/base/cron.go +++ b/base/cron.go @@ -23,7 +23,8 @@ func LocalShellScriptRunner(basepath string) cron.RunJobFunc { return func(ctx context.Context, streams ioes.IOStreams, job *cron.Job) error { path := job.Name if qfs.PathKind(job.Name) == "local" { - path = filepath.Join(basepath, path) + // TODO (b5) - need to first check that path can't be found + // path = filepath.Join(basepath, path) } cmd := exec.Command(path) diff --git a/cmd/factory_test.go b/cmd/factory_test.go index faba5eeec..e08f2e55d 100644 --- a/cmd/factory_test.go +++ b/cmd/factory_test.go @@ -63,6 +63,7 @@ func NewTestFactory(c *regclient.Client) (tf TestFactory, err error) { rpc: nil, config: cfg, node: tnode.(*p2p.QriNode), + inst: lib.NewInstanceFromConfigAndNode(cfg, tnode.(*p2p.QriNode)), }, nil } diff --git a/lib/lib.go b/lib/lib.go index 2e297cf26..08803f55e 100644 --- a/lib/lib.go +++ b/lib/lib.go @@ -529,3 +529,12 @@ func (inst *Instance) RPC() *rpc.Client { func (inst *Instance) Teardown() { inst.teardown() } + +// QriPath returns the path to where qri is operating from on the local filesystem +// there are situations where this will be a temporary directory +func (inst *Instance) QriPath() string { + if inst.cfg != nil && inst.cfg.Repo != nil && inst.cfg.Repo.Path != "" { + return inst.cfg.Repo.Path + } + return os.TempDir() +} diff --git a/lib/update.go b/lib/update.go index 0756d83e1..120dd05e0 100644 --- a/lib/update.go +++ b/lib/update.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "io" + "os" + "path/filepath" "github.com/qri-io/dataset" "github.com/qri-io/ioes" "github.com/qri-io/qfs" + "github.com/qri-io/qri/actions" "github.com/qri-io/qri/base" "github.com/qri-io/qri/cron" "github.com/qri-io/qri/repo" @@ -15,13 +18,22 @@ import ( // NewUpdateMethods creates a configuration handle from an instance func NewUpdateMethods(inst *Instance) *UpdateMethods { - return &UpdateMethods{inst: inst} + m := &UpdateMethods{ + inst: inst, + scriptsPath: filepath.Join(inst.QriPath(), "update_scripts"), + } + + if err := os.MkdirAll(m.scriptsPath, os.ModePerm); err != nil { + log.Error("creating update scripts directory: %s", err.Error()) + } + + return m } // UpdateMethods enapsulates logic for scheduled updates type UpdateMethods struct { inst *Instance - ScriptsPath string + scriptsPath string } // CoreRequestsName specifies this is a Methods object @@ -43,6 +55,12 @@ type ScheduleParams struct { // Schedule creates a job and adds it to the scheduler func (m *UpdateMethods) Schedule(in *ScheduleParams, out *cron.Job) (err error) { + if base.PossibleShellScript(in.Name) { + if err = qfs.AbsPath(&in.Name); err != nil { + return + } + } + if m.inst.rpc != nil { return m.inst.rpc.Call("UpdateMethods.Schedule", in, out) } @@ -64,6 +82,7 @@ func (m *UpdateMethods) Schedule(in *ScheduleParams, out *cron.Job) (err error) func (m *UpdateMethods) jobFromScheduleParams(p *ScheduleParams) (job *cron.Job, err error) { if base.PossibleShellScript(p.Name) { + // TODO (b5) - confirm file exists & is executable return base.ShellScriptToJob(qfs.NewMemfileBytes(p.Name, nil), p.Periodicity, nil) } @@ -170,7 +189,7 @@ func (m *UpdateMethods) Run(p *Job, res *repo.DatasetRef) (err error) { err = m.runDatasetUpdate(updateParams, res) case cron.JTShellScript: - runner := base.LocalShellScriptRunner(m.ScriptsPath) + runner := base.LocalShellScriptRunner(m.scriptsPath) err = runner(m.inst.ctx, m.inst.streams, p) default: @@ -214,10 +233,10 @@ func (m *UpdateMethods) runDatasetUpdate(p *UpdateParams, res *repo.DatasetRef) return err } - // if !base.InLocalNamespace(m.inst.Repo(), &ref) { - // *res, err = actions.UpdateRemoteDataset(m.node, &ref, true) - // return err - // } + if !base.InLocalNamespace(m.inst.Repo(), &ref) { + *res, err = actions.UpdateRemoteDataset(m.inst.Node(), &ref, true) + return err + } // default to recalling transfrom scripts for local updates // TODO (b5): not sure if this should be here or in client libraries @@ -261,7 +280,6 @@ func newUpdateRunner(newInst func(ctx context.Context, streams ioes.IOStreams) ( } m := NewUpdateMethods(inst) - m.ScriptsPath = scriptsPath res := &repo.DatasetRef{} return m.Run(job, res) }