Merge pull request #1032 from CiscoCloud/replace-settings

Replace pkg/settings with pkg/config
This commit is contained in:
Brad Rydzewski 2015-05-31 12:59:10 -07:00
commit 3d4596e7d1
11 changed files with 84 additions and 193 deletions

View file

@ -31,20 +31,18 @@ path = "/etc/drone/drone.db"
[docker] [docker]
cert = "" cert = ""
key = "" key = ""
nodes = [ addr = "unix:///var/run/docker.sock"
"unix:///var/run/docker.sock", swarm = ""
"unix:///var/run/docker.sock"
]
[service] [service]
name = "github" kind = "github"
base = "https://github.com" base = "https://github.com"
orgs = [] orgs = []
open = false open = false
private_mode = false private = false
skip_verify = true skip_verify = true
[service.oauth] [auth]
client = "" client = ""
secret = "" secret = ""
authorize = "https://github.com/login/oauth/authorize" authorize = "https://github.com/login/oauth/authorize"
@ -54,3 +52,17 @@ request_token = ""
[agents] [agents]
secret = "" secret = ""
``` ```
Configuration settings can also be set by environment variables using the scheme `DRONE_<section>_<confkey>`, substituting the section title for `<section>` and the key for `<confkey>`, in all caps. For example:
```shell
#!/bin/bash
# prepare environment for executing drone
DRONE_DOCKER_ADDR="tcp://10.0.0.1:2375" # for [docker] section, 'addr' setting
DRONE_AUTH_CLIENT="0123456789abcdef0123AA" # for [auth] section, 'client' setting
DRONE_AUTH_SECRET="<sha-1 hash secret>" # for [auth] section, 'secret' setting
exec ./drone -config=drone.toml
```
_NOTE: Configuration settings from environment variables override values set in the TOML file._

View file

