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

FileManager #112

Merged
merged 43 commits into from
Jun 7, 2017
Merged

FileManager #112

merged 43 commits into from
Jun 7, 2017

Conversation

gongweibao
Copy link
Collaborator

No description provided.

package main

import (
"flag"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use "github.com/namsral/flag" (a drop in replacement of "flag") instead of "flag", since the former can parse env variable as input.
Please see this discussion: PaddlePaddle/Paddle#2245 (comment)

Usage of "github.com/namsral/flag" see: https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/go/cmd/pserver/pserver.go#L9

Copy link
Collaborator Author

@gongweibao gongweibao May 31, 2017

Choose a reason for hiding this comment

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

@helinwang @typhoonzero github.com/golang/glog里边判断了

679     if !flag.Parsed() {
680         os.Stderr.Write([]byte("ERROR: logging before flag.Parse: "))
681         os.Stderr.Write(data)
682     }

而它用的是go原生的flag,所以用了github.com/namsral/flag,每一行打印中都会出现类似
ERROR: logging before flag.Parse: I0531 11:34:47.868818 3294 main.go:21]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

鉴于需要使用glog的level日志的功能,同时flag的功能已经足够用了。先用flag

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

Copy link
Collaborator

@helinwang helinwang May 31, 2017

Choose a reason for hiding this comment

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

Ok. It's fine as long as we don't need to parse environment variable for file server.


router := pfsserver.NewRouter()

portPtr := flag.Int("port", 8080, "listen port")
Copy link
Collaborator

Choose a reason for hiding this comment

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

port would suffice. Go prefer short variable names. See: https://github.com/golang/go/wiki/CodeReviewComments#variable-names

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

"strconv"
)

//ChunkCmd structure
Copy link
Collaborator

Choose a reason for hiding this comment

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

The code next line already shows it's a struct, I think we need a more descriptive comment.
Also, please leave a space after "//": // comment.
Please see: https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


