load all things via middleware

This commit is contained in:
Brad Rydzewski 2016-04-12 13:08:17 -07:00
parent 42d6d8d3b2
commit 9b306a1bc8
25 changed files with 289 additions and 408 deletions

View file

@ -18,7 +18,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/session" "github.com/drone/drone/router/middleware/session"
) )
@ -130,7 +129,7 @@ func GetBuildLogs(c *gin.Context) {
} }
func DeleteBuild(c *gin.Context) { func DeleteBuild(c *gin.Context) {
engine_ := context.Engine(c) engine_ := engine.FromContext(c)
repo := session.Repo(c) repo := session.Repo(c)
// parse the build number and job sequence number from // parse the build number and job sequence number from
@ -281,7 +280,7 @@ func PostBuild(c *gin.Context) {
// on status change notifications // on status change notifications
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
engine_ := context.Engine(c) engine_ := engine.FromContext(c)
go engine_.Schedule(c.Copy(), &engine.Task{ go engine_.Schedule(c.Copy(), &engine.Task{
User: user, User: user,
Repo: repo, Repo: repo,

View file

@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/drone/drone/engine"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/store" "github.com/drone/drone/store"
) )
@ -25,7 +25,7 @@ func GetNode(c *gin.Context) {
} }
func PostNode(c *gin.Context) { func PostNode(c *gin.Context) {
engine := context.Engine(c) engine := engine.FromContext(c)
in := struct { in := struct {
Addr string `json:"address"` Addr string `json:"address"`
@ -63,7 +63,7 @@ func PostNode(c *gin.Context) {
} }
func DeleteNode(c *gin.Context) { func DeleteNode(c *gin.Context) {
engine := context.Engine(c) engine := engine.FromContext(c)
id, _ := strconv.Atoi(c.Param("node")) id, _ := strconv.Atoi(c.Param("node"))
node, err := store.GetNode(c, int64(id)) node, err := store.GetNode(c, int64(id))

View file

@ -4,14 +4,8 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/drone/drone/engine"
"github.com/drone/drone/remote"
"github.com/drone/drone/router" "github.com/drone/drone/router"
"github.com/drone/drone/router/middleware/cache" "github.com/drone/drone/router/middleware"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store/datastore"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/gin-gonic/contrib/ginrus" "github.com/gin-gonic/contrib/ginrus"
@ -37,26 +31,14 @@ func main() {
logrus.SetLevel(logrus.WarnLevel) logrus.SetLevel(logrus.WarnLevel)
} }
// Load the configuration from env file
env := envconfig.Load(".env")
// Setup the database driver
store_ := datastore.Load(env)
// setup the remote driver
remote_ := remote.Load(env)
// setup the runner
engine_ := engine.Load(env, store_)
// setup the server and start the listener // setup the server and start the listener
handler := router.Load( handler := router.Load(
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true), ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
header.Version, middleware.Version,
cache.Default(), middleware.Cache(),
context.SetStore(store_), middleware.Store(),
context.SetRemote(remote_), middleware.Remote(),
context.SetEngine(engine_), middleware.Engine(),
) )
if *cert != "" { if *cert != "" {

23
engine/context.go Normal file
View file

@ -0,0 +1,23 @@
package engine
import (
"golang.org/x/net/context"
)
const key = "engine"
// Setter defines a context that enables setting values.
type Setter interface {
Set(string, interface{})
}
// FromContext returns the Engine associated with this context.
func FromContext(c context.Context) Engine {
return c.Value(key).(Engine)
}
// ToContext adds the Engine to this context if it supports
// the Setter interface.
func ToContext(c Setter, engine Engine) {
c.Set(key, engine)
}

View file

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os"
"runtime" "runtime"
"time" "time"
@ -15,7 +16,6 @@ import (
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/shared/docker" "github.com/drone/drone/shared/docker"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store" "github.com/drone/drone/store"
"github.com/samalba/dockerclient" "github.com/samalba/dockerclient"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -60,7 +60,7 @@ type engine struct {
// Load creates a new build engine, loaded with registered nodes from the // Load creates a new build engine, loaded with registered nodes from the
// database. The registered nodes are added to the pool of nodes to immediately // database. The registered nodes are added to the pool of nodes to immediately
// start accepting workloads. // start accepting workloads.
func Load(env envconfig.Env, s store.Store) Engine { func Load(s store.Store) Engine {
engine := &engine{} engine := &engine{}
engine.bus = newEventbus() engine.bus = newEventbus()
engine.pool = newPool() engine.pool = newPool()
@ -70,7 +70,7 @@ func Load(env envconfig.Env, s store.Store) Engine {
// throughout the build environment. // throughout the build environment.
var proxyVars = []string{"HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"} var proxyVars = []string{"HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"}
for _, proxyVar := range proxyVars { for _, proxyVar := range proxyVars {
proxyVal := env.Get(proxyVar) proxyVal := os.Getenv(proxyVar)
if len(proxyVal) != 0 { if len(proxyVal) != 0 {
engine.envs = append(engine.envs, proxyVar+"="+proxyVal) engine.envs = append(engine.envs, proxyVar+"="+proxyVal)
} }

View file

@ -9,7 +9,6 @@ import (
"strconv" "strconv"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/httputil"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
@ -24,8 +23,7 @@ type Bitbucket struct {
Open bool Open bool
} }
func Load(env envconfig.Env) *Bitbucket { func Load(config string) *Bitbucket {
config := env.String("REMOTE_CONFIG", "")
// parse the remote DSN configuration string // parse the remote DSN configuration string
url_, err := url.Parse(config) url_, err := url.Parse(config)

View file

@ -11,7 +11,6 @@ import (
"strings" "strings"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/oauth2" "github.com/drone/drone/shared/oauth2"
@ -42,8 +41,7 @@ type Github struct {
GitSSH bool GitSSH bool
} }
func Load(env envconfig.Env) *Github { func Load(config string) *Github {
config := env.String("REMOTE_CONFIG", "")
// parse the remote DSN configuration string // parse the remote DSN configuration string
url_, err := url.Parse(config) url_, err := url.Parse(config)

View file

@ -10,7 +10,6 @@ import (
"strings" "strings"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/oauth2" "github.com/drone/drone/shared/oauth2"
"github.com/drone/drone/shared/token" "github.com/drone/drone/shared/token"
@ -35,9 +34,7 @@ type Gitlab struct {
Search bool Search bool
} }
func Load(env envconfig.Env) *Gitlab { func Load(config string) *Gitlab {
config := env.String("REMOTE_CONFIG", "")
url_, err := url.Parse(config) url_, err := url.Parse(config)
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -9,7 +9,6 @@ import (
"strconv" "strconv"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/gogits/go-gogs-client" "github.com/gogits/go-gogs-client"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
@ -22,9 +21,7 @@ type Gogs struct {
SkipVerify bool SkipVerify bool
} }
func Load(env envconfig.Env) *Gogs { func Load(config string) *Gogs {
config := env.String("REMOTE_CONFIG", "")
// parse the remote DSN configuration string // parse the remote DSN configuration string
url_, err := url.Parse(config) url_, err := url.Parse(config)
if err != nil { if err != nil {

View file

@ -6,36 +6,10 @@ import (
"net/http" "net/http"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/remote/bitbucket"
"github.com/drone/drone/remote/github"
"github.com/drone/drone/remote/gitlab"
"github.com/drone/drone/remote/gogs"
"github.com/drone/drone/shared/envconfig"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
func Load(env envconfig.Env) Remote {
driver := env.Get("REMOTE_DRIVER")
switch driver {
case "bitbucket":
return bitbucket.Load(env)
case "github":
return github.Load(env)
case "gitlab":
return gitlab.Load(env)
case "gogs":
return gogs.Load(env)
default:
logrus.Fatalf("unknown remote driver %s", driver)
}
return nil
}
type Remote interface { type Remote interface {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.

View file

@ -0,0 +1,22 @@
package middleware
import (
"time"
"github.com/drone/drone/cache"
"github.com/gin-gonic/gin"
"github.com/ianschenck/envflag"
)
var ttl = envflag.Duration("CACHE_TTL", time.Minute*15, "")
// Cache is a middleware function that initializes the Cache and attaches to
// the context of every http.Request.
func Cache() gin.HandlerFunc {
cc := cache.NewTTL(*ttl)
return func(c *gin.Context) {
cache.ToContext(c, cc)
c.Next()
}
}

View file

@ -1,14 +0,0 @@
package cache
import (
"github.com/drone/drone/cache"
"github.com/gin-gonic/gin"
)
func Default() gin.HandlerFunc {
cc := cache.Default()
return func(c *gin.Context) {
cache.ToContext(c, cc)
c.Next()
}
}

View file

@ -1,37 +0,0 @@
package context
import (
"github.com/drone/drone/engine"
"github.com/drone/drone/remote"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
func SetStore(s store.Store) gin.HandlerFunc {
return func(c *gin.Context) {
store.ToContext(c, s)
c.Next()
}
}
func SetRemote(remote remote.Remote) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("remote", remote)
c.Next()
}
}
func Remote(c *gin.Context) remote.Remote {
return c.MustGet("remote").(remote.Remote)
}
func SetEngine(engine engine.Engine) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("engine", engine)
c.Next()
}
}
func Engine(c *gin.Context) engine.Engine {
return c.MustGet("engine").(engine.Engine)
}

View file

@ -0,0 +1,28 @@
package middleware
import (
"sync"
"github.com/drone/drone/engine"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
// Engine is a middleware function that initializes the Engine and attaches to
// the context of every http.Request.
func Engine() gin.HandlerFunc {
var once sync.Once
var engine_ engine.Engine
return func(c *gin.Context) {
once.Do(func() {
store_ := store.FromContext(c)
engine_ = engine.Load(store_)
})
engine.ToContext(c, engine_)
c.Next()
}
}

View file

@ -1,95 +0,0 @@
package location
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// Resolve is a middleware function that resolves the hostname
// and scheme for the http.Request and adds to the context.
func Resolve(c *gin.Context) {
c.Set("host", resolveHost(c.Request))
c.Set("scheme", resolveScheme(c.Request))
c.Next()
}
// parseHeader parses non unique headers value
// from a http.Request and return a slice of the values
// queried from the header
func parseHeader(r *http.Request, header string, token string) (val []string) {
for _, v := range r.Header[header] {
options := strings.Split(v, ";")
for _, o := range options {
keyvalue := strings.Split(o, "=")
var key, value string
if len(keyvalue) > 1 {
key, value = strings.TrimSpace(keyvalue[0]), strings.TrimSpace(keyvalue[1])
}
key = strings.ToLower(key)
if key == token {
val = append(val, value)
}
}
}
return
}
// resolveScheme is a helper function that evaluates the http.Request
// and returns the scheme, HTTP or HTTPS. It is able to detect,
// using the X-Forwarded-Proto, if the original request was HTTPS
// and routed through a reverse proxy with SSL termination.
func resolveScheme(r *http.Request) string {
switch {
case r.URL.Scheme == "https":
return "https"
case r.TLS != nil:
return "https"
case strings.HasPrefix(r.Proto, "HTTPS"):
return "https"
case r.Header.Get("X-Forwarded-Proto") == "https":
return "https"
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "proto")) != 0 && parseHeader(r, "Forwarded", "proto")[0] == "https":
return "https"
default:
return "http"
}
}
// resolveHost is a helper function that evaluates the http.Request
// and returns the hostname. It is able to detect, using the
// X-Forarded-For header, the original hostname when routed
// through a reverse proxy.
func resolveHost(r *http.Request) string {
switch {
case len(r.Host) != 0:
return r.Host
case len(r.URL.Host) != 0:
return r.URL.Host
case len(r.Header.Get("X-Forwarded-For")) != 0:
return r.Header.Get("X-Forwarded-For")
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "for")) != 0:
return parseHeader(r, "Forwarded", "for")[0]
case len(r.Header.Get("X-Host")) != 0:
return r.Header.Get("X-Host")
case len(r.Header.Get("Forwarded")) != 0 && len(parseHeader(r, "Forwarded", "host")) != 0:
return parseHeader(r, "Forwarded", "host")[0]
case len(r.Header.Get("XFF")) != 0:
return r.Header.Get("XFF")
case len(r.Header.Get("X-Real-IP")) != 0:
return r.Header.Get("X-Real-IP")
default:
return "localhost:8000"
}
}
// Hostname returns the hostname associated with
// the current context.
func Hostname(c *gin.Context) (host string) {
v, ok := c.Get("host")
if ok {
host = v.(string)
}
return
}

View file

@ -1,48 +0,0 @@
package location
import (
"github.com/franela/goblin"
"net/http"
"reflect"
"testing"
)
var mockHeader []string
var mockRequest *http.Request
var wronglyFormedHeader []string
var wronglyFormedRequest *http.Request
func init() {
mockHeader = []string{"For= 110.0.2.2", "for = \"[::1]\"; Host=example.com; foR=10.2.3.4; pRoto =https ; By = 127.0.0.1"}
mockRequest = &http.Request{Header: map[string][]string{"Forwarded": mockHeader}}
wronglyFormedHeader = []string{"Fro= 110.0.2.2", "for = \"[:1]\"% Host=example:.com| foR=10.278.3.4% poto =https | Bi % 127.0.0.1", ""}
wronglyFormedRequest = &http.Request{Header: map[string][]string{"Forwarded": wronglyFormedHeader}}
}
func TestParseForwardedHeadersProto(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Parse proto Forwarded Headers", func() {
g.It("Should parse a normal proto Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "proto")
g.Assert("https" == parsedHeader[0]).IsTrue()
})
g.It("Should parse a normal for Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "for")
g.Assert(reflect.DeepEqual([]string{"110.0.2.2", "\"[::1]\"", "10.2.3.4"}, parsedHeader)).IsTrue()
})
g.It("Should parse a normal host Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "host")
g.Assert("example.com" == parsedHeader[0]).IsTrue()
})
g.It("Should parse a normal by Forwarded header", func() {
parsedHeader := parseHeader(mockRequest, "Forwarded", "by")
g.Assert("127.0.0.1" == parsedHeader[0]).IsTrue()
})
g.It("Should not crash if a wrongly formed Forwarder header is sent", func() {
parsedHeader := parseHeader(wronglyFormedRequest, "Forwarded", "by")
g.Assert(len(parsedHeader) == 0).IsTrue()
})
})
}

View file

@ -0,0 +1,45 @@
package middleware
import (
"github.com/drone/drone/remote"
"github.com/drone/drone/remote/bitbucket"
"github.com/drone/drone/remote/github"
"github.com/drone/drone/remote/gitlab"
"github.com/drone/drone/remote/gogs"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/ianschenck/envflag"
)
var (
driver = envflag.String("REMOTE_DRIVER", "", "")
config = envflag.String("REMOTE_CONFIG", "", "")
)
// Remote is a middleware function that initializes the Remote and attaches to
// the context of every http.Request.
func Remote() gin.HandlerFunc {
logrus.Infof("using remote driver %s", *driver)
logrus.Infof("using remote config %s", *config)
var remote_ remote.Remote
switch *driver {
case "github":
remote_ = github.Load(*config)
case "bitbucket":
remote_ = bitbucket.Load(*config)
case "gogs":
remote_ = gogs.Load(*config)
case "gitlab":
remote_ = gitlab.Load(*config)
default:
logrus.Fatalln("remote configuraiton not found")
}
return func(c *gin.Context) {
remote.ToContext(c, remote_)
c.Next()
}
}

View file

@ -0,0 +1,29 @@
package middleware
import (
"github.com/drone/drone/store"
"github.com/drone/drone/store/datastore"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/ianschenck/envflag"
)
var (
database = envflag.String("DATABASE_DRIVER", "sqlite3", "")
datasource = envflag.String("DATABASE_CONFIG", "drone.sqlite", "")
)
// Store is a middleware function that initializes the Datastore and attaches to
// the context of every http.Request.
func Store() gin.HandlerFunc {
db := datastore.New(*database, *datasource)
logrus.Infof("using database driver %s", *database)
logrus.Infof("using database config %s", *datasource)
return func(c *gin.Context) {
store.ToContext(c, db)
c.Next()
}
}

View file

@ -0,0 +1,14 @@
package middleware
import (
"github.com/drone/drone/version"
"github.com/gin-gonic/gin"
)
// Version is a middleware function that appends the Drone
// version information to the HTTP response. This is intended
// for debugging and troubleshooting.
func Version(c *gin.Context) {
c.Header("X-DRONE-VERSION", version.Version)
c.Next()
}

View file

@ -8,7 +8,6 @@ import (
"github.com/drone/drone/api" "github.com/drone/drone/api"
"github.com/drone/drone/router/middleware/header" "github.com/drone/drone/router/middleware/header"
"github.com/drone/drone/router/middleware/location"
"github.com/drone/drone/router/middleware/session" "github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/router/middleware/token" "github.com/drone/drone/router/middleware/token"
"github.com/drone/drone/static" "github.com/drone/drone/static"
@ -23,7 +22,6 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
e.SetHTMLTemplate(template.Load()) e.SetHTMLTemplate(template.Load())
e.StaticFS("/static", static.FileSystem()) e.StaticFS("/static", static.FileSystem())
e.Use(location.Resolve)
e.Use(header.NoCache) e.Use(header.NoCache)
e.Use(header.Options) e.Use(header.Options)
e.Use(header.Secure) e.Use(header.Secure)
@ -146,6 +144,7 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
{ {
bots.Use(session.MustUser()) bots.Use(session.MustUser())
bots.POST("/slack", web.Slack) bots.POST("/slack", web.Slack)
bots.POST("/slack/:command", web.Slack)
} }
auth := e.Group("/authorize") auth := e.Group("/authorize")

View file

@ -1,117 +0,0 @@
package envconfig
import (
"bufio"
"errors"
"os"
"strconv"
"strings"
)
type Env map[string]string
// Get returns the value of the environment variable named by the key.
func (env Env) Get(key string) string {
return env[key]
}
// String returns the string value of the environment variable named by the
// key. If the variable is not present, the default value is returned.
func (env Env) String(key, value string) string {
got, ok := env[key]
if ok {
value = got
}
return value
}
// Bool returns the boolean value of the environment variable named by the key.
// If the variable is not present, the default value is returned.
func (env Env) Bool(name string, value bool) bool {
got, ok := env[name]
if ok {
value, _ = strconv.ParseBool(got)
}
return value
}
// Int returns the integer value of the environment variable named by the key.
// If the variable is not present, the default value is returned.
func (env Env) Int(name string, value int) int {
got, ok := env[name]
if ok {
value, _ = strconv.Atoi(got)
}
return value
}
// Load reads the environment file and reads variables in "key=value" format.
// Then it read the system environment variables. It returns the combined
// results in a key value map.
func Load(filepath string) Env {
var envs = map[string]string{}
// load the environment file
f, err := os.Open(filepath)
if err == nil {
defer f.Close()
r := bufio.NewReader(f)
for {
line, _, err := r.ReadLine()
if err != nil {
break
}
key, val, err := parseln(string(line))
if err != nil {
continue
}
os.Setenv(key, val)
}
}
// load the environment variables
for _, env := range os.Environ() {
key, val, err := parseln(env)
if err != nil {
continue
}
envs[key] = val
}
return Env(envs)
}
// helper function to parse a "key=value" environment variable string.
func parseln(line string) (key string, val string, err error) {
line = removeComments(line)
if len(line) == 0 {
return
}
splits := strings.SplitN(line, "=", 2)
if len(splits) < 2 {
err = errors.New("missing delimiter '='")
return
}
key = strings.Trim(splits[0], " ")
val = strings.Trim(splits[1], ` "'`)
return
}
// helper function to trim comments and whitespace from a string.
func removeComments(s string) (_ string) {
if len(s) == 0 || string(s[0]) == "#" {
return
} else {
index := strings.Index(s, " #")
if index > -1 {
s = strings.TrimSpace(s[0:index])
}
}
return s
}

View file

@ -5,7 +5,6 @@ import (
"os" "os"
"time" "time"
"github.com/drone/drone/shared/envconfig"
"github.com/drone/drone/store" "github.com/drone/drone/store"
"github.com/drone/drone/store/datastore/ddl" "github.com/drone/drone/store/datastore/ddl"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@ -23,20 +22,6 @@ type datastore struct {
*sql.DB *sql.DB
} }
// Load opens a new database connection with the specified driver
// and connection string specified in the environment variables.
func Load(env envconfig.Env) store.Store {
var (
driver = env.String("DATABASE_DRIVER", "sqlite3")
config = env.String("DATABASE_CONFIG", "drone.sqlite")
)
logrus.Infof("using database driver %s", driver)
logrus.Infof("using database config %s", config)
return New(driver, config)
}
// New creates a database connection for the given driver and datasource // New creates a database connection for the given driver and datasource
// and returns a new Store. // and returns a new Store.
func New(driver, config string) store.Store { func New(driver, config string) store.Store {

View file

@ -14,7 +14,6 @@ import (
"github.com/drone/drone/engine/parser" "github.com/drone/drone/engine/parser"
"github.com/drone/drone/model" "github.com/drone/drone/model"
"github.com/drone/drone/remote" "github.com/drone/drone/remote"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/shared/httputil" "github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/token" "github.com/drone/drone/shared/token"
"github.com/drone/drone/store" "github.com/drone/drone/store"
@ -205,7 +204,7 @@ func PostHook(c *gin.Context) {
// on status change notifications // on status change notifications
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
engine_ := context.Engine(c) engine_ := engine.FromContext(c)
go engine_.Schedule(c.Copy(), &engine.Task{ go engine_.Schedule(c.Copy(), &engine.Task{
User: user, User: user,
Repo: repo, Repo: repo,

View file

@ -1,9 +1,113 @@
package web package web
import "github.com/gin-gonic/gin" import (
"strings"
"github.com/drone/drone/store"
"github.com/gin-gonic/gin"
)
const (
slashDeploy = "deploy"
slashRestart = "restart"
slashStatus = "status"
)
// Slack is handler function that handles Slack slash commands. // Slack is handler function that handles Slack slash commands.
func Slack(c *gin.Context) { func Slack(c *gin.Context) {
command := c.Param("command")
text := c.PostForm("text") text := c.PostForm("text")
c.String(200, "received message %s", text) args := strings.Split(text, " ")
if command == "" {
command = args[0]
args = args[1:]
}
switch command {
case slashStatus:
slackStatus(c, args)
case slashRestart:
slackRestart(c, args)
case slashDeploy:
slackDeploy(c, args)
default:
c.String(200, "sorry, I didn't understand [%s]", text)
}
}
func slackDeploy(c *gin.Context, args []string) {
if len(args) != 3 {
c.String(200, "Invalid command. Please provide [repo] [build number] [environment]")
return
}
var (
repo = args[0]
num = args[1]
env = args[2]
)
owner, name, _ := parseRepoBranch(repo)
c.String(200, "deploying build %s/%s#%s to %s", owner, name, num, env)
}
func slackRestart(c *gin.Context, args []string) {
var (
repo = args[0]
num = args[1]
)
owner, name, _ := parseRepoBranch(repo)
c.String(200, "restarting build %s/%s#%s", owner, name, num)
}
func slackStatus(c *gin.Context, args []string) {
var (
owner string
name string
branch string
)
if len(args) > 0 {
owner, name, branch = parseRepoBranch(args[0])
}
repo, err := store.GetRepoOwnerName(c, owner, name)
if err != nil {
c.String(200, "cannot find repository %s/%s", owner, name)
return
}
if branch == "" {
branch = repo.Branch
}
build, err := store.GetBuildLast(c, repo, branch)
if err != nil {
c.String(200, "cannot find status for %s/%s@%s", owner, name, branch)
return
}
c.String(200, "%s@%s build number %d finished with status %s",
repo.FullName,
build.Branch,
build.Number,
build.Status,
)
}
func parseRepoBranch(repo string) (owner, name, branch string) {
parts := strings.Split(repo, "@")
if len(parts) == 2 {
branch = parts[1]
repo = parts[0]
}
parts = strings.Split(repo, "/")
if len(parts) == 2 {
owner = parts[0]
name = parts[1]
}
return owner, name, branch
} }

View file

@ -8,7 +8,6 @@ import (
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/drone/drone/engine" "github.com/drone/drone/engine"
"github.com/drone/drone/router/middleware/context"
"github.com/drone/drone/router/middleware/session" "github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/store" "github.com/drone/drone/store"
@ -20,7 +19,7 @@ import (
// GetRepoEvents will upgrade the connection to a Websocket and will stream // GetRepoEvents will upgrade the connection to a Websocket and will stream
// event updates to the browser. // event updates to the browser.
func GetRepoEvents(c *gin.Context) { func GetRepoEvents(c *gin.Context) {
engine_ := context.Engine(c) engine_ := engine.FromContext(c)
repo := session.Repo(c) repo := session.Repo(c)
c.Writer.Header().Set("Content-Type", "text/event-stream") c.Writer.Header().Set("Content-Type", "text/event-stream")
@ -55,7 +54,7 @@ func GetRepoEvents(c *gin.Context) {
func GetStream(c *gin.Context) { func GetStream(c *gin.Context) {
engine_ := context.Engine(c) engine_ := engine.FromContext(c)
repo := session.Repo(c) repo := session.Repo(c)
buildn, _ := strconv.Atoi(c.Param("build")) buildn, _ := strconv.Atoi(c.Param("build"))
jobn, _ := strconv.Atoi(c.Param("number")) jobn, _ := strconv.Atoi(c.Param("number"))