浏览代码

datasource as cfg: adds readonly datasources

bergquist 8 年之前
父节点
当前提交
36676e23c1

+ 30 - 3
pkg/api/datasources.go

@@ -33,6 +33,7 @@ func GetDataSources(c *middleware.Context) Response {
 			BasicAuth: ds.BasicAuth,
 			BasicAuth: ds.BasicAuth,
 			IsDefault: ds.IsDefault,
 			IsDefault: ds.IsDefault,
 			JsonData:  ds.JsonData,
 			JsonData:  ds.JsonData,
+			ReadOnly:  ds.ReadOnly,
 		}
 		}
 
 
 		if plugin, exists := plugins.DataSources[ds.Type]; exists {
 		if plugin, exists := plugins.DataSources[ds.Type]; exists {
@@ -76,9 +77,20 @@ func DeleteDataSourceById(c *middleware.Context) {
 		return
 		return
 	}
 	}
 
 
+	ds, err := getRawDataSourceById(id, c.OrgId)
+	if err != nil {
+		c.JsonApiErr(400, "Failed to delete datasource", nil)
+		return
+	}
+
+	if ds.ReadOnly {
+		c.JsonApiErr(403, "Cannot delete read-only data source", nil)
+		return
+	}
+
 	cmd := &m.DeleteDataSourceByIdCommand{Id: id, OrgId: c.OrgId}
 	cmd := &m.DeleteDataSourceByIdCommand{Id: id, OrgId: c.OrgId}
 
 
-	err := bus.Dispatch(cmd)
+	err = bus.Dispatch(cmd)
 	if err != nil {
 	if err != nil {
 		c.JsonApiErr(500, "Failed to delete datasource", err)
 		c.JsonApiErr(500, "Failed to delete datasource", err)
 		return
 		return
@@ -95,8 +107,18 @@ func DeleteDataSourceByName(c *middleware.Context) {
 		return
 		return
 	}
 	}
 
 
-	cmd := &m.DeleteDataSourceByNameCommand{Name: name, OrgId: c.OrgId}
+	getCmd := &m.GetDataSourceByNameQuery{Name: name, OrgId: c.OrgId}
+	if err := bus.Dispatch(getCmd); err != nil {
+		c.JsonApiErr(500, "Failed to delete datasource", err)
+		return
+	}
+
+	if getCmd.Result.ReadOnly {
+		c.JsonApiErr(403, "Cannot delete read-only data source", nil)
+		return
+	}
 
 
+	cmd := &m.DeleteDataSourceByNameCommand{Name: name, OrgId: c.OrgId}
 	err := bus.Dispatch(cmd)
 	err := bus.Dispatch(cmd)
 	if err != nil {
 	if err != nil {
 		c.JsonApiErr(500, "Failed to delete datasource", err)
 		c.JsonApiErr(500, "Failed to delete datasource", err)
@@ -160,11 +182,14 @@ func fillWithSecureJsonData(cmd *m.UpdateDataSourceCommand) error {
 	}
 	}
 
 
 	ds, err := getRawDataSourceById(cmd.Id, cmd.OrgId)
 	ds, err := getRawDataSourceById(cmd.Id, cmd.OrgId)
-
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	if ds.ReadOnly {
+		return m.ErrDatasourceIsReadOnly
+	}
+
 	secureJsonData := ds.SecureJsonData.Decrypt()
 	secureJsonData := ds.SecureJsonData.Decrypt()
 	for k, v := range secureJsonData {
 	for k, v := range secureJsonData {
 
 
@@ -201,6 +226,7 @@ func GetDataSourceByName(c *middleware.Context) Response {
 	}
 	}
 
 
 	dtos := convertModelToDtos(query.Result)
 	dtos := convertModelToDtos(query.Result)
+	dtos.ReadOnly = true
 	return Json(200, &dtos)
 	return Json(200, &dtos)
 }
 }
 
 
@@ -242,6 +268,7 @@ func convertModelToDtos(ds *m.DataSource) dtos.DataSource {
 		JsonData:          ds.JsonData,
 		JsonData:          ds.JsonData,
 		SecureJsonFields:  map[string]bool{},
 		SecureJsonFields:  map[string]bool{},
 		Version:           ds.Version,
 		Version:           ds.Version,
+		ReadOnly:          ds.ReadOnly,
 	}
 	}
 
 
 	for k, v := range ds.SecureJsonData {
 	for k, v := range ds.SecureJsonData {

+ 2 - 0
pkg/api/dtos/datasource.go

@@ -26,6 +26,7 @@ type DataSource struct {
 	JsonData          *simplejson.Json `json:"jsonData,omitempty"`
 	JsonData          *simplejson.Json `json:"jsonData,omitempty"`
 	SecureJsonFields  map[string]bool  `json:"secureJsonFields"`
 	SecureJsonFields  map[string]bool  `json:"secureJsonFields"`
 	Version           int              `json:"version"`
 	Version           int              `json:"version"`
+	ReadOnly          bool             `json:"readOnly"`
 }
 }
 
 
 type DataSourceListItemDTO struct {
 type DataSourceListItemDTO struct {
@@ -42,6 +43,7 @@ type DataSourceListItemDTO struct {
 	BasicAuth   bool             `json:"basicAuth"`
 	BasicAuth   bool             `json:"basicAuth"`
 	IsDefault   bool             `json:"isDefault"`
 	IsDefault   bool             `json:"isDefault"`
 	JsonData    *simplejson.Json `json:"jsonData,omitempty"`
 	JsonData    *simplejson.Json `json:"jsonData,omitempty"`
+	ReadOnly    bool             `json:"readOnly"`
 }
 }
 
 
 type DataSourceList []DataSourceListItemDTO
 type DataSourceList []DataSourceListItemDTO

+ 4 - 0
pkg/models/datasource.go

@@ -27,6 +27,7 @@ var (
 	ErrDataSourceNotFound           = errors.New("Data source not found")
 	ErrDataSourceNotFound           = errors.New("Data source not found")
 	ErrDataSourceNameExists         = errors.New("Data source with same name already exists")
 	ErrDataSourceNameExists         = errors.New("Data source with same name already exists")
 	ErrDataSourceUpdatingOldVersion = errors.New("Trying to update old version of datasource")
 	ErrDataSourceUpdatingOldVersion = errors.New("Trying to update old version of datasource")
+	ErrDatasourceIsReadOnly         = errors.New("Data source is readonly. Can only be updated from configuration.")
 )
 )
 
 
 type DsAccess string
 type DsAccess string
@@ -50,6 +51,7 @@ type DataSource struct {
 	IsDefault         bool
 	IsDefault         bool
 	JsonData          *simplejson.Json
 	JsonData          *simplejson.Json
 	SecureJsonData    securejsondata.SecureJsonData
 	SecureJsonData    securejsondata.SecureJsonData
+	ReadOnly          bool
 
 
 	Created time.Time
 	Created time.Time
 	Updated time.Time
 	Updated time.Time
@@ -109,6 +111,7 @@ type AddDataSourceCommand struct {
 	IsDefault         bool              `json:"isDefault"`
 	IsDefault         bool              `json:"isDefault"`
 	JsonData          *simplejson.Json  `json:"jsonData"`
 	JsonData          *simplejson.Json  `json:"jsonData"`
 	SecureJsonData    map[string]string `json:"secureJsonData"`
 	SecureJsonData    map[string]string `json:"secureJsonData"`
+	ReadOnly          bool              `json:"readOnly"`
 
 
 	OrgId int64 `json:"-"`
 	OrgId int64 `json:"-"`
 
 
@@ -132,6 +135,7 @@ type UpdateDataSourceCommand struct {
 	JsonData          *simplejson.Json  `json:"jsonData"`
 	JsonData          *simplejson.Json  `json:"jsonData"`
 	SecureJsonData    map[string]string `json:"secureJsonData"`
 	SecureJsonData    map[string]string `json:"secureJsonData"`
 	Version           int               `json:"version"`
 	Version           int               `json:"version"`
+	ReadOnly          bool              `json:"readOnly"`
 
 
 	OrgId int64 `json:"-"`
 	OrgId int64 `json:"-"`
 	Id    int64 `json:"-"`
 	Id    int64 `json:"-"`

+ 2 - 0
pkg/services/provisioning/datasources/types.go

@@ -50,6 +50,7 @@ func createInsertCommand(ds DataSourceFromConfig) *models.AddDataSourceCommand {
 		IsDefault:         ds.IsDefault,
 		IsDefault:         ds.IsDefault,
 		JsonData:          jsonData,
 		JsonData:          jsonData,
 		SecureJsonData:    ds.SecureJsonData,
 		SecureJsonData:    ds.SecureJsonData,
+		ReadOnly:          true,
 	}
 	}
 }
 }
 
 
@@ -76,5 +77,6 @@ func createUpdateCommand(ds DataSourceFromConfig, id int64) *models.UpdateDataSo
 		IsDefault:         ds.IsDefault,
 		IsDefault:         ds.IsDefault,
 		JsonData:          jsonData,
 		JsonData:          jsonData,
 		SecureJsonData:    ds.SecureJsonData,
 		SecureJsonData:    ds.SecureJsonData,
+		ReadOnly:          true,
 	}
 	}
 }
 }