//ToJSON encodes chunkcmd to json string
func (p *ChunkCmd) ToJSON() ([]byte, error) {
return nil, nil
Copy link
Collaborator

@helinwang helinwang May 30, 2017

Choose a reason for hiding this comment

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

Why it's returning nil, nil, is it not implemented? If so, do you plan to implement it later? If so, maybe panic("not implemented") is better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


const (
//DefaultMultiPartBoundary is the default multipart form boudary
DefaultMultiPartBoundary = "8d7b0e5709d756e21e971ff4d9ac3b20"
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's only used by package pfsserver, why not put it there?

Copy link
Collaborator Author

@gongweibao gongweibao May 31, 2017

Choose a reason for hiding this comment

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

这个是paddlecloud packagepfsserver package都用到了,所以放到了pfsmod package

Copy link
Collaborator

Choose a reason for hiding this comment

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

明白了。

//NewChunkCmdFromURLParam get a ChunkCmd structure
// path example:
// path=/pfs/datacenter1/1.txt&offset=4096&chunksize=4096
func NewChunkCmdFromURLParam(path string) (*ChunkCmd, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

NewChunkCmdFromURLParam is a very long name, maybe ParseChunkCmd is a better name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


//Run function runs a ChunkCmd
func (p *ChunkCmd) Run() (interface{}, error) {
return nil, nil
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why it's returning nil, nil, is it not implemented? If so, do you plan to implement it later? If so, maybe panic("not implemented") is better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

)

//ChunkCmd structure
type ChunkCmd struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we need a comment that describe what is ChunkCmd, I am confused why there is "cmd", from the definition, I can not see what "cmd" does?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

cmd := LsCmd{}

m, err := url.ParseQuery(path)
if err != nil ||
Copy link
Collaborator

Choose a reason for hiding this comment

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

if err != nil just indicate the parsing failed, it's not "not enough arguments", maybe we should use a different StatusText for it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

return nil, errors.New(StatusText(StatusJSONErr))
}

//var err error
Copy link
Collaborator

Choose a reason for hiding this comment

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

This line should be removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

// need a custome error type?

const (
StatusFileNotFound = 520
Copy link
Collaborator

Choose a reason for hiding this comment

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

好像只有在提取statusText的时候采用到了error code,还有其他时候需要用error code吗?
如果是这样的话,需要把error code暴露出package之外吗,以及,有必要有error code这个东西吗?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个我也在犹豫,是否应该定义一个自己的error类型,errno用来判定,errtext用来提示。所以上边写了一个注释:need a custom error type?

Copy link
Collaborator

Choose a reason for hiding this comment

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

感觉直接用HTTP的状态码也是可以的。

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.
这一版先用text。等我找到更好的办法的时候再重构一下。

break
}

//log.Println(n)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please no commented out code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

defaultMaxChunkSize = 4 * 1024 * 1024
defaultMinChunkSize = 4 * 1024
//DefaultChunkSize is the default chunk's size
DefaultChunkSize = 2 * 1024 * 1024
Copy link
Collaborator

Choose a reason for hiding this comment

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

DefaultChunkSize只被另一个package用到,是不是该放在另一个package里面?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

哦。有道理。我本来是想把关于同一个主题的都放到一个文件中。

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

package pfsmod

//JSONResponse is the common response from server
type JSONResponse struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The result field type of all response struct is interface{} in Go, so maybe we can only need one struct named:

type Response struct {
    Err string `json:"err"`,
    Result interface{} `json:"result"`
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

有道理。只是这个parse的时候会出错。可以把resp.go的内容重新分配了一下,删除了这个文件。

// need a custome error type?

const (
StatusFileNotFound = 520
Copy link
Collaborator

Choose a reason for hiding this comment

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

感觉直接用HTTP的状态码也是可以的。

type ChunkCmd struct {
Path string
Offset int64
ChunkSize int64
Copy link
Collaborator

Choose a reason for hiding this comment

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

ChunkSize may be a global const or read from some command-line arguments or config file.

Else if you intend to save the size of current chunk, it should be named size

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,118 @@
package pfsmod
Copy link
Collaborator

Choose a reason for hiding this comment

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

This package name filemanager/pfsmod seems confusing. You should name it like pfs/pfscommands or pfs/pfsmodules.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

return err
}

//SaveChunkData save data from r
Copy link
Collaborator

Choose a reason for hiding this comment

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

SaveChunkData save data from r => SaveChunkData save data from io.Reader

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

return &cmd, nil
}

//LoadChunkData loads a specified chunk to w
Copy link
Collaborator

Choose a reason for hiding this comment

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

LoadChunkData loads a specified chunk to w => LoadChunkData loads a specified chunk to io.Writer

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,221 @@
package pfsmod
Copy link
Collaborator

Choose a reason for hiding this comment

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

chunkmeta definations should also add to design doc: https://github.com/PaddlePaddle/Paddle/tree/develop/doc/design/file_manager

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个后续补上。

)

func main() {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpicking here, I know new line can separate regions of code of different functionalities, but a new line at the beginning of a function does not seem necessary?

Copy link
Collaborator Author

@gongweibao gongweibao Jun 1, 2017

Choose a reason for hiding this comment

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

Done
习惯于大括号在第二行居左了。

func main() {

port := flag.Int("port", 8080, "port of server")
ip := flag.String("ip", "0.0.0.0", "ip of server")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just curious, when will the ip of server necessary? I usually do ":8080" rather than "0.0.0.0:8080" so that I don't need to specify the ip.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ip port都可以不用指定。中间的是他们的默认值。

Copy link
Collaborator

Choose a reason for hiding this comment

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

Listening on an IP address is always useful, ":8080" is the same as "0.0.0.0:8080". When we need to bind TCP server to the specified network(in a LAN or VLAN or VPC), we need to specify the IP of the network card.

log "github.com/golang/glog"
)

// Chunk respresents a chunk info
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please see https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences , comments should be a full sentence. (end with a period ".")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

// need a custom error type?

const (
StatusFileNotFound = "no such file or directory"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does these errors needs to be public (visible outside of the package)?
Typically errors are used:

var ErrFileNotFound = errors.New("no such file or directory")

See: https://golang.org/src/io/io.go

But most of the time error do not need to be exported, since people who use the library does not need to differentiate between difference errors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. error类型没法序列化到JSON以及从JSON中反序列化。
  2. 确实需要区分错误:如文件不存在。

Copy link
Collaborator

@helinwang helinwang Jun 1, 2017

Choose a reason for hiding this comment

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

@gongweibao 我查了一下,貌似现在的code并没有把这些status序列化到json?如果需要序列化的话,err.Error()是不是更符合Go idiom?

}

writen, err := io.CopyN(w, f, p.Size)
log.V(2).Infof("writen:%d\n", writen)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I thought this function loads data, why here says "written"? Maybe "loaded" is more appropriate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


const (
// DefaultMultiPartBoundary is the default multipart form boudary
DefaultMultiPartBoundary = "8d7b0e5709d756e21e971ff4d9ac3b20"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think in general const block can be put together:

const (
    DefaultMultiPartBoundary = "8d7b0e5709d756e21e971ff4d9ac3b20"
    MaxJSONRequestSize = 2048

If you want to write them separately, you can do:

const DefaultMultiPartBoundary = "8d7b0e5709d756e21e971ff4d9ac3b20"
const MaxJSONRequestSize = 2048

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

func writeJSONResponse(w http.ResponseWriter,
req string,
httpStatus int,
resp *response) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe pass by value is better resp response. Please see https://github.com/golang/go/wiki/CodeReviewComments#pass-values

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


// PostFilesHandler processes files' POST request
func PostFilesHandler(w http.ResponseWriter, r *http.Request) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe this empty line is not necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

result, err := cmd.Run()
if err != nil {
resp.Err = err.Error()
resp.Results = result
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't result invalid when err != nil?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


// Run functions runs LsCmd and return LsResult and error if any happened
func (p *LsCmd) Run() (interface{}, error) {
results := make([]LsResult, 0, 100)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why the capacity is set to 100? Please do not optimize at the cost of code clarity if it's not identified as a bottleneck.
var results []LsResult is probably more appropriate. (calling append / len using a nil slice is fine)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

return nil, err
}

url := fmt.Sprintf("%s:%d/api/v1/files", s.config.ActiveConfig.Endpoint, s.port)
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to add s.port. Endpoint already contains it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

return &pfsSubmitter{
config: config,
client: client,
port: 8080,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Port is hard coded

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,246 @@
package paddlecloud
Copy link
Collaborator

Choose a reason for hiding this comment

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

PFS operations are not "submits", so we don't need a pfsSubmitter

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

submitter其实是提交命令的意思。接口是面向Command interface的。这个接口部分我觉得可以保留。

Copy link
Collaborator

Choose a reason for hiding this comment

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

还是有点牵强。一般会说submit a job, submit a task,run a command. 如果都是HTTP请求,直接调用Get, Post函数即可,可以省掉这个struct

}

// PostFiles implements files' POST method.
func (s *pfsSubmitter) PostFiles(cmd pfsmod.Command) ([]byte, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can put HTTP client operations in restclient.go, I'll do some refactor to restclient.go so it is more flexible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK。pfssubmitter里边可以调用这几个GetCallPostCall等几个函数。

)

type response struct {
Err string `json:"err"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to use a same form of general response, like:

{ "code": 0, "msg": "OK or Error", "items": {...}}

Copy link
Collaborator Author

@gongweibao gongweibao Jun 1, 2017

Choose a reason for hiding this comment

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

这个response就是一个general responseResults是一个interface,不赋值的时候就是nil。JSON解析没有问题,而且这个时候客户端是不需要知道具体的Results是什么。
code这个思路不太可行。golang里边都是error对象,是没有error code的。如果要传error code就要在所有return err的地方做一个转换

Copy link
Collaborator

Choose a reason for hiding this comment

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

是说需要一个规范性质的,描述所有的REST response json的格式。包括pfs和submit等API。如果没有code 也可以,是需要一个统一的返回格式。

Copy link
Collaborator

@helinwang helinwang Jun 1, 2017

Choose a reason for hiding this comment

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

可以考虑:

type responseStatus struct {
  Err string `json:"msg"`
}

type response {
  responseStatus
  Results interface{} `json:"results"`
}

// ...

type chunkMetaResponse struct {
  responseStatus
  Results []pfsmod.ChunkMeta `json:"results"`
}

这样的好处是,可以确保所有的HTTP Response都有固定的status格式。方便client实现统一的parsing logic(client需要parse了status再决定下一步干什么,是报错还是继续执行)。

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

helin,这个没有太理解。Err放到一个结构体中和单独出来的区别是?

}

// CloudCheck checks the conditions when running on cloud.
func (p *LsCmd) CloudCheck() error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

CloudCheck() and LocalCheck() seems the same at mkdir.go, rm.go etc. with slitly differences, can we make it more simpler?

By the way, I don't quite understand what does "checks the conditions when running on cloud." means.

Copy link
Collaborator Author

@gongweibao gongweibao Jun 1, 2017

Choose a reason for hiding this comment

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

  1. 命令面向用户的基本设定都是在Cloud端运行,所以按照道理是不需要LocalCheck
  2. 不过,像Ls这样的命令,cp用到了本地其本地运行的结果,所以把命令抽象为interface以后,加入了LocalCheckCloudCheck,以便
  3. Local只需要检查参数的个数等,而在Cloud上需要检查用户的对目录的权限。其实是不一样的。

Copy link
Collaborator

Choose a reason for hiding this comment

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

我的理解:

  • 检查参数一般都叫作:validate,如果只是检查参数合法性,是要实现validate函数,比如检查dest是否是/pfs开始
  • 服务端检查用户对目录的权限,只需要比较认证后的username == path[3]即可

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

可以缩短。

}

// Close closes c and log it.
func Close(c io.Closer) {
Copy link
Collaborator

@helinwang helinwang Jun 1, 2017

Choose a reason for hiding this comment

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

Close should not be visible from outside of the package.

Copy link
Collaborator Author

@gongweibao gongweibao Jun 2, 2017

Choose a reason for hiding this comment

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

我的思路是这样的:有两个package中用到这个函数,为了防止两个包相互调用,所以定义了一个pfsmodules作为中间的公共package,然后把公共函数放到了它里边。

Copy link
Collaborator

@helinwang helinwang Jun 2, 2017

Choose a reason for hiding this comment

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

这种简单的函数,就直接放在package内吧。另外,防止相互调用往往会用interface(好像挺少因为这个再去加一个新的package,创建新package的目的可能是为了模块化,而不是解决循环依赖),比如:

package a

import "b"

var c = b.Foo()

type T int

func (t T) Bar() {
}
package b

type interface Bar {
  Bar()
}

// can't import "a" and use a.T as an argument, using a interface.
func Baz(b Bar) {
}

func Foo() {
}
package main

func main() {
  b.Baz(a.T(0))
}

往往必须有循环依赖的时候还是比较少的。

// CloudCheck checks the conditions when running on cloud.
func (p *ChunkMetaCmd) CloudCheck() error {
if !IsCloudPath(p.FilePath) {
return errors.New(StatusShouldBePfsPath + ": p.FilePath")
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe should be return errors.New(StatusShouldBePfsPath + ":" + p.FilePath)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

惭愧,搞错了。
Done

return nil, err
}

metas := make([]ChunkMeta, 0, fi.Size()/len+1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry I don't understand why there is a +1 in fi.Size()/len+1, I thought (fi.Size() + len - 1)/len is the ceil(fi.Size()/len).
Anyway, I think var metas []ChunkMeta is more fool proof, and easy to read. The optimization here will not have any visible impact to the performance. We should choose code clarity rather than performance, unless performance is a bottleneck.

Copy link
Collaborator Author

@gongweibao gongweibao Jun 2, 2017

Choose a reason for hiding this comment

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

Done

if flag.Name == "v" {
cmd.V, err = strconv.ParseBool(flag.Value.String())
if err != nil {
panic(err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

https://github.com/golang/go/wiki/CodeReviewComments#dont-panic

I think in general no code should panic besides maybe it's ok if code in package main.

Copy link
Collaborator Author

@gongweibao gongweibao Jun 2, 2017

Choose a reason for hiding this comment

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

Visit这个函数没有返回值可以供使用。
goto跳出来?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe:

var err error
f.Visit(func(flag *flag.Flag) {
  if err != nil {
    return
  }
  if flag.Name == "v" {
    cmd.V, err = strconv.ParseBool(flag.Value.String())
  }
})

if err != nil {
  return nil, err
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

多谢helin。Done。

}

// NewLsCmdFromFlag returen a new LsCmd.
func NewLsCmdFromFlag(f *flag.FlagSet) (*LsCmd, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Need test case, it's fine to have it in another PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK,我记录了。


return &LsResult{
Path: p.Path,
ModTime: fi.ModTime().Format("2006-01-02 15:04:05"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not use info.ModTime().UnixNano(), which is more precise. We only need string when print out the data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

)

type response struct {
Err string `json:"err"`
Copy link
Collaborator

@helinwang helinwang Jun 1, 2017

Choose a reason for hiding this comment

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

可以考虑:

type responseStatus struct {
  Err string `json:"msg"`
}

type response {
  responseStatus
  Results interface{} `json:"results"`
}

// ...

type chunkMetaResponse struct {
  responseStatus
  Results []pfsmod.ChunkMeta `json:"results"`
}

这样的好处是,可以确保所有的HTTP Response都有固定的status格式。方便client实现统一的parsing logic(client需要parse了status再决定下一步干什么,是报错还是继续执行)。

}

// Execute runs a MkdirCommand.
func (p *MkdirCommand) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the purpose for context.Context, is it for cancellation, if so, why it's not implemented?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个是subcommands package里边Command interface要求实现的。

func newPfsCmdSubmitter(configFile string) *pfsSubmitter {
// TODO
/*https
pair, e := tls.LoadX509KeyPair(config.ActiveConfig.Usercert,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please no commented out code. You can put the code on Evernote :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK.


log.V(1).Infof("%#v\n", config)

//http
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment does not provide any information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@Yancey1989
Copy link
Collaborator

Yancey1989 commented Jun 2, 2017

With this PR, the user should configure two kinds of authentication: basic auth means username && password and tls key files at the same time, It look so confusing.
Because of the cloud server has already use basic auth as the authentication mechanism, so the PFS can also use the it, the main steps is as following:

  1. call the cloud server auth API /api-token-auth/ with username and password, and get the token string.
  2. call the cmd API with the token string.

// need a custom error type?

const (
StatusFileNotFound = "no such file or directory"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am still not convinced that we need these string constants, and these string constant need to be visible outside of the package.
Can you take a look at : #112 (comment)

Copy link
Collaborator

Choose a reason for hiding this comment

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

After offline discussion with @gongweibao , it's used by the client to compare these string with the error message returned from the server over HTTP.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

多谢helin.

}

// ToURLParam encodes ChunkMetaCmd to URL encoding string.
func (p *ChunkMetaCmd) ToURLParam() url.Values {
Copy link
Collaborator

Choose a reason for hiding this comment

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

These code are repetitive, a good candidate for automatically generating them. See: https://blog.golang.org/generate

Not required, just want to let you know :)

Copy link
Collaborator Author

@gongweibao gongweibao Jun 7, 2017

Choose a reason for hiding this comment

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

棒。我正要模仿JSON的Marshal函数自己搞一个那。多谢helin。
后续改进它。

}

// DownloadFile downloads src to dst. If dst does not exists, create it with srcFileSize.
func DownloadFile(src string, srcFileSize int64, dst string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this necessary to be visible outside of the package?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

}

// Download function downloads src to dst.
func Download(src, dst string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this necessary to be visible outside of the package?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

helinwang
helinwang previously approved these changes Jun 7, 2017
Copy link
Collaborator

@helinwang helinwang left a comment

Choose a reason for hiding this comment

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

LGTM after the two function being visible outside the package comment.

Results interface{} `json:"results"`
}

func makeRequest(uri string, method string, body io.Reader,
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can call this function from package paddlecloud instead of copy again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

会引入很多不必要的用来,比如全局变量config
或者把restclient放到公共的地方去。

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个merge进去以后,下个版本继续改正。不然改动太大,容易出问题。

return req, nil
}

func getResponse(req *http.Request) ([]byte, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can call this function from package paddlecloud instead of copy again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

会引入很多不必要的用来,比如全局变量config。
或者把restclient放到公共的地方去

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个merge进去以后,下个版本继续改正。不然改动太大,容易出问题。

func getUserName(uri string, token string) (string, error) {
authHeader := make(map[string]string)
authHeader["Authorization"] = "Token " + token
req, err := makeRequest(uri, "GET", nil, "", nil, authHeader)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can call makeTokenRequest directly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

会引入很多不必要的用来,比如全局变量config。
或者把restclient放到公共的地方去

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个merge进去以后,下个版本继续改正。不然改动太大,容易出问题。

return "", err
}

log.V(4).Infoln("before getResponse")
Copy link
Collaborator

Choose a reason for hiding this comment

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

This debug log maybe not needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

func GetChunkHandler(w http.ResponseWriter, r *http.Request) {
log.V(1).Infof("begin proc GetChunkHandler")

cmd, err := pfsmod.ParseChunk(r.URL.RawQuery)
Copy link
Collaborator

Choose a reason for hiding this comment

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

ParseChunk returns a *Chunk type but the variable is cmd. Previous review I commented ChunkCmd is confusing, so we changed ChunkCmd to Chunk. But when using it we still treat it as a Cmd.

Don't know how to do.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

}

switch method {
case "rm":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why "rm", "touch", "mkdir", shares the same URL route and use the same handler?

In REST, resource removal methods should use DELETE HTTP method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

switch method {
case "rm":
rmHandler(w, body, r.Header)
case "touch":
Copy link
Collaborator

Choose a reason for hiding this comment

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

touchHandler can reuse PostChunkHandler to create an empty file?

Copy link
Collaborator Author

@gongweibao gongweibao Jun 7, 2017

Choose a reason for hiding this comment

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

语义不一样。一个是创建文件,一个是上传Chunk。
touch创建的是sized的文件。


// Command is a interface of all commands.
type Command interface {
ToURLParam() url.Values
Copy link
Collaborator

Choose a reason for hiding this comment

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

Better to add comments for each interface usages? like
// ToURLParam convert the current pfs command to a url.Values struct which will be used by pfs server and client.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


// CheckUser checks if a user has authority to access a path.
func checkUser(path string, user string) error {
// TODO
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this function still a TODO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,131 @@
package paddlecloud
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we merge content of these files(pfscp.go, pfsls.go, pfsmkdir.go, pfsrm.go) to filemanager/pfsmodules/*.go?

For example, in ls.go:

// LsCmd means LsCommand structure.
type LsCmd struct {
	Method string
	R      bool
	Args   []string
}
// for subcommand
func (*LsCmd) Name() string { return "ls" }
func (*LsCommand) Synopsis() string { return "List files on PaddlePaddle Cloud" }
...
// original command implementations
func (p *LsCmd) ToURLParam() url.Values {
...

In cmd/paddlecloud/paddlecloud.go:

subcommands.Register(&pfsmod.LsCmd{}, "")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

这个merge进去以后,下个版本继续改正。不然改动太大,容易出问题。

Copy link
Collaborator

@typhoonzero typhoonzero left a comment

Choose a reason for hiding this comment

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

LGTM

@gongweibao gongweibao merged commit f647640 into PaddlePaddle:develop Jun 7, 2017
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.

None yet

4 participants