@ -8,10 +8,10 @@ import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs" "github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/remote/github" "github.com/drone/drone/pkg/remote/github"
"github.com/drone/drone/pkg/server" "github.com/drone/drone/pkg/server"
"github.com/drone/drone/pkg/server/session" "github.com/drone/drone/pkg/server/session"
"github.com/drone/drone/pkg/settings"
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
eventbus "github.com/drone/drone/pkg/bus/builtin" eventbus "github.com/drone/drone/pkg/bus/builtin"
@ -37,7 +37,7 @@ var (
func main() { func main() {
flag.Parse() flag.Parse()
settings, err := settings.Parse(*conf) settings, err := config.Load(*conf)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -46,8 +46,8 @@ func main() {
store := store.New(db) store := store.New(db)
defer db.Close() defer db.Close()
remote := github.New(settings.Service) remote := github.New(settings)
session := session.New(settings.Session) session := session.New(settings)
eventbus_ := eventbus.New() eventbus_ := eventbus.New()
queue_ := queue.New() queue_ := queue.New()
updater := runner.NewUpdater(eventbus_, store, remote) updater := runner.NewUpdater(eventbus_, store, remote)
@ -55,7 +55,7 @@ func main() {
// launch the local queue runner if the system // launch the local queue runner if the system
// is not conifugred to run in agent mode // is not conifugred to run in agent mode
if settings.Agents != nil && settings.Agents.Secret != "" { if len(settings.Agents.Secret) != 0 {
log.Infof("Run builds using remote build agents") log.Infof("Run builds using remote build agents")
} else { } else {
log.Infof("Run builds using the embedded build runner") log.Infof("Run builds using the embedded build runner")

View file

@ -40,6 +40,10 @@ type Config struct {
Expires int64 `envconfig:"optional"` Expires int64 `envconfig:"optional"`
} }
Agents struct {
Secret string `envconfig:"optional"`
}
Database struct { Database struct {
Driver string `envconfig:"optional"` Driver string `envconfig:"optional"`
Datasource string `envconfig:"optional"` Datasource string `envconfig:"optional"`
@ -56,13 +60,13 @@ type Config struct {
// Plugins represents a white-list of plugins // Plugins represents a white-list of plugins
// that the system is authorized to load. // that the system is authorized to load.
Plugins []string `envconfig:"white_list"` Plugins []string `envconfig:"optional"`
} }
// Load loads the configuration file and reads // Load loads the configuration file and reads
// parameters from environment variables. // parameters from environment variables.
func Load(path string) (*Config, error) { func Load(path string) (*Config, error) {
data, err := ioutil.ReadFile("drone.toml") data, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -81,5 +85,15 @@ func LoadBytes(data []byte) (*Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conf, nil return applyDefaults(conf), nil
}
func applyDefaults(c *Config) *Config {
// if no session token is provided we can
// instead use the client secret to sign
// our sessions and tokens.
if len(c.Session.Secret) == 0 {
c.Session.Secret = c.Auth.Secret
}
return c
} }

View file

@ -5,7 +5,7 @@ import (
"net/http" "net/http"
"net/rpc" "net/rpc"
"github.com/drone/drone/pkg/settings" "github.com/drone/drone/pkg/config"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
) )
@ -17,8 +17,8 @@ type Client struct {
// New returns a new, remote datastore backend that connects // New returns a new, remote datastore backend that connects
// via tcp and exchanges data using Go's RPC mechanism. // via tcp and exchanges data using Go's RPC mechanism.
func New(service *settings.Service) (*Client, error) { func New(conf *config.Config) (*Client, error) {
conn, err := net.Dial("tcp", service.Address) conn, err := net.Dial("tcp", conf.Server.Addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -10,7 +10,7 @@ import (
"time" "time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru"
"github.com/drone/drone/pkg/settings" "github.com/drone/drone/pkg/config"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/Godeps/_workspace/src/github.com/google/go-github/github" "github.com/drone/drone/Godeps/_workspace/src/github.com/google/go-github/github"
@ -33,14 +33,14 @@ type GitHub struct {
cache *lru.Cache cache *lru.Cache
} }
func New(service *settings.Service) *GitHub { func New(conf *config.Config) *GitHub {
var github = GitHub{ var github = GitHub{
API: DefaultAPI, API: DefaultAPI,
URL: DefaultURL, URL: DefaultURL,
Client: service.OAuth.Client, Client: conf.Auth.Client,
Secret: service.OAuth.Secret, Secret: conf.Auth.Secret,
PrivateMode: service.PrivateMode, PrivateMode: conf.Remote.Private,
SkipVerify: service.SkipVerify, SkipVerify: conf.Remote.SkipVerify,
} }
var err error var err error
github.cache, err = lru.New(1028) github.cache, err = lru.New(1028)
@ -50,9 +50,9 @@ func New(service *settings.Service) *GitHub {
// if GitHub enterprise then ensure we're using the // if GitHub enterprise then ensure we're using the
// appropriate URLs // appropriate URLs
if !strings.HasPrefix(service.Base, DefaultURL) && len(service.Base) != 0 { if !strings.HasPrefix(conf.Remote.Base, DefaultURL) && len(conf.Remote.Base) != 0 {
github.URL = service.Base github.URL = conf.Remote.Base
github.API = service.Base + "/api/v3/" github.API = conf.Remote.Base + "/api/v3/"
} }
// the API must have a trailing slash // the API must have a trailing slash
if !strings.HasSuffix(github.API, "/") { if !strings.HasSuffix(github.API, "/") {

View file

@ -36,9 +36,9 @@ func GetLogin(c *gin.Context) {
// Auth (username and password). This will delegate // Auth (username and password). This will delegate
// authorization accordingly. // authorization accordingly.
switch { switch {
case settings.Service.OAuth == nil: // case settings.Auth == nil:
getLoginBasic(c) // getLoginBasic(c)
case settings.Service.OAuth.RequestToken != "": case settings.Auth.RequestToken != "":
getLoginOauth1(c) getLoginOauth1(c)
default: default:
getLoginOauth2(c) getLoginOauth2(c)
@ -52,9 +52,9 @@ func GetLogin(c *gin.Context) {
login := ToUser(c) login := ToUser(c)
// check organization membership, if applicable // check organization membership, if applicable
if len(settings.Service.Orgs) != 0 { if len(settings.Remote.Orgs) != 0 {
orgs, _ := remote.Orgs(login) orgs, _ := remote.Orgs(login)
if !checkMembership(orgs, settings.Service.Orgs) { if !checkMembership(orgs, settings.Remote.Orgs) {
c.Redirect(303, "/login#error=access_denied_org") c.Redirect(303, "/login#error=access_denied_org")
return return
} }
@ -73,7 +73,7 @@ func GetLogin(c *gin.Context) {
// if self-registration is disabled we should // if self-registration is disabled we should
// return a notAuthorized error. the only exception // return a notAuthorized error. the only exception
// is if no users exist yet in the system we'll proceed. // is if no users exist yet in the system we'll proceed.
if !settings.Service.Open && count != 0 { if !settings.Remote.Open && count != 0 {
log.Errorf("cannot register %s. registration closed", login.Login) log.Errorf("cannot register %s. registration closed", login.Login)
c.Redirect(303, "/login#error=access_denied") c.Redirect(303, "/login#error=access_denied")
return return
@ -137,11 +137,11 @@ func getLoginOauth2(c *gin.Context) {
var remote = ToRemote(c) var remote = ToRemote(c)
var config = &oauth2.Config{ var config = &oauth2.Config{
ClientId: settings.Service.OAuth.Client, ClientId: settings.Auth.Client,
ClientSecret: settings.Service.OAuth.Secret, ClientSecret: settings.Auth.Secret,
Scope: strings.Join(settings.Service.OAuth.Scope, ","), Scope: strings.Join(settings.Auth.Scope, ","),
AuthURL: settings.Service.OAuth.Authorize, AuthURL: settings.Auth.Authorize,
TokenURL: settings.Service.OAuth.AccessToken, TokenURL: settings.Auth.AccessToken,
RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(c.Request)), RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(c.Request)),
//settings.Server.Scheme, settings.Server.Hostname), //settings.Server.Scheme, settings.Server.Hostname),
} }

View file

@ -7,11 +7,11 @@ import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/pkg/bus" "github.com/drone/drone/pkg/bus"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
"github.com/drone/drone/pkg/remote" "github.com/drone/drone/pkg/remote"
"github.com/drone/drone/pkg/runner" "github.com/drone/drone/pkg/runner"
"github.com/drone/drone/pkg/server/session" "github.com/drone/drone/pkg/server/session"
"github.com/drone/drone/pkg/settings"
"github.com/drone/drone/pkg/store" "github.com/drone/drone/pkg/store"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
) )
@ -91,17 +91,17 @@ func SetUpdater(u runner.Updater) gin.HandlerFunc {
} }
} }
func ToSettings(c *gin.Context) *settings.Settings { func ToSettings(c *gin.Context) *config.Config {
v, ok := c.Get("settings") v, ok := c.Get("config")
if !ok { if !ok {
return nil return nil
} }
return v.(*settings.Settings) return v.(*config.Config)
} }
func SetSettings(s *settings.Settings) gin.HandlerFunc { func SetSettings(s *config.Config) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
c.Set("settings", s) c.Set("config", s)
c.Next() c.Next()
} }
} }
@ -249,7 +249,7 @@ func MustAgent() gin.HandlerFunc {
conf := ToSettings(c) conf := ToSettings(c)
// verify remote agents are enabled // verify remote agents are enabled
if conf.Agents == nil || len(conf.Agents.Secret) == 0 { if len(conf.Agents.Secret) == 0 {
c.AbortWithStatus(405) c.AbortWithStatus(405)
return return
} }

View file

@ -6,7 +6,7 @@ import (
"time" "time"
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" "github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
"github.com/drone/drone/pkg/settings" "github.com/drone/drone/pkg/config"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
) )
@ -20,8 +20,8 @@ type session struct {
expire time.Duration expire time.Duration
} }
func New(s *settings.Session) Session { func New(s *config.Config) Session {
secret := []byte(s.Secret) secret := []byte(s.Session.Secret)
expire := time.Hour * 72 expire := time.Hour * 72
return &session{ return &session{
secret: secret, secret: secret,

View file

@ -12,9 +12,9 @@ import (
. "github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin" . "github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/mock" "github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/mock"
"github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/server/recorder" "github.com/drone/drone/pkg/server/recorder"
"github.com/drone/drone/pkg/server/session" "github.com/drone/drone/pkg/server/session"
"github.com/drone/drone/pkg/settings"
"github.com/drone/drone/pkg/store/mock" "github.com/drone/drone/pkg/store/mock"
"github.com/drone/drone/pkg/types" "github.com/drone/drone/pkg/types"
) )
@ -60,9 +60,10 @@ func TestToken(t *testing.T) {
ctx.Set("datastore", store) ctx.Set("datastore", store)
ctx.Set("user", &types.User{Login: "Freya"}) ctx.Set("user", &types.User{Login: "Freya"})
config := settings.Settings{Session: &settings.Session{Secret: "Otto"}} conf := &config.Config{}
ctx.Set("settings", &config) conf.Session.Secret = "Otto"
ctx.Set("session", session.New(config.Session)) ctx.Set("settings", conf)
ctx.Set("session", session.New(conf))
// prepare the mock // prepare the mock
store.On("AddToken", mock.AnythingOfType("*types.Token")).Return(test.storeErr).Once() store.On("AddToken", mock.AnythingOfType("*types.Token")).Return(test.storeErr).Once()
@ -98,9 +99,10 @@ func TestToken(t *testing.T) {
ctx.Set("datastore", store) ctx.Set("datastore", store)
ctx.Set("user", &types.User{Login: "Freya"}) ctx.Set("user", &types.User{Login: "Freya"})
config := settings.Settings{Session: &settings.Session{Secret: "Otto"}} conf := &config.Config{}
ctx.Set("settings", &config) conf.Session.Secret = "Otto"
ctx.Set("session", session.New(config.Session)) ctx.Set("settings", conf)
ctx.Set("session", session.New(conf))
// prepare the mock // prepare the mock
store.On("TokenLabel", mock.AnythingOfType("*types.User"), test.inLabel).Return(test.outToken, test.errTokenLabel).Once() store.On("TokenLabel", mock.AnythingOfType("*types.User"), test.inLabel).Return(test.outToken, test.errTokenLabel).Once()

View file

@ -76,7 +76,7 @@ func GetStream(c *gin.Context) {
// if the commit is being executed by an agent // if the commit is being executed by an agent
// we'll proxy the build output directly to the // we'll proxy the build output directly to the
// remote Docker client, through the agent. // remote Docker client, through the agent.
if conf.Agents != nil && conf.Agents.Secret != "" { if conf.Agents.Secret != "" {
addr, err := store.Agent(commit) addr, err := store.Agent(commit)
if err != nil { if err != nil {
c.Fail(500, err) c.Fail(500, err)

View file

@ -1,137 +0,0 @@
package settings
import "github.com/drone/drone/Godeps/_workspace/src/github.com/BurntSushi/toml"
// Service represents the configuration details required
// to connect to the revision control system (ie GitHub, Bitbucket)
type Service struct {
// Name defines the name of the plugin. Possible values
// may be github, gitlab, bitbucket, or gogs.
Name string `toml:"name"`
// Address defines the address (uri) of the plugin for
// communication via the net/rpc package.
Address string `toml:"address"`
// Base defines the base URL for the service. For example:
// https://github.com
// https://bitbucket.org
// https://gitlab.drone.io
Base string `toml:"base"`
// Indicates registration is open. If true any user
// will be able to setup an account. If false, the
// system administrator will need to provision accounts.
Open bool `toml:"open"`
// Orgs defines a list of organizations the user
// must belong to in order to register. This will
// take precedence over the `Open` paramter.
Orgs []string `toml:"orgs"`
// PrivateMode should be set to true if the
// remote system requires authentication for
// cloning public (open source) repositories.
PrivateMode bool `toml:"private_mode"`
// SkipVerify instructs the client to skip SSL verification.
// This may be used with self-signed certificates, however,
// is not recommended for security reasons.
SkipVerify bool `toml:"skip_verify"`
// OAuth configuration data. If nil or empty, Drone may
// assume basic authentication via username and password.
OAuth *OAuth `toml:"oauth"`
}
// OAuth defines how a user should autheticate with the service.
// This supports OAuth2 and OAuth1 protocols.
type OAuth struct {
Client string `toml:"client"`
Secret string `toml:"secret"`
Authorize string `toml:"authorize"`
AccessToken string `toml:"access_token"`
RequestToken string `toml:"request_token"`
Scope []string `toml:"scope"`
}
// Server represents the web server configuration details
// used to server HTTP requests.
type Server struct {
Base string `toml:"base"`
Addr string `toml:"addr"`
Cert string `toml:"cert"`
Key string `toml:"key"`
Scheme string `toml:"scheme"`
Hostname string `toml:"hostname"`
}
// Session represents the session configuration details
// used to generate, validate and expire authentication
// sessions.
type Session struct {
Secret string `toml:"secret"`
Expires int64 `toml:"expires"`
}
// Docker represents the configuration details used
// to connect to the Docker daemon when scheduling
// and executing builds in containers.
type Docker struct {
Cert string `toml:"cert"`
Key string `toml:"key"`
Nodes []string `toml:"nodes"`
}
// Database represents the configuration details used
// to connect to the embedded Bolt database.
type Database struct {
Driver string `toml:"driver"`
Datasource string `toml:"datasource"`
}
type Agents struct {
Secret string `toml:"secret"`
}
// Settings defines global settings for the Drone system.
type Settings struct {
Database *Database `toml:"database"`
Docker *Docker `toml:"docker"`
Service *Service `toml:"service"`
Server *Server `toml:"server"`
Session *Session `toml:"session"`
Agents *Agents `toml:"agents"`
Plugins map[string]interface{} `toml:"plugins"`
}
// Parse parses the Drone settings file at the specified path
// and unmarshals to a Settings structure.
func Parse(path string) (*Settings, error) {
s := &Settings{}
_, err := toml.DecodeFile(path, s)
return applyDefaults(s), err
}
// ParseString parses the Drone settings string and unmarshals
// to a Settings structure.
func ParseString(data string) (*Settings, error) {
s := &Settings{}
_, err := toml.Decode(data, s)
return applyDefaults(s), err
}
func applyDefaults(s *Settings) *Settings {
if s.Session == nil {
s.Session = &Session{}
}
// if no session token is provided we can
// instead use the client secret to sign
// our sessions and tokens.
if len(s.Session.Secret) == 0 {
s.Session.Secret = s.Service.OAuth.Secret
}
return s
}