mirror of
https://github.com/kemko/nomad.git
synced 2026-01-03 17:05:43 +03:00
Infer content type in alloc fs stat endpoint
This commit is contained in:
11
api/fs.go
11
api/fs.go
@@ -20,11 +20,12 @@ const (
|
||||
|
||||
// AllocFileInfo holds information about a file inside the AllocDir
|
||||
type AllocFileInfo struct {
|
||||
Name string
|
||||
IsDir bool
|
||||
Size int64
|
||||
FileMode string
|
||||
ModTime time.Time
|
||||
Name string
|
||||
IsDir bool
|
||||
Size int64
|
||||
FileMode string
|
||||
ModTime time.Time
|
||||
ContentType string
|
||||
}
|
||||
|
||||
// StreamFrame is used to frame data of a file when streaming
|
||||
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
cstructs "github.com/hashicorp/nomad/client/structs"
|
||||
@@ -392,15 +395,41 @@ func (d *AllocDir) Stat(path string) (*cstructs.AllocFileInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentType := detectContentType(info, p)
|
||||
|
||||
return &cstructs.AllocFileInfo{
|
||||
Size: info.Size(),
|
||||
Name: info.Name(),
|
||||
IsDir: info.IsDir(),
|
||||
FileMode: info.Mode().String(),
|
||||
ModTime: info.ModTime(),
|
||||
Size: info.Size(),
|
||||
Name: info.Name(),
|
||||
IsDir: info.IsDir(),
|
||||
FileMode: info.Mode().String(),
|
||||
ModTime: info.ModTime(),
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// detectContentType tries to infer the file type by reading the first
|
||||
// 512 bytes of the file. Json file extensions are special cased.
|
||||
func detectContentType(fileInfo os.FileInfo, path string) string {
|
||||
contentType := "unknown"
|
||||
if !fileInfo.IsDir() {
|
||||
f, err := os.Open(path)
|
||||
// Best effort content type detection
|
||||
// We ignore errors because this is optional information
|
||||
if err == nil {
|
||||
fileBytes := make([]byte, 512)
|
||||
_, err := f.Read(fileBytes)
|
||||
if err == nil {
|
||||
contentType = http.DetectContentType(fileBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Special case json files
|
||||
if strings.HasSuffix(path, ".json") {
|
||||
contentType = "application/json"
|
||||
}
|
||||
return contentType
|
||||
}
|
||||
|
||||
// ReadAt returns a reader for a file at the path relative to the alloc dir
|
||||
func (d *AllocDir) ReadAt(path string, offset int64) (io.ReadCloser, error) {
|
||||
if escapes, err := structs.PathEscapesAllocDir("", path); err != nil {
|
||||
|
||||
@@ -472,3 +472,18 @@ func TestPathFuncs(t *testing.T) {
|
||||
t.Errorf("%q is not empty. empty=%v error=%v", dir, empty, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocDir_DetectContentType(t *testing.T) {
|
||||
require := require.New(t)
|
||||
imgPath := "input/image.png"
|
||||
fileInfo, err := os.Stat(imgPath)
|
||||
require.Nil(err)
|
||||
res := detectContentType(fileInfo, imgPath)
|
||||
require.Equal("image/png", res)
|
||||
|
||||
jsonPath := "input/test.json"
|
||||
fileInfo, err = os.Stat(jsonPath)
|
||||
require.Nil(err)
|
||||
res = detectContentType(fileInfo, jsonPath)
|
||||
require.Equal("application/json", res)
|
||||
}
|
||||
|
||||
BIN
client/allocdir/input/image.png
Normal file
BIN
client/allocdir/input/image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
3
client/allocdir/input/test.json
Normal file
3
client/allocdir/input/test.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"test":"test"
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestFS_Stat(t *testing.T) {
|
||||
// Wait for alloc to be running
|
||||
alloc := testutil.WaitForRunning(t, s.RPC, job)[0]
|
||||
|
||||
// Make the request with bad allocation id
|
||||
// Make the request
|
||||
req := &cstructs.FsStatRequest{
|
||||
AllocID: alloc.ID,
|
||||
Path: "/",
|
||||
|
||||
@@ -36,11 +36,12 @@ type ClientStatsResponse struct {
|
||||
|
||||
// AllocFileInfo holds information about a file inside the AllocDir
|
||||
type AllocFileInfo struct {
|
||||
Name string
|
||||
IsDir bool
|
||||
Size int64
|
||||
FileMode string
|
||||
ModTime time.Time
|
||||
Name string
|
||||
IsDir bool
|
||||
Size int64
|
||||
FileMode string
|
||||
ModTime time.Time
|
||||
ContentType string `json:"contenttype,omitempty"`
|
||||
}
|
||||
|
||||
// FsListRequest is used to list an allocation's directory.
|
||||
|
||||
@@ -213,7 +213,7 @@ func (f *AllocFSCommand) Run(args []string) int {
|
||||
if stat {
|
||||
// Display the file information
|
||||
out := make([]string, 2)
|
||||
out[0] = "Mode|Size|Modified Time|Name"
|
||||
out[0] = "Mode|Size|Modified Time|Content Type|Name"
|
||||
if file != nil {
|
||||
fn := file.Name
|
||||
if file.IsDir {
|
||||
@@ -225,8 +225,8 @@ func (f *AllocFSCommand) Run(args []string) int {
|
||||
} else {
|
||||
size = humanize.IBytes(uint64(file.Size))
|
||||
}
|
||||
out[1] = fmt.Sprintf("%s|%s|%s|%s", file.FileMode, size,
|
||||
formatTime(file.ModTime), fn)
|
||||
out[1] = fmt.Sprintf("%s|%s|%s|%s|%s", file.FileMode, size,
|
||||
formatTime(file.ModTime), file.ContentType, fn)
|
||||
}
|
||||
f.Ui.Output(formatList(out))
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user