Merge branch 'develop' into apikey_hashed
Conflicts: pkg/api/apikey.go pkg/services/sqlstore/migrations.go
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func GetAccount(c *middleware.Context) {
|
||||
query := m.GetAccountByIdQuery{Id: c.AccountId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err == m.ErrAccountNotFound {
|
||||
c.JsonApiErr(404, "Account not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonApiErr(500, "Failed to get account", err)
|
||||
return
|
||||
}
|
||||
|
||||
account := m.AccountDTO{
|
||||
Id: query.Result.Id,
|
||||
Name: query.Result.Name,
|
||||
}
|
||||
|
||||
c.JSON(200, &account)
|
||||
}
|
||||
|
||||
func CreateAccount(c *middleware.Context, cmd m.CreateAccountCommand) {
|
||||
cmd.UserId = c.UserId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to create account", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Account created")
|
||||
}
|
||||
|
||||
func UpdateAccount(c *middleware.Context, cmd m.UpdateAccountCommand) {
|
||||
cmd.AccountId = c.AccountId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to update account", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Account updated")
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func AdminGetSettings(c *middleware.Context) {
|
||||
settings := make(map[string]interface{})
|
||||
|
||||
for _, section := range setting.Cfg.Sections() {
|
||||
jsonSec := make(map[string]interface{})
|
||||
settings[section.Name()] = jsonSec
|
||||
|
||||
for _, key := range section.Keys() {
|
||||
keyName := key.Name()
|
||||
value := key.Value()
|
||||
if strings.Contains(keyName, "secret") || strings.Contains(keyName, "password") {
|
||||
value = "************"
|
||||
}
|
||||
|
||||
jsonSec[keyName] = value
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(200, settings)
|
||||
}
|
||||
+138
-5
@@ -1,20 +1,153 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func AdminSearchUsers(c *middleware.Context) {
|
||||
// query := c.QueryStrings("q")
|
||||
// page := c.QueryStrings("p")
|
||||
|
||||
query := m.SearchUsersQuery{Query: "", Page: 0, Limit: 20}
|
||||
query := m.SearchUsersQuery{Query: "", Page: 0, Limit: 1000}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to fetch collaboratos", err)
|
||||
c.JsonApiErr(500, "Failed to fetch users", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, query.Result)
|
||||
}
|
||||
|
||||
func AdminGetUser(c *middleware.Context) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
query := m.GetUserByIdQuery{Id: userId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to fetch user", err)
|
||||
return
|
||||
}
|
||||
|
||||
result := m.UserDTO{
|
||||
Name: query.Result.Name,
|
||||
Email: query.Result.Email,
|
||||
Login: query.Result.Login,
|
||||
IsGrafanaAdmin: query.Result.IsAdmin,
|
||||
}
|
||||
|
||||
c.JSON(200, result)
|
||||
}
|
||||
|
||||
func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) {
|
||||
cmd := m.CreateUserCommand{
|
||||
Login: form.Login,
|
||||
Email: form.Email,
|
||||
Password: form.Password,
|
||||
Name: form.Name,
|
||||
}
|
||||
|
||||
if len(cmd.Login) == 0 {
|
||||
cmd.Login = cmd.Email
|
||||
if len(cmd.Login) == 0 {
|
||||
c.JsonApiErr(400, "Validation error, need specify either username or email", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(cmd.Password) < 4 {
|
||||
c.JsonApiErr(400, "Password is missing or too short", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "failed to create user", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User created")
|
||||
}
|
||||
|
||||
func AdminUpdateUser(c *middleware.Context, form dtos.AdminUpdateUserForm) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
cmd := m.UpdateUserCommand{
|
||||
UserId: userId,
|
||||
Login: form.Login,
|
||||
Email: form.Email,
|
||||
Name: form.Name,
|
||||
}
|
||||
|
||||
if len(cmd.Login) == 0 {
|
||||
cmd.Login = cmd.Email
|
||||
if len(cmd.Login) == 0 {
|
||||
c.JsonApiErr(400, "Validation error, need specify either username or email", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "failed to update user", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User updated")
|
||||
}
|
||||
|
||||
func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPasswordForm) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
if len(form.Password) < 4 {
|
||||
c.JsonApiErr(400, "New password too short", nil)
|
||||
return
|
||||
}
|
||||
|
||||
userQuery := m.GetUserByIdQuery{Id: userId}
|
||||
|
||||
if err := bus.Dispatch(&userQuery); err != nil {
|
||||
c.JsonApiErr(500, "Could not read user from database", err)
|
||||
return
|
||||
}
|
||||
|
||||
passwordHashed := util.EncodePassword(form.Password, userQuery.Result.Salt)
|
||||
|
||||
cmd := m.ChangeUserPasswordCommand{
|
||||
UserId: userId,
|
||||
NewPassword: passwordHashed,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to update user password", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User password updated")
|
||||
}
|
||||
|
||||
func AdminUpdateUserPermissions(c *middleware.Context, form dtos.AdminUpdateUserPermissionsForm) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
cmd := m.UpdateUserPermissionsCommand{
|
||||
UserId: userId,
|
||||
IsGrafanaAdmin: form.IsGrafanaAdmin,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to update user permissions", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User permissions updated")
|
||||
}
|
||||
|
||||
func AdminDeleteUser(c *middleware.Context) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
cmd := m.DeleteUserCommand{UserId: userId}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to delete user", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User deleted")
|
||||
}
|
||||
|
||||
+29
-15
@@ -25,12 +25,15 @@ func Register(r *macaron.Macaron) {
|
||||
|
||||
// authed views
|
||||
r.Get("/profile/", reqSignedIn, Index)
|
||||
r.Get("/account/", reqSignedIn, Index)
|
||||
r.Get("/account/datasources/", reqSignedIn, Index)
|
||||
r.Get("/account/users/", reqSignedIn, Index)
|
||||
r.Get("/account/apikeys/", reqSignedIn, Index)
|
||||
r.Get("/account/import/", reqSignedIn, Index)
|
||||
r.Get("/org/", reqSignedIn, Index)
|
||||
r.Get("/datasources/", reqSignedIn, Index)
|
||||
r.Get("/org/users/", reqSignedIn, Index)
|
||||
r.Get("/org/apikeys/", reqSignedIn, Index)
|
||||
r.Get("/dashboard/import/", reqSignedIn, Index)
|
||||
r.Get("/admin/settings", reqGrafanaAdmin, Index)
|
||||
r.Get("/admin/users", reqGrafanaAdmin, Index)
|
||||
r.Get("/admin/users/create", reqGrafanaAdmin, Index)
|
||||
r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index)
|
||||
r.Get("/dashboard/*", reqSignedIn, Index)
|
||||
|
||||
// sign up
|
||||
@@ -43,20 +46,21 @@ func Register(r *macaron.Macaron) {
|
||||
r.Group("/user", func() {
|
||||
r.Get("/", GetUser)
|
||||
r.Put("/", bind(m.UpdateUserCommand{}), UpdateUser)
|
||||
r.Post("/using/:id", SetUsingAccount)
|
||||
r.Get("/accounts", GetUserAccounts)
|
||||
r.Post("/using/:id", UserSetUsingOrg)
|
||||
r.Get("/orgs", GetUserOrgList)
|
||||
r.Post("/stars/dashboard/:id", StarDashboard)
|
||||
r.Delete("/stars/dashboard/:id", UnstarDashboard)
|
||||
r.Put("/password", bind(m.ChangeUserPasswordCommand{}), ChangeUserPassword)
|
||||
})
|
||||
|
||||
// account
|
||||
r.Group("/account", func() {
|
||||
r.Get("/", GetAccount)
|
||||
r.Post("/", bind(m.CreateAccountCommand{}), CreateAccount)
|
||||
r.Put("/", bind(m.UpdateAccountCommand{}), UpdateAccount)
|
||||
r.Post("/users", bind(m.AddAccountUserCommand{}), AddAccountUser)
|
||||
r.Get("/users", GetAccountUsers)
|
||||
r.Delete("/users/:id", RemoveAccountUser)
|
||||
r.Group("/org", func() {
|
||||
r.Get("/", GetOrg)
|
||||
r.Post("/", bind(m.CreateOrgCommand{}), CreateOrg)
|
||||
r.Put("/", bind(m.UpdateOrgCommand{}), UpdateOrg)
|
||||
r.Post("/users", bind(m.AddOrgUserCommand{}), AddOrgUser)
|
||||
r.Get("/users", GetOrgUsers)
|
||||
r.Delete("/users/:id", RemoveOrgUser)
|
||||
}, reqAccountAdmin)
|
||||
|
||||
// auth api keys
|
||||
@@ -70,9 +74,12 @@ func Register(r *macaron.Macaron) {
|
||||
r.Group("/datasources", func() {
|
||||
r.Combo("/").Get(GetDataSources).Put(AddDataSource).Post(UpdateDataSource)
|
||||
r.Delete("/:id", DeleteDataSource)
|
||||
r.Any("/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
|
||||
r.Get("/:id", GetDataSourceById)
|
||||
}, reqAccountAdmin)
|
||||
|
||||
r.Get("/frontend/settings/", GetFrontendSettings)
|
||||
r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
|
||||
|
||||
// Dashboard
|
||||
r.Group("/dashboards", func() {
|
||||
r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard)
|
||||
@@ -89,7 +96,14 @@ func Register(r *macaron.Macaron) {
|
||||
|
||||
// admin api
|
||||
r.Group("/api/admin", func() {
|
||||
r.Get("/settings", AdminGetSettings)
|
||||
r.Get("/users", AdminSearchUsers)
|
||||
r.Get("/users/:id", AdminGetUser)
|
||||
r.Post("/users", bind(dtos.AdminCreateUserForm{}), AdminCreateUser)
|
||||
r.Put("/users/:id/details", bind(dtos.AdminUpdateUserForm{}), AdminUpdateUser)
|
||||
r.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), AdminUpdateUserPassword)
|
||||
r.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), AdminUpdateUserPermissions)
|
||||
r.Delete("/users/:id", AdminDeleteUser)
|
||||
}, reqGrafanaAdmin)
|
||||
|
||||
// rendering
|
||||
|
||||
+20
-3
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func GetApiKeys(c *middleware.Context) {
|
||||
query := m.GetApiKeysQuery{AccountId: c.AccountId}
|
||||
query := m.GetApiKeysQuery{OrgId: c.OrgId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to list api keys", err)
|
||||
@@ -29,7 +29,7 @@ func GetApiKeys(c *middleware.Context) {
|
||||
func DeleteApiKey(c *middleware.Context) {
|
||||
id := c.ParamsInt64(":id")
|
||||
|
||||
cmd := &m.DeleteApiKeyCommand{Id: id, AccountId: c.AccountId}
|
||||
cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
|
||||
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
@@ -46,7 +46,7 @@ func AddApiKey(c *middleware.Context, cmd m.AddApiKeyCommand) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd.AccountId = c.AccountId
|
||||
cmd.OrgId = c.OrgId
|
||||
cmd.Key = util.GetRandomString(64)
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
@@ -62,3 +62,20 @@ func AddApiKey(c *middleware.Context, cmd m.AddApiKeyCommand) {
|
||||
|
||||
c.JSON(200, result)
|
||||
}
|
||||
|
||||
func UpdateApiKey(c *middleware.Context, cmd m.UpdateApiKeyCommand) {
|
||||
if !cmd.Role.IsValid() {
|
||||
c.JsonApiErr(400, "Invalid role specified", nil)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
err := bus.Dispatch(&cmd)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to update api key", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("API key updated")
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func isDasboardStarredByUser(c *middleware.Context, dashId int64) (bool, error)
|
||||
func GetDashboard(c *middleware.Context) {
|
||||
slug := c.Params(":slug")
|
||||
|
||||
query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId}
|
||||
query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId}
|
||||
err := bus.Dispatch(&query)
|
||||
if err != nil {
|
||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||
@@ -54,13 +54,13 @@ func GetDashboard(c *middleware.Context) {
|
||||
func DeleteDashboard(c *middleware.Context) {
|
||||
slug := c.Params(":slug")
|
||||
|
||||
query := m.GetDashboardQuery{Slug: slug, AccountId: c.AccountId}
|
||||
query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.AccountId}
|
||||
cmd := m.DeleteDashboardCommand{Slug: slug, OrgId: c.OrgId}
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to delete dashboard", err)
|
||||
return
|
||||
@@ -72,7 +72,7 @@ func DeleteDashboard(c *middleware.Context) {
|
||||
}
|
||||
|
||||
func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) {
|
||||
cmd.AccountId = c.AccountId
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
err := bus.Dispatch(&cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,6 +17,7 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy
|
||||
director := func(req *http.Request) {
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
req.Host = target.Host
|
||||
|
||||
reqQueryVals := req.URL.Query()
|
||||
|
||||
@@ -25,6 +26,12 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy
|
||||
reqQueryVals.Add("u", ds.User)
|
||||
reqQueryVals.Add("p", ds.Password)
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
} else if ds.Type == m.DS_INFLUXDB_08 {
|
||||
req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath)
|
||||
reqQueryVals.Add("db", ds.Database)
|
||||
reqQueryVals.Add("u", ds.User)
|
||||
reqQueryVals.Add("p", ds.Password)
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
} else {
|
||||
req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath)
|
||||
}
|
||||
@@ -38,8 +45,8 @@ func ProxyDataSourceRequest(c *middleware.Context) {
|
||||
id := c.ParamsInt64(":id")
|
||||
|
||||
query := m.GetDataSourceByIdQuery{
|
||||
Id: id,
|
||||
AccountId: c.AccountId,
|
||||
Id: id,
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
err := bus.Dispatch(&query)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestAccountDataAccess(t *testing.T) {
|
||||
func TestDataSourceProxy(t *testing.T) {
|
||||
|
||||
Convey("When getting graphite datasource proxy", t, func() {
|
||||
ds := m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
|
||||
|
||||
+34
-7
@@ -8,10 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func GetDataSources(c *middleware.Context) {
|
||||
query := m.GetDataSourcesQuery{AccountId: c.AccountId}
|
||||
err := bus.Dispatch(&query)
|
||||
query := m.GetDataSourcesQuery{OrgId: c.OrgId}
|
||||
|
||||
if err != nil {
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to query datasources", err)
|
||||
return
|
||||
}
|
||||
@@ -20,7 +19,7 @@ func GetDataSources(c *middleware.Context) {
|
||||
for i, ds := range query.Result {
|
||||
result[i] = &dtos.DataSource{
|
||||
Id: ds.Id,
|
||||
AccountId: ds.AccountId,
|
||||
OrgId: ds.OrgId,
|
||||
Name: ds.Name,
|
||||
Url: ds.Url,
|
||||
Type: ds.Type,
|
||||
@@ -36,6 +35,34 @@ func GetDataSources(c *middleware.Context) {
|
||||
c.JSON(200, result)
|
||||
}
|
||||
|
||||
func GetDataSourceById(c *middleware.Context) {
|
||||
query := m.GetDataSourceByIdQuery{
|
||||
Id: c.ParamsInt64(":id"),
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to query datasources", err)
|
||||
return
|
||||
}
|
||||
|
||||
ds := query.Result
|
||||
|
||||
c.JSON(200, &dtos.DataSource{
|
||||
Id: ds.Id,
|
||||
OrgId: ds.OrgId,
|
||||
Name: ds.Name,
|
||||
Url: ds.Url,
|
||||
Type: ds.Type,
|
||||
Access: ds.Access,
|
||||
Password: ds.Password,
|
||||
Database: ds.Database,
|
||||
User: ds.User,
|
||||
BasicAuth: ds.BasicAuth,
|
||||
IsDefault: ds.IsDefault,
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteDataSource(c *middleware.Context) {
|
||||
id := c.ParamsInt64(":id")
|
||||
|
||||
@@ -44,7 +71,7 @@ func DeleteDataSource(c *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.AccountId}
|
||||
cmd := &m.DeleteDataSourceCommand{Id: id, OrgId: c.OrgId}
|
||||
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
@@ -63,7 +90,7 @@ func AddDataSource(c *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd.AccountId = c.AccountId
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to add datasource", err)
|
||||
@@ -81,7 +108,7 @@ func UpdateDataSource(c *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd.AccountId = c.AccountId
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
err := bus.Dispatch(&cmd)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,8 +19,8 @@ type CurrentUser struct {
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
AccountRole m.RoleType `json:"accountRole"`
|
||||
AccountName string `json:"acountName"`
|
||||
OrgRole m.RoleType `json:"orgRole"`
|
||||
OrgName string `json:"orgName"`
|
||||
IsGrafanaAdmin bool `json:"isGrafanaAdmin"`
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
@@ -38,7 +38,7 @@ type Dashboard struct {
|
||||
|
||||
type DataSource struct {
|
||||
Id int64 `json:"id"`
|
||||
AccountId int64 `json:"accountId"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Type m.DsType `json:"type"`
|
||||
Access m.DsAccess `json:"access"`
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package dtos
|
||||
|
||||
type AdminCreateUserForm struct {
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password" binding:"Required"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserForm struct {
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserPasswordForm struct {
|
||||
Password string `json:"password" binding:"Required"`
|
||||
}
|
||||
|
||||
type AdminUpdateUserPermissionsForm struct {
|
||||
IsGrafanaAdmin bool `json:"IsGrafanaAdmin" binding:"Required"`
|
||||
}
|
||||
@@ -9,23 +9,23 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error) {
|
||||
accountDataSources := make([]*m.DataSource, 0)
|
||||
func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) {
|
||||
orgDataSources := make([]*m.DataSource, 0)
|
||||
|
||||
if c.IsSignedIn {
|
||||
query := m.GetDataSourcesQuery{AccountId: c.AccountId}
|
||||
query := m.GetDataSourcesQuery{OrgId: c.OrgId}
|
||||
err := bus.Dispatch(&query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accountDataSources = query.Result
|
||||
orgDataSources = query.Result
|
||||
}
|
||||
|
||||
datasources := make(map[string]interface{})
|
||||
|
||||
for _, ds := range accountDataSources {
|
||||
for _, ds := range orgDataSources {
|
||||
url := ds.Url
|
||||
|
||||
if ds.Access == m.DS_ACCESS_PROXY {
|
||||
@@ -38,7 +38,7 @@ func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error)
|
||||
"default": ds.IsDefault,
|
||||
}
|
||||
|
||||
if ds.Type == m.DS_INFLUXDB {
|
||||
if ds.Type == m.DS_INFLUXDB_08 {
|
||||
if ds.Access == m.DS_ACCESS_DIRECT {
|
||||
dsMap["username"] = ds.User
|
||||
dsMap["password"] = ds.Password
|
||||
@@ -46,6 +46,15 @@ func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error)
|
||||
}
|
||||
}
|
||||
|
||||
if ds.Type == m.DS_INFLUXDB {
|
||||
if ds.Access == m.DS_ACCESS_DIRECT {
|
||||
dsMap["username"] = ds.User
|
||||
dsMap["password"] = ds.Password
|
||||
dsMap["database"] = ds.Database
|
||||
dsMap["url"] = url
|
||||
}
|
||||
}
|
||||
|
||||
if ds.Type == m.DS_ES {
|
||||
dsMap["index"] = ds.Database
|
||||
}
|
||||
@@ -71,3 +80,13 @@ func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error)
|
||||
|
||||
return jsonObj, nil
|
||||
}
|
||||
|
||||
func GetFrontendSettings(c *middleware.Context) {
|
||||
settings, err := getFrontendSettingsMap(c)
|
||||
if err != nil {
|
||||
c.JsonApiErr(400, "Failed to get frontend settings", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, settings)
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func setIndexViewData(c *middleware.Context) error {
|
||||
settings, err := getFrontendSettings(c)
|
||||
settings, err := getFrontendSettingsMap(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -17,8 +17,8 @@ func setIndexViewData(c *middleware.Context) error {
|
||||
Login: c.Login,
|
||||
Email: c.Email,
|
||||
Name: c.Name,
|
||||
AccountName: c.AccountName,
|
||||
AccountRole: c.AccountRole,
|
||||
OrgName: c.OrgName,
|
||||
OrgRole: c.OrgRole,
|
||||
GravatarUrl: dtos.GetGravatarUrl(c.Email),
|
||||
IsGrafanaAdmin: c.IsGrafanaAdmin,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func GetOrg(c *middleware.Context) {
|
||||
query := m.GetOrgByIdQuery{Id: c.OrgId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err == m.ErrOrgNotFound {
|
||||
c.JsonApiErr(404, "Organization not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonApiErr(500, "Failed to get organization", err)
|
||||
return
|
||||
}
|
||||
|
||||
org := m.OrgDTO{
|
||||
Id: query.Result.Id,
|
||||
Name: query.Result.Name,
|
||||
}
|
||||
|
||||
c.JSON(200, &org)
|
||||
}
|
||||
|
||||
func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) {
|
||||
cmd.UserId = c.UserId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to create organization", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Organization created")
|
||||
}
|
||||
|
||||
func UpdateOrg(c *middleware.Context, cmd m.UpdateOrgCommand) {
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to update organization", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Organization updated")
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func AddAccountUser(c *middleware.Context, cmd m.AddAccountUserCommand) {
|
||||
func AddOrgUser(c *middleware.Context, cmd m.AddOrgUserCommand) {
|
||||
if !cmd.Role.IsValid() {
|
||||
c.JsonApiErr(400, "Invalid role specified", nil)
|
||||
return
|
||||
@@ -26,19 +26,19 @@ func AddAccountUser(c *middleware.Context, cmd m.AddAccountUserCommand) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd.AccountId = c.AccountId
|
||||
cmd.OrgId = c.OrgId
|
||||
cmd.UserId = userToAdd.Id
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Could not add user to account", err)
|
||||
c.JsonApiErr(500, "Could not add user to organization", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User added to account")
|
||||
c.JsonOK("User added to organization")
|
||||
}
|
||||
|
||||
func GetAccountUsers(c *middleware.Context) {
|
||||
query := m.GetAccountUsersQuery{AccountId: c.AccountId}
|
||||
func GetOrgUsers(c *middleware.Context) {
|
||||
query := m.GetOrgUsersQuery{OrgId: c.OrgId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to get account user", err)
|
||||
@@ -48,18 +48,18 @@ func GetAccountUsers(c *middleware.Context) {
|
||||
c.JSON(200, query.Result)
|
||||
}
|
||||
|
||||
func RemoveAccountUser(c *middleware.Context) {
|
||||
func RemoveOrgUser(c *middleware.Context) {
|
||||
userId := c.ParamsInt64(":id")
|
||||
|
||||
cmd := m.RemoveAccountUserCommand{AccountId: c.AccountId, UserId: userId}
|
||||
cmd := m.RemoveOrgUserCommand{OrgId: c.OrgId, UserId: userId}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == m.ErrLastAccountAdmin {
|
||||
c.JsonApiErr(400, "Cannot remove last account admin", nil)
|
||||
if err == m.ErrLastOrgAdmin {
|
||||
c.JsonApiErr(400, "Cannot remove last organization admin", nil)
|
||||
return
|
||||
}
|
||||
c.JsonApiErr(500, "Failed to remove user from account", err)
|
||||
c.JsonApiErr(500, "Failed to remove user from organization", err)
|
||||
}
|
||||
|
||||
c.JsonOK("User removed from account")
|
||||
c.JsonOK("User removed from organization")
|
||||
}
|
||||
+2
-6
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// TODO: this needs to be cached or improved somehow
|
||||
@@ -45,7 +44,7 @@ func Search(c *middleware.Context) {
|
||||
|
||||
if tagcloud == "true" {
|
||||
|
||||
query := m.GetDashboardTagsQuery{AccountId: c.AccountId}
|
||||
query := m.GetDashboardTagsQuery{OrgId: c.OrgId}
|
||||
err := bus.Dispatch(&query)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to get tags from database", err)
|
||||
@@ -61,7 +60,7 @@ func Search(c *middleware.Context) {
|
||||
UserId: c.UserId,
|
||||
Limit: limit,
|
||||
IsStarred: starred == "true",
|
||||
AccountId: c.AccountId,
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
err := bus.Dispatch(&query)
|
||||
@@ -76,9 +75,6 @@ func Search(c *middleware.Context) {
|
||||
}
|
||||
|
||||
result.Dashboards = query.Result
|
||||
for _, dash := range result.Dashboards {
|
||||
dash.Url = setting.ToAbsUrl("dashboard/db/" + dash.Slug)
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(200, result)
|
||||
|
||||
+51
-20
@@ -4,13 +4,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func GetUser(c *middleware.Context) {
|
||||
query := m.GetUserInfoQuery{UserId: c.UserId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to get account", err)
|
||||
c.JsonApiErr(500, "Failed to get user", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,23 +22,23 @@ func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) {
|
||||
cmd.UserId = c.UserId
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(400, "Failed to update account", err)
|
||||
c.JsonApiErr(400, "Failed to update user", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Account updated")
|
||||
c.JsonOK("User updated")
|
||||
}
|
||||
|
||||
func GetUserAccounts(c *middleware.Context) {
|
||||
query := m.GetUserAccountsQuery{UserId: c.UserId}
|
||||
func GetUserOrgList(c *middleware.Context) {
|
||||
query := m.GetUserOrgListQuery{UserId: c.UserId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(500, "Failed to get user accounts", err)
|
||||
c.JsonApiErr(500, "Failed to get user organizations", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, ac := range query.Result {
|
||||
if ac.AccountId == c.AccountId {
|
||||
if ac.OrgId == c.OrgId {
|
||||
ac.IsUsing = true
|
||||
break
|
||||
}
|
||||
@@ -46,17 +47,17 @@ func GetUserAccounts(c *middleware.Context) {
|
||||
c.JSON(200, query.Result)
|
||||
}
|
||||
|
||||
func validateUsingAccount(userId int64, accountId int64) bool {
|
||||
query := m.GetUserAccountsQuery{UserId: userId}
|
||||
func validateUsingOrg(userId int64, orgId int64) bool {
|
||||
query := m.GetUserOrgListQuery{UserId: userId}
|
||||
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// validate that the account id in the list
|
||||
// validate that the org id in the list
|
||||
valid := false
|
||||
for _, other := range query.Result {
|
||||
if other.AccountId == accountId {
|
||||
if other.OrgId == orgId {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
@@ -64,23 +65,53 @@ func validateUsingAccount(userId int64, accountId int64) bool {
|
||||
return valid
|
||||
}
|
||||
|
||||
func SetUsingAccount(c *middleware.Context) {
|
||||
usingAccountId := c.ParamsInt64(":id")
|
||||
func UserSetUsingOrg(c *middleware.Context) {
|
||||
orgId := c.ParamsInt64(":id")
|
||||
|
||||
if !validateUsingAccount(c.UserId, usingAccountId) {
|
||||
c.JsonApiErr(401, "Not a valid account", nil)
|
||||
if !validateUsingOrg(c.UserId, orgId) {
|
||||
c.JsonApiErr(401, "Not a valid organization", nil)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := m.SetUsingAccountCommand{
|
||||
UserId: c.UserId,
|
||||
AccountId: usingAccountId,
|
||||
cmd := m.SetUsingOrgCommand{
|
||||
UserId: c.UserId,
|
||||
OrgId: orgId,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed change active account", err)
|
||||
c.JsonApiErr(500, "Failed change active organization", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("Active account changed")
|
||||
c.JsonOK("Active organization changed")
|
||||
}
|
||||
|
||||
func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) {
|
||||
userQuery := m.GetUserByIdQuery{Id: c.UserId}
|
||||
|
||||
if err := bus.Dispatch(&userQuery); err != nil {
|
||||
c.JsonApiErr(500, "Could not read user from database", err)
|
||||
return
|
||||
}
|
||||
|
||||
passwordHashed := util.EncodePassword(cmd.OldPassword, userQuery.Result.Salt)
|
||||
if passwordHashed != userQuery.Result.Password {
|
||||
c.JsonApiErr(401, "Invalid old password", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if len(cmd.NewPassword) < 4 {
|
||||
c.JsonApiErr(400, "New password too short", nil)
|
||||
return
|
||||
}
|
||||
|
||||
cmd.UserId = c.UserId
|
||||
cmd.NewPassword = util.EncodePassword(cmd.NewPassword, userQuery.Result.Salt)
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to change user password", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonOK("User password changed")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func initRuntime(c *cli.Context) {
|
||||
setting.NewConfigContext(c.GlobalString("config"))
|
||||
sqlstore.NewEngine()
|
||||
sqlstore.EnsureAdminUser()
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
var ImportDashboard = cli.Command{
|
||||
Name: "dashboards:import",
|
||||
Usage: "imports dashboards in JSON from a directory",
|
||||
Description: "Starts Grafana import process",
|
||||
Action: runImport,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "dir",
|
||||
Usage: "path to folder containing json dashboards",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runImport(c *cli.Context) {
|
||||
dir := c.String("dir")
|
||||
if len(dir) == 0 {
|
||||
log.ConsoleFatalf("Missing command flag --dir")
|
||||
}
|
||||
|
||||
file, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
log.ConsoleFatalf("Directory does not exist: %v", dir)
|
||||
}
|
||||
|
||||
if !file.IsDir() {
|
||||
log.ConsoleFatalf("%v is not a directory", dir)
|
||||
}
|
||||
|
||||
if !c.Args().Present() {
|
||||
log.ConsoleFatal("Organization name arg is required")
|
||||
}
|
||||
|
||||
orgName := c.Args().First()
|
||||
|
||||
initRuntime(c)
|
||||
|
||||
orgQuery := m.GetOrgByNameQuery{Name: orgName}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find account", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
|
||||
visitor := func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".json") {
|
||||
if err := importDashboard(path, orgId); err != nil {
|
||||
log.ConsoleFatalf("Failed to import dashboard file: %v, err: %v", path, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := filepath.Walk(dir, visitor); err != nil {
|
||||
log.ConsoleFatalf("Failed to scan dir for json files: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func importDashboard(path string, orgId int64) error {
|
||||
log.ConsoleInfof("Importing %v", path)
|
||||
|
||||
reader, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dash := m.NewDashboard("temp")
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
|
||||
if err := jsonParser.Decode(&dash.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
dash.Data["id"] = nil
|
||||
|
||||
cmd := m.SaveDashboardCommand{
|
||||
OrgId: orgId,
|
||||
Dashboard: dash.Data,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
var (
|
||||
ListDataSources = cli.Command{
|
||||
Name: "datasources",
|
||||
Usage: "list datasources",
|
||||
Description: "Lists the datasources in the system",
|
||||
Action: listDatasources,
|
||||
}
|
||||
CreateDataSource = cli.Command{
|
||||
Name: "datasources:create",
|
||||
Usage: "creates a new datasource",
|
||||
Description: "Creates a new datasource",
|
||||
Action: createDataSource,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "type",
|
||||
Value: "graphite",
|
||||
Usage: fmt.Sprintf("Datasource type [%s,%s,%s,%s]",
|
||||
m.DS_GRAPHITE, m.DS_INFLUXDB, m.DS_ES, m.DS_OPENTSDB),
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "access",
|
||||
Value: "proxy",
|
||||
Usage: "Datasource access [proxy,direct]",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "default",
|
||||
Usage: "Make this the default datasource",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "db",
|
||||
Usage: "InfluxDB DB",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user",
|
||||
Usage: "InfluxDB username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "InfluxDB password",
|
||||
},
|
||||
},
|
||||
}
|
||||
DescribeDataSource = cli.Command{
|
||||
Name: "datasources:info",
|
||||
Usage: "describe the details of a datasource",
|
||||
Description: "Describes the details of a datasource",
|
||||
Action: describeDataSource,
|
||||
}
|
||||
DeleteDataSource = cli.Command{
|
||||
Name: "datasources:delete",
|
||||
Usage: "Deletes a datasource",
|
||||
Description: "Deletes a datasource",
|
||||
Action: deleteDataSource,
|
||||
}
|
||||
)
|
||||
|
||||
func createDataSource(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if len(c.Args()) != 3 {
|
||||
log.ConsoleFatal("Missing required arguments")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
ds := c.Args()[1]
|
||||
url := c.Args()[2]
|
||||
dsType := c.String("type")
|
||||
dsAccess := c.String("access")
|
||||
dsDefault := c.Bool("default")
|
||||
|
||||
orgQuery := m.GetOrgByNameQuery{Name: name}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organization: %s", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
|
||||
query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
if err != m.ErrDataSourceNotFound {
|
||||
log.ConsoleFatalf("Failed to query for existing datasource: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if query.Result.Id > 0 {
|
||||
log.ConsoleFatalf("DataSource %s already exists", ds)
|
||||
}
|
||||
|
||||
cmd := m.AddDataSourceCommand{
|
||||
OrgId: orgId,
|
||||
Name: ds,
|
||||
Url: url,
|
||||
Type: m.DsType(dsType),
|
||||
Access: m.DsAccess(dsAccess),
|
||||
IsDefault: dsDefault,
|
||||
}
|
||||
|
||||
switch dsType {
|
||||
case m.DS_INFLUXDB:
|
||||
db := c.String("db")
|
||||
if db == "" {
|
||||
log.ConsoleFatal("db name is required for influxdb datasources")
|
||||
}
|
||||
cmd.Database = db
|
||||
cmd.User = c.String("user")
|
||||
cmd.Password = c.String("password")
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
log.ConsoleFatalf("Failed to create datasource: %s", err)
|
||||
}
|
||||
datasource := cmd.Result
|
||||
|
||||
log.ConsoleInfof("Datasource %s created", datasource.Name)
|
||||
}
|
||||
|
||||
func listDatasources(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if !c.Args().Present() {
|
||||
log.ConsoleFatal("Account name arg is required")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
orgQuery := m.GetOrgByNameQuery{Name: name}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organization: %s", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
|
||||
query := m.GetDataSourcesQuery{OrgId: orgId}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
log.ConsoleFatalf("Failed to find datasources: %s", err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0)
|
||||
|
||||
fmt.Fprintf(w, "ID\tNAME\tURL\tTYPE\tACCESS\tDEFAULT\n")
|
||||
for _, ds := range query.Result {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\n", ds.Id, ds.Name, ds.Url, ds.Type,
|
||||
ds.Access, ds.IsDefault)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func describeDataSource(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if len(c.Args()) != 2 {
|
||||
log.ConsoleFatal("Organization and datasource name args are required")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
ds := c.Args()[1]
|
||||
|
||||
orgQuery := m.GetOrgByNameQuery{Name: name}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organization: %s", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
|
||||
query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
log.ConsoleFatalf("Failed to find datasource: %s", err)
|
||||
}
|
||||
datasource := query.Result
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0)
|
||||
fmt.Fprintf(w, "NAME\t%s\n", datasource.Name)
|
||||
fmt.Fprintf(w, "URL\t%s\n", datasource.Url)
|
||||
fmt.Fprintf(w, "DEFAULT\t%t\n", datasource.IsDefault)
|
||||
fmt.Fprintf(w, "ACCESS\t%s\n", datasource.Access)
|
||||
fmt.Fprintf(w, "TYPE\t%s\n", datasource.Type)
|
||||
|
||||
switch datasource.Type {
|
||||
case m.DS_INFLUXDB:
|
||||
fmt.Fprintf(w, "DATABASE\t%s\n", datasource.Database)
|
||||
fmt.Fprintf(w, "DB USER\t%s\n", datasource.User)
|
||||
fmt.Fprintf(w, "DB PASSWORD\t%s\n", datasource.Password)
|
||||
case m.DS_ES:
|
||||
fmt.Fprintf(w, "INDEX\t%s\n", datasource.Database)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func deleteDataSource(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if len(c.Args()) != 2 {
|
||||
log.ConsoleFatal("Account and datasource name args are required")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
ds := c.Args()[1]
|
||||
|
||||
orgQuery := m.GetOrgByNameQuery{Name: name}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organization: %s", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
|
||||
query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds}
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
log.ConsoleFatalf("Failed to find datasource: %s", err)
|
||||
}
|
||||
datasource := query.Result
|
||||
|
||||
cmd := m.DeleteDataSourceCommand{OrgId: orgId, Id: datasource.Id}
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
log.ConsoleFatalf("Failed to delete datasource: %s", err)
|
||||
}
|
||||
|
||||
log.ConsoleInfof("DataSource %s deleted", ds)
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var CmdImportJson = cli.Command{
|
||||
Name: "import-json",
|
||||
Usage: "grafana import",
|
||||
Description: "Starts Grafana import process",
|
||||
Action: runImport,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "dir",
|
||||
Usage: "path to folder containing json dashboards",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "account",
|
||||
Usage: "Account name to save dashboards under",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "grafana.ini",
|
||||
Usage: "path to config file",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runImport(c *cli.Context) {
|
||||
dir := c.String("dir")
|
||||
if len(dir) == 0 {
|
||||
log.Error(3, "Missing command flag --dir")
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
log.Error(3, "Directory does not exist: %v", dir)
|
||||
return
|
||||
}
|
||||
|
||||
if !file.IsDir() {
|
||||
log.Error(3, "%v is not a directory", dir)
|
||||
return
|
||||
}
|
||||
|
||||
accountName := c.String("account")
|
||||
if len(accountName) == 0 {
|
||||
log.Error(3, "Missing command flag --account")
|
||||
return
|
||||
}
|
||||
|
||||
setting.NewConfigContext()
|
||||
sqlstore.NewEngine()
|
||||
sqlstore.EnsureAdminUser()
|
||||
|
||||
accountQuery := m.GetAccountByNameQuery{Name: accountName}
|
||||
if err := bus.Dispatch(&accountQuery); err != nil {
|
||||
log.Error(3, "Failed to find account", err)
|
||||
return
|
||||
}
|
||||
|
||||
accountId := accountQuery.Result.Id
|
||||
|
||||
visitor := func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".json") {
|
||||
if err := importDashboard(path, accountId); err != nil {
|
||||
log.Error(3, "Failed to import dashboard file: %v, err: %v", path, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := filepath.Walk(dir, visitor); err != nil {
|
||||
log.Error(3, "failed to scan dir for json files: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func importDashboard(path string, accountId int64) error {
|
||||
log.Info("Importing %v", path)
|
||||
|
||||
reader, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dash := m.NewDashboard("temp")
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
|
||||
if err := jsonParser.Decode(&dash.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := m.SaveDashboardCommand{
|
||||
AccountId: accountId,
|
||||
Dashboard: dash.Data,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var ListOrgs = cli.Command{
|
||||
Name: "orgs",
|
||||
Usage: "list organizations",
|
||||
Description: "Lists the organizations in the system",
|
||||
Action: listOrgs,
|
||||
}
|
||||
|
||||
var CreateOrg = cli.Command{
|
||||
Name: "orgs:create",
|
||||
Usage: "Creates a new organization",
|
||||
Description: "Creates a new organization",
|
||||
Action: createOrg,
|
||||
}
|
||||
|
||||
var DeleteOrg = cli.Command{
|
||||
Name: "orgs:delete",
|
||||
Usage: "Delete an existing organization",
|
||||
Description: "Deletes an existing organization",
|
||||
Action: deleteOrg,
|
||||
}
|
||||
|
||||
func listOrgs(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
orgsQuery := m.GetOrgListQuery{}
|
||||
if err := bus.Dispatch(&orgsQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organizations: %s", err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0)
|
||||
|
||||
fmt.Fprintf(w, "ID\tNAME\n")
|
||||
for _, org := range orgsQuery.Result {
|
||||
fmt.Fprintf(w, "%d\t%s\n", org.Id, org.Name)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
func createOrg(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if !c.Args().Present() {
|
||||
log.ConsoleFatal("Organization name arg is required")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
|
||||
adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser}
|
||||
|
||||
if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound {
|
||||
log.ConsoleFatalf("Failed to find default admin user: %s", err)
|
||||
}
|
||||
|
||||
adminUser := adminQuery.Result
|
||||
|
||||
cmd := m.CreateOrgCommand{Name: name, UserId: adminUser.Id}
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
log.ConsoleFatalf("Failed to create organization: %s", err)
|
||||
}
|
||||
|
||||
log.ConsoleInfof("Organization %s created for admin user %s\n", name, adminUser.Email)
|
||||
}
|
||||
|
||||
func deleteOrg(c *cli.Context) {
|
||||
initRuntime(c)
|
||||
|
||||
if !c.Args().Present() {
|
||||
log.ConsoleFatal("Organization name arg is required")
|
||||
}
|
||||
|
||||
name := c.Args().First()
|
||||
orgQuery := m.GetOrgByNameQuery{Name: name}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
log.ConsoleFatalf("Failed to find organization: %s", err)
|
||||
}
|
||||
|
||||
orgId := orgQuery.Result.Id
|
||||
cmd := m.DeleteOrgCommand{Id: orgId}
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
log.ConsoleFatalf("Failed to delete organization: %s", err)
|
||||
}
|
||||
|
||||
log.ConsoleInfof("Organization %s deleted", name)
|
||||
}
|
||||
+4
-13
@@ -17,23 +17,15 @@ import (
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/services/eventpublisher"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/social"
|
||||
)
|
||||
|
||||
var CmdWeb = cli.Command{
|
||||
var Web = cli.Command{
|
||||
Name: "web",
|
||||
Usage: "grafana web",
|
||||
Usage: "Starts Grafana backend & web server",
|
||||
Description: "Starts Grafana backend & web server",
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "grafana.ini",
|
||||
Usage: "path to config file",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func newMacaron() *macaron.Macaron {
|
||||
@@ -78,10 +70,9 @@ func runWeb(c *cli.Context) {
|
||||
log.Info("Starting Grafana")
|
||||
log.Info("Version: %v, Commit: %v, Build date: %v", setting.BuildVersion, setting.BuildCommit, time.Unix(setting.BuildStamp, 0))
|
||||
|
||||
setting.NewConfigContext()
|
||||
initRuntime(c)
|
||||
|
||||
social.NewOAuthService()
|
||||
sqlstore.NewEngine()
|
||||
sqlstore.EnsureAdminUser()
|
||||
eventpublisher.Init()
|
||||
|
||||
var err error
|
||||
|
||||
@@ -50,13 +50,13 @@ func ToOnWriteEvent(event interface{}) (*OnTheWireEvent, error) {
|
||||
return &wireEvent, nil
|
||||
}
|
||||
|
||||
type AccountCreated struct {
|
||||
type OrgCreated struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AccountUpdated struct {
|
||||
type OrgUpdated struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
+92
-10
@@ -6,6 +6,7 @@ package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -21,15 +22,26 @@ func NewBrush(color string) Brush {
|
||||
}
|
||||
}
|
||||
|
||||
var colors = []Brush{
|
||||
NewBrush("1;36"), // Trace cyan
|
||||
NewBrush("1;34"), // Debug blue
|
||||
NewBrush("1;32"), // Info green
|
||||
NewBrush("1;33"), // Warn yellow
|
||||
NewBrush("1;31"), // Error red
|
||||
NewBrush("1;35"), // Critical purple
|
||||
NewBrush("1;31"), // Fatal red
|
||||
}
|
||||
var (
|
||||
Red = NewBrush("1;31")
|
||||
Purple = NewBrush("1;35")
|
||||
Yellow = NewBrush("1;33")
|
||||
Green = NewBrush("1;32")
|
||||
Blue = NewBrush("1;34")
|
||||
Cyan = NewBrush("1;36")
|
||||
|
||||
colors = []Brush{
|
||||
Cyan, // Trace cyan
|
||||
Blue, // Debug blue
|
||||
Green, // Info green
|
||||
Yellow, // Warn yellow
|
||||
Red, // Error red
|
||||
Purple, // Critical purple
|
||||
Red, // Fatal red
|
||||
}
|
||||
consoleWriter = &ConsoleWriter{lg: log.New(os.Stdout, "", 0),
|
||||
Level: TRACE}
|
||||
)
|
||||
|
||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
||||
type ConsoleWriter struct {
|
||||
@@ -40,7 +52,7 @@ type ConsoleWriter struct {
|
||||
// create ConsoleWriter returning as LoggerInterface.
|
||||
func NewConsole() LoggerInterface {
|
||||
return &ConsoleWriter{
|
||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
lg: log.New(os.Stderr, "", log.Ldate|log.Ltime),
|
||||
Level: TRACE,
|
||||
}
|
||||
}
|
||||
@@ -68,6 +80,76 @@ func (_ *ConsoleWriter) Flush() {
|
||||
func (_ *ConsoleWriter) Destroy() {
|
||||
}
|
||||
|
||||
func printConsole(level int, msg string) {
|
||||
consoleWriter.WriteMsg(msg, 0, level)
|
||||
}
|
||||
|
||||
func printfConsole(level int, format string, v ...interface{}) {
|
||||
consoleWriter.WriteMsg(fmt.Sprintf(format, v...), 0, level)
|
||||
}
|
||||
|
||||
// ConsoleTrace prints to stdout using TRACE colors
|
||||
func ConsoleTrace(s string) {
|
||||
printConsole(TRACE, s)
|
||||
}
|
||||
|
||||
// ConsoleTracef prints a formatted string to stdout using TRACE colors
|
||||
func ConsoleTracef(format string, v ...interface{}) {
|
||||
printfConsole(TRACE, format, v...)
|
||||
}
|
||||
|
||||
// ConsoleDebug prints to stdout using DEBUG colors
|
||||
func ConsoleDebug(s string) {
|
||||
printConsole(DEBUG, s)
|
||||
}
|
||||
|
||||
// ConsoleDebugf prints a formatted string to stdout using DEBUG colors
|
||||
func ConsoleDebugf(format string, v ...interface{}) {
|
||||
printfConsole(DEBUG, format, v...)
|
||||
}
|
||||
|
||||
// ConsoleInfo prints to stdout using INFO colors
|
||||
func ConsoleInfo(s string) {
|
||||
printConsole(INFO, s)
|
||||
}
|
||||
|
||||
// ConsoleInfof prints a formatted string to stdout using INFO colors
|
||||
func ConsoleInfof(format string, v ...interface{}) {
|
||||
printfConsole(INFO, format, v...)
|
||||
}
|
||||
|
||||
// ConsoleWarn prints to stdout using WARN colors
|
||||
func ConsoleWarn(s string) {
|
||||
printConsole(WARN, s)
|
||||
}
|
||||
|
||||
// ConsoleWarnf prints a formatted string to stdout using WARN colors
|
||||
func ConsoleWarnf(format string, v ...interface{}) {
|
||||
printfConsole(WARN, format, v...)
|
||||
}
|
||||
|
||||
// ConsoleError prints to stdout using ERROR colors
|
||||
func ConsoleError(s string) {
|
||||
printConsole(ERROR, s)
|
||||
}
|
||||
|
||||
// ConsoleErrorf prints a formatted string to stdout using ERROR colors
|
||||
func ConsoleErrorf(format string, v ...interface{}) {
|
||||
printfConsole(ERROR, format, v...)
|
||||
}
|
||||
|
||||
// ConsoleFatal prints to stdout using FATAL colors
|
||||
func ConsoleFatal(s string) {
|
||||
printConsole(FATAL, s)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// ConsoleFatalf prints a formatted string to stdout using FATAL colors
|
||||
func ConsoleFatalf(format string, v ...interface{}) {
|
||||
printfConsole(FATAL, format, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("console", NewConsole)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler {
|
||||
return func(c *Context) {
|
||||
ok := false
|
||||
for _, role := range roles {
|
||||
if role == c.AccountRole {
|
||||
if role == c.OrgRole {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
|
||||
@@ -56,22 +56,23 @@ func GetContextHandler() macaron.Handler {
|
||||
ctx.SignedInUser = &m.SignedInUser{}
|
||||
|
||||
// TODO: fix this
|
||||
ctx.AccountRole = keyInfo.Role
|
||||
ctx.OrgRole = keyInfo.Role
|
||||
ctx.ApiKeyId = keyInfo.Id
|
||||
ctx.AccountId = keyInfo.AccountId
|
||||
ctx.OrgId = keyInfo.OrgId
|
||||
}
|
||||
} else if setting.AnonymousEnabled {
|
||||
accountQuery := m.GetAccountByNameQuery{Name: setting.AnonymousAccountName}
|
||||
if err := bus.Dispatch(&accountQuery); err != nil {
|
||||
if err == m.ErrAccountNotFound {
|
||||
log.Error(3, "Anonymous access account name does not exist", nil)
|
||||
orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName}
|
||||
if err := bus.Dispatch(&orgQuery); err != nil {
|
||||
if err == m.ErrOrgNotFound {
|
||||
log.Error(3, "Anonymous access organization name does not exist", nil)
|
||||
}
|
||||
} else {
|
||||
ctx.IsSignedIn = false
|
||||
ctx.HasAnonymousAccess = true
|
||||
ctx.SignedInUser = &m.SignedInUser{}
|
||||
ctx.AccountRole = m.RoleType(setting.AnonymousAccountRole)
|
||||
ctx.AccountId = accountQuery.Result.Id
|
||||
ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
|
||||
ctx.OrgId = orgQuery.Result.Id
|
||||
ctx.OrgName = orgQuery.Result.Name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrAccountNotFound = errors.New("Account not found")
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
Id int64
|
||||
Version int
|
||||
Name string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type CreateAccountCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
|
||||
// initial admin user for account
|
||||
UserId int64 `json:"-"`
|
||||
Result Account `json:"-"`
|
||||
}
|
||||
|
||||
type UpdateAccountCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
AccountId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type GetUserAccountsQuery struct {
|
||||
UserId int64
|
||||
Result []*UserAccountDTO
|
||||
}
|
||||
|
||||
type GetAccountByIdQuery struct {
|
||||
Id int64
|
||||
Result *Account
|
||||
}
|
||||
|
||||
type GetAccountByNameQuery struct {
|
||||
Name string
|
||||
Result *Account
|
||||
}
|
||||
|
||||
type AccountDTO struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UserAccountDTO struct {
|
||||
AccountId int64 `json:"accountId"`
|
||||
Name string `json:"name"`
|
||||
Role RoleType `json:"role"`
|
||||
IsUsing bool `json:"isUsing"`
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrInvalidRoleType = errors.New("Invalid role type")
|
||||
ErrLastAccountAdmin = errors.New("Cannot remove last account admin")
|
||||
)
|
||||
|
||||
type RoleType string
|
||||
|
||||
const (
|
||||
ROLE_VIEWER RoleType = "Viewer"
|
||||
ROLE_EDITOR RoleType = "Editor"
|
||||
ROLE_ADMIN RoleType = "Admin"
|
||||
)
|
||||
|
||||
func (r RoleType) IsValid() bool {
|
||||
return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR
|
||||
}
|
||||
|
||||
type AccountUser struct {
|
||||
AccountId int64
|
||||
UserId int64
|
||||
Role RoleType
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type RemoveAccountUserCommand struct {
|
||||
UserId int64
|
||||
AccountId int64
|
||||
}
|
||||
|
||||
type AddAccountUserCommand struct {
|
||||
LoginOrEmail string `json:"loginOrEmail" binding:"Required"`
|
||||
Role RoleType `json:"role" binding:"Required"`
|
||||
|
||||
AccountId int64 `json:"-"`
|
||||
UserId int64 `json:"-"`
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
type GetAccountUsersQuery struct {
|
||||
AccountId int64
|
||||
Result []*AccountUserDTO
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Projections and DTOs
|
||||
|
||||
type AccountUserDTO struct {
|
||||
AccountId int64 `json:"accountId"`
|
||||
UserId int64 `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
+16
-16
@@ -8,22 +8,22 @@ import (
|
||||
var ErrInvalidApiKey = errors.New("Invalid API Key")
|
||||
|
||||
type ApiKey struct {
|
||||
Id int64
|
||||
AccountId int64
|
||||
Name string
|
||||
Key string
|
||||
Role RoleType
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
Id int64
|
||||
OrgId int64
|
||||
Name string
|
||||
Key string
|
||||
Role RoleType
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
type AddApiKeyCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Role RoleType `json:"role" binding:"Required"`
|
||||
AccountId int64 `json:"-"`
|
||||
Key string `json:"-"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Role RoleType `json:"role" binding:"Required"`
|
||||
OrgId int64 `json:"-"`
|
||||
Key string `json:"-"`
|
||||
|
||||
Result *ApiKey `json:"-"`
|
||||
}
|
||||
@@ -33,20 +33,20 @@ type UpdateApiKeyCommand struct {
|
||||
Name string `json:"name"`
|
||||
Role RoleType `json:"role"`
|
||||
|
||||
AccountId int64 `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type DeleteApiKeyCommand struct {
|
||||
Id int64 `json:"id"`
|
||||
AccountId int64 `json:"-"`
|
||||
Id int64 `json:"id"`
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
type GetApiKeysQuery struct {
|
||||
AccountId int64
|
||||
Result []*ApiKey
|
||||
OrgId int64
|
||||
Result []*ApiKey
|
||||
}
|
||||
|
||||
type GetApiKeyByKeyQuery struct {
|
||||
|
||||
+10
-10
@@ -14,10 +14,10 @@ var (
|
||||
)
|
||||
|
||||
type Dashboard struct {
|
||||
Id int64
|
||||
Slug string
|
||||
AccountId int64
|
||||
Version int
|
||||
Id int64
|
||||
Slug string
|
||||
OrgId int64
|
||||
Version int
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
@@ -53,7 +53,7 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
|
||||
dash := &Dashboard{}
|
||||
dash.Data = cmd.Dashboard
|
||||
dash.Title = dash.Data["title"].(string)
|
||||
dash.AccountId = cmd.AccountId
|
||||
dash.OrgId = cmd.OrgId
|
||||
dash.UpdateSlug()
|
||||
|
||||
if dash.Data["id"] != nil {
|
||||
@@ -80,14 +80,14 @@ func (dash *Dashboard) UpdateSlug() {
|
||||
|
||||
type SaveDashboardCommand struct {
|
||||
Dashboard map[string]interface{} `json:"dashboard"`
|
||||
AccountId int64 `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
|
||||
Result *Dashboard
|
||||
}
|
||||
|
||||
type DeleteDashboardCommand struct {
|
||||
Slug string
|
||||
AccountId int64
|
||||
Slug string
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
//
|
||||
@@ -95,8 +95,8 @@ type DeleteDashboardCommand struct {
|
||||
//
|
||||
|
||||
type GetDashboardQuery struct {
|
||||
Slug string
|
||||
AccountId int64
|
||||
Slug string
|
||||
OrgId int64
|
||||
|
||||
Result *Dashboard
|
||||
}
|
||||
|
||||
+20
-12
@@ -8,7 +8,9 @@ import (
|
||||
const (
|
||||
DS_GRAPHITE = "graphite"
|
||||
DS_INFLUXDB = "influxdb"
|
||||
DS_INFLUXDB_08 = "influxdb_08"
|
||||
DS_ES = "elasticsearch"
|
||||
DS_OPENTSDB = "opentsdb"
|
||||
DS_ACCESS_DIRECT = "direct"
|
||||
DS_ACCESS_PROXY = "proxy"
|
||||
)
|
||||
@@ -22,9 +24,9 @@ type DsType string
|
||||
type DsAccess string
|
||||
|
||||
type DataSource struct {
|
||||
Id int64
|
||||
AccountId int64
|
||||
Version int
|
||||
Id int64
|
||||
OrgId int64
|
||||
Version int
|
||||
|
||||
Name string
|
||||
Type DsType
|
||||
@@ -47,7 +49,7 @@ type DataSource struct {
|
||||
|
||||
// Also acts as api DTO
|
||||
type AddDataSourceCommand struct {
|
||||
AccountId int64 `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
Name string
|
||||
Type DsType
|
||||
Access DsAccess
|
||||
@@ -63,7 +65,7 @@ type AddDataSourceCommand struct {
|
||||
// Also acts as api DTO
|
||||
type UpdateDataSourceCommand struct {
|
||||
Id int64
|
||||
AccountId int64
|
||||
OrgId int64
|
||||
Name string
|
||||
Type DsType
|
||||
Access DsAccess
|
||||
@@ -75,22 +77,28 @@ type UpdateDataSourceCommand struct {
|
||||
}
|
||||
|
||||
type DeleteDataSourceCommand struct {
|
||||
Id int64
|
||||
AccountId int64
|
||||
Id int64
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// QUERIES
|
||||
|
||||
type GetDataSourcesQuery struct {
|
||||
AccountId int64
|
||||
Result []*DataSource
|
||||
OrgId int64
|
||||
Result []*DataSource
|
||||
}
|
||||
|
||||
type GetDataSourceByIdQuery struct {
|
||||
Id int64
|
||||
AccountId int64
|
||||
Result DataSource
|
||||
Id int64
|
||||
OrgId int64
|
||||
Result DataSource
|
||||
}
|
||||
|
||||
type GetDataSourceByNameQuery struct {
|
||||
Name string
|
||||
OrgId int64
|
||||
Result DataSource
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrOrgNotFound = errors.New("Organization not found")
|
||||
)
|
||||
|
||||
type Org struct {
|
||||
Id int64
|
||||
Version int
|
||||
Name string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type CreateOrgCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
|
||||
// initial admin user for account
|
||||
UserId int64 `json:"-"`
|
||||
Result Org `json:"-"`
|
||||
}
|
||||
|
||||
type DeleteOrgCommand struct {
|
||||
Id int64
|
||||
}
|
||||
|
||||
type UpdateOrgCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type GetOrgByIdQuery struct {
|
||||
Id int64
|
||||
Result *Org
|
||||
}
|
||||
|
||||
type GetOrgByNameQuery struct {
|
||||
Name string
|
||||
Result *Org
|
||||
}
|
||||
|
||||
type GetOrgListQuery struct {
|
||||
Result []*Org
|
||||
}
|
||||
|
||||
type OrgDTO struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UserOrgDTO struct {
|
||||
OrgId int64 `json:"orgId"`
|
||||
Name string `json:"name"`
|
||||
Role RoleType `json:"role"`
|
||||
IsUsing bool `json:"isUsing"`
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrInvalidRoleType = errors.New("Invalid role type")
|
||||
ErrLastOrgAdmin = errors.New("Cannot remove last organization admin")
|
||||
)
|
||||
|
||||
type RoleType string
|
||||
|
||||
const (
|
||||
ROLE_VIEWER RoleType = "Viewer"
|
||||
ROLE_EDITOR RoleType = "Editor"
|
||||
ROLE_ADMIN RoleType = "Admin"
|
||||
)
|
||||
|
||||
func (r RoleType) IsValid() bool {
|
||||
return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR
|
||||
}
|
||||
|
||||
type OrgUser struct {
|
||||
OrgId int64
|
||||
UserId int64
|
||||
Role RoleType
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// COMMANDS
|
||||
|
||||
type RemoveOrgUserCommand struct {
|
||||
UserId int64
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
type AddOrgUserCommand struct {
|
||||
LoginOrEmail string `json:"loginOrEmail" binding:"Required"`
|
||||
Role RoleType `json:"role" binding:"Required"`
|
||||
|
||||
OrgId int64 `json:"-"`
|
||||
UserId int64 `json:"-"`
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
type GetOrgUsersQuery struct {
|
||||
OrgId int64
|
||||
Result []*OrgUserDTO
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// Projections and DTOs
|
||||
|
||||
type OrgUserDTO struct {
|
||||
OrgId int64 `json:"orgId"`
|
||||
UserId int64 `json:"userId"`
|
||||
Email string `json:"email"`
|
||||
Login string `json:"login"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
@@ -11,7 +11,6 @@ type DashboardSearchHit struct {
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Tags []string `json:"tags"`
|
||||
Url string `json:"url"`
|
||||
IsStarred bool `json:"isStarred"`
|
||||
}
|
||||
|
||||
@@ -23,7 +22,7 @@ type DashboardTagCloudItem struct {
|
||||
type SearchDashboardsQuery struct {
|
||||
Title string
|
||||
Tag string
|
||||
AccountId int64
|
||||
OrgId int64
|
||||
UserId int64
|
||||
Limit int
|
||||
IsStarred bool
|
||||
@@ -32,6 +31,6 @@ type SearchDashboardsQuery struct {
|
||||
}
|
||||
|
||||
type GetDashboardTagsQuery struct {
|
||||
AccountId int64
|
||||
Result []*DashboardTagCloudItem
|
||||
OrgId int64
|
||||
Result []*DashboardTagCloudItem
|
||||
}
|
||||
|
||||
+49
-20
@@ -11,18 +11,20 @@ var (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Version int
|
||||
Email string
|
||||
Name string
|
||||
Login string
|
||||
Password string
|
||||
Salt string
|
||||
Rands string
|
||||
Company string
|
||||
Id int64
|
||||
Version int
|
||||
Email string
|
||||
Name string
|
||||
Login string
|
||||
Password string
|
||||
Salt string
|
||||
Rands string
|
||||
Company string
|
||||
EmailVerified bool
|
||||
Theme string
|
||||
|
||||
IsAdmin bool
|
||||
AccountId int64
|
||||
IsAdmin bool
|
||||
OrgId int64
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
@@ -50,9 +52,25 @@ type UpdateUserCommand struct {
|
||||
UserId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type SetUsingAccountCommand struct {
|
||||
UserId int64
|
||||
AccountId int64
|
||||
type ChangeUserPasswordCommand struct {
|
||||
OldPassword string `json:"oldPassword"`
|
||||
NewPassword string `json:"newPassword"`
|
||||
|
||||
UserId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type UpdateUserPermissionsCommand struct {
|
||||
IsGrafanaAdmin bool
|
||||
UserId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type DeleteUserCommand struct {
|
||||
UserId int64
|
||||
}
|
||||
|
||||
type SetUsingOrgCommand struct {
|
||||
UserId int64
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
@@ -63,6 +81,11 @@ type GetUserByLoginQuery struct {
|
||||
Result *User
|
||||
}
|
||||
|
||||
type GetUserByIdQuery struct {
|
||||
Id int64
|
||||
Result *User
|
||||
}
|
||||
|
||||
type GetSignedInUserQuery struct {
|
||||
UserId int64
|
||||
Result *SignedInUser
|
||||
@@ -81,14 +104,19 @@ type SearchUsersQuery struct {
|
||||
Result []*UserSearchHitDTO
|
||||
}
|
||||
|
||||
type GetUserOrgListQuery struct {
|
||||
UserId int64
|
||||
Result []*UserOrgDTO
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// DTO & Projections
|
||||
|
||||
type SignedInUser struct {
|
||||
UserId int64
|
||||
AccountId int64
|
||||
AccountName string
|
||||
AccountRole RoleType
|
||||
OrgId int64
|
||||
OrgName string
|
||||
OrgRole RoleType
|
||||
Login string
|
||||
Name string
|
||||
Email string
|
||||
@@ -97,9 +125,10 @@ type SignedInUser struct {
|
||||
}
|
||||
|
||||
type UserDTO struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
IsGrafanaAdmin bool `json:"isGrafanaAdmin"`
|
||||
}
|
||||
|
||||
type UserSearchHitDTO struct {
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/events"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetAccountById)
|
||||
bus.AddHandler("sql", CreateAccount)
|
||||
bus.AddHandler("sql", SetUsingAccount)
|
||||
bus.AddHandler("sql", UpdateAccount)
|
||||
bus.AddHandler("sql", GetAccountByName)
|
||||
}
|
||||
|
||||
func GetAccountById(query *m.GetAccountByIdQuery) error {
|
||||
var account m.Account
|
||||
exists, err := x.Id(query.Id).Get(&account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
query.Result = &account
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAccountByName(query *m.GetAccountByNameQuery) error {
|
||||
var account m.Account
|
||||
exists, err := x.Where("name=?", query.Name).Get(&account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
query.Result = &account
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateAccount(cmd *m.CreateAccountCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
account := m.Account{
|
||||
Name: cmd.Name,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(&account); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := m.AccountUser{
|
||||
AccountId: account.Id,
|
||||
UserId: cmd.UserId,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&user)
|
||||
cmd.Result = account
|
||||
|
||||
sess.publishAfterCommit(&events.AccountCreated{
|
||||
Timestamp: account.Created,
|
||||
Id: account.Id,
|
||||
Name: account.Name,
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateAccount(cmd *m.UpdateAccountCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
account := m.Account{
|
||||
Name: cmd.Name,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Id(cmd.AccountId).Update(&account); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess.publishAfterCommit(&events.AccountUpdated{
|
||||
Timestamp: account.Updated,
|
||||
Id: account.Id,
|
||||
Name: account.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", AddAccountUser)
|
||||
bus.AddHandler("sql", RemoveAccountUser)
|
||||
bus.AddHandler("sql", GetAccountUsers)
|
||||
}
|
||||
|
||||
func AddAccountUser(cmd *m.AddAccountUserCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
entity := m.AccountUser{
|
||||
AccountId: cmd.AccountId,
|
||||
UserId: cmd.UserId,
|
||||
Role: cmd.Role,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&entity)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetAccountUsers(query *m.GetAccountUsersQuery) error {
|
||||
query.Result = make([]*m.AccountUserDTO, 0)
|
||||
sess := x.Table("account_user")
|
||||
sess.Join("INNER", "user", fmt.Sprintf("account_user.user_id=%s.id", x.Dialect().Quote("user")))
|
||||
sess.Where("account_user.account_id=?", query.AccountId)
|
||||
sess.Cols("account_user.account_id", "account_user.user_id", "user.email", "user.login", "account_user.role")
|
||||
sess.Asc("user.email", "user.login")
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoveAccountUser(cmd *m.RemoveAccountUserCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM account_user WHERE account_id=? and user_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.AccountId, cmd.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate that there is an admin user left
|
||||
res, err := sess.Query("SELECT 1 from account_user WHERE account_id=? and role='Admin'", cmd.AccountId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return m.ErrLastAccountAdmin
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -17,7 +17,7 @@ func init() {
|
||||
}
|
||||
|
||||
func GetApiKeys(query *m.GetApiKeysQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name")
|
||||
sess := x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name")
|
||||
|
||||
query.Result = make([]*m.ApiKey, 0)
|
||||
return sess.Find(&query.Result)
|
||||
@@ -25,8 +25,8 @@ func GetApiKeys(query *m.GetApiKeysQuery) error {
|
||||
|
||||
func DeleteApiKey(cmd *m.DeleteApiKeyCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM api_key WHERE id=? and account_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId)
|
||||
var rawSql = "DELETE FROM api_key WHERE id=? and org_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.OrgId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -34,12 +34,12 @@ func DeleteApiKey(cmd *m.DeleteApiKeyCommand) error {
|
||||
func AddApiKey(cmd *m.AddApiKeyCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
t := m.ApiKey{
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Key: cmd.Key,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Key: cmd.Key,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(&t); err != nil {
|
||||
@@ -53,20 +53,20 @@ func AddApiKey(cmd *m.AddApiKeyCommand) error {
|
||||
func UpdateApiKey(cmd *m.UpdateApiKeyCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
t := m.ApiKey{
|
||||
Id: cmd.Id,
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Updated: time.Now(),
|
||||
Id: cmd.Id,
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
_, err := sess.Where("id=? and account_id=?", t.Id, t.AccountId).Update(&t)
|
||||
_, err := sess.Where("id=? and org_id=?", t.Id, t.OrgId).Update(&t)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetApiKeyByKey(query *m.GetApiKeyByKeyQuery) error {
|
||||
var apikey m.ApiKey
|
||||
has, err := x.Where("key=?", query.Key).Get(&apikey)
|
||||
has, err := x.Where("`key`=?", query.Key).Get(&apikey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func TestApiKeyDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Testing API Key data access", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Given saved api key", func() {
|
||||
cmd := m.AddApiKeyCommand{OrgId: 1, Key: "hello"}
|
||||
err := AddApiKey(&cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Should be able to get key by key", func() {
|
||||
query := m.GetApiKeyByKeyQuery{Key: "hello"}
|
||||
err = GetApiKeyByKey(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(query.Result, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
||||
dash := cmd.GetDashboardModel()
|
||||
|
||||
// try get existing dashboard
|
||||
existing := m.Dashboard{Slug: dash.Slug, AccountId: dash.AccountId}
|
||||
existing := m.Dashboard{Slug: dash.Slug, OrgId: dash.OrgId}
|
||||
hasExisting, err := sess.Get(&existing)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -61,7 +61,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
||||
}
|
||||
|
||||
func GetDashboard(query *m.GetDashboardQuery) error {
|
||||
dashboard := m.Dashboard{Slug: query.Slug, AccountId: query.AccountId}
|
||||
dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId}
|
||||
has, err := x.Get(&dashboard)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -98,9 +98,9 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error {
|
||||
sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id")
|
||||
}
|
||||
|
||||
sql.WriteString(` WHERE dashboard.account_id=?`)
|
||||
sql.WriteString(` WHERE dashboard.org_id=?`)
|
||||
|
||||
params = append(params, query.AccountId)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
if query.IsStarred {
|
||||
sql.WriteString(` AND star.user_id=?`)
|
||||
@@ -158,11 +158,11 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
||||
term
|
||||
FROM dashboard
|
||||
INNER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id
|
||||
WHERE dashboard.account_id=?
|
||||
WHERE dashboard.org_id=?
|
||||
GROUP BY term`
|
||||
|
||||
query.Result = make([]*m.DashboardTagCloudItem, 0)
|
||||
sess := x.Sql(sql, query.AccountId)
|
||||
sess := x.Sql(sql, query.OrgId)
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
@@ -171,8 +171,8 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
rawSql := "DELETE FROM Dashboard WHERE account_id=? and slug=?"
|
||||
_, err := sess.Exec(rawSql, cmd.AccountId, cmd.Slug)
|
||||
rawSql := "DELETE FROM dashboard WHERE org_id=? and slug=?"
|
||||
_, err := sess.Exec(rawSql, cmd.OrgId, cmd.Slug)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func insertTestDashboard(title string, accountId int64, tags ...interface{}) *m.Dashboard {
|
||||
func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
AccountId: accountId,
|
||||
OrgId: orgId,
|
||||
Dashboard: map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
@@ -40,8 +40,8 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Should be able to get dashboard", func() {
|
||||
query := m.GetDashboardQuery{
|
||||
Slug: "test-dash-23",
|
||||
AccountId: 1,
|
||||
Slug: "test-dash-23",
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
err := GetDashboard(&query)
|
||||
@@ -53,8 +53,8 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Should be able to search for dashboard", func() {
|
||||
query := m.SearchDashboardsQuery{
|
||||
Title: "test",
|
||||
AccountId: 1,
|
||||
Title: "test",
|
||||
OrgId: 1,
|
||||
}
|
||||
|
||||
err := SearchDashboards(&query)
|
||||
@@ -66,8 +66,8 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should be able to search for dashboards using tags", func() {
|
||||
query1 := m.SearchDashboardsQuery{Tag: "webapp", AccountId: 1}
|
||||
query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", AccountId: 1}
|
||||
query1 := m.SearchDashboardsQuery{Tag: "webapp", OrgId: 1}
|
||||
query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", OrgId: 1}
|
||||
|
||||
err := SearchDashboards(&query1)
|
||||
err = SearchDashboards(&query2)
|
||||
@@ -79,7 +79,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Should not be able to save dashboard with same name", func() {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
AccountId: 1,
|
||||
OrgId: 1,
|
||||
Dashboard: map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "test dash 23",
|
||||
@@ -92,7 +92,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should be able to get dashboard tags", func() {
|
||||
query := m.GetDashboardTagsQuery{AccountId: 1}
|
||||
query := m.GetDashboardTagsQuery{OrgId: 1}
|
||||
|
||||
err := GetDashboardTags(&query)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -113,7 +113,7 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should be able to search for starred dashboards", func() {
|
||||
query := m.SearchDashboardsQuery{AccountId: 1, UserId: 10, IsStarred: true}
|
||||
query := m.SearchDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true}
|
||||
err := SearchDashboards(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -15,10 +15,21 @@ func init() {
|
||||
bus.AddHandler("sql", DeleteDataSource)
|
||||
bus.AddHandler("sql", UpdateDataSource)
|
||||
bus.AddHandler("sql", GetDataSourceById)
|
||||
bus.AddHandler("sql", GetDataSourceByName)
|
||||
}
|
||||
|
||||
func GetDataSourceById(query *m.GetDataSourceByIdQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=? AND id=?", query.AccountId, query.Id)
|
||||
sess := x.Limit(100, 0).Where("org_id=? AND id=?", query.OrgId, query.Id)
|
||||
has, err := sess.Get(&query.Result)
|
||||
|
||||
if !has {
|
||||
return m.ErrDataSourceNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetDataSourceByName(query *m.GetDataSourceByNameQuery) error {
|
||||
sess := x.Limit(100, 0).Where("org_id=? AND name=?", query.OrgId, query.Name)
|
||||
has, err := sess.Get(&query.Result)
|
||||
|
||||
if !has {
|
||||
@@ -28,7 +39,7 @@ func GetDataSourceById(query *m.GetDataSourceByIdQuery) error {
|
||||
}
|
||||
|
||||
func GetDataSources(query *m.GetDataSourcesQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name")
|
||||
sess := x.Limit(100, 0).Where("org_id=?", query.OrgId).Asc("name")
|
||||
|
||||
query.Result = make([]*m.DataSource, 0)
|
||||
return sess.Find(&query.Result)
|
||||
@@ -36,8 +47,8 @@ func GetDataSources(query *m.GetDataSourcesQuery) error {
|
||||
|
||||
func DeleteDataSource(cmd *m.DeleteDataSourceCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM data_source WHERE id=? and account_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId)
|
||||
var rawSql = "DELETE FROM data_source WHERE id=? and org_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.OrgId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -46,7 +57,7 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error {
|
||||
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
ds := &m.DataSource{
|
||||
AccountId: cmd.AccountId,
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
Access: cmd.Access,
|
||||
@@ -74,8 +85,8 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error {
|
||||
func updateIsDefaultFlag(ds *m.DataSource, sess *xorm.Session) error {
|
||||
// Handle is default flag
|
||||
if ds.IsDefault {
|
||||
rawSql := "UPDATE data_source SET is_default = 0 WHERE account_id=? AND id <> ?"
|
||||
if _, err := sess.Exec(rawSql, ds.AccountId, ds.Id); err != nil {
|
||||
rawSql := "UPDATE data_source SET is_default = 0 WHERE org_id=? AND id <> ?"
|
||||
if _, err := sess.Exec(rawSql, ds.OrgId, ds.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -87,7 +98,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
ds := &m.DataSource{
|
||||
Id: cmd.Id,
|
||||
AccountId: cmd.AccountId,
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
Access: cmd.Access,
|
||||
@@ -101,7 +112,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
|
||||
|
||||
sess.UseBool("is_default")
|
||||
|
||||
_, err := sess.Where("id=? and account_id=?", ds.Id, ds.AccountId).Update(ds)
|
||||
_, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -42,16 +42,16 @@ func TestDataAccess(t *testing.T) {
|
||||
Convey("Can add datasource", func() {
|
||||
|
||||
err := AddDataSource(&m.AddDataSourceCommand{
|
||||
AccountId: 10,
|
||||
Type: m.DS_INFLUXDB,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
Database: "site",
|
||||
OrgId: 10,
|
||||
Type: m.DS_INFLUXDB,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
Database: "site",
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := m.GetDataSourcesQuery{AccountId: 10}
|
||||
query := m.GetDataSourcesQuery{OrgId: 10}
|
||||
err = GetDataSources(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -59,33 +59,33 @@ func TestDataAccess(t *testing.T) {
|
||||
|
||||
ds := query.Result[0]
|
||||
|
||||
So(ds.AccountId, ShouldEqual, 10)
|
||||
So(ds.OrgId, ShouldEqual, 10)
|
||||
So(ds.Database, ShouldEqual, "site")
|
||||
})
|
||||
|
||||
Convey("Given a datasource", func() {
|
||||
|
||||
AddDataSource(&m.AddDataSourceCommand{
|
||||
AccountId: 10,
|
||||
Type: m.DS_GRAPHITE,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
OrgId: 10,
|
||||
Type: m.DS_GRAPHITE,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
})
|
||||
|
||||
query := m.GetDataSourcesQuery{AccountId: 10}
|
||||
query := m.GetDataSourcesQuery{OrgId: 10}
|
||||
GetDataSources(&query)
|
||||
ds := query.Result[0]
|
||||
|
||||
Convey("Can delete datasource", func() {
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: ds.AccountId})
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, OrgId: ds.OrgId})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
GetDataSources(&query)
|
||||
So(len(query.Result), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Can not delete datasource with wrong accountId", func() {
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: 123123})
|
||||
Convey("Can not delete datasource with wrong orgId", func() {
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, OrgId: 123123})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
GetDataSources(&query)
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
// --- Migration Guide line ---
|
||||
// 1. Never change a migration that is committed and pushed to master
|
||||
// 2. Always add new migrations (to change or undo previous migrations)
|
||||
// 3. Some migraitons are not yet written (rename column, table, drop table, index etc)
|
||||
|
||||
func addMigrations(mg *Migrator) {
|
||||
addMigrationLogMigrations(mg)
|
||||
addUserMigrations(mg)
|
||||
addStarMigrations(mg)
|
||||
addAccountMigrations(mg)
|
||||
addDashboardMigration(mg)
|
||||
addDataSourceMigration(mg)
|
||||
addApiKeyMigrations(mg)
|
||||
}
|
||||
|
||||
func addMigrationLogMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create migration_log table", new(AddTableMigration).
|
||||
Name("migration_log").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "migration_id", Type: DB_NVarchar, Length: 255},
|
||||
&Column{Name: "sql", Type: DB_Text},
|
||||
&Column{Name: "success", Type: DB_Bool},
|
||||
&Column{Name: "error", Type: DB_Text},
|
||||
&Column{Name: "timestamp", Type: DB_DateTime},
|
||||
))
|
||||
}
|
||||
|
||||
func addUserMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create user table", new(AddTableMigration).
|
||||
Name("user").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "rands", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "company", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "is_admin", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
))
|
||||
|
||||
//------- user table indexes ------------------
|
||||
mg.AddMigration("add unique index user.login", new(AddIndexMigration).
|
||||
Table("user").Columns("login").Unique())
|
||||
mg.AddMigration("add unique index user.email", new(AddIndexMigration).
|
||||
Table("user").Columns("email").Unique())
|
||||
}
|
||||
|
||||
func addStarMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create star table", new(AddTableMigration).
|
||||
Name("star").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "user_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
))
|
||||
|
||||
mg.AddMigration("add unique index star.user_id_dashboard_id", new(AddIndexMigration).
|
||||
Table("star").Columns("user_id", "dashboard_id").Unique())
|
||||
}
|
||||
|
||||
func addAccountMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create account table", new(AddTableMigration).
|
||||
Name("account").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
))
|
||||
|
||||
mg.AddMigration("add unique index account.name", new(AddIndexMigration).
|
||||
Table("account").Columns("name").Unique())
|
||||
|
||||
//------- account_user table -------------------
|
||||
mg.AddMigration("create account_user table", new(AddTableMigration).
|
||||
Name("account_user").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt},
|
||||
&Column{Name: "user_id", Type: DB_BigInt},
|
||||
&Column{Name: "role", Type: DB_NVarchar, Length: 20},
|
||||
&Column{Name: "created", Type: DB_DateTime},
|
||||
&Column{Name: "updated", Type: DB_DateTime},
|
||||
))
|
||||
|
||||
mg.AddMigration("add unique index account_user_aid_uid", new(AddIndexMigration).
|
||||
Name("aid_uid").Table("account_user").Columns("account_id", "user_id").Unique())
|
||||
}
|
||||
|
||||
func addDashboardMigration(mg *Migrator) {
|
||||
mg.AddMigration("create dashboard table", new(AddTableMigration).
|
||||
Name("dashboard").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "slug", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "data", Type: DB_Text, Nullable: false},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
))
|
||||
|
||||
mg.AddMigration("create dashboard_tag table", new(AddTableMigration).
|
||||
Name("dashboard_tag").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "term", Type: DB_NVarchar, Length: 50, Nullable: false},
|
||||
))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index dashboard.account_id", new(AddIndexMigration).
|
||||
Table("dashboard").Columns("account_id"))
|
||||
|
||||
mg.AddMigration("add unique index dashboard_account_id_slug", new(AddIndexMigration).
|
||||
Table("dashboard").Columns("account_id", "slug").Unique())
|
||||
|
||||
mg.AddMigration("add unique index dashboard_tag.dasboard_id_term", new(AddIndexMigration).
|
||||
Table("dashboard_tag").Columns("dashboard_id", "term").Unique())
|
||||
}
|
||||
|
||||
func addDataSourceMigration(mg *Migrator) {
|
||||
mg.AddMigration("create data_source table", new(AddTableMigration).
|
||||
Name("data_source").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "database", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "basic_auth_user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth_password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "is_default", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index data_source.account_id", new(AddIndexMigration).
|
||||
Table("data_source").Columns("account_id"))
|
||||
|
||||
mg.AddMigration("add unique index data_source.account_id_name", new(AddIndexMigration).
|
||||
Table("data_source").Columns("account_id", "name").Unique())
|
||||
}
|
||||
|
||||
func addApiKeyMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create api_key table", new(AddTableMigration).
|
||||
Name("api_key").WithColumns(
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "key", Type: DB_Varchar, Length: 64, Nullable: false},
|
||||
&Column{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index api_key.account_id", new(AddIndexMigration).
|
||||
Table("api_key").Columns("account_id"))
|
||||
|
||||
mg.AddMigration("add index api_key.account_id_name", new(AddIndexMigration).
|
||||
Table("api_key").Columns("account_id", "name").Unique())
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addApiKeyMigrations(mg *Migrator) {
|
||||
apiKeyV1 := Table{
|
||||
Name: "api_key",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "key", Type: DB_Varchar, Length: 64, Nullable: false},
|
||||
&Column{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"account_id"}},
|
||||
&Index{Cols: []string{"key"}, Type: UniqueIndex},
|
||||
&Index{Cols: []string{"account_id", "name"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// create table
|
||||
mg.AddMigration("create api_key table", NewAddTableMigration(apiKeyV1))
|
||||
// create indices
|
||||
mg.AddMigration("add index api_key.account_id", NewAddIndexMigration(apiKeyV1, apiKeyV1.Indices[0]))
|
||||
mg.AddMigration("add index api_key.key", NewAddIndexMigration(apiKeyV1, apiKeyV1.Indices[1]))
|
||||
mg.AddMigration("add index api_key.account_id_name", NewAddIndexMigration(apiKeyV1, apiKeyV1.Indices[2]))
|
||||
|
||||
// ---------------------
|
||||
// account -> org changes
|
||||
|
||||
// drop indexes
|
||||
addDropAllIndicesMigrations(mg, "v1", apiKeyV1)
|
||||
// rename table
|
||||
addTableRenameMigration(mg, "api_key", "api_key_v1", "v1")
|
||||
|
||||
apiKeyV2 := Table{
|
||||
Name: "api_key",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "key", Type: DB_Varchar, Length: 64, Nullable: false},
|
||||
&Column{Name: "role", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"org_id"}},
|
||||
&Index{Cols: []string{"key"}, Type: UniqueIndex},
|
||||
&Index{Cols: []string{"org_id", "name"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// create v2 table
|
||||
mg.AddMigration("create api_key table v2", NewAddTableMigration(apiKeyV2))
|
||||
|
||||
// add v2 indíces
|
||||
addTableIndicesMigrations(mg, "v2", apiKeyV2)
|
||||
|
||||
//------- copy data from v1 to v2 -------------------
|
||||
mg.AddMigration("copy api_key v1 to v2", NewCopyTableDataMigration("api_key", "api_key_v1", map[string]string{
|
||||
"id": "id",
|
||||
"org_id": "account_id",
|
||||
"name": "name",
|
||||
"key": "key",
|
||||
"role": "role",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}))
|
||||
|
||||
mg.AddMigration("Drop old table api_key_v1", NewDropTableMigration("api_key_v1"))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
)
|
||||
|
||||
func addDropAllIndicesMigrations(mg *Migrator, versionSuffix string, table Table) {
|
||||
for _, index := range table.Indices {
|
||||
migrationId := fmt.Sprintf("drop index %s - %s", index.XName(table.Name), versionSuffix)
|
||||
mg.AddMigration(migrationId, NewDropIndexMigration(table, index))
|
||||
}
|
||||
}
|
||||
|
||||
func addTableIndicesMigrations(mg *Migrator, versionSuffix string, table Table) {
|
||||
for _, index := range table.Indices {
|
||||
migrationId := fmt.Sprintf("create index %s - %s", index.XName(table.Name), versionSuffix)
|
||||
mg.AddMigration(migrationId, NewAddIndexMigration(table, index))
|
||||
}
|
||||
}
|
||||
|
||||
func addTableRenameMigration(mg *Migrator, oldName string, newName string, versionSuffix string) {
|
||||
migrationId := fmt.Sprintf("Rename table %s to %s - %s", oldName, newName, versionSuffix)
|
||||
mg.AddMigration(migrationId, NewRenameTableMigration(oldName, newName))
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addDashboardMigration(mg *Migrator) {
|
||||
var dashboardV1 = Table{
|
||||
Name: "dashboard",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "slug", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "data", Type: DB_Text, Nullable: false},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"account_id"}},
|
||||
&Index{Cols: []string{"account_id", "slug"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create dashboard table", NewAddTableMigration(dashboardV1))
|
||||
|
||||
//------- indexes ------------------
|
||||
mg.AddMigration("add index dashboard.account_id", NewAddIndexMigration(dashboardV1, dashboardV1.Indices[0]))
|
||||
mg.AddMigration("add unique index dashboard_account_id_slug", NewAddIndexMigration(dashboardV1, dashboardV1.Indices[1]))
|
||||
|
||||
dashboardTagV1 := Table{
|
||||
Name: "dashboard_tag",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "term", Type: DB_NVarchar, Length: 50, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"dashboard_id", "term"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create dashboard_tag table", NewAddTableMigration(dashboardTagV1))
|
||||
mg.AddMigration("add unique index dashboard_tag.dasboard_id_term", NewAddIndexMigration(dashboardTagV1, dashboardTagV1.Indices[0]))
|
||||
|
||||
// ---------------------
|
||||
// account -> org changes
|
||||
|
||||
//------- drop dashboard indexes ------------------
|
||||
addDropAllIndicesMigrations(mg, "v1", dashboardTagV1)
|
||||
//------- rename table ------------------
|
||||
addTableRenameMigration(mg, "dashboard", "dashboard_v1", "v1")
|
||||
|
||||
// dashboard v2
|
||||
var dashboardV2 = Table{
|
||||
Name: "dashboard",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "slug", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "data", Type: DB_Text, Nullable: false},
|
||||
&Column{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"org_id"}},
|
||||
&Index{Cols: []string{"org_id", "slug"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// recreate table
|
||||
mg.AddMigration("create dashboard v2", NewAddTableMigration(dashboardV2))
|
||||
// recreate indices
|
||||
addTableIndicesMigrations(mg, "v2", dashboardV2)
|
||||
// copy data
|
||||
mg.AddMigration("copy dashboard v1 to v2", NewCopyTableDataMigration("dashboard", "dashboard_v1", map[string]string{
|
||||
"id": "id",
|
||||
"version": "version",
|
||||
"slug": "slug",
|
||||
"title": "title",
|
||||
"data": "data",
|
||||
"org_id": "account_id",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}))
|
||||
|
||||
mg.AddMigration("drop table dashboard_v1", NewDropTableMigration("dashboard_v1"))
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addDataSourceMigration(mg *Migrator) {
|
||||
var tableV1 = Table{
|
||||
Name: "data_source",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "database", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "basic_auth_user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth_password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "is_default", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"account_id"}},
|
||||
&Index{Cols: []string{"account_id", "name"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create data_source table", NewAddTableMigration(tableV1))
|
||||
mg.AddMigration("add index data_source.account_id", NewAddIndexMigration(tableV1, tableV1.Indices[0]))
|
||||
mg.AddMigration("add unique index data_source.account_id_name", NewAddIndexMigration(tableV1, tableV1.Indices[1]))
|
||||
|
||||
// ---------------------
|
||||
// account -> org changes
|
||||
|
||||
// drop v1 indices
|
||||
addDropAllIndicesMigrations(mg, "v1", tableV1)
|
||||
// rename table
|
||||
addTableRenameMigration(mg, "data_source", "data_source_v1", "v1")
|
||||
|
||||
// new table
|
||||
var tableV2 = Table{
|
||||
Name: "data_source",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "access", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "url", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "database", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "basic_auth_user", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "basic_auth_password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "is_default", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "json_data", Type: DB_Text, Nullable: true},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"org_id"}},
|
||||
&Index{Cols: []string{"org_id", "name"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// create v2 table
|
||||
mg.AddMigration("create data_source table v2", NewAddTableMigration(tableV2))
|
||||
|
||||
// add v2 indíces
|
||||
addTableIndicesMigrations(mg, "v2", tableV2)
|
||||
|
||||
//------- copy data from v1 to v2 -------------------
|
||||
mg.AddMigration("copy data_source v1 to v2", NewCopyTableDataMigration("data_source", "data_source_v1", map[string]string{
|
||||
"id": "id",
|
||||
"org_id": "account_id",
|
||||
"version": "version",
|
||||
"type": "type",
|
||||
"name": "name",
|
||||
"access": "access",
|
||||
"url": "password",
|
||||
"user": "user",
|
||||
"database": "database",
|
||||
"basic_auth": "basic_auth",
|
||||
"basic_auth_user": "basic_auth_user",
|
||||
"basic_auth_password": "basic_auth_password",
|
||||
"is_default": "is_default",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}))
|
||||
|
||||
mg.AddMigration("Drop old table data_source_v1", NewDropTableMigration("data_source_old"))
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
// --- Migration Guide line ---
|
||||
// 1. Never change a migration that is committed and pushed to master
|
||||
// 2. Always add new migrations (to change or undo previous migrations)
|
||||
// 3. Some migraitons are not yet written (rename column, table, drop table, index etc)
|
||||
|
||||
func AddMigrations(mg *Migrator) {
|
||||
addMigrationLogMigrations(mg)
|
||||
addUserMigrations(mg)
|
||||
addStarMigrations(mg)
|
||||
addOrgMigrations(mg)
|
||||
addDashboardMigration(mg)
|
||||
addDataSourceMigration(mg)
|
||||
addApiKeyMigrations(mg)
|
||||
}
|
||||
|
||||
func addMigrationLogMigrations(mg *Migrator) {
|
||||
migrationLogV1 := Table{
|
||||
Name: "migration_log",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "migration_id", Type: DB_NVarchar, Length: 255},
|
||||
&Column{Name: "sql", Type: DB_Text},
|
||||
&Column{Name: "success", Type: DB_Bool},
|
||||
&Column{Name: "error", Type: DB_Text},
|
||||
&Column{Name: "timestamp", Type: DB_DateTime},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create migration_log table", NewAddTableMigration(migrationLogV1))
|
||||
}
|
||||
|
||||
func addStarMigrations(mg *Migrator) {
|
||||
starV1 := Table{
|
||||
Name: "star",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "user_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"user_id", "dashboard_id"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create star table", NewAddTableMigration(starV1))
|
||||
mg.AddMigration("add unique index star.user_id_dashboard_id", NewAddIndexMigration(starV1, starV1.Indices[0]))
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package sqlstore
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -32,7 +32,7 @@ func TestMigrations(t *testing.T) {
|
||||
|
||||
mg := NewMigrator(x)
|
||||
mg.LogLevel = log.DEBUG
|
||||
addMigrations(mg)
|
||||
AddMigrations(mg)
|
||||
|
||||
err = mg.Start()
|
||||
So(err, ShouldBeNil)
|
||||
@@ -0,0 +1,71 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addOrgMigrations(mg *Migrator) {
|
||||
orgV1 := Table{
|
||||
Name: "org",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "address1", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "address2", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "city", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "zip_code", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "country", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "billing_email", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"name"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// add org v1
|
||||
mg.AddMigration("create org table v1", NewAddTableMigration(orgV1))
|
||||
addTableIndicesMigrations(mg, "v1", orgV1)
|
||||
|
||||
orgUserV1 := Table{
|
||||
Name: "org_user",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "org_id", Type: DB_BigInt},
|
||||
&Column{Name: "user_id", Type: DB_BigInt},
|
||||
&Column{Name: "role", Type: DB_NVarchar, Length: 20},
|
||||
&Column{Name: "created", Type: DB_DateTime},
|
||||
&Column{Name: "updated", Type: DB_DateTime},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"org_id"}},
|
||||
&Index{Cols: []string{"org_id", "user_id"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
//------- org_user table -------------------
|
||||
mg.AddMigration("create org_user table v1", NewAddTableMigration(orgUserV1))
|
||||
addTableIndicesMigrations(mg, "v1", orgUserV1)
|
||||
|
||||
//------- copy data from old table-------------------
|
||||
mg.AddMigration("copy data account to org", NewCopyTableDataMigration("org", "account", map[string]string{
|
||||
"id": "id",
|
||||
"version": "version",
|
||||
"name": "name",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}).IfTableExists("account"))
|
||||
|
||||
mg.AddMigration("copy data account_user to org_user", NewCopyTableDataMigration("org_user", "account_user", map[string]string{
|
||||
"id": "id",
|
||||
"org_id": "account_id",
|
||||
"user_id": "user_id",
|
||||
"role": "role",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}).IfTableExists("account_user"))
|
||||
|
||||
mg.AddMigration("Drop old table account", NewDropTableMigration("account"))
|
||||
mg.AddMigration("Drop old table account_user", NewDropTableMigration("account_user"))
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package migrations
|
||||
|
||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
|
||||
func addUserMigrations(mg *Migrator) {
|
||||
userV1 := Table{
|
||||
Name: "user",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "rands", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "company", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "is_admin", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"login"}, Type: UniqueIndex},
|
||||
&Index{Cols: []string{"email"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
// create table
|
||||
mg.AddMigration("create user table", NewAddTableMigration(userV1))
|
||||
// add indices
|
||||
mg.AddMigration("add unique index user.login", NewAddIndexMigration(userV1, userV1.Indices[0]))
|
||||
mg.AddMigration("add unique index user.email", NewAddIndexMigration(userV1, userV1.Indices[1]))
|
||||
|
||||
// ---------------------
|
||||
// account -> org changes
|
||||
|
||||
//------- drop indexes ------------------
|
||||
addDropAllIndicesMigrations(mg, "v1", userV1)
|
||||
|
||||
//------- rename table ------------------
|
||||
addTableRenameMigration(mg, "user", "user_v1", "v1")
|
||||
|
||||
//------- recreate table with new column names ------------------
|
||||
userV2 := Table{
|
||||
Name: "user",
|
||||
Columns: []*Column{
|
||||
&Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
&Column{Name: "version", Type: DB_Int, Nullable: false},
|
||||
&Column{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
&Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "rands", Type: DB_NVarchar, Length: 50, Nullable: true},
|
||||
&Column{Name: "company", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
&Column{Name: "is_admin", Type: DB_Bool, Nullable: false},
|
||||
&Column{Name: "email_verified", Type: DB_Bool, Nullable: true},
|
||||
&Column{Name: "theme", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
&Column{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
&Column{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
&Index{Cols: []string{"login"}, Type: UniqueIndex},
|
||||
&Index{Cols: []string{"email"}, Type: UniqueIndex},
|
||||
},
|
||||
}
|
||||
|
||||
mg.AddMigration("create user table v2", NewAddTableMigration(userV2))
|
||||
addTableIndicesMigrations(mg, "v2", userV2)
|
||||
|
||||
//------- copy data from v1 to v2 -------------------
|
||||
mg.AddMigration("copy data_source v1 to v2", NewCopyTableDataMigration("user", "user_v1", map[string]string{
|
||||
"id": "id",
|
||||
"version": "version",
|
||||
"login": "login",
|
||||
"email": "email",
|
||||
"name": "name",
|
||||
"password": "password",
|
||||
"salt": "salt",
|
||||
"rands": "rands",
|
||||
"company": "company",
|
||||
"org_id": "account_id",
|
||||
"is_admin": "is_admin",
|
||||
"created": "created",
|
||||
"updated": "updated",
|
||||
}))
|
||||
|
||||
mg.AddMigration("Drop old table user_v1", NewDropTableMigration("user_v1"))
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MigrationBase struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (m *MigrationBase) Id() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m *MigrationBase) SetId(id string) {
|
||||
m.id = id
|
||||
}
|
||||
|
||||
type RawSqlMigration struct {
|
||||
MigrationBase
|
||||
|
||||
sqlite string
|
||||
mysql string
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Sql(dialect Dialect) string {
|
||||
switch dialect.DriverName() {
|
||||
case MYSQL:
|
||||
return m.mysql
|
||||
case SQLITE:
|
||||
return m.sqlite
|
||||
}
|
||||
|
||||
panic("db type not supported")
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Sqlite(sql string) *RawSqlMigration {
|
||||
m.sqlite = sql
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Mysql(sql string) *RawSqlMigration {
|
||||
m.mysql = sql
|
||||
return m
|
||||
}
|
||||
|
||||
type AddColumnMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
column *Column
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Table(tableName string) *AddColumnMigration {
|
||||
m.tableName = tableName
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Column(col *Column) *AddColumnMigration {
|
||||
m.column = col
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Sql(dialect Dialect) string {
|
||||
return dialect.AddColumnSql(m.tableName, m.column)
|
||||
}
|
||||
|
||||
type AddIndexMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
index Index
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Name(name string) *AddIndexMigration {
|
||||
m.index.Name = name
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Table(tableName string) *AddIndexMigration {
|
||||
m.tableName = tableName
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Unique() *AddIndexMigration {
|
||||
m.index.Type = UniqueIndex
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Columns(columns ...string) *AddIndexMigration {
|
||||
m.index.Cols = columns
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Sql(dialect Dialect) string {
|
||||
if m.index.Name == "" {
|
||||
m.index.Name = fmt.Sprintf("%s", strings.Join(m.index.Cols, "_"))
|
||||
}
|
||||
return dialect.CreateIndexSql(m.tableName, &m.index)
|
||||
}
|
||||
|
||||
type AddTableMigration struct {
|
||||
MigrationBase
|
||||
table Table
|
||||
}
|
||||
|
||||
func (m *AddTableMigration) Sql(d Dialect) string {
|
||||
return d.CreateTableSql(&m.table)
|
||||
}
|
||||
|
||||
func (m *AddTableMigration) Name(name string) *AddTableMigration {
|
||||
m.table.Name = name
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddTableMigration) WithColumns(columns ...*Column) *AddTableMigration {
|
||||
for _, col := range columns {
|
||||
m.table.Columns = append(m.table.Columns, col)
|
||||
if col.IsPrimaryKey {
|
||||
m.table.PrimaryKeys = append(m.table.PrimaryKeys, col.Name)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddTableMigration) WithColumn(col *Column) *AddTableMigration {
|
||||
m.table.Columns = append(m.table.Columns, col)
|
||||
if col.IsPrimaryKey {
|
||||
m.table.PrimaryKeys = append(m.table.PrimaryKeys, col.Name)
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package migrator
|
||||
|
||||
type MigrationCondition interface {
|
||||
Sql(dialect Dialect) (string, []interface{})
|
||||
}
|
||||
|
||||
type IfTableExistsCondition struct {
|
||||
TableName string
|
||||
}
|
||||
|
||||
func (c *IfTableExistsCondition) Sql(dialect Dialect) (string, []interface{}) {
|
||||
return dialect.TableCheckSql(c.TableName)
|
||||
}
|
||||
@@ -20,8 +20,12 @@ type Dialect interface {
|
||||
CreateIndexSql(tableName string, index *Index) string
|
||||
CreateTableSql(table *Table) string
|
||||
AddColumnSql(tableName string, Col *Column) string
|
||||
CopyTableData(sourceTable string, targetTable string, sourceCols []string, targetCols []string) string
|
||||
DropTable(tableName string) string
|
||||
DropIndexSql(tableName string, index *Index) string
|
||||
|
||||
TableCheckSql(tableName string) (string, []interface{})
|
||||
RenameTable(oldName string, newName string) string
|
||||
}
|
||||
|
||||
func NewDialect(name string) Dialect {
|
||||
@@ -101,15 +105,47 @@ func (db *BaseDialect) AddColumnSql(tableName string, col *Column) string {
|
||||
func (db *BaseDialect) CreateIndexSql(tableName string, index *Index) string {
|
||||
quote := db.dialect.Quote
|
||||
var unique string
|
||||
var idxName string
|
||||
if index.Type == UniqueIndex {
|
||||
unique = " UNIQUE"
|
||||
idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
|
||||
} else {
|
||||
idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
|
||||
}
|
||||
|
||||
idxName := index.XName(tableName)
|
||||
|
||||
return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v);", unique,
|
||||
quote(idxName), quote(tableName),
|
||||
quote(strings.Join(index.Cols, quote(","))))
|
||||
}
|
||||
|
||||
func (db *BaseDialect) QuoteColList(cols []string) string {
|
||||
var sourceColsSql = ""
|
||||
for _, col := range cols {
|
||||
sourceColsSql += db.dialect.Quote(col)
|
||||
sourceColsSql += "\n, "
|
||||
}
|
||||
return strings.TrimSuffix(sourceColsSql, "\n, ")
|
||||
}
|
||||
|
||||
func (db *BaseDialect) CopyTableData(sourceTable string, targetTable string, sourceCols []string, targetCols []string) string {
|
||||
sourceColsSql := db.QuoteColList(sourceCols)
|
||||
targetColsSql := db.QuoteColList(targetCols)
|
||||
|
||||
quote := db.dialect.Quote
|
||||
return fmt.Sprintf("INSERT INTO %s (%s) SELECT %s FROM %s", quote(targetTable), targetColsSql, sourceColsSql, quote(sourceTable))
|
||||
}
|
||||
|
||||
func (db *BaseDialect) DropTable(tableName string) string {
|
||||
quote := db.dialect.Quote
|
||||
return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName))
|
||||
}
|
||||
|
||||
func (db *BaseDialect) RenameTable(oldName string, newName string) string {
|
||||
quote := db.dialect.Quote
|
||||
return fmt.Sprintf("ALTER TABLE %s RENAME TO %s", quote(oldName), quote(newName))
|
||||
}
|
||||
|
||||
func (db *BaseDialect) DropIndexSql(tableName string, index *Index) string {
|
||||
quote := db.dialect.Quote
|
||||
var name string
|
||||
name = index.XName(tableName)
|
||||
return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MigrationBase struct {
|
||||
id string
|
||||
Condition MigrationCondition
|
||||
}
|
||||
|
||||
func (m *MigrationBase) Id() string {
|
||||
return m.id
|
||||
}
|
||||
|
||||
func (m *MigrationBase) SetId(id string) {
|
||||
m.id = id
|
||||
}
|
||||
|
||||
func (m *MigrationBase) GetCondition() MigrationCondition {
|
||||
return m.Condition
|
||||
}
|
||||
|
||||
type RawSqlMigration struct {
|
||||
MigrationBase
|
||||
|
||||
sqlite string
|
||||
mysql string
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Sql(dialect Dialect) string {
|
||||
switch dialect.DriverName() {
|
||||
case MYSQL:
|
||||
return m.mysql
|
||||
case SQLITE:
|
||||
return m.sqlite
|
||||
}
|
||||
|
||||
panic("db type not supported")
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Sqlite(sql string) *RawSqlMigration {
|
||||
m.sqlite = sql
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *RawSqlMigration) Mysql(sql string) *RawSqlMigration {
|
||||
m.mysql = sql
|
||||
return m
|
||||
}
|
||||
|
||||
type AddColumnMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
column *Column
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Table(tableName string) *AddColumnMigration {
|
||||
m.tableName = tableName
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Column(col *Column) *AddColumnMigration {
|
||||
m.column = col
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddColumnMigration) Sql(dialect Dialect) string {
|
||||
return dialect.AddColumnSql(m.tableName, m.column)
|
||||
}
|
||||
|
||||
type AddIndexMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
index *Index
|
||||
}
|
||||
|
||||
func NewAddIndexMigration(table Table, index *Index) *AddIndexMigration {
|
||||
return &AddIndexMigration{tableName: table.Name, index: index}
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Table(tableName string) *AddIndexMigration {
|
||||
m.tableName = tableName
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *AddIndexMigration) Sql(dialect Dialect) string {
|
||||
return dialect.CreateIndexSql(m.tableName, m.index)
|
||||
}
|
||||
|
||||
type DropIndexMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
index *Index
|
||||
}
|
||||
|
||||
func NewDropIndexMigration(table Table, index *Index) *DropIndexMigration {
|
||||
return &DropIndexMigration{tableName: table.Name, index: index}
|
||||
}
|
||||
|
||||
func (m *DropIndexMigration) Sql(dialect Dialect) string {
|
||||
if m.index.Name == "" {
|
||||
m.index.Name = fmt.Sprintf("%s", strings.Join(m.index.Cols, "_"))
|
||||
}
|
||||
return dialect.DropIndexSql(m.tableName, m.index)
|
||||
}
|
||||
|
||||
type AddTableMigration struct {
|
||||
MigrationBase
|
||||
table Table
|
||||
}
|
||||
|
||||
func NewAddTableMigration(table Table) *AddTableMigration {
|
||||
for _, col := range table.Columns {
|
||||
if col.IsPrimaryKey {
|
||||
table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
|
||||
}
|
||||
}
|
||||
return &AddTableMigration{table: table}
|
||||
}
|
||||
|
||||
func (m *AddTableMigration) Sql(d Dialect) string {
|
||||
return d.CreateTableSql(&m.table)
|
||||
}
|
||||
|
||||
type DropTableMigration struct {
|
||||
MigrationBase
|
||||
tableName string
|
||||
}
|
||||
|
||||
func NewDropTableMigration(tableName string) *DropTableMigration {
|
||||
return &DropTableMigration{tableName: tableName}
|
||||
}
|
||||
|
||||
func (m *DropTableMigration) Sql(d Dialect) string {
|
||||
return d.DropTable(m.tableName)
|
||||
}
|
||||
|
||||
type RenameTableMigration struct {
|
||||
MigrationBase
|
||||
oldName string
|
||||
newName string
|
||||
}
|
||||
|
||||
func NewRenameTableMigration(oldName string, newName string) *RenameTableMigration {
|
||||
return &RenameTableMigration{oldName: oldName, newName: newName}
|
||||
}
|
||||
|
||||
func (m *RenameTableMigration) IfTableExists(tableName string) *RenameTableMigration {
|
||||
m.Condition = &IfTableExistsCondition{TableName: tableName}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *RenameTableMigration) Rename(oldName string, newName string) *RenameTableMigration {
|
||||
m.oldName = oldName
|
||||
m.newName = newName
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *RenameTableMigration) Sql(d Dialect) string {
|
||||
return d.RenameTable(m.oldName, m.newName)
|
||||
}
|
||||
|
||||
type CopyTableDataMigration struct {
|
||||
MigrationBase
|
||||
sourceTable string
|
||||
targetTable string
|
||||
sourceCols []string
|
||||
targetCols []string
|
||||
colMap map[string]string
|
||||
}
|
||||
|
||||
func NewCopyTableDataMigration(targetTable string, sourceTable string, colMap map[string]string) *CopyTableDataMigration {
|
||||
m := &CopyTableDataMigration{sourceTable: sourceTable, targetTable: targetTable}
|
||||
for key, value := range colMap {
|
||||
m.targetCols = append(m.targetCols, key)
|
||||
m.sourceCols = append(m.sourceCols, value)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *CopyTableDataMigration) IfTableExists(tableName string) *CopyTableDataMigration {
|
||||
m.Condition = &IfTableExistsCondition{TableName: tableName}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *CopyTableDataMigration) Sql(d Dialect) string {
|
||||
return d.CopyTableData(m.sourceTable, m.targetTable, m.sourceCols, m.targetCols)
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
)
|
||||
|
||||
type Migrator struct {
|
||||
@@ -70,7 +70,7 @@ func (mg *Migrator) GetMigrationLog() (map[string]MigrationLog, error) {
|
||||
|
||||
func (mg *Migrator) Start() error {
|
||||
if mg.LogLevel <= log.INFO {
|
||||
log.Info("Migrator:: Starting DB migration")
|
||||
log.Info("Migrator: Starting DB migration")
|
||||
}
|
||||
|
||||
logMap, err := mg.GetMigrationLog()
|
||||
@@ -82,7 +82,7 @@ func (mg *Migrator) Start() error {
|
||||
_, exists := logMap[m.Id()]
|
||||
if exists {
|
||||
if mg.LogLevel <= log.DEBUG {
|
||||
log.Debug("Migrator:: Skipping migration: %v, Already executed", m.Id())
|
||||
log.Debug("Migrator: Skipping migration: %v, Already executed", m.Id())
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -114,13 +114,24 @@ func (mg *Migrator) Start() error {
|
||||
|
||||
func (mg *Migrator) exec(m Migration) error {
|
||||
if mg.LogLevel <= log.INFO {
|
||||
log.Info("Migrator::exec migration id: %v", m.Id())
|
||||
log.Info("Migrator: exec migration id: %v", m.Id())
|
||||
}
|
||||
|
||||
err := mg.inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
condition := m.GetCondition()
|
||||
if condition != nil {
|
||||
sql, args := condition.Sql(mg.dialect)
|
||||
results, err := sess.Query(sql, args...)
|
||||
if err != nil || len(results) == 0 {
|
||||
log.Info("Migrator: skipping migration id: %v, condition not fulfilled", m.Id())
|
||||
return sess.Rollback()
|
||||
}
|
||||
}
|
||||
|
||||
_, err := sess.Exec(m.Sql(mg.dialect))
|
||||
if err != nil {
|
||||
log.Error(3, "Migrator::exec FAILED migration id: %v, err: %v", m.Id(), err)
|
||||
log.Error(3, "Migrator: exec FAILED migration id: %v, err: %v", m.Id(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package migrator
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Postgres struct {
|
||||
BaseDialect
|
||||
@@ -84,3 +87,9 @@ func (db *Postgres) TableCheckSql(tableName string) (string, []interface{}) {
|
||||
sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
|
||||
return sql, args
|
||||
}
|
||||
|
||||
func (db *Postgres) DropIndexSql(tableName string, index *Index) string {
|
||||
quote := db.Quote
|
||||
idxName := index.XName(tableName)
|
||||
return fmt.Sprintf("DROP INDEX %v", quote(idxName))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package migrator
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Sqlite3 struct {
|
||||
BaseDialect
|
||||
}
|
||||
@@ -57,3 +59,10 @@ func (db *Sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
|
||||
args := []interface{}{tableName}
|
||||
return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
|
||||
}
|
||||
|
||||
func (db *Sqlite3) DropIndexSql(tableName string, index *Index) string {
|
||||
quote := db.Quote
|
||||
//var unique string
|
||||
idxName := index.XName(tableName)
|
||||
return fmt.Sprintf("DROP INDEX %v", quote(idxName))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
POSTGRES = "postgres"
|
||||
SQLITE = "sqlite3"
|
||||
@@ -10,6 +15,7 @@ type Migration interface {
|
||||
Sql(dialect Dialect) string
|
||||
Id() string
|
||||
SetId(string)
|
||||
GetCondition() MigrationCondition
|
||||
}
|
||||
|
||||
type SQLType string
|
||||
@@ -24,6 +30,7 @@ type Table struct {
|
||||
Name string
|
||||
Columns []*Column
|
||||
PrimaryKeys []string
|
||||
Indices []*Index
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -37,6 +44,21 @@ type Index struct {
|
||||
Cols []string
|
||||
}
|
||||
|
||||
func (index *Index) XName(tableName string) string {
|
||||
if index.Name == "" {
|
||||
index.Name = fmt.Sprintf("%s", strings.Join(index.Cols, "_"))
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(index.Name, "UQE_") &&
|
||||
!strings.HasPrefix(index.Name, "IDX_") {
|
||||
if index.Type == UniqueIndex {
|
||||
return fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
|
||||
}
|
||||
return fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
|
||||
}
|
||||
return index.Name
|
||||
}
|
||||
|
||||
var (
|
||||
DB_Bit = "BIT"
|
||||
DB_TinyInt = "TINYINT"
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/events"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetOrgById)
|
||||
bus.AddHandler("sql", CreateOrg)
|
||||
bus.AddHandler("sql", UpdateOrg)
|
||||
bus.AddHandler("sql", GetOrgByName)
|
||||
bus.AddHandler("sql", GetOrgList)
|
||||
bus.AddHandler("sql", DeleteOrg)
|
||||
}
|
||||
|
||||
func GetOrgList(query *m.GetOrgListQuery) error {
|
||||
return x.Find(&query.Result)
|
||||
}
|
||||
|
||||
func GetOrgById(query *m.GetOrgByIdQuery) error {
|
||||
var org m.Org
|
||||
exists, err := x.Id(query.Id).Get(&org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return m.ErrOrgNotFound
|
||||
}
|
||||
|
||||
query.Result = &org
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOrgByName(query *m.GetOrgByNameQuery) error {
|
||||
var org m.Org
|
||||
exists, err := x.Where("name=?", query.Name).Get(&org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return m.ErrOrgNotFound
|
||||
}
|
||||
|
||||
query.Result = &org
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateOrg(cmd *m.CreateOrgCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
org := m.Org{
|
||||
Name: cmd.Name,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(&org); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := m.OrgUser{
|
||||
OrgId: org.Id,
|
||||
UserId: cmd.UserId,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&user)
|
||||
cmd.Result = org
|
||||
|
||||
sess.publishAfterCommit(&events.OrgCreated{
|
||||
Timestamp: org.Created,
|
||||
Id: org.Id,
|
||||
Name: org.Name,
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateOrg(cmd *m.UpdateOrgCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
org := m.Org{
|
||||
Name: cmd.Name,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Id(cmd.OrgId).Update(&org); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess.publishAfterCommit(&events.OrgUpdated{
|
||||
Timestamp: org.Updated,
|
||||
Id: org.Id,
|
||||
Name: org.Name,
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteOrg(cmd *m.DeleteOrgCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
deletes := []string{
|
||||
"DELETE FROM star WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ?)",
|
||||
"DELETE FROM dashboard_tag WHERE EXISTS (SELECT 1 FROM dashboard WHERE org_id = ?)",
|
||||
"DELETE FROM dashboard WHERE org_id = ?",
|
||||
"DELETE FROM api_key WHERE org_id = ?",
|
||||
"DELETE FROM data_source WHERE org_id = ?",
|
||||
"DELETE FROM org_user WHERE org_id = ?",
|
||||
"DELETE FROM org WHERE id = ?",
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
log.Trace(sql)
|
||||
_, err := sess.Exec(sql, cmd.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -14,12 +14,12 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
Convey("Testing Account DB Access", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Given single account mode", func() {
|
||||
setting.SingleAccountMode = true
|
||||
setting.DefaultAccountName = "test"
|
||||
setting.DefaultAccountRole = "Viewer"
|
||||
Convey("Given single org mode", func() {
|
||||
setting.SingleOrgMode = true
|
||||
setting.DefaultOrgName = "test"
|
||||
setting.DefaultOrgRole = "Viewer"
|
||||
|
||||
Convey("Users should be added to default account", func() {
|
||||
Convey("Users should be added to default organization", func() {
|
||||
ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
||||
ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
|
||||
|
||||
@@ -28,20 +28,20 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
err = CreateUser(&ac2cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
q1 := m.GetUserAccountsQuery{UserId: ac1cmd.Result.Id}
|
||||
q2 := m.GetUserAccountsQuery{UserId: ac2cmd.Result.Id}
|
||||
GetUserAccounts(&q1)
|
||||
GetUserAccounts(&q2)
|
||||
q1 := m.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
|
||||
q2 := m.GetUserOrgListQuery{UserId: ac2cmd.Result.Id}
|
||||
GetUserOrgList(&q1)
|
||||
GetUserOrgList(&q2)
|
||||
|
||||
So(q1.Result[0].AccountId, ShouldEqual, q2.Result[0].AccountId)
|
||||
So(q1.Result[0].OrgId, ShouldEqual, q2.Result[0].OrgId)
|
||||
So(q1.Result[0].Role, ShouldEqual, "Viewer")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given two saved users", func() {
|
||||
setting.SingleAccountMode = false
|
||||
setting.SingleOrgMode = false
|
||||
setting.DefaultOrgName = "test"
|
||||
|
||||
setting.DefaultAccountName = "test"
|
||||
ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
|
||||
ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
|
||||
|
||||
@@ -70,14 +70,14 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
So(query.Result[1].Email, ShouldEqual, "ac2@test.com")
|
||||
})
|
||||
|
||||
Convey("Given an added account user", func() {
|
||||
cmd := m.AddAccountUserCommand{
|
||||
AccountId: ac1.AccountId,
|
||||
UserId: ac2.Id,
|
||||
Role: m.ROLE_VIEWER,
|
||||
Convey("Given an added org user", func() {
|
||||
cmd := m.AddOrgUserCommand{
|
||||
OrgId: ac1.OrgId,
|
||||
UserId: ac2.Id,
|
||||
Role: m.ROLE_VIEWER,
|
||||
}
|
||||
|
||||
err := AddAccountUser(&cmd)
|
||||
err := AddOrgUser(&cmd)
|
||||
Convey("Should have been saved without error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
@@ -88,54 +88,54 @@ func TestAccountDataAccess(t *testing.T) {
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.Email, ShouldEqual, "ac2@test.com")
|
||||
So(query.Result.AccountId, ShouldEqual, ac2.AccountId)
|
||||
So(query.Result.OrgId, ShouldEqual, ac2.OrgId)
|
||||
So(query.Result.Name, ShouldEqual, "ac2 name")
|
||||
So(query.Result.Login, ShouldEqual, "ac2")
|
||||
So(query.Result.AccountRole, ShouldEqual, "Admin")
|
||||
So(query.Result.AccountName, ShouldEqual, "ac2@test.com")
|
||||
So(query.Result.OrgRole, ShouldEqual, "Admin")
|
||||
So(query.Result.OrgName, ShouldEqual, "ac2@test.com")
|
||||
So(query.Result.IsGrafanaAdmin, ShouldBeTrue)
|
||||
})
|
||||
|
||||
Convey("Can get user accounts", func() {
|
||||
query := m.GetUserAccountsQuery{UserId: ac2.Id}
|
||||
err := GetUserAccounts(&query)
|
||||
Convey("Can get user organizations", func() {
|
||||
query := m.GetUserOrgListQuery{UserId: ac2.Id}
|
||||
err := GetUserOrgList(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 2)
|
||||
})
|
||||
|
||||
Convey("Can get account users", func() {
|
||||
query := m.GetAccountUsersQuery{AccountId: ac1.AccountId}
|
||||
err := GetAccountUsers(&query)
|
||||
Convey("Can get organization users", func() {
|
||||
query := m.GetOrgUsersQuery{OrgId: ac1.OrgId}
|
||||
err := GetOrgUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result), ShouldEqual, 2)
|
||||
So(query.Result[0].Role, ShouldEqual, "Admin")
|
||||
})
|
||||
|
||||
Convey("Can set using account", func() {
|
||||
cmd := m.SetUsingAccountCommand{UserId: ac2.Id, AccountId: ac1.Id}
|
||||
err := SetUsingAccount(&cmd)
|
||||
Convey("Can set using org", func() {
|
||||
cmd := m.SetUsingOrgCommand{UserId: ac2.Id, OrgId: ac1.Id}
|
||||
err := SetUsingOrg(&cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("SignedInUserQuery with a different account", func() {
|
||||
Convey("SignedInUserQuery with a different org", func() {
|
||||
query := m.GetSignedInUserQuery{UserId: ac2.Id}
|
||||
err := GetSignedInUser(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.AccountId, ShouldEqual, ac1.Id)
|
||||
So(query.Result.OrgId, ShouldEqual, ac1.Id)
|
||||
So(query.Result.Email, ShouldEqual, "ac2@test.com")
|
||||
So(query.Result.Name, ShouldEqual, "ac2 name")
|
||||
So(query.Result.Login, ShouldEqual, "ac2")
|
||||
So(query.Result.AccountName, ShouldEqual, "ac1@test.com")
|
||||
So(query.Result.AccountRole, ShouldEqual, "Viewer")
|
||||
So(query.Result.OrgName, ShouldEqual, "ac1@test.com")
|
||||
So(query.Result.OrgRole, ShouldEqual, "Viewer")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Cannot delete last admin account user", func() {
|
||||
cmd := m.RemoveAccountUserCommand{AccountId: ac1.AccountId, UserId: ac1.Id}
|
||||
err := RemoveAccountUser(&cmd)
|
||||
So(err, ShouldEqual, m.ErrLastAccountAdmin)
|
||||
cmd := m.RemoveOrgUserCommand{OrgId: ac1.OrgId, UserId: ac1.Id}
|
||||
err := RemoveOrgUser(&cmd)
|
||||
So(err, ShouldEqual, m.ErrLastOrgAdmin)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,67 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", AddOrgUser)
|
||||
bus.AddHandler("sql", RemoveOrgUser)
|
||||
bus.AddHandler("sql", GetOrgUsers)
|
||||
}
|
||||
|
||||
func AddOrgUser(cmd *m.AddOrgUserCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
entity := m.OrgUser{
|
||||
OrgId: cmd.OrgId,
|
||||
UserId: cmd.UserId,
|
||||
Role: cmd.Role,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&entity)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetOrgUsers(query *m.GetOrgUsersQuery) error {
|
||||
query.Result = make([]*m.OrgUserDTO, 0)
|
||||
sess := x.Table("org_user")
|
||||
sess.Join("INNER", "user", fmt.Sprintf("org_user.user_id=%s.id", x.Dialect().Quote("user")))
|
||||
sess.Where("org_user.org_id=?", query.OrgId)
|
||||
sess.Cols("org_user.org_id", "org_user.user_id", "user.email", "user.login", "org_user.role")
|
||||
sess.Asc("user.email", "user.login")
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM org_user WHERE org_id=? and user_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.OrgId, cmd.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate that there is an admin user left
|
||||
res, err := sess.Query("SELECT 1 from org_user WHERE org_id=? and role='Admin'", cmd.OrgId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return m.ErrLastOrgAdmin
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
@@ -73,7 +74,7 @@ func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
|
||||
|
||||
migrator := migrator.NewMigrator(x)
|
||||
migrator.LogLevel = log.INFO
|
||||
addMigrations(migrator)
|
||||
migrations.AddMigrations(migrator)
|
||||
|
||||
if err := migrator.Start(); err != nil {
|
||||
return fmt.Errorf("Sqlstore::Migration failed err: %v\n", err)
|
||||
|
||||
+104
-47
@@ -1,6 +1,7 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -15,59 +16,64 @@ import (
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", CreateUser)
|
||||
bus.AddHandler("sql", GetUserById)
|
||||
bus.AddHandler("sql", UpdateUser)
|
||||
bus.AddHandler("sql", ChangeUserPassword)
|
||||
bus.AddHandler("sql", GetUserByLogin)
|
||||
bus.AddHandler("sql", SetUsingAccount)
|
||||
bus.AddHandler("sql", SetUsingOrg)
|
||||
bus.AddHandler("sql", GetUserInfo)
|
||||
bus.AddHandler("sql", GetSignedInUser)
|
||||
bus.AddHandler("sql", SearchUsers)
|
||||
bus.AddHandler("sql", GetUserAccounts)
|
||||
bus.AddHandler("sql", GetUserOrgList)
|
||||
bus.AddHandler("sql", DeleteUser)
|
||||
bus.AddHandler("sql", SetUsingOrg)
|
||||
bus.AddHandler("sql", UpdateUserPermissions)
|
||||
}
|
||||
|
||||
func getAccountIdForNewUser(userEmail string, sess *session) (int64, error) {
|
||||
var account m.Account
|
||||
func getOrgIdForNewUser(userEmail string, sess *session) (int64, error) {
|
||||
var org m.Org
|
||||
|
||||
if setting.SingleAccountMode {
|
||||
has, err := sess.Where("name=?", setting.DefaultAccountName).Get(&account)
|
||||
if setting.SingleOrgMode {
|
||||
has, err := sess.Where("name=?", setting.DefaultOrgName).Get(&org)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if has {
|
||||
return account.Id, nil
|
||||
return org.Id, nil
|
||||
} else {
|
||||
account.Name = setting.DefaultAccountName
|
||||
org.Name = setting.DefaultOrgName
|
||||
}
|
||||
} else {
|
||||
account.Name = userEmail
|
||||
org.Name = userEmail
|
||||
}
|
||||
|
||||
account.Created = time.Now()
|
||||
account.Updated = time.Now()
|
||||
org.Created = time.Now()
|
||||
org.Updated = time.Now()
|
||||
|
||||
if _, err := sess.Insert(&account); err != nil {
|
||||
if _, err := sess.Insert(&org); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return account.Id, nil
|
||||
return org.Id, nil
|
||||
}
|
||||
|
||||
func CreateUser(cmd *m.CreateUserCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
accountId, err := getAccountIdForNewUser(cmd.Email, sess)
|
||||
orgId, err := getOrgIdForNewUser(cmd.Email, sess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create user
|
||||
user := m.User{
|
||||
Email: cmd.Email,
|
||||
Name: cmd.Name,
|
||||
Login: cmd.Login,
|
||||
Company: cmd.Company,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
AccountId: accountId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
Email: cmd.Email,
|
||||
Name: cmd.Name,
|
||||
Login: cmd.Login,
|
||||
Company: cmd.Company,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
OrgId: orgId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if len(cmd.Password) > 0 {
|
||||
@@ -82,20 +88,20 @@ func CreateUser(cmd *m.CreateUserCommand) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// create account user link
|
||||
accountUser := m.AccountUser{
|
||||
AccountId: accountId,
|
||||
UserId: user.Id,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
// create org user link
|
||||
orgUser := m.OrgUser{
|
||||
OrgId: orgId,
|
||||
UserId: user.Id,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if setting.SingleAccountMode && !user.IsAdmin {
|
||||
accountUser.Role = m.RoleType(setting.DefaultAccountRole)
|
||||
if setting.SingleOrgMode && !user.IsAdmin {
|
||||
orgUser.Role = m.RoleType(setting.DefaultOrgRole)
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(&accountUser); err != nil {
|
||||
if _, err = sess.Insert(&orgUser); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -112,16 +118,31 @@ func CreateUser(cmd *m.CreateUserCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func GetUserById(query *m.GetUserByIdQuery) error {
|
||||
user := new(m.User)
|
||||
has, err := x.Id(query.Id).Get(user)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrUserNotFound
|
||||
}
|
||||
|
||||
query.Result = user
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserByLogin(query *m.GetUserByLoginQuery) error {
|
||||
if query.LoginOrEmail == "" {
|
||||
return m.ErrAccountNotFound
|
||||
return m.ErrUserNotFound
|
||||
}
|
||||
|
||||
user := new(m.User)
|
||||
if strings.Contains(query.LoginOrEmail, "@") {
|
||||
user = &m.User{Email: query.LoginOrEmail}
|
||||
} else {
|
||||
user = &m.User{Login: strings.ToLower(query.LoginOrEmail)}
|
||||
user = &m.User{Login: query.LoginOrEmail}
|
||||
}
|
||||
|
||||
has, err := x.Get(user)
|
||||
@@ -163,12 +184,28 @@ func UpdateUser(cmd *m.UpdateUserCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func SetUsingAccount(cmd *m.SetUsingAccountCommand) error {
|
||||
func ChangeUserPassword(cmd *m.ChangeUserPasswordCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
|
||||
user := m.User{
|
||||
Password: cmd.NewPassword,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Id(cmd.UserId).Update(&user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func SetUsingOrg(cmd *m.SetUsingOrgCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
user := m.User{}
|
||||
sess.Id(cmd.UserId).Get(&user)
|
||||
|
||||
user.AccountId = cmd.AccountId
|
||||
user.OrgId = cmd.OrgId
|
||||
_, err := sess.Id(user.Id).Update(&user)
|
||||
return err
|
||||
})
|
||||
@@ -193,12 +230,12 @@ func GetUserInfo(query *m.GetUserInfoQuery) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUserAccounts(query *m.GetUserAccountsQuery) error {
|
||||
query.Result = make([]*m.UserAccountDTO, 0)
|
||||
sess := x.Table("account_user")
|
||||
sess.Join("INNER", "account", "account_user.account_id=account.id")
|
||||
sess.Where("account_user.user_id=?", query.UserId)
|
||||
sess.Cols("account.name", "account_user.role", "account_user.account_id")
|
||||
func GetUserOrgList(query *m.GetUserOrgListQuery) error {
|
||||
query.Result = make([]*m.UserOrgDTO, 0)
|
||||
sess := x.Table("org_user")
|
||||
sess.Join("INNER", "org", "org_user.org_id=org.id")
|
||||
sess.Where("org_user.user_id=?", query.UserId)
|
||||
sess.Cols("org.name", "org_user.role", "org_user.org_id")
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
@@ -210,12 +247,12 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
|
||||
u.email as email,
|
||||
u.login as login,
|
||||
u.name as name,
|
||||
account.name as account_name,
|
||||
account_user.role as account_role,
|
||||
account.id as account_id
|
||||
org.name as org_name,
|
||||
org_user.role as org_role,
|
||||
org.id as org_id
|
||||
FROM ` + dialect.Quote("user") + ` as u
|
||||
LEFT OUTER JOIN account_user on account_user.account_id = u.account_id and account_user.user_id = u.id
|
||||
LEFT OUTER JOIN account on account.id = u.account_id
|
||||
LEFT OUTER JOIN org_user on org_user.org_id = u.org_id and org_user.user_id = u.id
|
||||
LEFT OUTER JOIN org on org.id = u.org_id
|
||||
WHERE u.id=?`
|
||||
|
||||
var user m.SignedInUser
|
||||
@@ -240,3 +277,23 @@ func SearchUsers(query *m.SearchUsersQuery) error {
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteUser(cmd *m.DeleteUserCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = fmt.Sprintf("DELETE FROM %s WHERE id=?", x.Dialect().Quote("user"))
|
||||
_, err := sess.Exec(rawSql, cmd.UserId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
user := m.User{}
|
||||
sess.Id(cmd.UserId).Get(&user)
|
||||
|
||||
user.IsAdmin = cmd.IsGrafanaAdmin
|
||||
sess.UseBool("is_admin")
|
||||
_, err := sess.Id(user.Id).Update(&user)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
+37
-25
@@ -4,6 +4,7 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
@@ -65,18 +66,18 @@ var (
|
||||
CookieRememberName string
|
||||
DisableUserSignUp bool
|
||||
|
||||
// single account
|
||||
SingleAccountMode bool
|
||||
DefaultAccountName string
|
||||
DefaultAccountRole string
|
||||
// single organization
|
||||
SingleOrgMode bool
|
||||
DefaultOrgName string
|
||||
DefaultOrgRole string
|
||||
|
||||
// Http auth
|
||||
AdminUser string
|
||||
AdminPassword string
|
||||
|
||||
AnonymousEnabled bool
|
||||
AnonymousAccountName string
|
||||
AnonymousAccountRole string
|
||||
AnonymousEnabled bool
|
||||
AnonymousOrgName string
|
||||
AnonymousOrgRole string
|
||||
|
||||
// Session settings.
|
||||
SessionOptions session.Options
|
||||
@@ -98,15 +99,10 @@ var (
|
||||
func init() {
|
||||
IsWindows = runtime.GOOS == "windows"
|
||||
log.NewLogger(0, "console", `{"level": 0}`)
|
||||
}
|
||||
|
||||
func getWorkDir() string {
|
||||
p, _ := filepath.Abs(".")
|
||||
return p
|
||||
WorkDir, _ = filepath.Abs(".")
|
||||
}
|
||||
|
||||
func findConfigFiles() []string {
|
||||
WorkDir = getWorkDir()
|
||||
ConfRootPath = path.Join(WorkDir, "conf")
|
||||
filenames := make([]string, 0)
|
||||
|
||||
@@ -152,10 +148,29 @@ func ToAbsUrl(relativeUrl string) string {
|
||||
return AppUrl + relativeUrl
|
||||
}
|
||||
|
||||
func NewConfigContext() {
|
||||
func loadEnvVariableOverrides() {
|
||||
for _, section := range Cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
|
||||
keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
|
||||
envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName)
|
||||
envValue := os.Getenv(envKey)
|
||||
|
||||
if len(envValue) > 0 {
|
||||
log.Info("Setting: ENV override found: %s", envKey)
|
||||
key.SetValue(envValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfigContext(config string) {
|
||||
configFiles := findConfigFiles()
|
||||
|
||||
//log.Info("Loading config files: %v", configFiles)
|
||||
if config != "" {
|
||||
configFiles = append(configFiles, config)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
for i, file := range configFiles {
|
||||
@@ -170,6 +185,8 @@ func NewConfigContext() {
|
||||
}
|
||||
}
|
||||
|
||||
loadEnvVariableOverrides()
|
||||
|
||||
AppName = Cfg.Section("").Key("app_name").MustString("Grafana")
|
||||
Env = Cfg.Section("").Key("app_mode").MustString("development")
|
||||
|
||||
@@ -187,11 +204,6 @@ func NewConfigContext() {
|
||||
HttpAddr = server.Key("http_addr").MustString("0.0.0.0")
|
||||
HttpPort = server.Key("http_port").MustString("3000")
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port != "" {
|
||||
HttpPort = port
|
||||
}
|
||||
|
||||
StaticRootPath = server.Key("static_root_path").MustString(path.Join(WorkDir, "webapp"))
|
||||
RouterLogging = server.Key("router_logging").MustBool(false)
|
||||
EnableGzip = server.Key("enable_gzip").MustBool(false)
|
||||
@@ -208,14 +220,14 @@ func NewConfigContext() {
|
||||
AdminPassword = security.Key("admin_password").String()
|
||||
|
||||
// single account
|
||||
SingleAccountMode = Cfg.Section("account.single").Key("enabled").MustBool(false)
|
||||
DefaultAccountName = Cfg.Section("account.single").Key("account_name").MustString("main")
|
||||
DefaultAccountRole = Cfg.Section("account.single").Key("default_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
|
||||
SingleOrgMode = Cfg.Section("organization.single").Key("enabled").MustBool(false)
|
||||
DefaultOrgName = Cfg.Section("organization.single").Key("org_name").MustString("main")
|
||||
DefaultOrgRole = Cfg.Section("organization.single").Key("default_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
|
||||
|
||||
// anonymous access
|
||||
AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
|
||||
AnonymousAccountName = Cfg.Section("auth.anonymous").Key("account_name").String()
|
||||
AnonymousAccountRole = Cfg.Section("auth.anonymous").Key("account_role").String()
|
||||
AnonymousOrgName = Cfg.Section("auth.anonymous").Key("org_name").String()
|
||||
AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String()
|
||||
|
||||
// PhantomJS rendering
|
||||
ImagesDir = "data/png"
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestLoadingSettings(t *testing.T) {
|
||||
|
||||
WorkDir, _ = filepath.Abs("../../")
|
||||
|
||||
Convey("Testing loading settings from ini file", t, func() {
|
||||
|
||||
Convey("Given the default ini files", func() {
|
||||
NewConfigContext("")
|
||||
|
||||
So(AppName, ShouldEqual, "Grafana")
|
||||
So(AdminUser, ShouldEqual, "admin")
|
||||
})
|
||||
|
||||
Convey("Should be able to override via environment variables", func() {
|
||||
os.Setenv("GF_SECURITY_ADMIN_USER", "superduper")
|
||||
NewConfigContext("")
|
||||
|
||||
So(AdminUser, ShouldEqual, "superduper")
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user