From f03c271e19f4b417a21515048fd026ddb5dff47e Mon Sep 17 00:00:00 2001 From: Shulhan Date: Fri, 22 Dec 2023 16:04:54 +0700 Subject: all: support cancellation when running command using SSH client This changes require us to replace golang.org/x/crypto with our fork, since the [ssh.Session.Run] with context is not available yet in upstream. Implements: https://todo.sr.ht/~shulhan/awwan/9 --- _example/localhost/test.aww | 3 +++ go.mod | 4 +++- go.sum | 8 ++++---- session.go | 22 +++++++++++----------- ssh_client.go | 37 +++++++++++++++++++------------------ 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/_example/localhost/test.aww b/_example/localhost/test.aww index 6773668..ff4019f 100644 --- a/_example/localhost/test.aww +++ b/_example/localhost/test.aww @@ -6,3 +6,6 @@ ls -al ~/ | grep .ssh echo "test" > /tmp/awwan.test cat /tmp/awwan.test # test + +## Test cancellation. +sleep 300 diff --git a/go.mod b/go.mod index d7ed685..b19d88a 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ go 1.20 require ( git.sr.ht/~shulhan/ciigo v0.11.0 github.com/evanw/esbuild v0.19.8 - github.com/shuLhan/share v0.51.1-0.20231222030113-7a63c70fb199 + github.com/shuLhan/share v0.51.1-0.20231222082140-8932e7ab20fc ) require ( @@ -24,6 +24,8 @@ require ( replace github.com/evanw/esbuild => github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1 +replace golang.org/x/crypto => git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339 + //replace github.com/shuLhan/share => ../share //replace git.sr.ht/~shulhan/ciigo => ../ciigo diff --git a/go.sum b/go.sum index 56fcb8b..dd7350b 100644 --- a/go.sum +++ b/go.sum @@ -2,16 +2,16 @@ git.sr.ht/~shulhan/asciidoctor-go v0.5.1 h1:TuuLo+N61+qsXkiFgtiW5W1q7xHzeSID4zH+ git.sr.ht/~shulhan/asciidoctor-go v0.5.1/go.mod h1:5audSCN6jDr2+/cMvx1MdZxkCurjl/k6A5OGYWRtB0o= git.sr.ht/~shulhan/ciigo v0.11.0 h1:t8/PqVQVOsG025WLjNjJSI4S37jN5CkY+LyC+zd1snI= git.sr.ht/~shulhan/ciigo v0.11.0/go.mod h1:pyt2kxKvipCAO+jrjHuEXOWJ2h0ss/hnO9j7Xot3JHc= +git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339 h1:iq2/NVwTZvvs4QWBJaBt1r+ZNaXf/cAOAO5k8Eplqtg= +git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1 h1:U4DRlREmugTNkevukauQjjUsz82o3YRjtbxDILoN/Xs= github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= -github.com/shuLhan/share v0.51.1-0.20231222030113-7a63c70fb199 h1:1aREJiATkXEyEtK7OYwQ+BFIkWqAzxvoqQoHafuJwOs= -github.com/shuLhan/share v0.51.1-0.20231222030113-7a63c70fb199/go.mod h1:rmp14f8JWRl/2/ruiWMSN6H/P7CupdCqstTNBs1Dwy4= +github.com/shuLhan/share v0.51.1-0.20231222082140-8932e7ab20fc h1:S5i3VxbBTnEGeeUIl12EXrSzk5KHyUOGoUcFZqdBp4E= +github.com/shuLhan/share v0.51.1-0.20231222082140-8932e7ab20fc/go.mod h1:aNDs/SjnVYXaLEEJzjmfrUFeLD6u0YHWy6pI8o8iqYw= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/session.go b/session.go index f4c14da..f2b5a20 100644 --- a/session.go +++ b/session.go @@ -188,7 +188,7 @@ func (ses *Session) Get(stmt *Statement) (err error) { } // Put copy file from local to remote system. -func (ses *Session) Put(req *ExecRequest, stmt *Statement) (err error) { +func (ses *Session) Put(ctx context.Context, req *ExecRequest, stmt *Statement) (err error) { var ( logp = `Put` src = stmt.args[0] @@ -213,13 +213,13 @@ func (ses *Session) Put(req *ExecRequest, stmt *Statement) (err error) { return fmt.Errorf("%s: %w", logp, err) } if stmt.mode != 0 { - err = ses.sshc.chmod(dst, stmt.mode) + err = ses.sshc.chmod(ctx, dst, stmt.mode) if err != nil { return fmt.Errorf(`%s: %w`, logp, err) } } if len(stmt.owner) != 0 { - err = ses.sshc.chown(dst, stmt.owner) + err = ses.sshc.chown(ctx, dst, stmt.owner) if err != nil { return fmt.Errorf(`%s: %w`, logp, err) } @@ -307,7 +307,7 @@ func (ses *Session) SudoGet(ctx context.Context, req *ExecRequest, stmt *Stateme dst = stmt.args[1] ) - err = ses.sshc.sudoGet(src, dst) + err = ses.sshc.sudoGet(ctx, src, dst) if err != nil { return fmt.Errorf("%s: %w", logp, err) } @@ -329,7 +329,7 @@ func (ses *Session) SudoGet(ctx context.Context, req *ExecRequest, stmt *Stateme } // SudoPut copy file from local to remote using sudo. -func (ses *Session) SudoPut(req *ExecRequest, stmt *Statement) (err error) { +func (ses *Session) SudoPut(ctx context.Context, req *ExecRequest, stmt *Statement) (err error) { var ( logp = `SudoPut` src = stmt.args[0] @@ -343,7 +343,7 @@ func (ses *Session) SudoPut(req *ExecRequest, stmt *Statement) (err error) { return fmt.Errorf("%s: %w", logp, err) } - err = ses.sshc.sudoPut(src, dst) + err = ses.sshc.sudoPut(ctx, src, dst) if isVault { var errRemove = os.Remove(src) if errRemove != nil { @@ -355,13 +355,13 @@ func (ses *Session) SudoPut(req *ExecRequest, stmt *Statement) (err error) { } if stmt.mode != 0 { - err = ses.sshc.sudoChmod(dst, stmt.mode) + err = ses.sshc.sudoChmod(ctx, dst, stmt.mode) if err != nil { return fmt.Errorf(`%s: %w`, logp, err) } } if len(stmt.owner) != 0 { - err = ses.sshc.sudoChown(dst, stmt.owner) + err = ses.sshc.sudoChown(ctx, dst, stmt.owner) if err != nil { return fmt.Errorf(`%s: %w`, logp, err) } @@ -518,17 +518,17 @@ func (ses *Session) executeScriptOnRemote(ctx context.Context, req *ExecRequest, switch stmt.kind { case statementKindDefault: - err = ses.sshc.conn.Execute(string(stmt.raw)) + err = ses.sshc.conn.Execute(ctx, string(stmt.raw)) case statementKindGet: err = ses.Get(stmt) case statementKindLocal: err = ExecLocal(ctx, req, stmt) case statementKindPut: - err = ses.Put(req, stmt) + err = ses.Put(ctx, req, stmt) case statementKindSudoGet: err = ses.SudoGet(ctx, req, stmt) case statementKindSudoPut: - err = ses.SudoPut(req, stmt) + err = ses.SudoPut(ctx, req, stmt) } if err != nil { return err diff --git a/ssh_client.go b/ssh_client.go index b80e8f0..3007df1 100644 --- a/ssh_client.go +++ b/ssh_client.go @@ -5,6 +5,7 @@ package awwan import ( "bytes" + "context" "errors" "fmt" "io/fs" @@ -73,7 +74,7 @@ func newSSHClient(req *ExecRequest, section *config.Section) (sshc *sshClient, e sshc.dirHome = string(bytes.TrimSpace(stdout)) sshc.dirTmp = strings.Replace(defTmpDirPlay, `~`, sshc.dirHome, 1) - err = sshc.mkdir(sshc.dirTmp, 0700) + err = sshc.mkdir(context.Background(), sshc.dirTmp, 0700) if err != nil { return nil, err } @@ -82,10 +83,10 @@ func newSSHClient(req *ExecRequest, section *config.Section) (sshc *sshClient, e } // chmod change the remoteFile permission. -func (sshc *sshClient) chmod(remoteFile string, perm fs.FileMode) (err error) { +func (sshc *sshClient) chmod(ctx context.Context, remoteFile string, perm fs.FileMode) (err error) { var chmodStmt = fmt.Sprintf(`chmod %o %q`, perm, remoteFile) - err = sshc.conn.Execute(chmodStmt) + err = sshc.conn.Execute(ctx, chmodStmt) if err != nil { return err } @@ -95,10 +96,10 @@ func (sshc *sshClient) chmod(remoteFile string, perm fs.FileMode) (err error) { // chown change the owner of remoteFile. // The owner parameter can be set to user only "user", group only // ":group", or user and group "user:group". -func (sshc *sshClient) chown(remoteFile, owner string) (err error) { +func (sshc *sshClient) chown(ctx context.Context, remoteFile, owner string) (err error) { var chownStmt = fmt.Sprintf(`chown %s %q`, owner, remoteFile) - err = sshc.conn.Execute(chownStmt) + err = sshc.conn.Execute(ctx, chownStmt) if err != nil { return err } @@ -138,11 +139,11 @@ func (sshc *sshClient) get(remote, local string) (err error) { } // mkdir create directory on the remote server. -func (sshc *sshClient) mkdir(dir string, permission uint32) (err error) { +func (sshc *sshClient) mkdir(ctx context.Context, dir string, permission uint32) (err error) { if sshc.sftpc == nil { var mkdirStmt = fmt.Sprintf(`mkdir -p %s`, dir) - err = sshc.conn.Execute(mkdirStmt) + err = sshc.conn.Execute(ctx, mkdirStmt) } else { var fa = sftp.FileAttrs{} @@ -163,10 +164,10 @@ func (sshc *sshClient) put(local, remote string) (err error) { } // rmdirAll remove the directory on server recursively. -func (sshc *sshClient) rmdirAll(dir string) (err error) { +func (sshc *sshClient) rmdirAll(ctx context.Context, dir string) (err error) { var rmdirStmt = fmt.Sprintf(`rm -rf %s`, dir) - err = sshc.conn.Execute(rmdirStmt) + err = sshc.conn.Execute(ctx, rmdirStmt) if err != nil { return fmt.Errorf(`rmdirAll: %s: %w`, dir, err) } @@ -175,10 +176,10 @@ func (sshc *sshClient) rmdirAll(dir string) (err error) { } // sudoChmod change the permission of remoteFile using sudo. -func (sshc *sshClient) sudoChmod(remoteFile string, mode fs.FileMode) (err error) { +func (sshc *sshClient) sudoChmod(ctx context.Context, remoteFile string, mode fs.FileMode) (err error) { var cmd = fmt.Sprintf(`sudo chmod %o %q`, mode, remoteFile) - err = sshc.conn.Execute(cmd) + err = sshc.conn.Execute(ctx, cmd) if err != nil { return err } @@ -187,10 +188,10 @@ func (sshc *sshClient) sudoChmod(remoteFile string, mode fs.FileMode) (err error } // sudoChown change the owner of remoteFile using sudo. -func (sshc *sshClient) sudoChown(remoteFile, owner string) (err error) { +func (sshc *sshClient) sudoChown(ctx context.Context, remoteFile, owner string) (err error) { var cmd = fmt.Sprintf(`sudo chown %s %q`, owner, remoteFile) - err = sshc.conn.Execute(cmd) + err = sshc.conn.Execute(ctx, cmd) if err != nil { return err } @@ -201,21 +202,21 @@ func (sshc *sshClient) sudoChown(remoteFile, owner string) (err error) { // The remote file is copied to temporary directory first, chmod-ed to // current SSH user so it can be read. // The temporary file then copied from remote to local. -func (sshc *sshClient) sudoGet(remote, local string) (err error) { +func (sshc *sshClient) sudoGet(ctx context.Context, remote, local string) (err error) { var ( remoteBase = filepath.Base(remote) remoteTmp = filepath.Join(sshc.dirTmp, remoteBase) cpRemoteToTmp = fmt.Sprintf(`sudo cp -f %s %s`, remote, remoteTmp) ) - err = sshc.conn.Execute(cpRemoteToTmp) + err = sshc.conn.Execute(ctx, cpRemoteToTmp) if err != nil { return err } var chmod = fmt.Sprintf(`sudo chown %s %s`, sshc.section.User(), remoteTmp) - err = sshc.conn.Execute(chmod) + err = sshc.conn.Execute(ctx, chmod) if err != nil { return err } @@ -227,7 +228,7 @@ func (sshc *sshClient) sudoGet(remote, local string) (err error) { // sudoPut copy local file to remote using sudo. // The file from local copied to remote in temporary directory first, and // then the temporary file moved to original destination using sudo. -func (sshc *sshClient) sudoPut(local, remote string) (err error) { +func (sshc *sshClient) sudoPut(ctx context.Context, local, remote string) (err error) { var ( baseName = filepath.Base(local) remoteTmp = filepath.Join(sshc.dirTmp, baseName) @@ -244,6 +245,6 @@ func (sshc *sshClient) sudoPut(local, remote string) (err error) { var moveStmt = fmt.Sprintf(`sudo mv -f %s %s`, remoteTmp, remote) - err = sshc.conn.Execute(moveStmt) + err = sshc.conn.Execute(ctx, moveStmt) return err } -- cgit v1.3