+ 2 - 0
pkg/services/sqlstore/datasource.go

@@ -106,6 +106,7 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error {
 			Created:           time.Now(),
 			Created:           time.Now(),
 			Updated:           time.Now(),
 			Updated:           time.Now(),
 			Version:           1,
 			Version:           1,
+			ReadOnly:          cmd.ReadOnly,
 		}
 		}
 
 
 		if _, err := sess.Insert(ds); err != nil {
 		if _, err := sess.Insert(ds); err != nil {
@@ -151,6 +152,7 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
 			JsonData:          cmd.JsonData,
 			JsonData:          cmd.JsonData,
 			SecureJsonData:    securejsondata.GetEncryptedJsonData(cmd.SecureJsonData),
 			SecureJsonData:    securejsondata.GetEncryptedJsonData(cmd.SecureJsonData),
 			Updated:           time.Now(),
 			Updated:           time.Now(),
+			ReadOnly:          cmd.ReadOnly,
 			Version:           cmd.Version + 1,
 			Version:           cmd.Version + 1,
 		}
 		}
 
 

+ 4 - 0
pkg/services/sqlstore/migrations/datasource_mig.go

@@ -126,4 +126,8 @@ func addDataSourceMigration(mg *Migrator) {
 		Sqlite(setVersionToOneWhereZero).
 		Sqlite(setVersionToOneWhereZero).
 		Postgres(setVersionToOneWhereZero).
 		Postgres(setVersionToOneWhereZero).
 		Mysql(setVersionToOneWhereZero))
 		Mysql(setVersionToOneWhereZero))
+
+	mg.AddMigration("Add read_only data column", NewAddColumnMigration(tableV2, &Column{
+		Name: "read_only", Type: DB_Bool, Nullable: true,
+	}))
 }
 }

+ 4 - 0
public/app/features/plugins/ds_edit_ctrl.ts

@@ -149,6 +149,10 @@ export class DataSourceEditCtrl {
       return;
       return;
     }
     }
 
 
+    if (this.current.readOnly) {
+      return;
+    }
+
     if (this.current.id) {
     if (this.current.id) {
       return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then((result) => {
       return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then((result) => {
         this.current = result.datasource;
         this.current = result.datasource;

+ 8 - 6
public/app/features/plugins/partials/ds_edit.html

@@ -6,6 +6,8 @@
 		<h1 ng-show="ctrl.isNew">Add data source</h1>
 		<h1 ng-show="ctrl.isNew">Add data source</h1>
 		<h1 ng-hide="ctrl.isNew">Edit data source</h1>
 		<h1 ng-hide="ctrl.isNew">Edit data source</h1>
 
 
+		<div ng-if="ctrl.current.readOnly" class="grafana-info-box span8">Disclaimer. This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource.</div>
+
 		<div class="page-header-tabs" ng-show="ctrl.hasDashboards">
 		<div class="page-header-tabs" ng-show="ctrl.hasDashboards">
 			<ul class="gf-tabs">
 			<ul class="gf-tabs">
 				<li class="gf-tabs-item">
 				<li class="gf-tabs-item">
@@ -29,20 +31,20 @@
         		<div class="gf-form-inline">
         		<div class="gf-form-inline">
 					<div class="gf-form max-width-30">
 					<div class="gf-form max-width-30">
 						<span class="gf-form-label width-7">Name</span>
 						<span class="gf-form-label width-7">Name</span>
-						<input class="gf-form-input max-width-23" type="text" ng-model="ctrl.current.name" placeholder="name" required>
+						<input class="gf-form-input max-width-23" type="text" ng-model="ctrl.current.name" placeholder="name" ng-disabled="ctrl.current.readOnly"  required>
 						<info-popover offset="0px -135px" mode="right-absolute">
 						<info-popover offset="0px -135px" mode="right-absolute">
 							The name is used when you select the data source in panels.
 							The name is used when you select the data source in panels.
 							The <em>Default</em> data source is preselected in new
 							The <em>Default</em> data source is preselected in new
 							panels.
 							panels.
 						</info-popover>
 						</info-popover>
 					</div>
 					</div>
-					<gf-form-switch class="gf-form" label="Default" checked="ctrl.current.isDefault" switch-class="max-width-6"></gf-form-switch>
+					<gf-form-switch class="gf-form" label="Default" checked="ctrl.current.isDefault" ng-disabled="ctrl.current.readOnly"  switch-class="max-width-6"></gf-form-switch>
 				</div>
 				</div>
 
 
 				<div class="gf-form max-width-30">
 				<div class="gf-form max-width-30">
 					<span class="gf-form-label width-7">Type</span>
 					<span class="gf-form-label width-7">Type</span>
 					<div class="gf-form-select-wrapper max-width-23">
 					<div class="gf-form-select-wrapper max-width-23">
-						<select class="gf-form-input" ng-model="ctrl.current.type" ng-options="v.id as v.name for v in ctrl.types" ng-change="ctrl.userChangedType()"></select>
+						<select class="gf-form-input" ng-model="ctrl.current.type" ng-disabled="ctrl.current.readOnly" ng-options="v.id as v.name for v in ctrl.types" ng-change="ctrl.userChangedType()"></select>
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
@@ -71,9 +73,9 @@
 			</div>
 			</div>
 
 
 			<div class="gf-form-button-row">
 			<div class="gf-form-button-row">
-				<button type="submit" class="btn btn-success width-6" ng-show="ctrl.isNew" ng-click="ctrl.saveChanges()">Add</button>
-				<button type="submit" class="btn btn-success width-8" ng-show="!ctrl.isNew" ng-click="ctrl.saveChanges()">Save &amp; Test</button>
-				<button type="submit" class="btn btn-danger width-6" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
+				<button type="submit" class="btn btn-success width-6" ng-disabled="ctrl.current.readOnly" ng-show="ctrl.isNew" ng-click="ctrl.saveChanges()">Add</button>
+				<button type="submit" class="btn btn-success width-8" ng-disabled="ctrl.current.readOnly" ng-show="!ctrl.isNew" ng-click="ctrl.saveChanges()">Save &amp; Test</button>
+				<button type="submit" class="btn btn-danger width-6" ng-disabled="ctrl.current.readOnly" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
 					Delete
 					Delete
 				</button>
 				</button>
 				<a class="btn btn-link" href="datasources">Cancel</a>
 				<a class="btn btn-link" href="datasources">Cancel</a>