add config.yml file

master
codeskyblue 8 years ago
parent 800ea949b9
commit 8ead7afe9f

8
Godeps/Godeps.json generated

@ -7,6 +7,10 @@
"ImportPath": "github.com/codeskyblue/kexec",
"Rev": "098ccba5e5e7e676f3631983c5ea931a3592871f"
},
{
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
"Rev": "d5cac425555ca5cf00694df246e04f05e6a55150"
},
{
"ImportPath": "github.com/equinox-io/equinox",
"Rev": "6f97d0d3970881d3e53dd6f547a41109eb055e54"
@ -39,6 +43,10 @@
"ImportPath": "github.com/go-yaml/yaml",
"Rev": "e4d366fc3c7938e2958e662b4258c7a89e1f0e3e"
},
{
"ImportPath": "github.com/goji/httpauth",
"Rev": "2da839ab0f4df05a6db5eb277995589dadbd4fb9"
},
{
"ImportPath": "github.com/gorilla/context",
"Rev": "1c83b3eabd45b6d76072b66b746c20815fb2872d"

@ -47,13 +47,31 @@ $ gosuv status
Server is running
```
Open web <http://localhost:8000> to see the manager page.
Open web <http://localhost:11313> to see the manager page.
![gosuv web](docs/gosuv.gif)
## Configuration
Default config file stored in directory `$HOME/.gosuv/`
- file `programs.yml` contains all program settings.
- file `config.yml` contains server config
File `config.yml` can be generated by `gosuv conftest`
Example config.yaml
```
server:
httpauth:
enabled: false
username: uu
password: pp
addr: :11313
client:
server_url: http://localhost:11313
```
## Design
HTTP is follow the RESTFul guide.

@ -11,6 +11,7 @@ import (
type Configuration struct {
Server struct {
HttpAuth struct {
Enabled bool `yaml:"enabled"`
User string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"httpauth"`

@ -11,6 +11,7 @@ import (
"path/filepath"
"github.com/equinox-io/equinox"
"github.com/goji/httpauth"
"github.com/urfave/cli"
)
@ -57,10 +58,17 @@ func equinoxUpdate(channel string) error {
}
func actionStartServer(c *cli.Context) error {
if err := registerHTTPHandlers(); err != nil {
return err
hdlr, err := newSupervisorHandler()
if err != nil {
log.Fatal(err)
}
auth := cfg.Server.HttpAuth
if auth.Enabled {
hdlr = httpauth.SimpleBasicAuth(auth.User, auth.Password)(hdlr)
}
addr := c.String("address")
http.Handle("/", hdlr)
addr := cfg.Server.Addr
if c.Bool("foreground") {
fmt.Println("added serv: ", addr)
log.Fatal(http.ListenAndServe(addr, nil))
@ -76,7 +84,7 @@ func actionStartServer(c *cli.Context) error {
}
func actionStatus(c *cli.Context) error {
resp, err := http.Get("http://localhost:8000/api/status")
resp, err := http.Get(cfg.Client.ServerURL + "/api/status")
if err != nil {
log.Fatal(err)
}
@ -95,7 +103,7 @@ func actionStatus(c *cli.Context) error {
}
func actionShutdown(c *cli.Context) error {
resp, err := http.Get("http://localhost:8000/api/shutdown")
resp, err := http.Get(cfg.Client.ServerURL + "/api/status")
if err != nil {
log.Fatal(err)
}
@ -114,7 +122,7 @@ func actionShutdown(c *cli.Context) error {
}
func actionConfigTest(c *cli.Context) error {
if err := registerHTTPHandlers(); err != nil {
if _, err := newSupervisorHandler(); err != nil {
log.Fatal(err)
}
log.Println("test is successful")
@ -168,11 +176,6 @@ func main() {
Name: "foreground, f",
Usage: "start in foreground",
},
cli.StringFlag{
Name: "address, addr",
Usage: "listen address",
Value: ":8000",
},
cli.StringFlag{
Name: "conf, c",
Usage: "config file",

@ -0,0 +1,23 @@
Copyright (c) 2014, Elazar Leibovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,46 @@
# go-bindata-assetfs
Serve embedded files from [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) with `net/http`.
[GoDoc](http://godoc.org/github.com/elazarl/go-bindata-assetfs)
### Installation
Install with
$ go get github.com/jteeuwen/go-bindata/...
$ go get github.com/elazarl/go-bindata-assetfs/...
### Creating embedded data
Usage is identical to [jteeuwen/go-bindata](https://github.com/jteeuwen/go-bindata) usage,
instead of running `go-bindata` run `go-bindata-assetfs`.
The tool will create a `bindata_assetfs.go` file, which contains the embedded data.
A typical use case is
$ go-bindata-assetfs data/...
### Using assetFS in your code
The generated file provides an `assetFS()` function that returns a `http.Filesystem`
wrapping the embedded files. What you usually want to do is:
http.Handle("/", http.FileServer(assetFS()))
This would run an HTTP server serving the embedded files.
## Without running binary tool
You can always just run the `go-bindata` tool, and then
use
import "github.com/elazarl/go-bindata-assetfs"
...
http.Handle("/",
http.FileServer(
&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
to serve files embedded from the `data` directory.

@ -0,0 +1,147 @@
package assetfs
import (
"bytes"
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"time"
)
var (
fileTimestamp = time.Now()
)
// FakeFile implements os.FileInfo interface for a given path and size
type FakeFile struct {
// Path is the path of this file
Path string
// Dir marks of the path is a directory
Dir bool
// Len is the length of the fake file, zero if it is a directory
Len int64
}
func (f *FakeFile) Name() string {
_, name := filepath.Split(f.Path)
return name
}
func (f *FakeFile) Mode() os.FileMode {
mode := os.FileMode(0644)
if f.Dir {
return mode | os.ModeDir
}
return mode
}
func (f *FakeFile) ModTime() time.Time {
return fileTimestamp
}
func (f *FakeFile) Size() int64 {
return f.Len
}
func (f *FakeFile) IsDir() bool {
return f.Mode().IsDir()
}
func (f *FakeFile) Sys() interface{} {
return nil
}
// AssetFile implements http.File interface for a no-directory file with content
type AssetFile struct {
*bytes.Reader
io.Closer
FakeFile
}
func NewAssetFile(name string, content []byte) *AssetFile {
return &AssetFile{
bytes.NewReader(content),
ioutil.NopCloser(nil),
FakeFile{name, false, int64(len(content))}}
}
func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, errors.New("not a directory")
}
func (f *AssetFile) Size() int64 {
return f.FakeFile.Size()
}
func (f *AssetFile) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetDirectory implements http.File interface for a directory
type AssetDirectory struct {
AssetFile
ChildrenRead int
Children []os.FileInfo
}
func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
fileinfos := make([]os.FileInfo, 0, len(children))
for _, child := range children {
_, err := fs.AssetDir(filepath.Join(name, child))
fileinfos = append(fileinfos, &FakeFile{child, err == nil, 0})
}
return &AssetDirectory{
AssetFile{
bytes.NewReader(nil),
ioutil.NopCloser(nil),
FakeFile{name, true, 0},
},
0,
fileinfos}
}
func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
if count <= 0 {
return f.Children, nil
}
if f.ChildrenRead+count > len(f.Children) {
count = len(f.Children) - f.ChildrenRead
}
rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
f.ChildrenRead += count
return rv, nil
}
func (f *AssetDirectory) Stat() (os.FileInfo, error) {
return f, nil
}
// AssetFS implements http.FileSystem, allowing
// embedded files to be served from net/http package.
type AssetFS struct {
// Asset should return content of file in path if exists
Asset func(path string) ([]byte, error)
// AssetDir should return list of files in the path
AssetDir func(path string) ([]string, error)
// Prefix would be prepended to http requests
Prefix string
}
func (fs *AssetFS) Open(name string) (http.File, error) {
name = path.Join(fs.Prefix, name)
if len(name) > 0 && name[0] == '/' {
name = name[1:]
}
if b, err := fs.Asset(name); err == nil {
return NewAssetFile(name, b), nil
}
if children, err := fs.AssetDir(name); err == nil {
return NewAssetDirectory(name, children, fs), nil
} else {
return nil, err
}
}

@ -0,0 +1,13 @@
// assetfs allows packages to serve static content embedded
// with the go-bindata tool with the standard net/http package.
//
// See https://github.com/jteeuwen/go-bindata for more information
// about embedding binary data with go-bindata.
//
// Usage example, after running
// $ go-bindata data/...
// use:
// http.Handle("/",
// http.FileServer(
// &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "data"}))
package assetfs

@ -0,0 +1,22 @@
language: go
sudo: false
matrix:
include:
- go: 1.2
- go: 1.3
- go: 1.4
- go: 1.5
- go: 1.6
- go: tip
allow_failures:
- go: tip
install:
- # skip
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d .)
- go vet $(go list ./... | grep -v /vendor/)
- go test -v -race ./...

@ -0,0 +1,20 @@
Copyright (c) 2014 Carl Jackson (carl@avtok.com), Matt Silverlock (matt@eatsleeprepeat.net)
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,163 @@
# goji/httpauth [![GoDoc](https://godoc.org/github.com/goji/httpauth?status.svg)](https://godoc.org/github.com/goji/httpauth) [![Build Status](https://travis-ci.org/goji/httpauth.svg)](https://travis-ci.org/goji/httpauth)
`httpauth` currently provides [HTTP Basic Authentication middleware](http://tools.ietf.org/html/rfc2617) for Go. It is compatible with Go's own `net/http`, [goji](https://goji.io), Gin & anything that speaks the `http.Handler` interface.
## Example
`httpauth` provides a `SimpleBasicAuth` function to get you up and running. Particularly ideal for development servers.
Note that HTTP Basic Authentication credentials are sent over the wire "in the clear" (read: plaintext!) and therefore should not be considered a robust way to secure a HTTP server. If you're after that, you'll need to use SSL/TLS ("HTTPS") at a minimum.
### Install It
```sh
$ go get github.com/goji/httpauth
```
### Goji v2
#### Simple Usage
The fastest and simplest way to get started using `httpauth` is to use the
`SimpleBasicAuth` function.
```go
package main
import(
"net/http"
"goji.io"
)
func main() {
mux := goji.NewMux()
mux.Use(httpauth.SimpleBasicAuth("dave", "somepassword"))
mux.Use(SomeOtherMiddleware)
// YourHandler now requires HTTP Basic Auth
mux.Handle(pat.Get("/some-route"), YourHandler))
log.Fatal(http.ListenAndServe("localhost:8000", mux))
}
```
#### Advanced Usage
For more control over the process, pass a `AuthOptions` struct to `BasicAuth` instead. This allows you to:
* Configure the authentication realm.
* Provide your own UnauthorizedHandler (anything that satisfies `http.Handler`) so you can return a better looking 401 page.
* Define a custom authentication function, which is discussed in the next section.
```go
func main() {
authOpts := httpauth.AuthOptions{
Realm: "DevCo",
User: "dave",
Password: "plaintext!",
UnauthorizedHandler: myUnauthorizedHandler,
}
mux := goji.NewMux()
mux.Use(BasicAuth(authOpts))
mux.Use(SomeOtherMiddleware)
mux.Handle(pat.Get("/some-route"), YourHandler))
log.Fatal(http.ListenAndServe("localhost:8000", mux))
}
```
#### Custom Authentication Function
`httpauth` will accept a custom authentication function.
Normally, you would not set `AuthOptions.User` nor `AuthOptions.Password` in this scenario.
You would instead validate the given credentials against an external system such as a database.
The contrived example below is for demonstration purposes only.
```go
func main() {
authOpts := httpauth.AuthOptions{
Realm: "DevCo",
AuthFunc: myAuthFunc,
UnauthorizedHandler: myUnauthorizedHandler,
}
mux := goji.NewMux()
mux.Use(BasicAuth(authOpts))
mux.Use(SomeOtherMiddleware)
mux.Handle(pat.Get("/some-route"), YourHandler))
log.Fatal(http.ListenAndServe("localhost:8000", mux))
}
// myAuthFunc is not secure. It checks to see if the password is simply
// the username repeated three times.
func myAuthFunc(user, pass string, r *http.Request) bool {
return pass == strings.Repeat(user, 3)
}
```
### gorilla/mux
Since it's all `http.Handler`, `httpauth` works with [gorilla/mux](https://github.com/gorilla/mux) (and most other routers) as well:
```go
package main
import (
"net/http"
"github.com/goji/httpauth"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", YourHandler)
http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(r))
http.ListenAndServe(":7000", nil)
}
func YourHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Gorilla!\n"))
}
```
### net/http
If you're using vanilla `net/http`:
```go
package main
import(
"net/http"
"github.com/goji/httpauth"
)
func main() {
http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(http.HandlerFunc(YourHandler)))
http.ListenAndServe(":7000", nil)
}
```
## Contributing
Send a pull request! Note that features on the (informal) roadmap include HTTP Digest Auth.
## License
MIT Licensed. See the LICENSE file for details.

@ -0,0 +1,185 @@
package httpauth
import (
"bytes"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"
"net/http"
"strings"
)
type basicAuth struct {
h http.Handler
opts AuthOptions
}
// AuthOptions stores the configuration for HTTP Basic Authentication.
//
// A http.Handler may also be passed to UnauthorizedHandler to override the
// default error handler if you wish to serve a custom template/response.
type AuthOptions struct {
Realm string
User string
Password string
AuthFunc func(string, string, *http.Request) bool
UnauthorizedHandler http.Handler
}
// Satisfies the http.Handler interface for basicAuth.
func (b basicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Check if we have a user-provided error handler, else set a default
if b.opts.UnauthorizedHandler == nil {
b.opts.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
}
// Check that the provided details match
if b.authenticate(r) == false {
b.requestAuth(w, r)
return
}
// Call the next handler on success.
b.h.ServeHTTP(w, r)
}
// authenticate retrieves and then validates the user:password combination provided in
// the request header. Returns 'false' if the user has not successfully authenticated.
func (b *basicAuth) authenticate(r *http.Request) bool {
const basicScheme string = "Basic "
if r == nil {
return false
}
// In simple mode, prevent authentication with empty credentials if User is
// not set. Allow empty passwords to support non-password use-cases.
if b.opts.AuthFunc == nil && b.opts.User == "" {
return false
}
// Confirm the request is sending Basic Authentication credentials.
auth := r.Header.Get("Authorization")
if !strings.HasPrefix(auth, basicScheme) {
return false
}
// Get the plain-text username and password from the request.
// The first six characters are skipped - e.g. "Basic ".
str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):])
if err != nil {
return false
}
// Split on the first ":" character only, with any subsequent colons assumed to be part
// of the password. Note that the RFC2617 standard does not place any limitations on
// allowable characters in the password.
creds := bytes.SplitN(str, []byte(":"), 2)
if len(creds) != 2 {
return false
}
givenUser := string(creds[0])
givenPass := string(creds[1])
// Default to Simple mode if no AuthFunc is defined.
if b.opts.AuthFunc == nil {
b.opts.AuthFunc = b.simpleBasicAuthFunc
}
return b.opts.AuthFunc(givenUser, givenPass, r)
}
// simpleBasicAuthFunc authenticates the supplied username and password against
// the User and Password set in the Options struct.
func (b *basicAuth) simpleBasicAuthFunc(user, pass string, r *http.Request) bool {
// Equalize lengths of supplied and required credentials
// by hashing them
givenUser := sha256.Sum256([]byte(user))
givenPass := sha256.Sum256([]byte(pass))
requiredUser := sha256.Sum256([]byte(b.opts.User))
requiredPass := sha256.Sum256([]byte(b.opts.Password))
// Compare the supplied credentials to those set in our options
if subtle.ConstantTimeCompare(givenUser[:], requiredUser[:]) == 1 &&
subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1 {
return true
}
return false
}
// Require authentication, and serve our error handler otherwise.
func (b *basicAuth) requestAuth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.opts.Realm))
b.opts.UnauthorizedHandler.ServeHTTP(w, r)
}
// defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response.
func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
// BasicAuth provides HTTP middleware for protecting URIs with HTTP Basic Authentication
// as per RFC 2617. The server authenticates a user:password combination provided in the
// "Authorization" HTTP header.
//
// Example:
//
// package main
//
// import(
// "net/http"
// "github.com/zenazn/goji"
// "github.com/goji/httpauth"
// )
//
// func main() {
// basicOpts := httpauth.AuthOptions{
// Realm: "Restricted",
// User: "Dave",
// Password: "ClearText",
// }
//
// goji.Use(httpauth.BasicAuth(basicOpts), SomeOtherMiddleware)
// goji.Get("/thing", myHandler)
// }
//
// Note: HTTP Basic Authentication credentials are sent in plain text, and therefore it does
// not make for a wholly secure authentication mechanism. You should serve your content over
// HTTPS to mitigate this, noting that "Basic Authentication" is meant to be just that: basic!
func BasicAuth(o AuthOptions) func(http.Handler) http.Handler {
fn := func(h http.Handler) http.Handler {
return basicAuth{h, o}
}
return fn
}
// SimpleBasicAuth is a convenience wrapper around BasicAuth. It takes a user and password, and
// returns a pre-configured BasicAuth handler using the "Restricted" realm and a default 401 handler.
//
// Example:
//
// package main
//
// import(
// "net/http"
// "github.com/zenazn/goji/web/httpauth"
// )
//
// func main() {
//
// goji.Use(httpauth.SimpleBasicAuth("dave", "somepassword"), SomeOtherMiddleware)
// goji.Get("/thing", myHandler)
// }
//
func SimpleBasicAuth(user, password string) func(http.Handler) http.Handler {
opts := AuthOptions{
Realm: "Restricted",
User: user,
Password: password,
}
return BasicAuth(opts)
}

@ -399,7 +399,7 @@ func (s *Supervisor) catchExitSignal() {
}()
}
func registerHTTPHandlers() error {
func newSupervisorHandler() (hdlr http.Handler, err error) {
suv := &Supervisor{
ConfigDir: defaultConfigDir,
pgMap: make(map[string]*Program, 0),
@ -407,8 +407,8 @@ func registerHTTPHandlers() error {
// eventCs: make(map[chan string]bool),
eventB: NewBroadcastString(),
}
if err := suv.loadDB(); err != nil {
return err
if err = suv.loadDB(); err != nil {
return
}
suv.catchExitSignal()
@ -424,8 +424,5 @@ func registerHTTPHandlers() error {
r.HandleFunc("/ws/events", suv.wsEvents)
r.HandleFunc("/ws/logs/{name}", suv.wsLog)
// fs := http.FileServer(http.Dir("res"))
http.Handle("/", r)
// http.Handle("/res/", http.StripPrefix("/res/", fs))
return nil
return r, nil
}

Loading…
Cancel
Save