mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Replace go-bindata with vfsgen (#4430)
Looking at https://tech.townsourced.com/post/embedding-static-files-in-go/ (which was mentioned in the issue), vfsgen has all the needed features. In particular: - Reproducible builds (no issue with timestamping). - Well maintained and relatively popular. - Integration with go generate. - Self-contained (no external dependency). * [WIP] Replace go-bindata by vfsgen Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Add license + remove doc.go Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Generate templates assets Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Use new templates assets Signed-off-by: Simon Pasquier <spasquie@redhat.com> * split static assets Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Idempotent make assets Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Update vendor/ Signed-off-by: Simon Pasquier <spasquie@redhat.com> * vendor vfsgendev Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Update README.md Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Simplify assets generation Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Fix README.md Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Use generate helper program instead of vfsgen This avoids installing vfsgendev in the target environment. Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Remove unused vfsgen package Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Fix Makefile Signed-off-by: Simon Pasquier <spasquie@redhat.com> * vendoring shurcooL/vfsgen Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Fix go generate command Signed-off-by: Simon Pasquier <spasquie@redhat.com> * Sync web/ui/assets_vfsdata.go Signed-off-by: Simon Pasquier <spasquie@redhat.com>
This commit is contained in:
parent
ecf676cf97
commit
3581377e5d
8
Makefile
8
Makefile
|
@ -23,13 +23,7 @@ STATICCHECK_IGNORE = \
|
|||
|
||||
DOCKER_IMAGE_NAME ?= prometheus
|
||||
|
||||
ifdef DEBUG
|
||||
bindata_flags = -debug
|
||||
endif
|
||||
|
||||
.PHONY: assets
|
||||
assets:
|
||||
@echo ">> writing assets"
|
||||
@$(GO) get -u github.com/jteeuwen/go-bindata/...
|
||||
@go-bindata $(bindata_flags) -pkg ui -o web/ui/bindata.go -ignore '(.*\.map|bootstrap\.js|bootstrap-theme\.css|bootstrap\.css)' web/ui/templates/... web/ui/static/...
|
||||
@$(GO) fmt ./web/ui
|
||||
cd $(PREFIX)/web/ui && go generate
|
||||
|
|
133
vendor/github.com/shurcooL/httpfs/filter/filter.go
generated
vendored
Normal file
133
vendor/github.com/shurcooL/httpfs/filter/filter.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Package filter offers an http.FileSystem wrapper with the ability to keep or skip files.
|
||||
package filter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Func is a selection function which is provided two arguments,
|
||||
// its '/'-separated cleaned rooted absolute path (i.e., it always begins with "/"),
|
||||
// and the os.FileInfo of the considered file.
|
||||
//
|
||||
// The path is cleaned via pathpkg.Clean("/" + path).
|
||||
//
|
||||
// For example, if the considered file is named "a" and it's inside a directory "dir",
|
||||
// then the value of path will be "/dir/a".
|
||||
type Func func(path string, fi os.FileInfo) bool
|
||||
|
||||
// Keep returns a filesystem that contains only those entries in source for which
|
||||
// keep returns true.
|
||||
func Keep(source http.FileSystem, keep Func) http.FileSystem {
|
||||
return &filterFS{source: source, keep: keep}
|
||||
}
|
||||
|
||||
// Skip returns a filesystem that contains everything in source, except entries
|
||||
// for which skip returns true.
|
||||
func Skip(source http.FileSystem, skip Func) http.FileSystem {
|
||||
keep := func(path string, fi os.FileInfo) bool {
|
||||
return !skip(path, fi)
|
||||
}
|
||||
return &filterFS{source: source, keep: keep}
|
||||
}
|
||||
|
||||
type filterFS struct {
|
||||
source http.FileSystem
|
||||
keep Func // Keep entries that keep returns true for.
|
||||
}
|
||||
|
||||
func (fs *filterFS) Open(path string) (http.File, error) {
|
||||
f, err := fs.source.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !fs.keep(clean(path), fi) {
|
||||
// Skip.
|
||||
f.Close()
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
return f, nil
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fis, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entries []os.FileInfo
|
||||
for _, fi := range fis {
|
||||
if !fs.keep(clean(pathpkg.Join(path, fi.Name())), fi) {
|
||||
// Skip.
|
||||
continue
|
||||
}
|
||||
entries = append(entries, fi)
|
||||
}
|
||||
|
||||
return &dir{
|
||||
name: fi.Name(),
|
||||
entries: entries,
|
||||
modTime: fi.ModTime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// clean turns a potentially relative path into an absolute one.
|
||||
//
|
||||
// This is needed to normalize path parameter for selection function.
|
||||
func clean(path string) string {
|
||||
return pathpkg.Clean("/" + path)
|
||||
}
|
||||
|
||||
// dir is an opened dir instance.
|
||||
type dir struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
entries []os.FileInfo
|
||||
pos int // Position within entries for Seek and Readdir.
|
||||
}
|
||||
|
||||
func (d *dir) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("cannot Read from directory %s", d.name)
|
||||
}
|
||||
func (d *dir) Close() error { return nil }
|
||||
func (d *dir) Stat() (os.FileInfo, error) { return d, nil }
|
||||
|
||||
func (d *dir) Name() string { return d.name }
|
||||
func (d *dir) Size() int64 { return 0 }
|
||||
func (d *dir) Mode() os.FileMode { return 0755 | os.ModeDir }
|
||||
func (d *dir) ModTime() time.Time { return d.modTime }
|
||||
func (d *dir) IsDir() bool { return true }
|
||||
func (d *dir) Sys() interface{} { return nil }
|
||||
|
||||
func (d *dir) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset == 0 && whence == io.SeekStart {
|
||||
d.pos = 0
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
|
||||
}
|
||||
|
||||
func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
if d.pos >= len(d.entries) && count > 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if count <= 0 || count > len(d.entries)-d.pos {
|
||||
count = len(d.entries) - d.pos
|
||||
}
|
||||
e := d.entries[d.pos : d.pos+count]
|
||||
d.pos += count
|
||||
return e, nil
|
||||
}
|
26
vendor/github.com/shurcooL/httpfs/filter/filters.go
generated
vendored
Normal file
26
vendor/github.com/shurcooL/httpfs/filter/filters.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package filter
|
||||
|
||||
import (
|
||||
"os"
|
||||
pathpkg "path"
|
||||
)
|
||||
|
||||
// FilesWithExtensions returns a filter func that selects files (but not directories)
|
||||
// that have any of the given extensions. For example:
|
||||
//
|
||||
// filter.FilesWithExtensions(".go", ".html")
|
||||
//
|
||||
// Would select both .go and .html files. It would not select any directories.
|
||||
func FilesWithExtensions(exts ...string) Func {
|
||||
return func(path string, fi os.FileInfo) bool {
|
||||
if fi.IsDir() {
|
||||
return false
|
||||
}
|
||||
for _, ext := range exts {
|
||||
if pathpkg.Ext(path) == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
106
vendor/github.com/shurcooL/httpfs/union/union.go
generated
vendored
Normal file
106
vendor/github.com/shurcooL/httpfs/union/union.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Package union offers a simple http.FileSystem that can unify multiple filesystems at various mount points.
|
||||
package union
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// New creates an union filesystem with the provided mapping of mount points to filesystems.
|
||||
//
|
||||
// Each mount point must be of form "/mydir". It must start with a '/', and contain a single directory name.
|
||||
func New(mapping map[string]http.FileSystem) http.FileSystem {
|
||||
u := &unionFS{
|
||||
ns: make(map[string]http.FileSystem),
|
||||
root: &dirInfo{
|
||||
name: "/",
|
||||
},
|
||||
}
|
||||
for mountPoint, fs := range mapping {
|
||||
u.bind(mountPoint, fs)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
type unionFS struct {
|
||||
ns map[string]http.FileSystem // Key is mount point, e.g., "/mydir".
|
||||
root *dirInfo
|
||||
}
|
||||
|
||||
// bind mounts fs at mountPoint.
|
||||
// mountPoint must be of form "/mydir". It must start with a '/', and contain a single directory name.
|
||||
func (u *unionFS) bind(mountPoint string, fs http.FileSystem) {
|
||||
u.ns[mountPoint] = fs
|
||||
u.root.entries = append(u.root.entries, &dirInfo{
|
||||
name: mountPoint[1:],
|
||||
})
|
||||
}
|
||||
|
||||
// Open opens the named file.
|
||||
func (u *unionFS) Open(path string) (http.File, error) {
|
||||
// TODO: Maybe clean path?
|
||||
if path == "/" {
|
||||
return &dir{
|
||||
dirInfo: u.root,
|
||||
}, nil
|
||||
}
|
||||
for prefix, fs := range u.ns {
|
||||
if path == prefix || strings.HasPrefix(path, prefix+"/") {
|
||||
innerPath := path[len(prefix):]
|
||||
if innerPath == "" {
|
||||
innerPath = "/"
|
||||
}
|
||||
return fs.Open(innerPath)
|
||||
}
|
||||
}
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
// dirInfo is a static definition of a directory.
|
||||
type dirInfo struct {
|
||||
name string
|
||||
entries []os.FileInfo
|
||||
}
|
||||
|
||||
func (d *dirInfo) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("cannot Read from directory %s", d.name)
|
||||
}
|
||||
func (d *dirInfo) Close() error { return nil }
|
||||
func (d *dirInfo) Stat() (os.FileInfo, error) { return d, nil }
|
||||
|
||||
func (d *dirInfo) Name() string { return d.name }
|
||||
func (d *dirInfo) Size() int64 { return 0 }
|
||||
func (d *dirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
|
||||
func (d *dirInfo) ModTime() time.Time { return time.Time{} } // Actual mod time is not computed because it's expensive and rarely needed.
|
||||
func (d *dirInfo) IsDir() bool { return true }
|
||||
func (d *dirInfo) Sys() interface{} { return nil }
|
||||
|
||||
// dir is an opened dir instance.
|
||||
type dir struct {
|
||||
*dirInfo
|
||||
pos int // Position within entries for Seek and Readdir.
|
||||
}
|
||||
|
||||
func (d *dir) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset == 0 && whence == io.SeekStart {
|
||||
d.pos = 0
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported Seek in directory %s", d.dirInfo.name)
|
||||
}
|
||||
|
||||
func (d *dir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
if d.pos >= len(d.dirInfo.entries) && count > 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if count <= 0 || count > len(d.dirInfo.entries)-d.pos {
|
||||
count = len(d.dirInfo.entries) - d.pos
|
||||
}
|
||||
e := d.dirInfo.entries[d.pos : d.pos+count]
|
||||
d.pos += count
|
||||
return e, nil
|
||||
}
|
21
vendor/github.com/shurcooL/httpfs/vfsutil/file.go
generated
vendored
Normal file
21
vendor/github.com/shurcooL/httpfs/vfsutil/file.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package vfsutil
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
// File implements http.FileSystem using the native file system restricted to a
|
||||
// specific file served at root.
|
||||
//
|
||||
// While the FileSystem.Open method takes '/'-separated paths, a File's string
|
||||
// value is a filename on the native file system, not a URL, so it is separated
|
||||
// by filepath.Separator, which isn't necessarily '/'.
|
||||
type File string
|
||||
|
||||
func (f File) Open(name string) (http.File, error) {
|
||||
if name != "/" {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
|
||||
}
|
||||
return os.Open(string(f))
|
||||
}
|
39
vendor/github.com/shurcooL/httpfs/vfsutil/vfsutil.go
generated
vendored
Normal file
39
vendor/github.com/shurcooL/httpfs/vfsutil/vfsutil.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Package vfsutil implements some I/O utility functions for http.FileSystem.
|
||||
package vfsutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadDir reads the contents of the directory associated with file and
|
||||
// returns a slice of FileInfo values in directory order.
|
||||
func ReadDir(fs http.FileSystem, name string) ([]os.FileInfo, error) {
|
||||
f, err := fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Readdir(0)
|
||||
}
|
||||
|
||||
// Stat returns the FileInfo structure describing file.
|
||||
func Stat(fs http.FileSystem, name string) (os.FileInfo, error) {
|
||||
f, err := fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Stat()
|
||||
}
|
||||
|
||||
// ReadFile reads the file named by path from fs and returns the contents.
|
||||
func ReadFile(fs http.FileSystem, path string) ([]byte, error) {
|
||||
rc, err := fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
return ioutil.ReadAll(rc)
|
||||
}
|
146
vendor/github.com/shurcooL/httpfs/vfsutil/walk.go
generated
vendored
Normal file
146
vendor/github.com/shurcooL/httpfs/vfsutil/walk.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
package vfsutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Walk walks the filesystem rooted at root, calling walkFn for each file or
|
||||
// directory in the filesystem, including root. All errors that arise visiting files
|
||||
// and directories are filtered by walkFn. The files are walked in lexical
|
||||
// order.
|
||||
func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
|
||||
info, err := Stat(fs, root)
|
||||
if err != nil {
|
||||
return walkFn(root, nil, err)
|
||||
}
|
||||
return walk(fs, root, info, walkFn)
|
||||
}
|
||||
|
||||
// readDirNames reads the directory named by dirname and returns
|
||||
// a sorted list of directory entries.
|
||||
func readDirNames(fs http.FileSystem, dirname string) ([]string, error) {
|
||||
fis, err := ReadDir(fs, dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, len(fis))
|
||||
for i := range fis {
|
||||
names[i] = fis[i].Name()
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// walk recursively descends path, calling walkFn.
|
||||
func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
||||
err := walkFn(path, info, nil)
|
||||
if err != nil {
|
||||
if info.IsDir() && err == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
names, err := readDirNames(fs, path)
|
||||
if err != nil {
|
||||
return walkFn(path, info, err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
filename := pathpkg.Join(path, name)
|
||||
fileInfo, err := Stat(fs, filename)
|
||||
if err != nil {
|
||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = walk(fs, filename, fileInfo, walkFn)
|
||||
if err != nil {
|
||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WalkFilesFunc is the type of the function called for each file or directory visited by WalkFiles.
|
||||
// It's like filepath.WalkFunc, except it provides an additional ReadSeeker parameter for file being visited.
|
||||
type WalkFilesFunc func(path string, info os.FileInfo, rs io.ReadSeeker, err error) error
|
||||
|
||||
// WalkFiles walks the filesystem rooted at root, calling walkFn for each file or
|
||||
// directory in the filesystem, including root. In addition to FileInfo, it passes an
|
||||
// ReadSeeker to walkFn for each file it visits.
|
||||
func WalkFiles(fs http.FileSystem, root string, walkFn WalkFilesFunc) error {
|
||||
file, info, err := openStat(fs, root)
|
||||
if err != nil {
|
||||
return walkFn(root, nil, nil, err)
|
||||
}
|
||||
return walkFiles(fs, root, info, file, walkFn)
|
||||
}
|
||||
|
||||
// walkFiles recursively descends path, calling walkFn.
|
||||
// It closes the input file after it's done with it, so the caller shouldn't.
|
||||
func walkFiles(fs http.FileSystem, path string, info os.FileInfo, file http.File, walkFn WalkFilesFunc) error {
|
||||
err := walkFn(path, info, file, nil)
|
||||
file.Close()
|
||||
if err != nil {
|
||||
if info.IsDir() && err == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
names, err := readDirNames(fs, path)
|
||||
if err != nil {
|
||||
return walkFn(path, info, nil, err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
filename := pathpkg.Join(path, name)
|
||||
file, fileInfo, err := openStat(fs, filename)
|
||||
if err != nil {
|
||||
if err := walkFn(filename, nil, nil, err); err != nil && err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = walkFiles(fs, filename, fileInfo, file, walkFn)
|
||||
// file is closed by walkFiles, so we don't need to close it here.
|
||||
if err != nil {
|
||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// openStat performs Open and Stat and returns results, or first error encountered.
|
||||
// The caller is responsible for closing the returned file when done.
|
||||
func openStat(fs http.FileSystem, name string) (http.File, os.FileInfo, error) {
|
||||
f, err := fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return f, fi, nil
|
||||
}
|
10
vendor/github.com/shurcooL/vfsgen/CONTRIBUTING.md
generated
vendored
Normal file
10
vendor/github.com/shurcooL/vfsgen/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
Contributing
|
||||
============
|
||||
|
||||
vfsgen is open source, thanks for considering contributing!
|
||||
|
||||
Please note that vfsgen aims to be simple and minimalistic, with as little to configure as possible. If you'd like to remove or simplify code (while having tests continue to pass), fix bugs, or improve code (e.g., add missing error checking, etc.), PRs and issues are welcome.
|
||||
|
||||
However, if you'd like to add new functionality that increases complexity or scope, please make an issue and discuss your proposal first. I'm unlikely to accept such changes outright. It might be that your request is already a part of other similar packages, or it might fit in their scope better. See [Comparison and Alternatives](https://github.com/shurcooL/vfsgen/tree/README-alternatives-and-comparison-section#comparison) sections.
|
||||
|
||||
Thank you!
|
178
vendor/github.com/shurcooL/vfsgen/README.md
generated
vendored
Normal file
178
vendor/github.com/shurcooL/vfsgen/README.md
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
vfsgen
|
||||
======
|
||||
|
||||
[![Build Status](https://travis-ci.org/shurcooL/vfsgen.svg?branch=master)](https://travis-ci.org/shurcooL/vfsgen) [![GoDoc](https://godoc.org/github.com/shurcooL/vfsgen?status.svg)](https://godoc.org/github.com/shurcooL/vfsgen)
|
||||
|
||||
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and
|
||||
generates Go code that statically implements the provided http.FileSystem.
|
||||
|
||||
Features:
|
||||
|
||||
- Efficient generated code without unneccessary overhead.
|
||||
|
||||
- Uses gzip compression internally (selectively, only for files that compress well).
|
||||
|
||||
- Enables direct access to internal gzip compressed bytes via an optional interface.
|
||||
|
||||
- Outputs `gofmt`ed Go code.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
go get -u github.com/shurcooL/vfsgen
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
This code will generate an assets_vfsdata.go file with `var assets http.FileSystem = ...` that statically implements the contents of "assets" directory.
|
||||
|
||||
```Go
|
||||
var fs http.FileSystem = http.Dir("assets")
|
||||
|
||||
err := vfsgen.Generate(fs, vfsgen.Options{})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
```
|
||||
|
||||
Then, in your program, you can use `assets` as any other [`http.FileSystem`](https://godoc.org/net/http#FileSystem), for example:
|
||||
|
||||
```Go
|
||||
file, err := assets.Open("/some/file.txt")
|
||||
if err != nil { ... }
|
||||
defer file.Close()
|
||||
```
|
||||
|
||||
```Go
|
||||
http.Handle("/assets/", http.FileServer(assets))
|
||||
```
|
||||
|
||||
### `go generate` Usage
|
||||
|
||||
vfsgen is great to use with go generate directives. The code invoking `vfsgen.Generate` can go in an assets_generate.go file, which can then be invoked via "//go:generate go run assets_generate.go". The input virtual filesystem can read directly from disk, or it can be more involved.
|
||||
|
||||
By using build tags, you can create a development mode where assets are loaded directly from disk via `http.Dir`, but then statically implemented for final releases.
|
||||
|
||||
For example, suppose your source filesystem is defined in a package with import path "example.com/project/data" as:
|
||||
|
||||
```Go
|
||||
// +build dev
|
||||
|
||||
package data
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Assets contains project assets.
|
||||
var Assets http.FileSystem = http.Dir("assets")
|
||||
```
|
||||
|
||||
When built with the "dev" build tag, accessing `data.Assets` will read from disk directly via `http.Dir`.
|
||||
|
||||
A generate helper file assets_generate.go can be invoked via "//go:generate go run -tags=dev assets_generate.go" directive:
|
||||
|
||||
```Go
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"example.com/project/data"
|
||||
"github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := vfsgen.Generate(data.Assets, vfsgen.Options{
|
||||
PackageName: "data",
|
||||
BuildTags: "!dev",
|
||||
VariableName: "Assets",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that "dev" build tag is used to access the source filesystem, and the output file will contain "!dev" build tag. That way, the statically implemented version will be used during normal builds and `go get`, when custom builds tags are not specified.
|
||||
|
||||
### `vfsgendev` Usage
|
||||
|
||||
`vfsgendev` is a binary that can be used to replace the need for the assets_generate.go file.
|
||||
|
||||
Make sure it's installed and available in your PATH.
|
||||
|
||||
```bash
|
||||
go get -u github.com/shurcooL/vfsgen/cmd/vfsgendev
|
||||
```
|
||||
|
||||
Then the "//go:generate go run -tags=dev assets_generate.go" directive can be replaced with:
|
||||
|
||||
```
|
||||
//go:generate vfsgendev -source="example.com/project/data".Assets
|
||||
```
|
||||
|
||||
vfsgendev accesses the source variable using "dev" build tag, and generates an output file with "!dev" build tag.
|
||||
|
||||
### Additional Embedded Information
|
||||
|
||||
All compressed files implement [`httpgzip.GzipByter` interface](https://godoc.org/github.com/shurcooL/httpgzip#GzipByter) for efficient direct access to the internal compressed bytes:
|
||||
|
||||
```Go
|
||||
// GzipByter is implemented by compressed files for
|
||||
// efficient direct access to the internal compressed bytes.
|
||||
type GzipByter interface {
|
||||
// GzipBytes returns gzip compressed contents of the file.
|
||||
GzipBytes() []byte
|
||||
}
|
||||
```
|
||||
|
||||
Files that have been determined to not be worth gzip compressing (their compressed size is larger than original) implement [`httpgzip.NotWorthGzipCompressing` interface](https://godoc.org/github.com/shurcooL/httpgzip#NotWorthGzipCompressing):
|
||||
|
||||
```Go
|
||||
// NotWorthGzipCompressing is implemented by files that were determined
|
||||
// not to be worth gzip compressing (the file size did not decrease as a result).
|
||||
type NotWorthGzipCompressing interface {
|
||||
// NotWorthGzipCompressing is a noop. It's implemented in order to indicate
|
||||
// the file is not worth gzip compressing.
|
||||
NotWorthGzipCompressing()
|
||||
}
|
||||
```
|
||||
|
||||
Comparison
|
||||
----------
|
||||
|
||||
vfsgen aims to be conceptually simple to use. The [`http.FileSystem`](https://godoc.org/net/http#FileSystem) abstraction is central to vfsgen. It's used as both input for code generation, and as output in the generated code.
|
||||
|
||||
That enables great flexibility through orthogonality, since helpers and wrappers can operate on `http.FileSystem` without knowing about vfsgen. If you want, you can perform pre-processing, minifying assets, merging folders, filtering out files and otherwise modifying input via generic `http.FileSystem` middleware.
|
||||
|
||||
It avoids unneccessary overhead by merging what was previously done with two distinct packages into a single package.
|
||||
|
||||
It strives to be the best in its class in terms of code quality and efficiency of generated code. However, if your use goals are different, there are other similar packages that may fit your needs better.
|
||||
|
||||
### Alternatives
|
||||
|
||||
- [`go-bindata`](https://github.com/jteeuwen/go-bindata) - Reads from disk, generates Go code that provides access to data via a [custom API](https://github.com/jteeuwen/go-bindata#accessing-an-asset).
|
||||
- [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs) - Takes output of go-bindata and provides a wrapper that implements `http.FileSystem` interface (the same as what vfsgen outputs directly).
|
||||
- [`becky`](https://github.com/tv42/becky) - Embeds assets as string literals in Go source.
|
||||
- [`statik`](https://github.com/rakyll/statik) - Embeds a directory of static files to be accessed via `http.FileSystem` interface (sounds very similar to vfsgen); implementation sourced from [camlistore](https://camlistore.org).
|
||||
- [`go.rice`](https://github.com/GeertJohan/go.rice) - Makes working with resources such as HTML, JS, CSS, images and templates very easy.
|
||||
- [`esc`](https://github.com/mjibson/esc) - Embeds files into Go programs and provides `http.FileSystem` interfaces to them.
|
||||
- [`staticfiles`](https://github.com/bouk/staticfiles) - Allows you to embed a directory of files into your Go binary.
|
||||
- [`togo`](https://github.com/flazz/togo) - Generates a Go source file with a `[]byte` var containing the given file's contents.
|
||||
- [`fileb0x`](https://github.com/UnnoTed/fileb0x) - Simple customizable tool to embed files in Go.
|
||||
- [`embedfiles`](https://github.com/leighmcculloch/embedfiles) - Simple tool for embedding files in Go code as a map.
|
||||
- [`packr`](https://github.com/gobuffalo/packr) - Simple solution for bundling static assets inside of Go binaries.
|
||||
- [`rsrc`](https://github.com/akavel/rsrc) - Tool for embedding .ico & manifest resources in Go programs for Windows.
|
||||
|
||||
Attribution
|
||||
-----------
|
||||
|
||||
This package was originally based on the excellent work by [@jteeuwen](https://github.com/jteeuwen) on [`go-bindata`](https://github.com/jteeuwen/go-bindata) and [@elazarl](https://github.com/elazarl) on [`go-bindata-assetfs`](https://github.com/elazarl/go-bindata-assetfs).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
- [MIT License](https://opensource.org/licenses/mit-license.php)
|
45
vendor/github.com/shurcooL/vfsgen/commentwriter.go
generated
vendored
Normal file
45
vendor/github.com/shurcooL/vfsgen/commentwriter.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package vfsgen
|
||||
|
||||
import "io"
|
||||
|
||||
// commentWriter writes a Go comment to the underlying io.Writer,
|
||||
// using line comment form (//).
|
||||
type commentWriter struct {
|
||||
W io.Writer
|
||||
wroteSlashes bool // Wrote "//" at the beginning of the current line.
|
||||
}
|
||||
|
||||
func (c *commentWriter) Write(p []byte) (int, error) {
|
||||
var n int
|
||||
for i, b := range p {
|
||||
if !c.wroteSlashes {
|
||||
s := "//"
|
||||
if b != '\n' {
|
||||
s = "// "
|
||||
}
|
||||
if _, err := io.WriteString(c.W, s); err != nil {
|
||||
return n, err
|
||||
}
|
||||
c.wroteSlashes = true
|
||||
}
|
||||
n0, err := c.W.Write(p[i : i+1])
|
||||
n += n0
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if b == '\n' {
|
||||
c.wroteSlashes = false
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *commentWriter) Close() error {
|
||||
if !c.wroteSlashes {
|
||||
if _, err := io.WriteString(c.W, "//"); err != nil {
|
||||
return err
|
||||
}
|
||||
c.wroteSlashes = true
|
||||
}
|
||||
return nil
|
||||
}
|
15
vendor/github.com/shurcooL/vfsgen/doc.go
generated
vendored
Normal file
15
vendor/github.com/shurcooL/vfsgen/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
Package vfsgen takes an http.FileSystem (likely at `go generate` time) and
|
||||
generates Go code that statically implements the provided http.FileSystem.
|
||||
|
||||
Features:
|
||||
|
||||
- Efficient generated code without unneccessary overhead.
|
||||
|
||||
- Uses gzip compression internally (selectively, only for files that compress well).
|
||||
|
||||
- Enables direct access to internal gzip compressed bytes via an optional interface.
|
||||
|
||||
- Outputs `gofmt`ed Go code.
|
||||
*/
|
||||
package vfsgen
|
486
vendor/github.com/shurcooL/vfsgen/generator.go
generated
vendored
Normal file
486
vendor/github.com/shurcooL/vfsgen/generator.go
generated
vendored
Normal file
|
@ -0,0 +1,486 @@
|
|||
package vfsgen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/shurcooL/httpfs/vfsutil"
|
||||
)
|
||||
|
||||
// Generate Go code that statically implements input filesystem,
|
||||
// write the output to a file specified in opt.
|
||||
func Generate(input http.FileSystem, opt Options) error {
|
||||
opt.fillMissing()
|
||||
|
||||
// Use an in-memory buffer to generate the entire output.
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
err := t.ExecuteTemplate(buf, "Header", opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var toc toc
|
||||
err = findAndWriteFiles(buf, input, &toc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.ExecuteTemplate(buf, "DirEntries", toc.dirs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.ExecuteTemplate(buf, "Trailer", toc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write output file (all at once).
|
||||
fmt.Println("writing", opt.Filename)
|
||||
err = ioutil.WriteFile(opt.Filename, buf.Bytes(), 0644)
|
||||
return err
|
||||
}
|
||||
|
||||
type toc struct {
|
||||
dirs []*dirInfo
|
||||
|
||||
HasCompressedFile bool // There's at least one compressedFile.
|
||||
HasFile bool // There's at least one uncompressed file.
|
||||
}
|
||||
|
||||
// fileInfo is a definition of a file.
|
||||
type fileInfo struct {
|
||||
Path string
|
||||
Name string
|
||||
ModTime time.Time
|
||||
UncompressedSize int64
|
||||
}
|
||||
|
||||
// dirInfo is a definition of a directory.
|
||||
type dirInfo struct {
|
||||
Path string
|
||||
Name string
|
||||
ModTime time.Time
|
||||
Entries []string
|
||||
}
|
||||
|
||||
// findAndWriteFiles recursively finds all the file paths in the given directory tree.
|
||||
// They are added to the given map as keys. Values will be safe function names
|
||||
// for each file, which will be used when generating the output code.
|
||||
func findAndWriteFiles(buf *bytes.Buffer, fs http.FileSystem, toc *toc) error {
|
||||
walkFn := func(path string, fi os.FileInfo, r io.ReadSeeker, err error) error {
|
||||
if err != nil {
|
||||
log.Printf("can't stat file %q: %v\n", path, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch fi.IsDir() {
|
||||
case false:
|
||||
file := &fileInfo{
|
||||
Path: path,
|
||||
Name: pathpkg.Base(path),
|
||||
ModTime: fi.ModTime().UTC(),
|
||||
UncompressedSize: fi.Size(),
|
||||
}
|
||||
|
||||
marker := buf.Len()
|
||||
|
||||
// Write CompressedFileInfo.
|
||||
err = writeCompressedFileInfo(buf, file, r)
|
||||
switch err {
|
||||
default:
|
||||
return err
|
||||
case nil:
|
||||
toc.HasCompressedFile = true
|
||||
// If compressed file is not smaller than original, revert and write original file.
|
||||
case errCompressedNotSmaller:
|
||||
_, err = r.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf.Truncate(marker)
|
||||
|
||||
// Write FileInfo.
|
||||
err = writeFileInfo(buf, file, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toc.HasFile = true
|
||||
}
|
||||
case true:
|
||||
entries, err := readDirPaths(fs, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir := &dirInfo{
|
||||
Path: path,
|
||||
Name: pathpkg.Base(path),
|
||||
ModTime: fi.ModTime().UTC(),
|
||||
Entries: entries,
|
||||
}
|
||||
|
||||
toc.dirs = append(toc.dirs, dir)
|
||||
|
||||
// Write DirInfo.
|
||||
err = t.ExecuteTemplate(buf, "DirInfo", dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := vfsutil.WalkFiles(fs, "/", walkFn)
|
||||
return err
|
||||
}
|
||||
|
||||
// readDirPaths reads the directory named by dirname and returns
|
||||
// a sorted list of directory paths.
|
||||
func readDirPaths(fs http.FileSystem, dirname string) ([]string, error) {
|
||||
fis, err := vfsutil.ReadDir(fs, dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths := make([]string, len(fis))
|
||||
for i := range fis {
|
||||
paths[i] = pathpkg.Join(dirname, fis[i].Name())
|
||||
}
|
||||
sort.Strings(paths)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// writeCompressedFileInfo writes CompressedFileInfo.
|
||||
// It returns errCompressedNotSmaller if compressed file is not smaller than original.
|
||||
func writeCompressedFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
|
||||
err := t.ExecuteTemplate(w, "CompressedFileInfo-Before", file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sw := &stringWriter{Writer: w}
|
||||
gw := gzip.NewWriter(sw)
|
||||
_, err = io.Copy(gw, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gw.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sw.N >= file.UncompressedSize {
|
||||
return errCompressedNotSmaller
|
||||
}
|
||||
err = t.ExecuteTemplate(w, "CompressedFileInfo-After", file)
|
||||
return err
|
||||
}
|
||||
|
||||
var errCompressedNotSmaller = errors.New("compressed file is not smaller than original")
|
||||
|
||||
// Write FileInfo.
|
||||
func writeFileInfo(w io.Writer, file *fileInfo, r io.Reader) error {
|
||||
err := t.ExecuteTemplate(w, "FileInfo-Before", file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sw := &stringWriter{Writer: w}
|
||||
_, err = io.Copy(sw, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = t.ExecuteTemplate(w, "FileInfo-After", file)
|
||||
return err
|
||||
}
|
||||
|
||||
var t = template.Must(template.New("").Funcs(template.FuncMap{
|
||||
"quote": strconv.Quote,
|
||||
"comment": func(s string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
cw := &commentWriter{W: &buf}
|
||||
_, err := io.WriteString(cw, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = cw.Close()
|
||||
return buf.String(), err
|
||||
},
|
||||
}).Parse(`{{define "Header"}}// Code generated by vfsgen; DO NOT EDIT.
|
||||
|
||||
{{with .BuildTags}}// +build {{.}}
|
||||
|
||||
{{end}}package {{.PackageName}}
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
pathpkg "path"
|
||||
"time"
|
||||
)
|
||||
|
||||
{{comment .VariableComment}}
|
||||
var {{.VariableName}} = func() http.FileSystem {
|
||||
fs := vfsgen۰FS{
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "CompressedFileInfo-Before"}} {{quote .Path}}: &vfsgen۰CompressedFileInfo{
|
||||
name: {{quote .Name}},
|
||||
modTime: {{template "Time" .ModTime}},
|
||||
uncompressedSize: {{.UncompressedSize}},
|
||||
{{/* This blank line separating compressedContent is neccessary to prevent potential gofmt issues. See issue #19. */}}
|
||||
compressedContent: []byte("{{end}}{{define "CompressedFileInfo-After"}}"),
|
||||
},
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "FileInfo-Before"}} {{quote .Path}}: &vfsgen۰FileInfo{
|
||||
name: {{quote .Name}},
|
||||
modTime: {{template "Time" .ModTime}},
|
||||
content: []byte("{{end}}{{define "FileInfo-After"}}"),
|
||||
},
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "DirInfo"}} {{quote .Path}}: &vfsgen۰DirInfo{
|
||||
name: {{quote .Name}},
|
||||
modTime: {{template "Time" .ModTime}},
|
||||
},
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "DirEntries"}} }
|
||||
{{range .}}{{if .Entries}} fs[{{quote .Path}}].(*vfsgen۰DirInfo).entries = []os.FileInfo{{"{"}}{{range .Entries}}
|
||||
fs[{{quote .}}].(os.FileInfo),{{end}}
|
||||
}
|
||||
{{end}}{{end}}
|
||||
return fs
|
||||
}()
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "Trailer"}}
|
||||
type vfsgen۰FS map[string]interface{}
|
||||
|
||||
func (fs vfsgen۰FS) Open(path string) (http.File, error) {
|
||||
path = pathpkg.Clean("/" + path)
|
||||
f, ok := fs[path]
|
||||
if !ok {
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
switch f := f.(type) {{"{"}}{{if .HasCompressedFile}}
|
||||
case *vfsgen۰CompressedFileInfo:
|
||||
gr, err := gzip.NewReader(bytes.NewReader(f.compressedContent))
|
||||
if err != nil {
|
||||
// This should never happen because we generate the gzip bytes such that they are always valid.
|
||||
panic("unexpected error reading own gzip compressed bytes: " + err.Error())
|
||||
}
|
||||
return &vfsgen۰CompressedFile{
|
||||
vfsgen۰CompressedFileInfo: f,
|
||||
gr: gr,
|
||||
}, nil{{end}}{{if .HasFile}}
|
||||
case *vfsgen۰FileInfo:
|
||||
return &vfsgen۰File{
|
||||
vfsgen۰FileInfo: f,
|
||||
Reader: bytes.NewReader(f.content),
|
||||
}, nil{{end}}
|
||||
case *vfsgen۰DirInfo:
|
||||
return &vfsgen۰Dir{
|
||||
vfsgen۰DirInfo: f,
|
||||
}, nil
|
||||
default:
|
||||
// This should never happen because we generate only the above types.
|
||||
panic(fmt.Sprintf("unexpected type %T", f))
|
||||
}
|
||||
}
|
||||
{{if .HasCompressedFile}}
|
||||
// vfsgen۰CompressedFileInfo is a static definition of a gzip compressed file.
|
||||
type vfsgen۰CompressedFileInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
compressedContent []byte
|
||||
uncompressedSize int64
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
|
||||
}
|
||||
func (f *vfsgen۰CompressedFileInfo) Stat() (os.FileInfo, error) { return f, nil }
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) GzipBytes() []byte {
|
||||
return f.compressedContent
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFileInfo) Name() string { return f.name }
|
||||
func (f *vfsgen۰CompressedFileInfo) Size() int64 { return f.uncompressedSize }
|
||||
func (f *vfsgen۰CompressedFileInfo) Mode() os.FileMode { return 0444 }
|
||||
func (f *vfsgen۰CompressedFileInfo) ModTime() time.Time { return f.modTime }
|
||||
func (f *vfsgen۰CompressedFileInfo) IsDir() bool { return false }
|
||||
func (f *vfsgen۰CompressedFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// vfsgen۰CompressedFile is an opened compressedFile instance.
|
||||
type vfsgen۰CompressedFile struct {
|
||||
*vfsgen۰CompressedFileInfo
|
||||
gr *gzip.Reader
|
||||
grPos int64 // Actual gr uncompressed position.
|
||||
seekPos int64 // Seek uncompressed position.
|
||||
}
|
||||
|
||||
func (f *vfsgen۰CompressedFile) Read(p []byte) (n int, err error) {
|
||||
if f.grPos > f.seekPos {
|
||||
// Rewind to beginning.
|
||||
err = f.gr.Reset(bytes.NewReader(f.compressedContent))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.grPos = 0
|
||||
}
|
||||
if f.grPos < f.seekPos {
|
||||
// Fast-forward.
|
||||
_, err = io.CopyN(ioutil.Discard, f.gr, f.seekPos-f.grPos)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.grPos = f.seekPos
|
||||
}
|
||||
n, err = f.gr.Read(p)
|
||||
f.grPos += int64(n)
|
||||
f.seekPos = f.grPos
|
||||
return n, err
|
||||
}
|
||||
func (f *vfsgen۰CompressedFile) Seek(offset int64, whence int) (int64, error) {
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
f.seekPos = 0 + offset
|
||||
case io.SeekCurrent:
|
||||
f.seekPos += offset
|
||||
case io.SeekEnd:
|
||||
f.seekPos = f.uncompressedSize + offset
|
||||
default:
|
||||
panic(fmt.Errorf("invalid whence value: %v", whence))
|
||||
}
|
||||
return f.seekPos, nil
|
||||
}
|
||||
func (f *vfsgen۰CompressedFile) Close() error {
|
||||
return f.gr.Close()
|
||||
}
|
||||
{{else}}
|
||||
// We already imported "compress/gzip" and "io/ioutil", but ended up not using them. Avoid unused import error.
|
||||
var _ = gzip.Reader{}
|
||||
var _ = ioutil.Discard
|
||||
{{end}}{{if .HasFile}}
|
||||
// vfsgen۰FileInfo is a static definition of an uncompressed file (because it's not worth gzip compressing).
|
||||
type vfsgen۰FileInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (f *vfsgen۰FileInfo) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, fmt.Errorf("cannot Readdir from file %s", f.name)
|
||||
}
|
||||
func (f *vfsgen۰FileInfo) Stat() (os.FileInfo, error) { return f, nil }
|
||||
|
||||
func (f *vfsgen۰FileInfo) NotWorthGzipCompressing() {}
|
||||
|
||||
func (f *vfsgen۰FileInfo) Name() string { return f.name }
|
||||
func (f *vfsgen۰FileInfo) Size() int64 { return int64(len(f.content)) }
|
||||
func (f *vfsgen۰FileInfo) Mode() os.FileMode { return 0444 }
|
||||
func (f *vfsgen۰FileInfo) ModTime() time.Time { return f.modTime }
|
||||
func (f *vfsgen۰FileInfo) IsDir() bool { return false }
|
||||
func (f *vfsgen۰FileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// vfsgen۰File is an opened file instance.
|
||||
type vfsgen۰File struct {
|
||||
*vfsgen۰FileInfo
|
||||
*bytes.Reader
|
||||
}
|
||||
|
||||
func (f *vfsgen۰File) Close() error {
|
||||
return nil
|
||||
}
|
||||
{{else if not .HasCompressedFile}}
|
||||
// We already imported "bytes", but ended up not using it. Avoid unused import error.
|
||||
var _ = bytes.Reader{}
|
||||
{{end}}
|
||||
// vfsgen۰DirInfo is a static definition of a directory.
|
||||
type vfsgen۰DirInfo struct {
|
||||
name string
|
||||
modTime time.Time
|
||||
entries []os.FileInfo
|
||||
}
|
||||
|
||||
func (d *vfsgen۰DirInfo) Read([]byte) (int, error) {
|
||||
return 0, fmt.Errorf("cannot Read from directory %s", d.name)
|
||||
}
|
||||
func (d *vfsgen۰DirInfo) Close() error { return nil }
|
||||
func (d *vfsgen۰DirInfo) Stat() (os.FileInfo, error) { return d, nil }
|
||||
|
||||
func (d *vfsgen۰DirInfo) Name() string { return d.name }
|
||||
func (d *vfsgen۰DirInfo) Size() int64 { return 0 }
|
||||
func (d *vfsgen۰DirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
|
||||
func (d *vfsgen۰DirInfo) ModTime() time.Time { return d.modTime }
|
||||
func (d *vfsgen۰DirInfo) IsDir() bool { return true }
|
||||
func (d *vfsgen۰DirInfo) Sys() interface{} { return nil }
|
||||
|
||||
// vfsgen۰Dir is an opened dir instance.
|
||||
type vfsgen۰Dir struct {
|
||||
*vfsgen۰DirInfo
|
||||
pos int // Position within entries for Seek and Readdir.
|
||||
}
|
||||
|
||||
func (d *vfsgen۰Dir) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset == 0 && whence == io.SeekStart {
|
||||
d.pos = 0
|
||||
return 0, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unsupported Seek in directory %s", d.name)
|
||||
}
|
||||
|
||||
func (d *vfsgen۰Dir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
if d.pos >= len(d.entries) && count > 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if count <= 0 || count > len(d.entries)-d.pos {
|
||||
count = len(d.entries) - d.pos
|
||||
}
|
||||
e := d.entries[d.pos : d.pos+count]
|
||||
d.pos += count
|
||||
return e, nil
|
||||
}
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
{{define "Time"}}
|
||||
{{- if .IsZero -}}
|
||||
time.Time{}
|
||||
{{- else -}}
|
||||
time.Date({{.Year}}, {{printf "%d" .Month}}, {{.Day}}, {{.Hour}}, {{.Minute}}, {{.Second}}, {{.Nanosecond}}, time.UTC)
|
||||
{{- end -}}
|
||||
{{end}}
|
||||
`))
|
45
vendor/github.com/shurcooL/vfsgen/options.go
generated
vendored
Normal file
45
vendor/github.com/shurcooL/vfsgen/options.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package vfsgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Options for vfsgen code generation.
|
||||
type Options struct {
|
||||
// Filename of the generated Go code output (including extension).
|
||||
// If left empty, it defaults to "{{toLower .VariableName}}_vfsdata.go".
|
||||
Filename string
|
||||
|
||||
// PackageName is the name of the package in the generated code.
|
||||
// If left empty, it defaults to "main".
|
||||
PackageName string
|
||||
|
||||
// BuildTags are the optional build tags in the generated code.
|
||||
// The build tags syntax is specified by the go tool.
|
||||
BuildTags string
|
||||
|
||||
// VariableName is the name of the http.FileSystem variable in the generated code.
|
||||
// If left empty, it defaults to "assets".
|
||||
VariableName string
|
||||
|
||||
// VariableComment is the comment of the http.FileSystem variable in the generated code.
|
||||
// If left empty, it defaults to "{{.VariableName}} statically implements the virtual filesystem provided to vfsgen.".
|
||||
VariableComment string
|
||||
}
|
||||
|
||||
// fillMissing sets default values for mandatory options that are left empty.
|
||||
func (opt *Options) fillMissing() {
|
||||
if opt.PackageName == "" {
|
||||
opt.PackageName = "main"
|
||||
}
|
||||
if opt.VariableName == "" {
|
||||
opt.VariableName = "assets"
|
||||
}
|
||||
if opt.Filename == "" {
|
||||
opt.Filename = fmt.Sprintf("%s_vfsdata.go", strings.ToLower(opt.VariableName))
|
||||
}
|
||||
if opt.VariableComment == "" {
|
||||
opt.VariableComment = fmt.Sprintf("%s statically implements the virtual filesystem provided to vfsgen.", opt.VariableName)
|
||||
}
|
||||
}
|
27
vendor/github.com/shurcooL/vfsgen/stringwriter.go
generated
vendored
Normal file
27
vendor/github.com/shurcooL/vfsgen/stringwriter.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package vfsgen
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// stringWriter writes given bytes to underlying io.Writer as a Go interpreted string literal value,
|
||||
// not including double quotes. It tracks the total number of bytes written.
|
||||
type stringWriter struct {
|
||||
io.Writer
|
||||
N int64 // Total bytes written.
|
||||
}
|
||||
|
||||
func (sw *stringWriter) Write(p []byte) (n int, err error) {
|
||||
const hex = "0123456789abcdef"
|
||||
buf := []byte{'\\', 'x', 0, 0}
|
||||
for _, b := range p {
|
||||
buf[2], buf[3] = hex[b/16], hex[b%16]
|
||||
_, err = sw.Writer.Write(buf)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n++
|
||||
sw.N++
|
||||
}
|
||||
return n, nil
|
||||
}
|
30
vendor/vendor.json
vendored
30
vendor/vendor.json
vendored
|
@ -895,6 +895,36 @@
|
|||
"revision": "84bc9597164f671c0130543778228928d6865c5c",
|
||||
"revisionTime": "2017-06-08T03:40:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fSVkjXu8SIkCLI9RTt/vnkg9M5A=",
|
||||
"path": "github.com/shurcooL/httpfs/filter",
|
||||
"revision": "809beceb23714880abc4a382a00c05f89d13b1cc",
|
||||
"revisionTime": "2017-11-19T17:43:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "NJs03JE5kTrf/0jMSQzn6GGnqtI=",
|
||||
"path": "github.com/shurcooL/httpfs/union",
|
||||
"revision": "809beceb23714880abc4a382a00c05f89d13b1cc",
|
||||
"revisionTime": "2017-11-19T17:43:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fhngc51BJ8gGzM5b8BpSuWjCLao=",
|
||||
"path": "github.com/shurcooL/httpfs/vfsutil",
|
||||
"revision": "809beceb23714880abc4a382a00c05f89d13b1cc",
|
||||
"revisionTime": "2017-11-19T17:43:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "mAsjjGWwuClAdK2+Jdfupe4t4Y4=",
|
||||
"path": "github.com/shurcooL/vfsgen",
|
||||
"revision": "62bca832be04bd2bcaabd3b68a6b19a7ec044411",
|
||||
"revisionTime": "2018-07-11T16:37:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "STxYqRb4gnlSr3mRpT+Igfdz/kM=",
|
||||
"path": "github.com/spf13/pflag",
|
||||
"revision": "e57e3eeb33f795204c1ca35f56c44f83227c6e66",
|
||||
"revisionTime": "2017-05-08T18:43:26Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "iydUphwYqZRq3WhstEdGsbvBAKs=",
|
||||
"path": "github.com/stretchr/testify/assert",
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
The `ui` package contains static files and templates used in the web UI. For
|
||||
The `ui` directory contains static files and templates used in the web UI. For
|
||||
easier distribution they are statically compiled into the Prometheus binary
|
||||
using the go-bindata tool (c.f. Makefile).
|
||||
using the vfsgen library (c.f. Makefile).
|
||||
|
||||
During development it is more convenient to always use the files on disk to
|
||||
directly see changes without recompiling.
|
||||
To make this work, set the environment variable `DEBUG=1`, run `make assets`, and then `make build`.
|
||||
|
||||
This will put `go-bindata` in DEBUG mode where it serves from your local filesystem.
|
||||
To make this work, add `-tags dev` to the `flags` entry in `.promu.yml`, and then `make build`.
|
||||
|
||||
This will serve all files from your local filesystem.
|
||||
This is for development purposes only.
|
||||
|
||||
After making changes to any file, run `make assets` (without the `DEBUG=1`) before committing to update
|
||||
After making changes to any file, run `make assets` before committing to update
|
||||
the generated inline version of the file.
|
||||
|
|
34
web/ui/assets_generate.go
Normal file
34
web/ui/assets_generate.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/prometheus/prometheus/web/ui"
|
||||
"github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := vfsgen.Generate(ui.Assets, vfsgen.Options{
|
||||
PackageName: "ui",
|
||||
BuildTags: "!dev",
|
||||
VariableName: "Assets",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
679
web/ui/assets_vfsdata.go
Normal file
679
web/ui/assets_vfsdata.go
Normal file
File diff suppressed because one or more lines are too long
1266
web/ui/bindata.go
1266
web/ui/bindata.go
File diff suppressed because one or more lines are too long
22
web/ui/doc.go
Normal file
22
web/ui/doc.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2018 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ui provides the assets via a virtual filesystem.
|
||||
package ui
|
||||
|
||||
import (
|
||||
// The blank import is to make govendor happy.
|
||||
_ "github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
//go:generate go run -tags=dev assets_generate.go
|
59
web/ui/ui.go
Normal file
59
web/ui/ui.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2018 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build dev
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/shurcooL/httpfs/filter"
|
||||
"github.com/shurcooL/httpfs/union"
|
||||
)
|
||||
|
||||
func importPathToDir(importPath string) string {
|
||||
p, err := build.Import(importPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
return p.Dir
|
||||
}
|
||||
|
||||
var static http.FileSystem = filter.Keep(
|
||||
http.Dir(importPathToDir("github.com/prometheus/prometheus/web/ui/static")),
|
||||
func(path string, fi os.FileInfo) bool {
|
||||
return fi.IsDir() ||
|
||||
(!strings.HasSuffix(path, "map.js") &&
|
||||
!strings.HasSuffix(path, "/bootstrap.js") &&
|
||||
!strings.HasSuffix(path, "/bootstrap-theme.css") &&
|
||||
!strings.HasSuffix(path, "/bootstrap.css"))
|
||||
},
|
||||
)
|
||||
|
||||
var templates http.FileSystem = filter.Keep(
|
||||
http.Dir(importPathToDir("github.com/prometheus/prometheus/web/ui/templates")),
|
||||
func(path string, fi os.FileInfo) bool {
|
||||
return fi.IsDir() || strings.HasSuffix(path, ".html")
|
||||
},
|
||||
)
|
||||
|
||||
// Assets contains the project's assets.
|
||||
var Assets http.FileSystem = union.New(map[string]http.FileSystem{
|
||||
"/templates": templates,
|
||||
"/static": static,
|
||||
})
|
52
web/web.go
52
web/web.go
|
@ -14,7 +14,6 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -262,7 +261,11 @@ func New(logger log.Logger, o *Options) *Handler {
|
|||
|
||||
router.Get("/consoles/*filepath", readyf(h.consoles))
|
||||
|
||||
router.Get("/static/*filepath", h.serveStaticAsset)
|
||||
router.Get("/static/*filepath", func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = filepath.Join("/static", route.Param(r.Context(), "filepath"))
|
||||
fs := http.FileServer(ui.Assets)
|
||||
fs.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
if o.UserAssetsPath != "" {
|
||||
router.Get("/user/*filepath", route.FileServe(o.UserAssetsPath))
|
||||
|
@ -349,28 +352,6 @@ func serveDebug(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Handler) serveStaticAsset(w http.ResponseWriter, req *http.Request) {
|
||||
fp := route.Param(req.Context(), "filepath")
|
||||
fp = filepath.Join("web/ui/static", fp)
|
||||
|
||||
info, err := ui.AssetInfo(fp)
|
||||
if err != nil {
|
||||
level.Warn(h.logger).Log("msg", "Could not get file info", "err", err, "file", fp)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
file, err := ui.Asset(fp)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
level.Warn(h.logger).Log("msg", "Could not get file", "err", err, "file", fp)
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeContent(w, req, info.Name(), info.ModTime(), bytes.NewReader(file))
|
||||
}
|
||||
|
||||
// Ready sets Handler to be ready.
|
||||
func (h *Handler) Ready() {
|
||||
atomic.StoreUint32(&h.ready, 1)
|
||||
|
@ -848,15 +829,32 @@ func tmplFuncs(consolesPath string, opts *Options) template_text.FuncMap {
|
|||
}
|
||||
|
||||
func (h *Handler) getTemplate(name string) (string, error) {
|
||||
baseTmpl, err := ui.Asset("web/ui/templates/_base.html")
|
||||
var tmpl string
|
||||
|
||||
appendf := func(name string) error {
|
||||
f, err := ui.Assets.Open(filepath.Join("/templates", name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl += string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := appendf("_base.html")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading base template: %s", err)
|
||||
}
|
||||
pageTmpl, err := ui.Asset(filepath.Join("web/ui/templates", name))
|
||||
err = appendf(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading page template %s: %s", name, err)
|
||||
}
|
||||
return string(baseTmpl) + string(pageTmpl), nil
|
||||
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
|
|
Loading…
Reference in a new issue