Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http file transfer #2055

Merged
merged 1 commit into from
Jul 13, 2018
Merged

http file transfer #2055

merged 1 commit into from
Jul 13, 2018

Conversation

alex-kovoy
Copy link
Contributor

This work is still in progress but I would like you guys to take a look to provide an early feedback.

@alex-kovoy alex-kovoy force-pushed the alexey/files branch 2 times, most recently from e0c5544 to 3b504e3 Compare July 5, 2018 13:39
Copy link
Contributor

@klizhentas klizhentas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your overall design looks solid, I like it.

There are several rough edges that needs to be polished on the backend:

  • It's not clear to me why do you need to create a new client instead of using the method of the session
  • I would like you to run a check for file leaks - make sure all the resources are closed
  • Apply/address my general comments

@@ -905,6 +906,59 @@ func (tc *TeleportClient) Play(ctx context.Context, namespace, sessionId string)
return trace.Wrap(err)
}

// RunSCPCommand executes scp command
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it ExecuteSCP and elaborate in comments that it executes scp.Command and is used in lower-level API integrations opposed to SCP function that is used to mimic SCP CLI command behavior

}
defer proxyClient.Close()

siteInfo, err := proxyClient.currentCluster()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clusterInfo, we are slowly renaming site to cluster everywhere

}

// gets called to convert SSH error code to tc.ExitStatus
onError := func(err error) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onError is used only once, so I don't see a reason to factor this into closure, just inline the code

@@ -983,7 +1052,21 @@ func (tc *TeleportClient) SCP(ctx context.Context, args []string, port int, recu
}
// copy everything except the last arg (that's destination)
for _, dest := range args[1:] {
err = client.Download(src, dest, recursive, tc.Stderr, progressWriter)
scpParams := scp.Config{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be scpConfig for consistency (in similar code above, it is called scpCfg)

// copy everything except the last arg (that's destination)
for _, src := range args[:len(args)-1] {
err = client.Upload(src, dest, recursive, tc.Stderr, progressWriter)
scpCfg := scp.Config{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's call it scpConfig for consistency

@@ -181,6 +181,10 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
h.GET("/webapi/sites/:site/namespaces/:namespace/sessions/:sid/events", h.WithClusterAuth(h.siteSessionEventsGet)) // get recorded session's timing information (from events)
h.GET("/webapi/sites/:site/namespaces/:namespace/sessions/:sid/stream", h.siteSessionStreamGet) // get recorded session's bytes (from events)

// scp file transfer
h.GET("/webapi/sites/:site/namespaces/:namespace/nodes/:node/:login/scp/*filepath", h.WithClusterAuth(h.downloadFile))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels like login should be really a query parameter, not part of the path, will be more REST-y this way

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also I would stick to sid notation as in the function above for consistency instead of node

@@ -181,6 +181,10 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
h.GET("/webapi/sites/:site/namespaces/:namespace/sessions/:sid/events", h.WithClusterAuth(h.siteSessionEventsGet)) // get recorded session's timing information (from events)
h.GET("/webapi/sites/:site/namespaces/:namespace/sessions/:sid/stream", h.siteSessionStreamGet) // get recorded session's bytes (from events)

// scp file transfer
h.GET("/webapi/sites/:site/namespaces/:namespace/nodes/:node/:login/scp/*filepath", h.WithClusterAuth(h.downloadFile))
h.POST("/webapi/sites/:site/namespaces/:namespace/nodes/:node/:login/scp/*filepath", h.WithClusterAuth(h.uploadFile))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

lib/web/files.go Outdated

func (h *Handler) downloadFile(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *SessionContext, site reversetunnel.RemoteSite) (interface{}, error) {
req := fileTransferRequest{
Login: p.ByName("login"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the structure is not exported, so it does make sense to use lower case for struct member fields as well

lib/web/files.go Outdated
Username: f.ctx.user,
ProxyHostPort: f.proxyHostPort,
// TODO: replace os io streams with custom streams
Stdout: os.Stdout,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done before the release for sure

return nil
}

func (f *fileTransfer) createClient(req fileTransferRequest) (*client.TeleportClient, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, why not use the client from the context? there is a function there that does pretty much the same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is similar to our web handlers for handling ssh connections. This method returns TeleportClient which then we use to connect to a node to run SCP command.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k, can you then try to factor it into the common utility function to reduce boilerplate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i looked at 2 places where we create this config and moved initialization of common parameters to a separate method makeTeleportClientConfig

@alex-kovoy alex-kovoy force-pushed the alexey/files branch 4 times, most recently from 2ba7a39 to fd43171 Compare July 11, 2018 21:47
// to teleport can pretend it launches real scp behind the scenes
type command struct {
Config
*log.Entry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you call this variable log? Embedded *log.Entry like this pollutes autocomplete for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@russjones
Copy link
Contributor

russjones commented Jul 11, 2018

A general comment, you have a lot of Create* commands.

CreateCommand
CreateDownloadCommand
CreateUploadCommand
CreateHTTPUpload
CreateHTTPDownload

What do you think about consolidating them? Maybe something like the following:

cmd, err := scp.New(&scp.Config{
   Type: scp.HTTP | scp.File,
   Transfer: scp.Upload | scp.Download,
   ...
})
if err != nil {
   return trace.Wrap(err)
}

That way all the logic for how to construct the SCP command can be pushed down to the scp package.

@alex-kovoy
Copy link
Contributor Author

Since there are many flags and parameters, I figured it would be simpler for a caller if some of it can be encapsulated by specific commands so there is no need to become familiar with every aspect of SCP to create, for example, a HTTPDownload command. CreateHTTPUpload accepts only HTTP specific parameters and takes care of the rest.

All this factory methods are inside scp package.

@klizhentas
Copy link
Contributor

@alex-kovoy please backport this to branch/2.7 as well

@alex-kovoy alex-kovoy merged commit 775edea into master Jul 13, 2018
@alex-kovoy alex-kovoy deleted the alexey/files branch July 17, 2018 22:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants