mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-27 12:21:03 +00:00
Merge pull request #1570 from bradrydzewski/master
stub handler for slash commands
This commit is contained in:
commit
7129cbf184
27 changed files with 309 additions and 416 deletions
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/router/middleware/context"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
)
|
||||
|
||||
|
@ -130,7 +129,7 @@ func GetBuildLogs(c *gin.Context) {
|
|||
}
|
||||
|
||||
func DeleteBuild(c *gin.Context) {
|
||||
engine_ := context.Engine(c)
|
||||
engine_ := engine.FromContext(c)
|
||||
repo := session.Repo(c)
|
||||
|
||||
// parse the build number and job sequence number from
|
||||
|
@ -281,7 +280,7 @@ func PostBuild(c *gin.Context) {
|
|||
// on status change notifications
|
||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||
|
||||
engine_ := context.Engine(c)
|
||||
engine_ := engine.FromContext(c)
|
||||
go engine_.Schedule(c.Copy(), &engine.Task{
|
||||
User: user,
|
||||
Repo: repo,
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/engine"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/router/middleware/context"
|
||||
"github.com/drone/drone/store"
|
||||
)
|
||||
|
||||
|
@ -25,7 +25,7 @@ func GetNode(c *gin.Context) {
|
|||
}
|
||||
|
||||
func PostNode(c *gin.Context) {
|
||||
engine := context.Engine(c)
|
||||
engine := engine.FromContext(c)
|
||||
|
||||
in := struct {
|
||||
Addr string `json:"address"`
|
||||
|
@ -63,7 +63,7 @@ func PostNode(c *gin.Context) {
|
|||
}
|
||||
|
||||
func DeleteNode(c *gin.Context) {
|
||||
engine := context.Engine(c)
|
||||
engine := engine.FromContext(c)
|
||||
|
||||
id, _ := strconv.Atoi(c.Param("node"))
|
||||
node, err := store.GetNode(c, int64(id))
|
||||
|
|
30
drone.go
30
drone.go
|
@ -4,14 +4,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/engine"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/router"
|
||||
"github.com/drone/drone/router/middleware/cache"
|
||||
"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/drone/drone/router/middleware"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/contrib/ginrus"
|
||||
|
@ -37,26 +31,14 @@ func main() {
|
|||
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
|
||||
handler := router.Load(
|
||||
ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true),
|
||||
header.Version,
|
||||
cache.Default(),
|
||||
context.SetStore(store_),
|
||||
context.SetRemote(remote_),
|
||||
context.SetEngine(engine_),
|
||||
middleware.Version,
|
||||
middleware.Cache(),
|
||||
middleware.Store(),
|
||||
middleware.Remote(),
|
||||
middleware.Engine(),
|
||||
)
|
||||
|
||||
if *cert != "" {
|
||||
|
|
23
engine/context.go
Normal file
23
engine/context.go
Normal 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)
|
||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
|
@ -15,7 +16,6 @@ import (
|
|||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/docker"
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/samalba/dockerclient"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -60,7 +60,7 @@ type engine struct {
|
|||
// 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
|
||||
// start accepting workloads.
|
||||
func Load(env envconfig.Env, s store.Store) Engine {
|
||||
func Load(s store.Store) Engine {
|
||||
engine := &engine{}
|
||||
engine.bus = newEventbus()
|
||||
engine.pool = newPool()
|
||||
|
@ -70,7 +70,7 @@ func Load(env envconfig.Env, s store.Store) Engine {
|
|||
// throughout the build environment.
|
||||
var proxyVars = []string{"HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"}
|
||||
for _, proxyVar := range proxyVars {
|
||||
proxyVal := env.Get(proxyVar)
|
||||
proxyVal := os.Getenv(proxyVar)
|
||||
if len(proxyVal) != 0 {
|
||||
engine.envs = append(engine.envs, proxyVar+"="+proxyVal)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
@ -24,8 +23,7 @@ type Bitbucket struct {
|
|||
Open bool
|
||||
}
|
||||
|
||||
func Load(env envconfig.Env) *Bitbucket {
|
||||
config := env.String("REMOTE_CONFIG", "")
|
||||
func Load(config string) *Bitbucket {
|
||||
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/oauth2"
|
||||
|
||||
|
@ -42,8 +41,7 @@ type Github struct {
|
|||
GitSSH bool
|
||||
}
|
||||
|
||||
func Load(env envconfig.Env) *Github {
|
||||
config := env.String("REMOTE_CONFIG", "")
|
||||
func Load(config string) *Github {
|
||||
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/franela/goblin"
|
||||
)
|
||||
|
||||
|
@ -48,12 +47,11 @@ func TestHook(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
env := envconfig.Env{
|
||||
"REMOTE_CONFIG": "https://github.com?client_id=client&client_secret=secret&scope=scope1,scope2",
|
||||
}
|
||||
g := Load(env)
|
||||
conf := "https://github.com?client_id=client&client_secret=secret&scope=scope1,scope2"
|
||||
|
||||
g := Load(conf)
|
||||
if g.URL != "https://github.com" {
|
||||
t.Errorf("g.URL = %q; want https://github.com")
|
||||
t.Errorf("g.URL = %q; want https://github.com", g.URL)
|
||||
}
|
||||
if g.Client != "client" {
|
||||
t.Errorf("g.Client = %q; want client", g.Client)
|
||||
|
@ -71,7 +69,7 @@ func TestLoad(t *testing.T) {
|
|||
t.Errorf("g.MergeRef = %q; want %q", g.MergeRef, DefaultMergeRef)
|
||||
}
|
||||
|
||||
g = Load(envconfig.Env{})
|
||||
g = Load("")
|
||||
if g.Scope != DefaultScope {
|
||||
t.Errorf("g.Scope = %q; want %q", g.Scope, DefaultScope)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/oauth2"
|
||||
"github.com/drone/drone/shared/token"
|
||||
|
@ -35,9 +34,7 @@ type Gitlab struct {
|
|||
Search bool
|
||||
}
|
||||
|
||||
func Load(env envconfig.Env) *Gitlab {
|
||||
config := env.String("REMOTE_CONFIG", "")
|
||||
|
||||
func Load(config string) *Gitlab {
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -15,8 +15,7 @@ func Test_Gitlab(t *testing.T) {
|
|||
var server = testdata.NewServer()
|
||||
defer server.Close()
|
||||
|
||||
env := map[string]string{}
|
||||
env["REMOTE_CONFIG"] = server.URL + "?client_id=test&client_secret=test"
|
||||
env := server.URL + "?client_id=test&client_secret=test"
|
||||
|
||||
gitlab := Load(env)
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/gogits/go-gogs-client"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
@ -22,9 +21,7 @@ type Gogs struct {
|
|||
SkipVerify bool
|
||||
}
|
||||
|
||||
func Load(env envconfig.Env) *Gogs {
|
||||
config := env.String("REMOTE_CONFIG", "")
|
||||
|
||||
func Load(config string) *Gogs {
|
||||
// parse the remote DSN configuration string
|
||||
url_, err := url.Parse(config)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,36 +6,10 @@ import (
|
|||
"net/http"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
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 {
|
||||
// Login authenticates the session and returns the
|
||||
// remote user details.
|
||||
|
|
22
router/middleware/cache.go
Normal file
22
router/middleware/cache.go
Normal 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()
|
||||
}
|
||||
}
|
14
router/middleware/cache/cache.go
vendored
14
router/middleware/cache/cache.go
vendored
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
28
router/middleware/engine.go
Normal file
28
router/middleware/engine.go
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
})
|
||||
})
|
||||
}
|
45
router/middleware/remote.go
Normal file
45
router/middleware/remote.go
Normal 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()
|
||||
}
|
||||
}
|
29
router/middleware/store.go
Normal file
29
router/middleware/store.go
Normal 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()
|
||||
}
|
||||
}
|
14
router/middleware/version.go
Normal file
14
router/middleware/version.go
Normal 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()
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/drone/drone/api"
|
||||
"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/token"
|
||||
"github.com/drone/drone/static"
|
||||
|
@ -23,7 +22,6 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
|||
e.SetHTMLTemplate(template.Load())
|
||||
e.StaticFS("/static", static.FileSystem())
|
||||
|
||||
e.Use(location.Resolve)
|
||||
e.Use(header.NoCache)
|
||||
e.Use(header.Options)
|
||||
e.Use(header.Secure)
|
||||
|
@ -142,6 +140,13 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
|
|||
stream.GET("/:owner/:name/:build/:number", web.GetStream)
|
||||
}
|
||||
|
||||
bots := e.Group("/bots")
|
||||
{
|
||||
bots.Use(session.MustUser())
|
||||
bots.POST("/slack", web.Slack)
|
||||
bots.POST("/slack/:command", web.Slack)
|
||||
}
|
||||
|
||||
auth := e.Group("/authorize")
|
||||
{
|
||||
auth.GET("", web.GetLogin)
|
||||
|
@ -172,7 +177,7 @@ func normalize(h http.Handler) http.Handler {
|
|||
|
||||
parts := strings.Split(r.URL.Path, "/")[1:]
|
||||
switch parts[0] {
|
||||
case "settings", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
|
||||
case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
|
||||
// no-op
|
||||
default:
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/shared/envconfig"
|
||||
"github.com/drone/drone/store"
|
||||
"github.com/drone/drone/store/datastore/ddl"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
@ -23,20 +22,6 @@ type datastore struct {
|
|||
*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
|
||||
// and returns a new Store.
|
||||
func New(driver, config string) store.Store {
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/drone/drone/engine/parser"
|
||||
"github.com/drone/drone/model"
|
||||
"github.com/drone/drone/remote"
|
||||
"github.com/drone/drone/router/middleware/context"
|
||||
"github.com/drone/drone/shared/httputil"
|
||||
"github.com/drone/drone/shared/token"
|
||||
"github.com/drone/drone/store"
|
||||
|
@ -205,7 +204,7 @@ func PostHook(c *gin.Context) {
|
|||
// on status change notifications
|
||||
last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID)
|
||||
|
||||
engine_ := context.Engine(c)
|
||||
engine_ := engine.FromContext(c)
|
||||
go engine_.Schedule(c.Copy(), &engine.Task{
|
||||
User: user,
|
||||
Repo: repo,
|
||||
|
|
113
web/slack.go
Normal file
113
web/slack.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
package web
|
||||
|
||||
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.
|
||||
func Slack(c *gin.Context) {
|
||||
command := c.Param("command")
|
||||
text := c.PostForm("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
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/drone/drone/engine"
|
||||
"github.com/drone/drone/router/middleware/context"
|
||||
"github.com/drone/drone/router/middleware/session"
|
||||
"github.com/drone/drone/store"
|
||||
|
||||
|
@ -20,7 +19,7 @@ import (
|
|||
// GetRepoEvents will upgrade the connection to a Websocket and will stream
|
||||
// event updates to the browser.
|
||||
func GetRepoEvents(c *gin.Context) {
|
||||
engine_ := context.Engine(c)
|
||||
engine_ := engine.FromContext(c)
|
||||
repo := session.Repo(c)
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
|
||||
|
@ -55,7 +54,7 @@ func GetRepoEvents(c *gin.Context) {
|
|||
|
||||
func GetStream(c *gin.Context) {
|
||||
|
||||
engine_ := context.Engine(c)
|
||||
engine_ := engine.FromContext(c)
|
||||
repo := session.Repo(c)
|
||||
buildn, _ := strconv.Atoi(c.Param("build"))
|
||||
jobn, _ := strconv.Atoi(c.Param("number"))
|
||||
|
|
Loading…
Reference in a new issue