diff --git a/.gitignore b/.gitignore index 01abb6461..3252b2c16 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ drone/drone .env temp/ -api/swagger/files/* +server/swagger/files/*.json # vendored repositories that we don't actually need # to vendor. so exclude them diff --git a/Dockerfile b/Dockerfile index 9a634564d..363afd3b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,4 +21,4 @@ ADD drone/drone /drone #RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf ENTRYPOINT ["/drone"] -CMD ["serve"] +CMD ["daemon"] diff --git a/api/swagger/swagger.go b/api/swagger/swagger.go deleted file mode 100644 index 7921031e5..000000000 --- a/api/swagger/swagger.go +++ /dev/null @@ -1,3 +0,0 @@ -package swagger - -//go:generate go-bindata -pkg swagger -o swagger_gen.go files/ diff --git a/drone/daemon.go b/drone/daemon.go index bb8e0d898..91b271ccb 100644 --- a/drone/daemon.go +++ b/drone/daemon.go @@ -6,20 +6,9 @@ import ( "os" "time" - "github.com/drone/drone/bus" - "github.com/drone/drone/cache" - "github.com/drone/drone/queue" - "github.com/drone/drone/remote" - "github.com/drone/drone/remote/bitbucket" - "github.com/drone/drone/remote/bitbucketserver" - "github.com/drone/drone/remote/github" - "github.com/drone/drone/remote/gitlab" - "github.com/drone/drone/remote/gogs" - "github.com/drone/drone/server" - "github.com/drone/drone/shared/token" - "github.com/drone/drone/store" - "github.com/drone/drone/store/datastore" - "github.com/drone/drone/stream" + "github.com/drone/drone/router" + "github.com/drone/drone/router/middleware" + "github.com/gin-gonic/contrib/ginrus" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -300,22 +289,19 @@ func start(c *cli.Context) error { logrus.SetLevel(logrus.WarnLevel) } - // print the agent secret to the console - // TODO(bradrydzewski) this overall approach should be re-considered - if err := printSecret(c); err != nil { - return err - } - // setup the server and start the listener - server := server.Server{ - Bus: setupBus(c), - Cache: setupCache(c), - Config: setupConfig(c), - Queue: setupQueue(c), - Remote: setupRemote(c), - Stream: setupStream(c), - Store: setupStore(c), - } + handler := router.Load( + ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true), + middleware.Version, + middleware.Config(c), + middleware.Queue(c), + middleware.Stream(c), + middleware.Bus(c), + middleware.Cache(c), + middleware.Store(c), + middleware.Remote(c), + middleware.Agents(c), + ) // start the server with tls enabled if c.String("server-cert") != "" { @@ -323,150 +309,151 @@ func start(c *cli.Context) error { c.String("server-addr"), c.String("server-cert"), c.String("server-key"), - server.Handler(), + handler, ) } // start the server without tls enabled return http.ListenAndServe( c.String("server-addr"), - server.Handler(), + handler, ) } -func setupCache(c *cli.Context) cache.Cache { - return cache.NewTTL( - c.Duration("cache-ttl"), - ) -} - -func setupBus(c *cli.Context) bus.Bus { - return bus.New() -} - -func setupQueue(c *cli.Context) queue.Queue { - return queue.New() -} - -func setupStream(c *cli.Context) stream.Stream { - return stream.New() -} - -func setupStore(c *cli.Context) store.Store { - return datastore.New( - c.String("driver"), - c.String("datasource"), - ) -} - -func setupRemote(c *cli.Context) remote.Remote { - var remote remote.Remote - var err error - switch { - case c.Bool("github"): - remote, err = setupGithub(c) - case c.Bool("gitlab"): - remote, err = setupGitlab(c) - case c.Bool("bitbucket"): - remote, err = setupBitbucket(c) - case c.Bool("stash"): - remote, err = setupStash(c) - case c.Bool("gogs"): - remote, err = setupGogs(c) - default: - err = fmt.Errorf("version control system not configured") - } - if err != nil { - logrus.Fatalln(err) - } - return remote -} - -func setupBitbucket(c *cli.Context) (remote.Remote, error) { - return bitbucket.New( - c.String("bitbucket-client"), - c.String("bitbucket-server"), - ), nil -} - -func setupGogs(c *cli.Context) (remote.Remote, error) { - return gogs.New(gogs.Opts{ - URL: c.String("gogs-server"), - Username: c.String("gogs-git-username"), - Password: c.String("gogs-git-password"), - PrivateMode: c.Bool("gogs-private-mode"), - SkipVerify: c.Bool("gogs-skip-verify"), - }) -} - -func setupStash(c *cli.Context) (remote.Remote, error) { - return bitbucketserver.New(bitbucketserver.Opts{ - URL: c.String("stash-server"), - Username: c.String("stash-git-username"), - Password: c.String("stash-git-password"), - ConsumerKey: c.String("stash-consumer-key"), - ConsumerRSA: c.String("stash-consumer-rsa"), - SkipVerify: c.Bool("stash-skip-verify"), - }) -} - -func setupGitlab(c *cli.Context) (remote.Remote, error) { - return gitlab.New(gitlab.Opts{ - URL: c.String("gitlab-server"), - Client: c.String("gitlab-client"), - Secret: c.String("gitlab-sercret"), - Username: c.String("gitlab-git-username"), - Password: c.String("gitlab-git-password"), - PrivateMode: c.Bool("gitlab-private-mode"), - SkipVerify: c.Bool("gitlab-skip-verify"), - }) -} - -func setupGithub(c *cli.Context) (remote.Remote, error) { - return github.New( - c.String("github-server"), - c.String("github-client"), - c.String("github-sercret"), - c.StringSlice("github-scope"), - c.Bool("github-private-mode"), - c.Bool("github-skip-verify"), - c.BoolT("github-merge-ref"), - ) -} - -func setupConfig(c *cli.Context) *server.Config { - return &server.Config{ - Open: c.Bool("open"), - Yaml: c.String("yaml"), - Secret: c.String("agent-secret"), - Admins: sliceToMap(c.StringSlice("admin")), - Orgs: sliceToMap(c.StringSlice("orgs")), - } -} - -func sliceToMap(s []string) map[string]bool { - v := map[string]bool{} - for _, ss := range s { - v[ss] = true - } - return v -} - -func printSecret(c *cli.Context) error { - secret := c.String("agent-secret") - if secret == "" { - return fmt.Errorf("missing DRONE_AGENT_SECRET configuration parameter") - } - t := token.New(secret, "") - s, err := t.Sign(secret) - if err != nil { - return fmt.Errorf("invalid value for DRONE_AGENT_SECRET. %s", s) - } - - logrus.Infof("using agent secret %s", secret) - logrus.Warnf("agents can connect with token %s", s) - return nil -} +// +// func setupCache(c *cli.Context) cache.Cache { +// return cache.NewTTL( +// c.Duration("cache-ttl"), +// ) +// } +// +// func setupBus(c *cli.Context) bus.Bus { +// return bus.New() +// } +// +// func setupQueue(c *cli.Context) queue.Queue { +// return queue.New() +// } +// +// func setupStream(c *cli.Context) stream.Stream { +// return stream.New() +// } +// +// func setupStore(c *cli.Context) store.Store { +// return datastore.New( +// c.String("driver"), +// c.String("datasource"), +// ) +// } +// +// func setupRemote(c *cli.Context) remote.Remote { +// var remote remote.Remote +// var err error +// switch { +// case c.Bool("github"): +// remote, err = setupGithub(c) +// case c.Bool("gitlab"): +// remote, err = setupGitlab(c) +// case c.Bool("bitbucket"): +// remote, err = setupBitbucket(c) +// case c.Bool("stash"): +// remote, err = setupStash(c) +// case c.Bool("gogs"): +// remote, err = setupGogs(c) +// default: +// err = fmt.Errorf("version control system not configured") +// } +// if err != nil { +// logrus.Fatalln(err) +// } +// return remote +// } +// +// func setupBitbucket(c *cli.Context) (remote.Remote, error) { +// return bitbucket.New( +// c.String("bitbucket-client"), +// c.String("bitbucket-server"), +// ), nil +// } +// +// func setupGogs(c *cli.Context) (remote.Remote, error) { +// return gogs.New(gogs.Opts{ +// URL: c.String("gogs-server"), +// Username: c.String("gogs-git-username"), +// Password: c.String("gogs-git-password"), +// PrivateMode: c.Bool("gogs-private-mode"), +// SkipVerify: c.Bool("gogs-skip-verify"), +// }) +// } +// +// func setupStash(c *cli.Context) (remote.Remote, error) { +// return bitbucketserver.New(bitbucketserver.Opts{ +// URL: c.String("stash-server"), +// Username: c.String("stash-git-username"), +// Password: c.String("stash-git-password"), +// ConsumerKey: c.String("stash-consumer-key"), +// ConsumerRSA: c.String("stash-consumer-rsa"), +// SkipVerify: c.Bool("stash-skip-verify"), +// }) +// } +// +// func setupGitlab(c *cli.Context) (remote.Remote, error) { +// return gitlab.New(gitlab.Opts{ +// URL: c.String("gitlab-server"), +// Client: c.String("gitlab-client"), +// Secret: c.String("gitlab-sercret"), +// Username: c.String("gitlab-git-username"), +// Password: c.String("gitlab-git-password"), +// PrivateMode: c.Bool("gitlab-private-mode"), +// SkipVerify: c.Bool("gitlab-skip-verify"), +// }) +// } +// +// func setupGithub(c *cli.Context) (remote.Remote, error) { +// return github.New( +// c.String("github-server"), +// c.String("github-client"), +// c.String("github-sercret"), +// c.StringSlice("github-scope"), +// c.Bool("github-private-mode"), +// c.Bool("github-skip-verify"), +// c.BoolT("github-merge-ref"), +// ) +// } +// +// func setupConfig(c *cli.Context) *server.Config { +// return &server.Config{ +// Open: c.Bool("open"), +// Yaml: c.String("yaml"), +// Secret: c.String("agent-secret"), +// Admins: sliceToMap(c.StringSlice("admin")), +// Orgs: sliceToMap(c.StringSlice("orgs")), +// } +// } +// +// func sliceToMap(s []string) map[string]bool { +// v := map[string]bool{} +// for _, ss := range s { +// v[ss] = true +// } +// return v +// } +// +// func printSecret(c *cli.Context) error { +// secret := c.String("agent-secret") +// if secret == "" { +// return fmt.Errorf("missing DRONE_AGENT_SECRET configuration parameter") +// } +// t := token.New(secret, "") +// s, err := t.Sign(secret) +// if err != nil { +// return fmt.Errorf("invalid value for DRONE_AGENT_SECRET. %s", s) +// } +// +// logrus.Infof("using agent secret %s", secret) +// logrus.Warnf("agents can connect with token %s", s) +// return nil +// } var agreement = ` --- diff --git a/model/config.go b/model/config.go new file mode 100644 index 000000000..393d5e3ba --- /dev/null +++ b/model/config.go @@ -0,0 +1,26 @@ +package model + +// Config defines system configuration parameters. +type Config struct { + Open bool // Enables open registration + Yaml string // Customize the Yaml configuration file name + Shasum string // Customize the Yaml checksum file name + Secret string // Secret token used to authenticate agents + Admins map[string]bool // Administrative users + Orgs map[string]bool // Organization whitelist +} + +// IsAdmin returns true if the user is a member of the administrator list. +func (c *Config) IsAdmin(user *User) bool { + return c.Admins[user.Login] +} + +// IsMember returns true if the user is a member of the whitelisted teams. +func (c *Config) IsMember(teams []*Team) bool { + for _, team := range teams { + if c.Orgs[team.Login] { + return true + } + } + return false +} diff --git a/model/user.go b/model/user.go index e33d1fc3e..fdd46168a 100644 --- a/model/user.go +++ b/model/user.go @@ -32,11 +32,17 @@ type User struct { Avatar string `json:"avatar_url" meddler:"user_avatar"` // Activate indicates the user is active in the system. - Active bool `json:"active," meddler:"user_active"` + Active bool `json:"active" meddler:"user_active"` // Admin indicates the user is a system administrator. - Admin bool `json:"admin," meddler:"user_admin"` + // + // NOTE: This is sourced from the DRONE_ADMINS environment variable and is no + // longer persisted in the database. + Admin bool `json:"admin,omitempty" meddler:"-"` // Hash is a unique token used to sign tokens. Hash string `json:"-" meddler:"user_hash"` + + // DEPRECATED Admin indicates the user is a system administrator. + XAdmin bool `json:"-" meddler:"user_admin"` } diff --git a/remote/cache.go b/remote/cache.go deleted file mode 100644 index 032edad93..000000000 --- a/remote/cache.go +++ /dev/null @@ -1,85 +0,0 @@ -package remote - -import ( - "time" - - "github.com/drone/drone/model" -) - -// WithCache returns a the parent Remote with a front-end Cache. Remote items -// are cached for duration d. -func WithCache(r Remote, d time.Duration) Remote { - return r -} - -// Cacher implements purge functionality so that we can evict stale data and -// force a refresh. The indended use case is when the repository list is out -// of date and requires manual refresh. -type Cacher interface { - Purge(*model.User) -} - -// Because the cache is so closely tied to the remote we should just include -// them in the same package together. The below code are stubs for merging -// the Cache with the Remote package. - -type cache struct { - Remote -} - -func (c *cache) Repos(u *model.User) ([]*model.RepoLite, error) { - // key := fmt.Sprintf("repos:%s", - // user.Login, - // ) - // // if we fetch from the cache we can return immediately - // val, err := Get(c, key) - // if err == nil { - // return val.([]*model.RepoLite), nil - // } - // // else we try to grab from the remote system and - // // populate our cache. - // repos, err := remote.Repos(c, user) - // if err != nil { - // return nil, err - // } - // - // Set(c, key, repos) - // return repos, nil - return nil, nil -} - -func (c *cache) Perm(u *model.User, owner, repo string) (*model.Perm, error) { - // key := fmt.Sprintf("perms:%s:%s/%s", - // user.Login, - // owner, - // name, - // ) - // // if we fetch from the cache we can return immediately - // val, err := Get(c, key) - // if err == nil { - // return val.(*model.Perm), nil - // } - // // else we try to grab from the remote system and - // // populate our cache. - // perm, err := remote.Perm(c, user, owner, name) - // if err != nil { - // return nil, err - // } - // Set(c, key, perm) - // return perm, nil - return nil, nil -} - -func (c *cache) Purge(*model.User) { - return -} - -func (c *cache) Refresh(u *model.User) (bool, error) { - if r, ok := c.Remote.(Refresher); ok { - return r.Refresh(u) - } - return false, nil -} - -var _ Remote = &cache{} -var _ Refresher = &cache{} diff --git a/router/middleware/agent.go b/router/middleware/agent.go index 0a3265519..f3d884a72 100644 --- a/router/middleware/agent.go +++ b/router/middleware/agent.go @@ -1,45 +1,33 @@ package middleware import ( + "github.com/codegangsta/cli" "github.com/drone/drone/shared/token" "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" - "github.com/ianschenck/envflag" ) -var ( - secret = envflag.String("DRONE_AGENT_SECRET", "", "") - noauth = envflag.Bool("AGENT_NO_AUTH", false, "") -) +const agentKey = "agent" -// Agent is a middleware function that initializes the authorization middleware +// Agents is a middleware function that initializes the authorization middleware // for agents to connect to the queue. -func AgentMust() gin.HandlerFunc { - - if *secret == "" { - logrus.Fatalf("please provide the agent secret to authenticate agent requests") +func Agents(cli *cli.Context) gin.HandlerFunc { + secret := cli.String("agent-secret") + if secret == "" { + logrus.Fatalf("failed to generate token from DRONE_AGENT_SECRET") } - t := token.New(token.AgentToken, "") - s, err := t.Sign(*secret) + t := token.New(secret, "") + s, err := t.Sign(secret) if err != nil { - logrus.Fatalf("invalid agent secret. %s", err) + logrus.Fatalf("failed to generate token from DRONE_AGENT_SECRET. %s", err) } - logrus.Infof("using agent secret %s", *secret) + logrus.Infof("using agent secret %s", secret) logrus.Warnf("agents can connect with token %s", s) return func(c *gin.Context) { - parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { - return *secret, nil - }) - if err != nil { - c.AbortWithError(403, err) - } else if parsed.Kind != token.AgentToken { - c.AbortWithStatus(403) - } else { - c.Next() - } + c.Set(agentKey, secret) } } diff --git a/router/middleware/bus.go b/router/middleware/bus.go new file mode 100644 index 000000000..25665da1d --- /dev/null +++ b/router/middleware/bus.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/drone/drone/bus" + + "github.com/codegangsta/cli" + "github.com/gin-gonic/gin" +) + +// Bus is a middleware function that initializes the Event Bus and attaches to +// the context of every http.Request. +func Bus(cli *cli.Context) gin.HandlerFunc { + v := bus.New() + return func(c *gin.Context) { + bus.ToContext(c, v) + } +} diff --git a/router/middleware/cache.go b/router/middleware/cache.go new file mode 100644 index 000000000..6f6dec465 --- /dev/null +++ b/router/middleware/cache.go @@ -0,0 +1,24 @@ +package middleware + +import ( + "github.com/drone/drone/cache" + + "github.com/codegangsta/cli" + "github.com/gin-gonic/gin" +) + +// Cache is a middleware function that initializes the Cache and attaches to +// the context of every http.Request. +func Cache(cli *cli.Context) gin.HandlerFunc { + v := setupCache(cli) + return func(c *gin.Context) { + cache.ToContext(c, v) + } +} + +// helper function to create the cache from the CLI context. +func setupCache(c *cli.Context) cache.Cache { + return cache.NewTTL( + c.Duration("cache-ttl"), + ) +} diff --git a/router/middleware/config.go b/router/middleware/config.go new file mode 100644 index 000000000..e2f65dd10 --- /dev/null +++ b/router/middleware/config.go @@ -0,0 +1,40 @@ +package middleware + +import ( + "github.com/drone/drone/model" + + "github.com/codegangsta/cli" + "github.com/gin-gonic/gin" +) + +const configKey = "config" + +// Config is a middleware function that initializes the Configuration and +// attaches to the context of every http.Request. +func Config(cli *cli.Context) gin.HandlerFunc { + v := setupConfig(cli) + return func(c *gin.Context) { + c.Set(configKey, v) + } +} + +// helper function to create the configuration from the CLI context. +func setupConfig(c *cli.Context) *model.Config { + return &model.Config{ + Open: c.Bool("open"), + Yaml: c.String("yaml"), + Shasum: c.String("yaml") + ".sig", + Secret: c.String("agent-secret"), + Admins: sliceToMap(c.StringSlice("admin")), + Orgs: sliceToMap(c.StringSlice("orgs")), + } +} + +// helper function to convert a string slice to a map. +func sliceToMap(s []string) map[string]bool { + v := map[string]bool{} + for _, ss := range s { + v[ss] = true + } + return v +} diff --git a/router/middleware/queue.go b/router/middleware/queue.go new file mode 100644 index 000000000..d2791033e --- /dev/null +++ b/router/middleware/queue.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/drone/drone/queue" + + "github.com/codegangsta/cli" + "github.com/gin-gonic/gin" +) + +// Queue is a middleware function that initializes the Queue and attaches to +// the context of every http.Request. +func Queue(cli *cli.Context) gin.HandlerFunc { + v := queue.New() + return func(c *gin.Context) { + queue.ToContext(c, v) + } +} diff --git a/router/middleware/remote.go b/router/middleware/remote.go new file mode 100644 index 000000000..58881078f --- /dev/null +++ b/router/middleware/remote.go @@ -0,0 +1,102 @@ +package middleware + +import ( + "fmt" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/drone/drone/remote" + "github.com/drone/drone/remote/bitbucket" + "github.com/drone/drone/remote/bitbucketserver" + "github.com/drone/drone/remote/github" + "github.com/drone/drone/remote/gitlab" + "github.com/drone/drone/remote/gogs" + "github.com/gin-gonic/gin" +) + +// Remote is a middleware function that initializes the Remote and attaches to +// the context of every http.Request. +func Remote(c *cli.Context) gin.HandlerFunc { + v, err := setupRemote(c) + if err != nil { + logrus.Fatalln(err) + } + return func(c *gin.Context) { + remote.ToContext(c, v) + } +} + +// helper function to setup the remote from the CLI arguments. +func setupRemote(c *cli.Context) (remote.Remote, error) { + switch { + case c.Bool("github"): + return setupGithub(c) + case c.Bool("gitlab"): + return setupGitlab(c) + case c.Bool("bitbucket"): + return setupBitbucket(c) + case c.Bool("stash"): + return setupStash(c) + case c.Bool("gogs"): + return setupGogs(c) + default: + return nil, fmt.Errorf("version control system not configured") + } +} + +// helper function to setup the Bitbucket remote from the CLI arguments. +func setupBitbucket(c *cli.Context) (remote.Remote, error) { + return bitbucket.New( + c.String("bitbucket-client"), + c.String("bitbucket-server"), + ), nil +} + +// helper function to setup the Gogs remote from the CLI arguments. +func setupGogs(c *cli.Context) (remote.Remote, error) { + return gogs.New(gogs.Opts{ + URL: c.String("gogs-server"), + Username: c.String("gogs-git-username"), + Password: c.String("gogs-git-password"), + PrivateMode: c.Bool("gogs-private-mode"), + SkipVerify: c.Bool("gogs-skip-verify"), + }) +} + +// helper function to setup the Stash remote from the CLI arguments. +func setupStash(c *cli.Context) (remote.Remote, error) { + return bitbucketserver.New(bitbucketserver.Opts{ + URL: c.String("stash-server"), + Username: c.String("stash-git-username"), + Password: c.String("stash-git-password"), + ConsumerKey: c.String("stash-consumer-key"), + ConsumerRSA: c.String("stash-consumer-rsa"), + SkipVerify: c.Bool("stash-skip-verify"), + }) +} + +// helper function to setup the Gitlab remote from the CLI arguments. +func setupGitlab(c *cli.Context) (remote.Remote, error) { + return gitlab.New(gitlab.Opts{ + URL: c.String("gitlab-server"), + Client: c.String("gitlab-client"), + Secret: c.String("gitlab-sercret"), + Username: c.String("gitlab-git-username"), + Password: c.String("gitlab-git-password"), + PrivateMode: c.Bool("gitlab-private-mode"), + SkipVerify: c.Bool("gitlab-skip-verify"), + }) +} + +// helper function to setup the GitHub remote from the CLI arguments. +func setupGithub(c *cli.Context) (remote.Remote, error) { + return github.New( + c.String("github-server"), + c.String("github-client"), + c.String("github-sercret"), + c.StringSlice("github-scope"), + c.Bool("github-private-mode"), + c.Bool("github-skip-verify"), + c.BoolT("github-merge-ref"), + ) +} diff --git a/router/middleware/session/agent.go b/router/middleware/session/agent.go new file mode 100644 index 000000000..8f8c722b8 --- /dev/null +++ b/router/middleware/session/agent.go @@ -0,0 +1,22 @@ +package session + +import ( + "github.com/drone/drone/shared/token" + "github.com/gin-gonic/gin" +) + +// AuthorizeAgent authorizes requsts from build agents to access the queue. +func AuthorizeAgent(c *gin.Context) { + secret := c.MustGet("agent").(string) + + parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { + return secret, nil + }) + if err != nil { + c.AbortWithError(403, err) + } else if parsed.Kind != token.AgentToken { + c.AbortWithStatus(403) + } else { + c.Next() + } +} diff --git a/router/middleware/session/user.go b/router/middleware/session/user.go index 8453d208b..78f0a16bf 100644 --- a/router/middleware/session/user.go +++ b/router/middleware/session/user.go @@ -44,6 +44,10 @@ func SetUser() gin.HandlerFunc { return user.Hash, err }) if err == nil { + confv := c.MustGet("config") + if conf, ok := confv.(*model.Config); ok { + user.Admin = conf.IsAdmin(user) + } c.Set("user", user) // if this is a session token (ie not the API token) diff --git a/router/middleware/store.go b/router/middleware/store.go new file mode 100644 index 000000000..33b9731bf --- /dev/null +++ b/router/middleware/store.go @@ -0,0 +1,27 @@ +package middleware + +import ( + "github.com/codegangsta/cli" + "github.com/drone/drone/store" + "github.com/drone/drone/store/datastore" + + "github.com/gin-gonic/gin" +) + +// Store is a middleware function that initializes the Datastore and attaches to +// the context of every http.Request. +func Store(cli *cli.Context) gin.HandlerFunc { + v := setupStore(cli) + return func(c *gin.Context) { + store.ToContext(c, v) + c.Next() + } +} + +// helper function to create the datastore from the CLI context. +func setupStore(c *cli.Context) store.Store { + return datastore.New( + c.String("driver"), + c.String("datasource"), + ) +} diff --git a/router/middleware/stream.go b/router/middleware/stream.go new file mode 100644 index 000000000..d78a119c2 --- /dev/null +++ b/router/middleware/stream.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "github.com/drone/drone/stream" + + "github.com/codegangsta/cli" + "github.com/gin-gonic/gin" +) + +// Stream is a middleware function that initializes the Stream and attaches to +// the context of every http.Request. +func Stream(cli *cli.Context) gin.HandlerFunc { + v := stream.New() + return func(c *gin.Context) { + stream.ToContext(c, v) + } +} diff --git a/router/middleware/version.go b/router/middleware/version.go new file mode 100644 index 000000000..20466d8ec --- /dev/null +++ b/router/middleware/version.go @@ -0,0 +1,12 @@ +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) +} diff --git a/router/router.go b/router/router.go index 2d62d42b3..3c4a7a44d 100644 --- a/router/router.go +++ b/router/router.go @@ -1,201 +1,199 @@ package router +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" + + "github.com/drone/drone/router/middleware/header" + "github.com/drone/drone/router/middleware/session" + "github.com/drone/drone/router/middleware/token" + "github.com/drone/drone/server" + "github.com/drone/drone/static" + "github.com/drone/drone/template" +) + +func Load(middleware ...gin.HandlerFunc) http.Handler { + + e := gin.New() + e.Use(gin.Recovery()) + + e.SetHTMLTemplate(template.Load()) + e.StaticFS("/static", static.FileSystem()) + + e.Use(header.NoCache) + e.Use(header.Options) + e.Use(header.Secure) + e.Use(middleware...) + e.Use(session.SetUser()) + e.Use(token.Refresh) + + e.GET("/", server.ShowIndex) + e.GET("/repos", server.ShowAllRepos) + e.GET("/login", server.ShowLogin) + e.GET("/login/form", server.ShowLoginForm) + e.GET("/logout", server.GetLogout) + + // TODO below will Go away with React UI + settings := e.Group("/settings") + { + settings.Use(session.MustUser()) + settings.GET("/profile", server.ShowUser) + } + repo := e.Group("/repos/:owner/:name") + { + repo.Use(session.SetRepo()) + repo.Use(session.SetPerm()) + repo.Use(session.MustPull) + + repo.GET("", server.ShowRepo) + repo.GET("/builds/:number", server.ShowBuild) + repo.GET("/builds/:number/:job", server.ShowBuild) + + repo_settings := repo.Group("/settings") + { + repo_settings.GET("", session.MustPush, server.ShowRepoConf) + repo_settings.GET("/encrypt", session.MustPush, server.ShowRepoEncrypt) + repo_settings.GET("/badges", server.ShowRepoBadges) + } + } + // TODO above will Go away with React UI + + user := e.Group("/api/user") + { + user.Use(session.MustUser()) + user.GET("", server.GetSelf) + user.GET("/feed", server.GetFeed) + user.GET("/repos", server.GetRepos) + user.GET("/repos/remote", server.GetRemoteRepos) + user.POST("/token", server.PostToken) + user.DELETE("/token", server.DeleteToken) + } + + users := e.Group("/api/users") + { + users.Use(session.MustAdmin()) + users.GET("", server.GetUsers) + users.POST("", server.PostUser) + users.GET("/:login", server.GetUser) + users.PATCH("/:login", server.PatchUser) + users.DELETE("/:login", server.DeleteUser) + } + + repos := e.Group("/api/repos/:owner/:name") + { + repos.POST("", server.PostRepo) + + repo := repos.Group("") + { + repo.Use(session.SetRepo()) + repo.Use(session.SetPerm()) + repo.Use(session.MustPull) + + repo.GET("", server.GetRepo) + repo.GET("/builds", server.GetBuilds) + repo.GET("/builds/:number", server.GetBuild) + repo.GET("/logs/:number/:job", server.GetBuildLogs) + repo.POST("/sign", session.MustPush, server.Sign) + + repo.POST("/secrets", session.MustPush, server.PostSecret) + repo.DELETE("/secrets/:secret", session.MustPush, server.DeleteSecret) + + // requires push permissions + repo.PATCH("", session.MustPush, server.PatchRepo) + repo.DELETE("", session.MustPush, server.DeleteRepo) + + repo.POST("/builds/:number", session.MustPush, server.PostBuild) + repo.DELETE("/builds/:number/:job", session.MustPush, server.DeleteBuild) + } + } + + badges := e.Group("/api/badges/:owner/:name") + { + badges.GET("/status.svg", server.GetBadge) + badges.GET("/cc.xml", server.GetCC) + } + + e.POST("/hook", server.PostHook) + e.POST("/api/hook", server.PostHook) + + stream := e.Group("/api/stream") + { + stream.Use(session.SetRepo()) + stream.Use(session.SetPerm()) + stream.Use(session.MustPull) + + stream.GET("/:owner/:name", server.GetRepoEvents) + stream.GET("/:owner/:name/:build/:number", server.GetStream) + } + + auth := e.Group("/authorize") + { + auth.GET("", server.GetLogin) + auth.POST("", server.GetLogin) + auth.POST("/token", server.GetLoginToken) + } + + queue := e.Group("/api/queue") + { + queue.Use(session.AuthorizeAgent) + queue.POST("/pull", server.Pull) + queue.POST("/pull/:os/:arch", server.Pull) + queue.POST("/wait/:id", server.Wait) + queue.POST("/stream/:id", server.Stream) + queue.POST("/status/:id", server.Update) + } + + // DELETE THESE + // gitlab := e.Group("/gitlab/:owner/:name") + // { + // gitlab.Use(session.SetRepo()) + // gitlab.GET("/commits/:sha", GetCommit) + // gitlab.GET("/pulls/:number", GetPullRequest) + // + // redirects := gitlab.Group("/redirect") + // { + // redirects.GET("/commits/:sha", RedirectSha) + // redirects.GET("/pulls/:number", RedirectPullRequest) + // } + // } + + // bots := e.Group("/bots") + // { + // bots.Use(session.MustUser()) + // bots.POST("/slack", Slack) + // bots.POST("/slack/:command", Slack) + // } + + return normalize(e) +} + +// THIS HACK JOB IS GOING AWAY SOON. // -// import ( -// "net/http" -// "strings" -// -// "github.com/gin-gonic/gin" -// -// "github.com/drone/drone/api" -// "github.com/drone/drone/router/middleware" -// "github.com/drone/drone/router/middleware/header" -// "github.com/drone/drone/router/middleware/session" -// "github.com/drone/drone/router/middleware/token" -// "github.com/drone/drone/static" -// "github.com/drone/drone/template" -// "github.com/drone/drone/web" -// ) -// -// func Load(middlewares ...gin.HandlerFunc) http.Handler { -// e := gin.New() -// e.Use(gin.Recovery()) -// -// e.SetHTMLTemplate(template.Load()) -// e.StaticFS("/static", static.FileSystem()) -// -// e.Use(header.NoCache) -// e.Use(header.Options) -// e.Use(header.Secure) -// e.Use(middlewares...) -// e.Use(session.SetUser()) -// e.Use(token.Refresh) -// -// e.GET("/", web.ShowIndex) -// e.GET("/repos", web.ShowAllRepos) -// e.GET("/login", web.ShowLogin) -// e.GET("/login/form", web.ShowLoginForm) -// e.GET("/logout", web.GetLogout) -// -// settings := e.Group("/settings") -// { -// settings.Use(session.MustUser()) -// settings.GET("/profile", web.ShowUser) -// } -// repo := e.Group("/repos/:owner/:name") -// { -// repo.Use(session.SetRepo()) -// repo.Use(session.SetPerm()) -// repo.Use(session.MustPull) -// -// repo.GET("", web.ShowRepo) -// repo.GET("/builds/:number", web.ShowBuild) -// repo.GET("/builds/:number/:job", web.ShowBuild) -// -// repo_settings := repo.Group("/settings") -// { -// repo_settings.GET("", session.MustPush, web.ShowRepoConf) -// repo_settings.GET("/encrypt", session.MustPush, web.ShowRepoEncrypt) -// repo_settings.GET("/badges", web.ShowRepoBadges) -// } -// } -// -// user := e.Group("/api/user") -// { -// user.Use(session.MustUser()) -// user.GET("", api.GetSelf) -// user.GET("/feed", api.GetFeed) -// user.GET("/repos", api.GetRepos) -// user.GET("/repos/remote", api.GetRemoteRepos) -// user.POST("/token", api.PostToken) -// user.DELETE("/token", api.DeleteToken) -// } -// -// users := e.Group("/api/users") -// { -// users.Use(session.MustAdmin()) -// users.GET("", api.GetUsers) -// users.POST("", api.PostUser) -// users.GET("/:login", api.GetUser) -// users.PATCH("/:login", api.PatchUser) -// users.DELETE("/:login", api.DeleteUser) -// } -// -// repos := e.Group("/api/repos/:owner/:name") -// { -// repos.POST("", api.PostRepo) -// -// repo := repos.Group("") -// { -// repo.Use(session.SetRepo()) -// repo.Use(session.SetPerm()) -// repo.Use(session.MustPull) -// -// repo.GET("", api.GetRepo) -// repo.GET("/key", api.GetRepoKey) -// repo.POST("/key", api.PostRepoKey) -// repo.GET("/builds", api.GetBuilds) -// repo.GET("/builds/:number", api.GetBuild) -// repo.GET("/logs/:number/:job", api.GetBuildLogs) -// repo.POST("/sign", session.MustPush, api.Sign) -// -// repo.POST("/secrets", session.MustPush, api.PostSecret) -// repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret) -// -// // requires authenticated user -// repo.POST("/encrypt", session.MustUser(), api.PostSecure) -// -// // requires push permissions -// repo.PATCH("", session.MustPush, api.PatchRepo) -// repo.DELETE("", session.MustPush, api.DeleteRepo) -// -// repo.POST("/builds/:number", session.MustPush, api.PostBuild) -// repo.DELETE("/builds/:number/:job", session.MustPush, api.DeleteBuild) -// } -// } -// -// badges := e.Group("/api/badges/:owner/:name") -// { -// badges.GET("/status.svg", web.GetBadge) -// badges.GET("/cc.xml", web.GetCC) -// } -// -// e.POST("/hook", web.PostHook) -// e.POST("/api/hook", web.PostHook) -// -// stream := e.Group("/api/stream") -// { -// stream.Use(session.SetRepo()) -// stream.Use(session.SetPerm()) -// stream.Use(session.MustPull) -// -// stream.GET("/:owner/:name", web.GetRepoEvents) -// 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) -// auth.POST("", web.GetLogin) -// auth.POST("/token", web.GetLoginToken) -// } -// -// queue := e.Group("/api/queue") -// { -// queue.Use(middleware.AgentMust()) -// queue.POST("/pull", api.Pull) -// queue.POST("/pull/:os/:arch", api.Pull) -// queue.POST("/wait/:id", api.Wait) -// queue.POST("/stream/:id", api.Stream) -// queue.POST("/status/:id", api.Update) -// } -// -// gitlab := e.Group("/gitlab/:owner/:name") -// { -// gitlab.Use(session.SetRepo()) -// gitlab.GET("/commits/:sha", web.GetCommit) -// gitlab.GET("/pulls/:number", web.GetPullRequest) -// -// redirects := gitlab.Group("/redirect") -// { -// redirects.GET("/commits/:sha", web.RedirectSha) -// redirects.GET("/pulls/:number", web.RedirectPullRequest) -// } -// } -// -// return normalize(e) -// } -// -// // normalize is a helper function to work around the following -// // issue with gin. https://github.com/gin-gonic/gin/issues/388 -// func normalize(h http.Handler) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// -// parts := strings.Split(r.URL.Path, "/")[1:] -// switch parts[0] { -// case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab": -// // no-op -// default: -// -// if len(parts) > 2 && parts[2] != "settings" { -// parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...) -// } -// -// // prefix the URL with /repo so that it -// // can be effectively routed. -// parts = append([]string{"", "repos"}, parts...) -// -// // reconstruct the path -// r.URL.Path = strings.Join(parts, "/") -// } -// -// h.ServeHTTP(w, r) -// }) -// } +// normalize is a helper function to work around the following +// issue with gin. https://github.com/gin-gonic/gin/issues/388 +func normalize(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + parts := strings.Split(r.URL.Path, "/")[1:] + switch parts[0] { + case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab": + // no-op + default: + + if len(parts) > 2 && parts[2] != "settings" { + parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...) + } + + // prefix the URL with /repo so that it + // can be effectively routed. + parts = append([]string{"", "repos"}, parts...) + + // reconstruct the path + r.URL.Path = strings.Join(parts, "/") + } + + h.ServeHTTP(w, r) + }) +} diff --git a/web/badge.go b/server/badge.go similarity index 99% rename from web/badge.go rename to server/badge.go index e987079c5..acece2356 100644 --- a/web/badge.go +++ b/server/badge.go @@ -1,4 +1,4 @@ -package web +package server import ( "fmt" diff --git a/api/build.go b/server/build.go similarity index 95% rename from api/build.go rename to server/build.go index 3ca688a44..1d75fd4df 100644 --- a/api/build.go +++ b/server/build.go @@ -1,10 +1,8 @@ -package api +package server import ( - "fmt" "io" "net/http" - "os" "strconv" "time" @@ -21,18 +19,6 @@ import ( "github.com/drone/drone/router/middleware/session" ) -var ( - droneYml = os.Getenv("BUILD_CONFIG_FILE") - droneSec string -) - -func init() { - if droneYml == "" { - droneYml = ".drone.yml" - } - droneSec = fmt.Sprintf("%s.sig", droneYml) -} - func GetBuilds(c *gin.Context) { repo := session.Repo(c) builds, err := store.GetBuildList(c, repo) @@ -189,7 +175,8 @@ func PostBuild(c *gin.Context) { } // fetch the .drone.yml file from the database - raw, err := remote_.File(user, repo, build, droneYml) + config := ToConfig(c) + raw, err := remote_.File(user, repo, build, config.Yaml) if err != nil { log.Errorf("failure to get build config for %s. %s", repo.FullName, err) c.AbortWithError(404, err) @@ -197,7 +184,7 @@ func PostBuild(c *gin.Context) { } // Fetch secrets file but don't exit on error as it's optional - sec, err := remote_.File(user, repo, build, droneSec) + sec, err := remote_.File(user, repo, build, config.Shasum) if err != nil { log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err) } diff --git a/web/gitlab.go b/server/gitlab.go similarity index 99% rename from web/gitlab.go rename to server/gitlab.go index 36364e550..77c504c39 100644 --- a/web/gitlab.go +++ b/server/gitlab.go @@ -1,4 +1,4 @@ -package web +package server import ( "fmt" diff --git a/server/handler.go b/server/handler.go deleted file mode 100644 index fa7b14499..000000000 --- a/server/handler.go +++ /dev/null @@ -1,85 +0,0 @@ -package server - -import ( - "github.com/drone/drone/bus" - "github.com/drone/drone/cache" - "github.com/drone/drone/queue" - "github.com/drone/drone/remote" - "github.com/drone/drone/store" - "github.com/drone/drone/stream" - "github.com/drone/drone/version" - - "github.com/gin-gonic/gin" -) - -// HandlerCache returns a HandlerFunc that passes a Cache to the Context. -func HandlerCache(v cache.Cache) gin.HandlerFunc { - return func(c *gin.Context) { - cache.ToContext(c, v) - } -} - -// HandlerBus returns a HandlerFunc that passes a Bus to the Context. -func HandlerBus(v bus.Bus) gin.HandlerFunc { - return func(c *gin.Context) { - bus.ToContext(c, v) - } -} - -// HandlerStore returns a HandlerFunc that passes a Store to the Context. -func HandlerStore(v store.Store) gin.HandlerFunc { - return func(c *gin.Context) { - store.ToContext(c, v) - } -} - -// HandlerQueue returns a HandlerFunc that passes a Queue to the Context. -func HandlerQueue(v queue.Queue) gin.HandlerFunc { - return func(c *gin.Context) { - queue.ToContext(c, v) - } -} - -// HandlerStream returns a HandlerFunc that passes a Stream to the Context. -func HandlerStream(v stream.Stream) gin.HandlerFunc { - return func(c *gin.Context) { - stream.ToContext(c, v) - } -} - -// HandlerRemote returns a HandlerFunc that passes a Remote to the Context. -func HandlerRemote(v remote.Remote) gin.HandlerFunc { - return func(c *gin.Context) { - remote.ToContext(c, v) - } -} - -// HandlerConfig returns a HandlerFunc that passes server Config to the Context. -func HandlerConfig(v *Config) gin.HandlerFunc { - const k = "config" - return func(c *gin.Context) { - c.Set(k, v) - } -} - -// HandlerVersion returns a HandlerFunc that writes the Version information to -// the http.Response as a the X-Drone-Version header. -func HandlerVersion() gin.HandlerFunc { - return func(c *gin.Context) { - c.Header("X-Drone-Version", version.Version) - } -} - -// HandlerAgent returns a HandlerFunc that passes an Agent token to the Context. -func HandlerAgent(v string) gin.HandlerFunc { - const k = "agent" - return func(c *gin.Context) { - c.Set(k, v) - } -} - -// ToConfig returns the config from the Context -func ToConfig(c *gin.Context) *Config { - v := c.MustGet("config") - return v.(*Config) -} diff --git a/server/handler_test.go b/server/handler_test.go deleted file mode 100644 index abb4e431a..000000000 --- a/server/handler_test.go +++ /dev/null @@ -1 +0,0 @@ -package server diff --git a/web/hook.go b/server/hook.go similarity index 95% rename from web/hook.go rename to server/hook.go index b61519949..a2f293f58 100644 --- a/web/hook.go +++ b/server/hook.go @@ -1,8 +1,7 @@ -package web +package server import ( "fmt" - "os" "regexp" "github.com/gin-gonic/gin" @@ -19,18 +18,6 @@ import ( "github.com/drone/drone/yaml" ) -var ( - droneYml = os.Getenv("BUILD_CONFIG_FILE") - droneSec string -) - -func init() { - if droneYml == "" { - droneYml = ".drone.yml" - } - droneSec = fmt.Sprintf("%s.sig", droneYml) -} - var skipRe = regexp.MustCompile(`\[(?i:ci *skip|skip *ci)\]`) func PostHook(c *gin.Context) { @@ -135,13 +122,14 @@ func PostHook(c *gin.Context) { } // fetch the build file from the database - raw, err := remote_.File(user, repo, build, droneYml) + config := ToConfig(c) + raw, err := remote_.File(user, repo, build, config.Yaml) if err != nil { log.Errorf("failure to get build config for %s. %s", repo.FullName, err) c.AbortWithError(404, err) return } - sec, err := remote_.File(user, repo, build, droneSec) + sec, err := remote_.File(user, repo, build, config.Shasum) if err != nil { log.Debugf("cannot find build secrets for %s. %s", repo.FullName, err) // NOTE we don't exit on failure. The sec file is optional diff --git a/server/login.go b/server/login.go index bfd1d2d67..0934155d1 100644 --- a/server/login.go +++ b/server/login.go @@ -159,3 +159,9 @@ type tokenPayload struct { Refresh string `json:"refresh_token,omitempty"` Expires int64 `json:"expires_in,omitempty"` } + +// ToConfig returns the config from the Context +func ToConfig(c *gin.Context) *model.Config { + v := c.MustGet("config") + return v.(*model.Config) +} diff --git a/web/pages.go b/server/pages.go similarity index 99% rename from web/pages.go rename to server/pages.go index 7163063ff..4d765face 100644 --- a/web/pages.go +++ b/server/pages.go @@ -1,4 +1,4 @@ -package web +package server import ( "net/http" diff --git a/api/queue.go b/server/queue.go similarity index 99% rename from api/queue.go rename to server/queue.go index d18c544c6..2230125a1 100644 --- a/api/queue.go +++ b/server/queue.go @@ -1,4 +1,4 @@ -package api +package server import ( "fmt" diff --git a/api/repo.go b/server/repo.go similarity index 99% rename from api/repo.go rename to server/repo.go index 5b631973b..a5768c8cf 100644 --- a/api/repo.go +++ b/server/repo.go @@ -1,4 +1,4 @@ -package api +package server import ( "fmt" diff --git a/api/secret.go b/server/secret.go similarity index 98% rename from api/secret.go rename to server/secret.go index cc3384552..f39d97680 100644 --- a/api/secret.go +++ b/server/secret.go @@ -1,4 +1,4 @@ -package api +package server import ( "github.com/drone/drone/model" diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 0a3109793..000000000 --- a/server/server.go +++ /dev/null @@ -1,241 +0,0 @@ -package server - -import ( - "net/http" - "strings" - "time" - - "github.com/Sirupsen/logrus" - "github.com/drone/drone/api" - "github.com/drone/drone/bus" - "github.com/drone/drone/cache" - "github.com/drone/drone/queue" - "github.com/drone/drone/remote" - "github.com/drone/drone/router/middleware" - "github.com/drone/drone/router/middleware/header" - "github.com/drone/drone/router/middleware/session" - "github.com/drone/drone/router/middleware/token" - "github.com/drone/drone/static" - "github.com/drone/drone/store" - "github.com/drone/drone/stream" - "github.com/drone/drone/template" - "github.com/drone/drone/web" - - "github.com/gin-gonic/contrib/ginrus" - "github.com/gin-gonic/gin" -) - -// Config defines system configuration parameters. -type Config struct { - Open bool // Enables open registration - Yaml string // Customize the Yaml configuration file name - Secret string // Secret token used to authenticate agents - Admins map[string]bool // Administrative users - Orgs map[string]bool // Organization whitelist -} - -// Server defines the server configuration. -type Server struct { - Bus bus.Bus - Cache cache.Cache - Queue queue.Queue - Remote remote.Remote - Stream stream.Stream - Store store.Store - Config *Config -} - -// Handler returns an http.Handler for servering Drone requests. -func (s *Server) Handler() http.Handler { - - e := gin.New() - e.Use(gin.Recovery()) - - e.SetHTMLTemplate(template.Load()) - e.StaticFS("/static", static.FileSystem()) - - e.Use(header.NoCache) - e.Use(header.Options) - e.Use(header.Secure) - e.Use( - ginrus.Ginrus(logrus.StandardLogger(), time.RFC3339, true), - HandlerVersion(), - HandlerQueue(s.Queue), - HandlerStream(s.Stream), - HandlerBus(s.Bus), - HandlerCache(s.Cache), - HandlerStore(s.Store), - HandlerRemote(s.Remote), - HandlerConfig(s.Config), - ) - e.Use(session.SetUser()) - e.Use(token.Refresh) - - e.GET("/", web.ShowIndex) - e.GET("/repos", web.ShowAllRepos) - e.GET("/login", web.ShowLogin) - e.GET("/login/form", web.ShowLoginForm) - e.GET("/logout", GetLogout) - - // TODO below will Go away with React UI - settings := e.Group("/settings") - { - settings.Use(session.MustUser()) - settings.GET("/profile", web.ShowUser) - } - repo := e.Group("/repos/:owner/:name") - { - repo.Use(session.SetRepo()) - repo.Use(session.SetPerm()) - repo.Use(session.MustPull) - - repo.GET("", web.ShowRepo) - repo.GET("/builds/:number", web.ShowBuild) - repo.GET("/builds/:number/:job", web.ShowBuild) - - repo_settings := repo.Group("/settings") - { - repo_settings.GET("", session.MustPush, web.ShowRepoConf) - repo_settings.GET("/encrypt", session.MustPush, web.ShowRepoEncrypt) - repo_settings.GET("/badges", web.ShowRepoBadges) - } - } - // TODO above will Go away with React UI - - user := e.Group("/api/user") - { - user.Use(session.MustUser()) - user.GET("", api.GetSelf) - user.GET("/feed", api.GetFeed) - user.GET("/repos", api.GetRepos) - user.GET("/repos/remote", api.GetRemoteRepos) - user.POST("/token", api.PostToken) - user.DELETE("/token", api.DeleteToken) - } - - users := e.Group("/api/users") - { - users.Use(session.MustAdmin()) - users.GET("", api.GetUsers) - users.POST("", api.PostUser) - users.GET("/:login", api.GetUser) - users.PATCH("/:login", api.PatchUser) - users.DELETE("/:login", api.DeleteUser) - } - - repos := e.Group("/api/repos/:owner/:name") - { - repos.POST("", api.PostRepo) - - repo := repos.Group("") - { - repo.Use(session.SetRepo()) - repo.Use(session.SetPerm()) - repo.Use(session.MustPull) - - repo.GET("", api.GetRepo) - repo.GET("/builds", api.GetBuilds) - repo.GET("/builds/:number", api.GetBuild) - repo.GET("/logs/:number/:job", api.GetBuildLogs) - repo.POST("/sign", session.MustPush, api.Sign) - - repo.POST("/secrets", session.MustPush, api.PostSecret) - repo.DELETE("/secrets/:secret", session.MustPush, api.DeleteSecret) - - // requires push permissions - repo.PATCH("", session.MustPush, api.PatchRepo) - repo.DELETE("", session.MustPush, api.DeleteRepo) - - repo.POST("/builds/:number", session.MustPush, api.PostBuild) - repo.DELETE("/builds/:number/:job", session.MustPush, api.DeleteBuild) - } - } - - badges := e.Group("/api/badges/:owner/:name") - { - badges.GET("/status.svg", web.GetBadge) - badges.GET("/cc.xml", web.GetCC) - } - - e.POST("/hook", web.PostHook) - e.POST("/api/hook", web.PostHook) - - stream := e.Group("/api/stream") - { - stream.Use(session.SetRepo()) - stream.Use(session.SetPerm()) - stream.Use(session.MustPull) - - stream.GET("/:owner/:name", web.GetRepoEvents) - stream.GET("/:owner/:name/:build/:number", web.GetStream) - } - - auth := e.Group("/authorize") - { - auth.GET("", GetLogin) - auth.POST("", GetLogin) - auth.POST("/token", GetLoginToken) - } - - queue := e.Group("/api/queue") - { - queue.Use(middleware.AgentMust()) - queue.POST("/pull", api.Pull) - queue.POST("/pull/:os/:arch", api.Pull) - queue.POST("/wait/:id", api.Wait) - queue.POST("/stream/:id", api.Stream) - queue.POST("/status/:id", api.Update) - } - - // DELETE THESE - // gitlab := e.Group("/gitlab/:owner/:name") - // { - // gitlab.Use(session.SetRepo()) - // gitlab.GET("/commits/:sha", web.GetCommit) - // gitlab.GET("/pulls/:number", web.GetPullRequest) - // - // redirects := gitlab.Group("/redirect") - // { - // redirects.GET("/commits/:sha", web.RedirectSha) - // redirects.GET("/pulls/:number", web.RedirectPullRequest) - // } - // } - - // bots := e.Group("/bots") - // { - // bots.Use(session.MustUser()) - // bots.POST("/slack", web.Slack) - // bots.POST("/slack/:command", web.Slack) - // } - - return normalize(e) -} - -// THIS HACK JOB IS GOING AWAY SOON. -// -// normalize is a helper function to work around the following -// issue with gin. https://github.com/gin-gonic/gin/issues/388 -func normalize(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - - parts := strings.Split(r.URL.Path, "/")[1:] - switch parts[0] { - case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab": - // no-op - default: - - if len(parts) > 2 && parts[2] != "settings" { - parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...) - } - - // prefix the URL with /repo so that it - // can be effectively routed. - parts = append([]string{"", "repos"}, parts...) - - // reconstruct the path - r.URL.Path = strings.Join(parts, "/") - } - - h.ServeHTTP(w, r) - }) -} diff --git a/api/sign.go b/server/sign.go similarity index 98% rename from api/sign.go rename to server/sign.go index 95d13c5ad..df34996e6 100644 --- a/api/sign.go +++ b/server/sign.go @@ -1,4 +1,4 @@ -package api +package server import ( "io/ioutil" diff --git a/web/slack.go b/server/slack.go similarity index 99% rename from web/slack.go rename to server/slack.go index bb03422ce..f6b78c907 100644 --- a/web/slack.go +++ b/server/slack.go @@ -1,4 +1,4 @@ -package web +package server import ( "strings" diff --git a/web/stream.go b/server/stream.go similarity index 99% rename from web/stream.go rename to server/stream.go index cceff005c..aae7bf2fd 100644 --- a/web/stream.go +++ b/server/stream.go @@ -1,4 +1,4 @@ -package web +package server import ( "bufio" diff --git a/api/doc.go b/server/swagger/doc.go similarity index 62% rename from api/doc.go rename to server/swagger/doc.go index 6c00e13ef..2642c47d4 100644 --- a/api/doc.go +++ b/server/swagger/doc.go @@ -11,6 +11,7 @@ // - application/json // // swagger:meta -package api +package swagger -//go:generate swagger generate spec -o swagger/files/swagger.json +//go:generate swagger generate spec -o files/swagger.json +//go:generate go-bindata -pkg swagger -o swagger_gen.go files/ diff --git a/api/swagger/files/swagger.yml b/server/swagger/files/swagger.yml similarity index 100% rename from api/swagger/files/swagger.yml rename to server/swagger/files/swagger.yml diff --git a/server/swagger/swagger.go b/server/swagger/swagger.go new file mode 100644 index 000000000..2e7a006f1 --- /dev/null +++ b/server/swagger/swagger.go @@ -0,0 +1,85 @@ +package swagger + +import ( + "net/http" + + "github.com/drone/drone/model" +) + +// swagger:route GET /users/{login} user getUser +// +// Get the user with the matching login. +// +// Responses: +// 200: user +// +func userFind(w http.ResponseWriter, r *http.Request) {} + +// swagger:route GET /user user getCurrentUser +// +// Get the currently authenticated user. +// +// Responses: +// 200: user +// +func userCurrent(w http.ResponseWriter, r *http.Request) {} + +// swagger:route GET /users user getUserList +// +// Get the list of all registered users. +// +// Responses: +// 200: user +// +func userList(w http.ResponseWriter, r *http.Request) {} + +// swagger:route GET /user/feed user getUserFeed +// +// Get the currently authenticated user's build feed. +// +// Responses: +// 200: feed +// +func userFeed(w http.ResponseWriter, r *http.Request) {} + +// swagger:route DELETE /users/{login} user deleteUserLogin +// +// Delete the user with the matching login. +// +// Responses: +// 200: user +// +func userDelete(w http.ResponseWriter, r *http.Request) {} + +// swagger:route GET /user/repos user getUserRepos +// +// Get the currently authenticated user's active repository list. +// +// Responses: +// 200: repos +// +func repoList(w http.ResponseWriter, r *http.Request) {} + +// swagger:response user +type userResp struct { + // in: body + Body model.User +} + +// swagger:response users +type usersResp struct { + // in: body + Body []model.User +} + +// swagger:response feed +type feedResp struct { + // in: body + Body []model.Feed +} + +// swagger:response repos +type reposResp struct { + // in: body + Body []model.Repo +} diff --git a/api/user.go b/server/user.go similarity index 69% rename from api/user.go rename to server/user.go index 667bf4ed7..210aa7abf 100644 --- a/api/user.go +++ b/server/user.go @@ -1,4 +1,4 @@ -package api +package server import ( "net/http" @@ -6,31 +6,16 @@ import ( "github.com/gin-gonic/gin" "github.com/drone/drone/cache" - "github.com/drone/drone/model" "github.com/drone/drone/router/middleware/session" "github.com/drone/drone/shared/crypto" "github.com/drone/drone/shared/token" "github.com/drone/drone/store" ) -// swagger:route GET /user user getUser -// -// Get the currently authenticated user. -// -// Responses: -// 200: user -// func GetSelf(c *gin.Context) { c.JSON(200, session.User(c)) } -// swagger:route GET /user/feed user getUserFeed -// -// Get the currently authenticated user's build feed. -// -// Responses: -// 200: feed -// func GetFeed(c *gin.Context) { repos, err := cache.GetRepos(c, session.User(c)) if err != nil { @@ -46,13 +31,6 @@ func GetFeed(c *gin.Context) { c.JSON(200, feed) } -// swagger:route GET /user/repos user getUserRepos -// -// Get the currently authenticated user's active repository list. -// -// Responses: -// 200: repos -// func GetRepos(c *gin.Context) { repos, err := cache.GetRepos(c, session.User(c)) if err != nil { @@ -105,27 +83,3 @@ func DeleteToken(c *gin.Context) { } c.String(http.StatusOK, tokenstr) } - -// swagger:response user -type userResp struct { - // in: body - Body model.User -} - -// swagger:response users -type usersResp struct { - // in: body - Body []model.User -} - -// swagger:response feed -type feedResp struct { - // in: body - Body []model.Feed -} - -// swagger:response repos -type reposResp struct { - // in: body - Body []model.Repo -} diff --git a/api/users.go b/server/users.go similarity index 67% rename from api/users.go rename to server/users.go index 81756aec4..3661692f2 100644 --- a/api/users.go +++ b/server/users.go @@ -1,4 +1,4 @@ -package api +package server import ( "net/http" @@ -10,13 +10,6 @@ import ( "github.com/drone/drone/store" ) -// swagger:route GET /users user getUserList -// -// Get the list of all registered users. -// -// Responses: -// 200: user -// func GetUsers(c *gin.Context) { users, err := store.GetUserList(c) if err != nil { @@ -26,20 +19,13 @@ func GetUsers(c *gin.Context) { } } -// swagger:route GET /users/{login} user getUserLogin -// -// Get the user with the matching login. -// -// Responses: -// 200: user -// func GetUser(c *gin.Context) { user, err := store.GetUserLogin(c, c.Param("login")) if err != nil { c.String(404, "Cannot find user. %s", err) - } else { - c.JSON(200, user) + return } + c.JSON(200, user) } func PatchUser(c *gin.Context) { @@ -74,31 +60,20 @@ func PostUser(c *gin.Context) { c.String(http.StatusBadRequest, err.Error()) return } - - user := &model.User{} - user.Login = in.Login - user.Email = in.Email - user.Admin = in.Admin - user.Avatar = in.Avatar - user.Active = true - user.Hash = crypto.Rand() - - err = store.CreateUser(c, user) - if err != nil { + user := &model.User{ + Active: true, + Login: in.Login, + Email: in.Email, + Avatar: in.Avatar, + Hash: crypto.Rand(), + } + if err = store.CreateUser(c, user); err != nil { c.String(http.StatusInternalServerError, err.Error()) return } - c.JSON(http.StatusOK, user) } -// swagger:route DELETE /users/{login} user deleteUserLogin -// -// Delete the user with the matching login. -// -// Responses: -// 200: user -// func DeleteUser(c *gin.Context) { user, err := store.GetUserLogin(c, c.Param("login")) if err != nil { @@ -107,7 +82,7 @@ func DeleteUser(c *gin.Context) { } if err = store.DeleteUser(c, user); err != nil { c.String(500, "Error deleting user. %s", err) - } else { - c.String(200, "") + return } + c.String(200, "") } diff --git a/shared/docker/docker.go b/shared/docker/docker.go deleted file mode 100644 index 6615871e0..000000000 --- a/shared/docker/docker.go +++ /dev/null @@ -1,97 +0,0 @@ -package docker - -import ( - "errors" - - log "github.com/Sirupsen/logrus" - "github.com/samalba/dockerclient" -) - -var ( - LogOpts = &dockerclient.LogOptions{ - Stdout: true, - Stderr: true, - } - - LogOptsTail = &dockerclient.LogOptions{ - Follow: true, - Stdout: true, - Stderr: true, - } -) - -// Run creates the docker container, pulling images if necessary, starts -// the container and blocks until the container exits, returning the exit -// information. -func Run(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) { - info, err := RunDaemon(client, conf, name) - if err != nil { - return nil, err - } - - return Wait(client, info.Id) -} - -// RunDaemon creates the docker container, pulling images if necessary, starts -// the container and returns the container information. It does not wait for -// the container to exit. -func RunDaemon(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) { - - // attempts to create the container - id, err := client.CreateContainer(conf, name, nil) - if err != nil { - // and pull the image and re-create if that fails - err = client.PullImage(conf.Image, nil) - if err != nil { - return nil, err - } - id, err = client.CreateContainer(conf, name, nil) - if err != nil { - client.RemoveContainer(id, true, true) - return nil, err - } - } - - // fetches the container information - info, err := client.InspectContainer(id) - if err != nil { - client.RemoveContainer(id, true, true) - return nil, err - } - - // starts the container - err = client.StartContainer(id, &conf.HostConfig) - if err != nil { - client.RemoveContainer(id, true, true) - return nil, err - } - - return info, err -} - -// Wait blocks until the named container exits, returning the exit information. -func Wait(client dockerclient.Client, name string) (*dockerclient.ContainerInfo, error) { - - defer func() { - client.StopContainer(name, 5) - client.KillContainer(name, "9") - }() - - for attempts := 0; attempts < 5; attempts++ { - done := client.Wait(name) - <-done - - info, err := client.InspectContainer(name) - if err != nil { - return nil, err - } - - if !info.State.Running { - return info, nil - } - - log.Debugf("attempting to resume waiting after %d attempts.\n", attempts) - } - - return nil, errors.New("reached maximum wait attempts") -} diff --git a/web/login.go b/web/login.go deleted file mode 100644 index 53e7ab8e1..000000000 --- a/web/login.go +++ /dev/null @@ -1,149 +0,0 @@ -package web - -// -// import ( -// "net/http" -// "time" -// -// "github.com/drone/drone/model" -// "github.com/drone/drone/remote" -// "github.com/drone/drone/shared/crypto" -// "github.com/drone/drone/shared/httputil" -// "github.com/drone/drone/shared/token" -// "github.com/drone/drone/store" -// -// "github.com/Sirupsen/logrus" -// "github.com/gin-gonic/gin" -// ) -// -// func GetLogin(c *gin.Context) { -// remote := remote.FromContext(c) -// -// // when dealing with redirects we may need -// // to adjust the content type. I cannot, however, -// // remember why, so need to revisit this line. -// c.Writer.Header().Del("Content-Type") -// -// tmpuser, err := remote.Login(c.Writer, c.Request) -// if err != nil { -// logrus.Errorf("cannot authenticate user. %s", err) -// c.Redirect(303, "/login?error=oauth_error") -// return -// } -// // this will happen when the user is redirected by -// // the remote provide as part of the oauth dance. -// if tmpuser == nil { -// return -// } -// -// var open = false // TODO get this from context -// -// // get the user from the database -// u, err := store.GetUserLogin(c, tmpuser.Login) -// if err != nil { -// -// // if self-registration is disabled we should -// // return a notAuthorized error. the only exception -// // is if no users exist yet in the system we'll proceed. -// if !open { -// logrus.Errorf("cannot register %s. registration closed", tmpuser.Login) -// c.Redirect(303, "/login?error=access_denied") -// return -// } -// -// // create the user account -// u = &model.User{} -// u.Login = tmpuser.Login -// u.Token = tmpuser.Token -// u.Secret = tmpuser.Secret -// u.Email = tmpuser.Email -// u.Avatar = tmpuser.Avatar -// u.Hash = crypto.Rand() -// -// // insert the user into the database -// if err := store.CreateUser(c, u); err != nil { -// logrus.Errorf("cannot insert %s. %s", u.Login, err) -// c.Redirect(303, "/login?error=internal_error") -// return -// } -// } -// -// // update the user meta data and authorization -// // data and cache in the datastore. -// u.Token = tmpuser.Token -// u.Secret = tmpuser.Secret -// u.Email = tmpuser.Email -// u.Avatar = tmpuser.Avatar -// -// if err := store.UpdateUser(c, u); err != nil { -// logrus.Errorf("cannot update %s. %s", u.Login, err) -// c.Redirect(303, "/login?error=internal_error") -// return -// } -// -// exp := time.Now().Add(time.Hour * 72).Unix() -// token := token.New(token.SessToken, u.Login) -// tokenstr, err := token.SignExpires(u.Hash, exp) -// if err != nil { -// logrus.Errorf("cannot create token for %s. %s", u.Login, err) -// c.Redirect(303, "/login?error=internal_error") -// return -// } -// -// httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr) -// redirect := httputil.GetCookie(c.Request, "user_last") -// if len(redirect) == 0 { -// redirect = "/" -// } -// c.Redirect(303, redirect) -// -// } -// -// func GetLogout(c *gin.Context) { -// -// httputil.DelCookie(c.Writer, c.Request, "user_sess") -// httputil.DelCookie(c.Writer, c.Request, "user_last") -// c.Redirect(303, "/login") -// } -// -// func GetLoginToken(c *gin.Context) { -// remote := remote.FromContext(c) -// -// in := &tokenPayload{} -// err := c.Bind(in) -// if err != nil { -// c.AbortWithError(http.StatusBadRequest, err) -// return -// } -// -// login, err := remote.Auth(in.Access, in.Refresh) -// if err != nil { -// c.AbortWithError(http.StatusUnauthorized, err) -// return -// } -// -// user, err := store.GetUserLogin(c, login) -// if err != nil { -// c.AbortWithError(http.StatusNotFound, err) -// return -// } -// -// exp := time.Now().Add(time.Hour * 72).Unix() -// token := token.New(token.SessToken, user.Login) -// tokenstr, err := token.SignExpires(user.Hash, exp) -// if err != nil { -// c.AbortWithError(http.StatusInternalServerError, err) -// return -// } -// -// c.IndentedJSON(http.StatusOK, &tokenPayload{ -// Access: tokenstr, -// Expires: exp - time.Now().Unix(), -// }) -// } -// -// type tokenPayload struct { -// Access string `json:"access_token,omitempty"` -// Refresh string `json:"refresh_token,omitempty"` -// Expires int64 `json:"expires_in,omitempty"` -// } diff --git a/yaml/checksum/checksum.go b/yaml/checksum/checksum.go deleted file mode 100644 index 4a021a692..000000000 --- a/yaml/checksum/checksum.go +++ /dev/null @@ -1,70 +0,0 @@ -package checksum - -import ( - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "fmt" - "io" - "strings" -) - -// Check is a calculates and verifies a file checksum. This supports the sha1, -// sha256 and sha512 values. -func Check(in, checksum string) bool { - hash, size, _ := split(checksum) - - // if a byte size is provided for the - // Yaml file it must match. - if size > 0 && int64(len(in)) != size { - return false - } - - switch len(hash) { - case 64: - return sha256sum(in) == hash - case 128: - return sha512sum(in) == hash - case 40: - return sha1sum(in) == hash - } - - return false -} - -func sha1sum(in string) string { - h := sha1.New() - io.WriteString(h, in) - return fmt.Sprintf("%x", h.Sum(nil)) -} - -func sha256sum(in string) string { - h := sha256.New() - io.WriteString(h, in) - return fmt.Sprintf("%x", h.Sum(nil)) -} - -func sha512sum(in string) string { - h := sha512.New() - io.WriteString(h, in) - return fmt.Sprintf("%x", h.Sum(nil)) -} - -func split(in string) (string, int64, string) { - var hash string - var name string - var size int64 - - // the checksum might be split into multiple - // sections including the file size and name. - switch strings.Count(in, " ") { - case 1: - fmt.Sscanf(in, "%s %s", &hash, &name) - case 2: - fmt.Sscanf(in, "%s %d %s", &hash, &size, &name) - default: - hash = in - } - - return hash, size, name -} diff --git a/yaml/checksum/checksum_test.go b/yaml/checksum/checksum_test.go deleted file mode 100644 index 9dd6925c7..000000000 --- a/yaml/checksum/checksum_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package checksum - -import ( - "testing" - - "github.com/franela/goblin" -) - -func TestParse(t *testing.T) { - - g := goblin.Goblin(t) - g.Describe("Shasum", func() { - - g.It("Should parse the shasum string", func() { - hash, _, _ := split("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - g.Assert(hash).Equal("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - }) - - g.It("Should parse a two-part shasum string", func() { - hash, _, name := split("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 .drone.yml") - g.Assert(hash).Equal("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - g.Assert(name).Equal(".drone.yml") - }) - - g.It("Should parse a three-part shasum string", func() { - hash, size, name := split("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 42 .drone.yml") - g.Assert(hash).Equal("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - g.Assert(name).Equal(".drone.yml") - g.Assert(size).Equal(int64(42)) - }) - - g.It("Should calc a sha1 sum", func() { - hash := sha1sum("foo\n") - g.Assert(hash).Equal("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - }) - - g.It("Should calc a sha256 sum", func() { - hash := sha256sum("foo\n") - g.Assert(hash).Equal("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c") - }) - - g.It("Should calc a sha512 sum", func() { - hash := sha512sum("foo\n") - g.Assert(hash).Equal("0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6") - }) - - g.It("Should calc a sha1 sum", func() { - hash := sha1sum("foo\n") - g.Assert(hash).Equal("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") - }) - - g.It("Should validate sha1 sum with file size", func() { - ok := Check("foo\n", "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 4 -") - g.Assert(ok).IsTrue() - }) - - g.It("Should validate sha256 sum with file size", func() { - ok := Check("foo\n", "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c 4 -") - g.Assert(ok).IsTrue() - }) - - g.It("Should validate sha512 sum with file size", func() { - ok := Check("foo\n", "0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6 4 -") - g.Assert(ok).IsTrue() - }) - - g.It("Should fail validation if incorrect sha1", func() { - ok := Check("bar\n", "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 4 -") - g.Assert(ok).IsFalse() - }) - - g.It("Should fail validation if incorrect sha256", func() { - ok := Check("bar\n", "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c 4 -") - g.Assert(ok).IsFalse() - }) - - g.It("Should fail validation if incorrect sha512", func() { - ok := Check("bar\n", "0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6 4 -") - g.Assert(ok).IsFalse() - }) - - g.It("Should return false if file size mismatch", func() { - ok := Check("foo\n", "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 12 -") - g.Assert(ok).IsFalse() - }) - - g.It("Should return false if invalid checksum string", func() { - ok := Check("foo\n", "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15234") - g.Assert(ok).IsFalse() - }) - - g.It("Should return false if empty checksum", func() { - ok := Check("foo\n", "") - g.Assert(ok).IsFalse() - }) - }) -}