Merge branch 'master' of github.com:grafana/grafana

Conflicts:
	examples/nginx-app/package.json
	examples/nginx-app/plugin.json
This commit is contained in:
Torkel Ödegaard
2016-03-11 12:39:10 +01:00
52 changed files with 155 additions and 291 deletions

View File

@ -64,9 +64,19 @@ Name | Description
`metrics(namespace)` | Returns a list of metrics in the namespace. `metrics(namespace)` | Returns a list of metrics in the namespace.
`dimension_keys(namespace)` | Returns a list of dimension keys in the namespace. `dimension_keys(namespace)` | Returns a list of dimension keys in the namespace.
`dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. `dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`.
`ebs_volume_ids(region, instance_id)` | Returns a list of volume id matching the specified `region`, `instance_id`.
`ec2_instance_attribute(region, attribute_name, filters)` | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`.
For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html).
The `ec2_instance_attribute` query take `filters` in JSON format.
You can specify [pre-defined filters of ec2:DescribeInstances](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html).
Specify like `{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] }`
Example `ec2_instance_attribute()` query
ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] })
![](/img/v2/cloudwatch_templating.png) ![](/img/v2/cloudwatch_templating.png)
## Cost ## Cost

View File

@ -74,7 +74,7 @@ page_keywords: grafana, admin, http, api, documentation, datasource
"jsonData":null "jsonData":null
} }
## Get a single data sources by Name ## Get a single data source by Name
`GET /api/datasources/name/:name` `GET /api/datasources/name/:name`
@ -107,6 +107,26 @@ page_keywords: grafana, admin, http, api, documentation, datasource
"jsonData":null "jsonData":null
} }
## Get data source Id by Name
`GET /api/datasources/id/:name`
**Example Request**:
GET /api/datasources/id/test_datasource HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
{
"id":1
}
## Create data source ## Create data source
`POST /api/datasources` `POST /api/datasources`

View File

@ -373,7 +373,7 @@ Set to `true` to enable auto sign up of users who do not exist in Grafana DB. De
### provider ### provider
Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache`. Default is `file`. Valid values are `memory`, `file`, `mysql`, `postgres`, `memcache` or `redis`. Default is `file`.
### provider_config ### provider_config
@ -384,6 +384,7 @@ session provider you have configured.
- **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name` - **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
- **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=disable - **postgres:** ex: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
- **memcache:** ex: 127.0.0.1:11211 - **memcache:** ex: 127.0.0.1:11211
- **redis:** ex: `addr=127.0.0.1:6379,pool_size=100,db=grafana`
If you use MySQL or Postgres as the session store you need to create the If you use MySQL or Postgres as the session store you need to create the
session table manually. session table manually.

View File

@ -6,7 +6,7 @@ page_keywords: grafana, ldap, configuration, documentation, integration
# LDAP Integration # LDAP Integration
Grafana 2.1 ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your Grafana (2.1 and newer) ships with a strong LDAP integration feature. The LDAP integration in Grafana allows your
Grafana users to login with their LDAP credentials. You can also specify mappings between LDAP Grafana users to login with their LDAP credentials. You can also specify mappings between LDAP
group memberships and Grafana Organization user roles. group memberships and Grafana Organization user roles.

View File

@ -1,3 +1,4 @@
## Example plugin implementations ## Example plugin implementations
[datasource-plugin-genericdatsource](https://github.com/grafana/datasource-plugin-genericdatasource/tree/3.0) datasource:[simple-json-datasource](https://github.com/grafana/simple-json-datasource)
app: [example-app](https://github.com/grafana/example-app)

View File

@ -1,7 +0,0 @@
.DS_Store
node_modules
tmp/*
npm-debug.log
dist/*

View File

@ -1,13 +0,0 @@
{
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}

View File

@ -1,36 +0,0 @@
{
"browser": true,
"esnext": true,
"bitwise":false,
"curly": true,
"eqnull": true,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"sub": true,
"unused": true,
"maxdepth": 6,
"maxlen": 140,
"globals": {
"System": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true
}
}

View File

@ -1,54 +0,0 @@
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.initConfig({
clean: ["dist"],
copy: {
src_to_dist: {
cwd: 'src',
expand: true,
src: ['**/*', '!**/*.js', '!**/*.scss'],
dest: 'dist'
},
pluginDef: {
expand: true,
src: ['plugin.json', 'readme.md'],
dest: 'dist',
}
},
watch: {
rebuild_all: {
files: ['src/**/*', 'plugin.json', 'readme.md'],
tasks: ['default'],
options: {spawn: false}
},
},
babel: {
options: {
sourceMap: true,
presets: ["es2015"],
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
},
dist: {
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist',
ext:'.js'
}]
},
},
});
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']);
};

View File

@ -1,37 +0,0 @@
{
"name": "kentik-app",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/raintank/kentik-app-poc.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/raintank/kentik-app-poc/issues"
},
"devDependencies": {
"grunt": "~0.4.5",
"babel": "~6.5.1",
"grunt-babel": "~6.0.0",
"grunt-contrib-copy": "~0.8.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-contrib-uglify": "~0.11.0",
"grunt-systemjs-builder": "^0.2.5",
"load-grunt-tasks": "~3.2.0",
"grunt-execute": "~0.2.2",
"grunt-contrib-clean": "~0.6.0"
},
"dependencies": {
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"lodash": "~4.0.0"
},
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
}

View File

@ -1,7 +0,0 @@
## Overview
This application is an example app.
### Awesome
Even though it does not have any features it is still pretty awesome.

View File

@ -1,3 +0,0 @@
<h3>
Nginx config!
</h3>

View File

@ -1,6 +0,0 @@
export class NginxAppConfigCtrl {
}
NginxAppConfigCtrl.templateUrl = 'components/config.html';

View File

@ -1,3 +0,0 @@
<h3>
Logs page!
</h3>

View File

@ -1,6 +0,0 @@
export class LogsPageCtrl {
}
LogsPageCtrl.templateUrl = 'components/logs.html';

View File

@ -1,3 +0,0 @@
<h3>
Stream page!
</h3>

View File

@ -1,6 +0,0 @@
export class StreamPageCtrl {
}
StreamPageCtrl.templateUrl = 'components/stream.html';

View File

@ -1,12 +0,0 @@
export default class NginxDatasource {
constructor() {}
query(options) {
return [];
}
testDatasource() {
return false;
}
}

View File

@ -1,5 +0,0 @@
import {Datasource} from './datasource';
export {
Datasource
};

View File

@ -1,5 +0,0 @@
{
"type": "datasource",
"name": "Nginx Datasource",
"id": "nginx-datasource"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -1,9 +0,0 @@
import {LogsPageCtrl} from './components/logs';
import {StreamPageCtrl} from './components/stream';
import {NginxAppConfigCtrl} from './components/config';
export {
NginxAppConfigCtrl as ConfigCtrl,
StreamPageCtrl,
LogsPageCtrl
};

View File

@ -1,15 +0,0 @@
import {PanelCtrl} from 'app/plugins/sdk';
class NginxPanelCtrl extends PanelCtrl {
constructor($scope, $injector) {
super($scope, $injector);
}
}
NginxPanelCtrl.template = '<h2>nginx!</h2>';
export {
NginxPanelCtrl as PanelCtrl
};

View File

@ -1,5 +0,0 @@
{
"type": "panel",
"name": "Nginx Panel",
"id": "nginx-panel"
}

View File

@ -167,11 +167,10 @@ func Register(r *macaron.Macaron) {
r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource) r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
r.Delete("/:id", DeleteDataSource) r.Delete("/:id", DeleteDataSource)
r.Get("/:id", wrap(GetDataSourceById)) r.Get("/:id", wrap(GetDataSourceById))
r.Get("/name/:name", wrap(GetDataSourceByName))
}, reqOrgAdmin) }, reqOrgAdmin)
r.Group("/datasources/name/:name", func() { r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
r.Get("/", wrap(GetDataSourceByName))
}, reqOrgAdmin)
r.Group("/plugins", func() { r.Group("/plugins", func() {
r.Get("/", wrap(GetPluginList)) r.Get("/", wrap(GetPluginList))

View File

@ -55,8 +55,10 @@ func init() {
"S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB", "S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB",
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"}, "HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"}, "AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"}, "AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"},
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"}, "AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
"AWS/ML": {"PredictCount", "PredictFailureCount"}, "AWS/ML": {"PredictCount", "PredictFailureCount"},
"AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"},
"AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"}, "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"},
@ -85,8 +87,10 @@ func init() {
"AWS/ELB": {"LoadBalancerName", "AvailabilityZone"}, "AWS/ELB": {"LoadBalancerName", "AvailabilityZone"},
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"}, "AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
"AWS/ES": {}, "AWS/ES": {},
"AWS/Events": {"RuleName"},
"AWS/Kinesis": {"StreamName"}, "AWS/Kinesis": {"StreamName"},
"AWS/Lambda": {"FunctionName"}, "AWS/Lambda": {"FunctionName"},
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
"AWS/ML": {"MLModelId", "RequestMode"}, "AWS/ML": {"MLModelId", "RequestMode"},
"AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"}, "AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"},
"AWS/Redshift": {"NodeID", "ClusterIdentifier"}, "AWS/Redshift": {"NodeID", "ClusterIdentifier"},

View File

@ -116,6 +116,25 @@ func GetDataSourceByName(c *middleware.Context) Response {
return Json(200, &dtos) return Json(200, &dtos)
} }
// Get /api/datasources/id/:name
func GetDataSourceIdByName(c *middleware.Context) Response {
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrDataSourceNotFound {
return ApiError(404, "Data source not found", nil)
}
return ApiError(500, "Failed to query datasources", err)
}
ds := query.Result
dtos := dtos.AnyId{
Id: ds.Id,
}
return Json(200, &dtos)
}
func convertModelToDtos(ds m.DataSource) dtos.DataSource { func convertModelToDtos(ds m.DataSource) dtos.DataSource {
return dtos.DataSource{ return dtos.DataSource{
Id: ds.Id, Id: ds.Id,

View File

@ -10,6 +10,10 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
type AnyId struct {
Id int64 `json:"id"`
}
type LoginCommand struct { type LoginCommand struct {
User string `json:"user" binding:"Required"` User string `json:"user" binding:"Required"`
Password string `json:"password" binding:"Required"` Password string `json:"password" binding:"Required"`

View File

@ -48,18 +48,23 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
data.User.LightTheme = true data.User.LightTheme = true
} }
dashboardChildNavs := []*dtos.NavLink{
{Text: "Home", Url: setting.AppSubUrl + "/"},
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
}
if c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR {
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Divider: true})
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"})
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"})
}
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
Text: "Dashboards", Text: "Dashboards",
Icon: "icon-gf icon-gf-dashboard", Icon: "icon-gf icon-gf-dashboard",
Url: setting.AppSubUrl + "/", Url: setting.AppSubUrl + "/",
Children: []*dtos.NavLink{ Children: dashboardChildNavs,
{Text: "Home", Url: setting.AppSubUrl + "/"},
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
{Divider: true},
{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"},
{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"},
},
}) })
if c.OrgRole == m.ROLE_ADMIN { if c.OrgRole == m.ROLE_ADMIN {

View File

@ -31,6 +31,7 @@ func RenderToPng(c *middleware.Context) {
Width: queryReader.Get("width", "800"), Width: queryReader.Get("width", "800"),
Height: queryReader.Get("height", "400"), Height: queryReader.Get("height", "400"),
SessionId: c.Session.ID(), SessionId: c.Session.ID(),
Timeout: queryReader.Get("timeout", "15"),
} }
renderOpts.Url = setting.ToAbsUrl(renderOpts.Url) renderOpts.Url = setting.ToAbsUrl(renderOpts.Url)

View File

@ -4,6 +4,7 @@ import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"errors" "errors"
"fmt"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models" m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
s "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" s "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
@ -64,32 +65,33 @@ func InstallPlugin(pluginName, version string, c CommandLine) error {
return err return err
} }
url := v.Url
commit := v.Commit
if version == "" { if version == "" {
version = v.Version version = v.Version
} }
downloadURL := url + "/archive/" + commit + ".zip" downloadURL := fmt.Sprintf("%s/%s/versions/%s/download",
c.GlobalString("repo"),
pluginName,
version)
log.Infof("installing %v @ %v\n", plugin.Id, version) log.Infof("installing %v @ %v\n", plugin.Id, version)
log.Infof("from url: %v\n", downloadURL) log.Infof("from url: %v\n", downloadURL)
log.Infof("on commit: %v\n", commit)
log.Infof("into: %v\n", pluginFolder) log.Infof("into: %v\n", pluginFolder)
err = downloadFile(plugin.Id, pluginFolder, downloadURL) err = downloadFile(plugin.Id, pluginFolder, downloadURL)
if err == nil { if err != nil {
log.Infof("Installed %v successfully ✔\n", plugin.Id) return err
} }
res, _ := s.ReadPlugin(pluginFolder, pluginName) log.Infof("Installed %v successfully ✔\n", plugin.Id)
/* Enable once we need support for downloading depedencies
res, _ := s.ReadPlugin(pluginFolder, pluginName)
for _, v := range res.Dependency.Plugins { for _, v := range res.Dependency.Plugins {
InstallPlugin(v.Id, version, c) InstallPlugin(v.Id, version, c)
log.Infof("Installed Dependency: %v ✔\n", v.Id) log.Infof("Installed dependency: %v ✔\n", v.Id)
} }
*/
return err return err
} }

View File

@ -10,7 +10,7 @@ import (
var ls_getPlugins func(path string) []m.InstalledPlugin = s.GetLocalPlugins var ls_getPlugins func(path string) []m.InstalledPlugin = s.GetLocalPlugins
var validateLsCommmand = func(pluginDir string) error { var validateLsCommand = func(pluginDir string) error {
if pluginDir == "" { if pluginDir == "" {
return errors.New("missing path flag") return errors.New("missing path flag")
} }
@ -31,7 +31,7 @@ var validateLsCommmand = func(pluginDir string) error {
func lsCommand(c CommandLine) error { func lsCommand(c CommandLine) error {
pluginDir := c.GlobalString("path") pluginDir := c.GlobalString("path")
if err := validateLsCommmand(pluginDir); err != nil { if err := validateLsCommand(pluginDir); err != nil {
return err return err
} }

View File

@ -9,10 +9,10 @@ import (
) )
func TestMissingPath(t *testing.T) { func TestMissingPath(t *testing.T) {
var org = validateLsCommmand var org = validateLsCommand
Convey("ls command", t, func() { Convey("ls command", t, func() {
validateLsCommmand = org validateLsCommand = org
Convey("Missing path", func() { Convey("Missing path", func() {
commandLine := &commandstest.FakeCommandLine{ commandLine := &commandstest.FakeCommandLine{
@ -61,7 +61,7 @@ func TestMissingPath(t *testing.T) {
}, },
} }
validateLsCommmand = func(pluginDir string) error { validateLsCommand = func(pluginDir string) error {
return errors.New("dummie error") return errors.New("dummie error")
} }

View File

@ -41,7 +41,7 @@ func main() {
cli.StringFlag{ cli.StringFlag{
Name: "repo", Name: "repo",
Usage: "url to the plugin repository", Usage: "url to the plugin repository",
Value: "https://raw.githubusercontent.com/grafana/grafana-plugin-repository/master/repo.json", Value: "",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "debug, d", Name: "debug, d",

View File

@ -12,7 +12,8 @@ import (
var IoHelper m.IoUtil = IoUtilImp{} var IoHelper m.IoUtil = IoUtilImp{}
func ListAllPlugins(repoUrl string) (m.PluginRepo, error) { func ListAllPlugins(repoUrl string) (m.PluginRepo, error) {
res, _ := goreq.Request{Uri: repoUrl}.Do()
res, _ := goreq.Request{Uri: repoUrl + "/repo", MaxRedirects: 3}.Do()
var resp m.PluginRepo var resp m.PluginRepo
err := res.Body.FromJsonTo(&resp) err := res.Body.FromJsonTo(&resp)

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"strconv"
) )
type RenderOpts struct { type RenderOpts struct {
@ -18,6 +19,7 @@ type RenderOpts struct {
Width string Width string
Height string Height string
SessionId string SessionId string
Timeout string
} }
func RenderToPng(params *RenderOpts) (string, error) { func RenderToPng(params *RenderOpts) (string, error) {
@ -60,8 +62,13 @@ func RenderToPng(params *RenderOpts) (string, error) {
close(done) close(done)
}() }()
timeout, err := strconv.Atoi(params.Timeout)
if err != nil {
timeout = 15
}
select { select {
case <-time.After(15 * time.Second): case <-time.After(time.Duration(timeout) * time.Second):
if err := cmd.Process.Kill(); err != nil { if err := cmd.Process.Kill(); err != nil {
log.Error(4, "failed to kill: %v", err) log.Error(4, "failed to kill: %v", err)
} }

View File

@ -14,8 +14,8 @@ func TestPluginDashboards(t *testing.T) {
Convey("When asking plugin dashboard info", t, func() { Convey("When asking plugin dashboard info", t, func() {
setting.Cfg = ini.Empty() setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.nginx-app") sec, _ := setting.Cfg.NewSection("plugin.test-app")
sec.NewKey("path", "../../examples/nginx-app") sec.NewKey("path", "../../tests/test-app")
err := Init() err := Init()
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -31,7 +31,7 @@ func TestPluginDashboards(t *testing.T) {
return m.ErrDashboardNotFound return m.ErrDashboardNotFound
}) })
dashboards, err := GetPluginDashboards(1, "nginx-app") dashboards, err := GetPluginDashboards(1, "test-app")
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -43,7 +43,7 @@ func TestPluginDashboards(t *testing.T) {
So(dashboards[0].Title, ShouldEqual, "Nginx Connections") So(dashboards[0].Title, ShouldEqual, "Nginx Connections")
So(dashboards[0].Revision, ShouldEqual, "1.5") So(dashboards[0].Revision, ShouldEqual, "1.5")
So(dashboards[0].InstalledRevision, ShouldEqual, "1.1") So(dashboards[0].InstalledRevision, ShouldEqual, "1.1")
So(dashboards[0].InstalledURI, ShouldEqual, "db/nginx-connections") So(dashboards[0].InstalledUri, ShouldEqual, "db/nginx-connections")
So(dashboards[1].Revision, ShouldEqual, "2.0") So(dashboards[1].Revision, ShouldEqual, "2.0")
So(dashboards[1].InstalledRevision, ShouldEqual, "") So(dashboards[1].InstalledRevision, ShouldEqual, "")

View File

@ -28,14 +28,14 @@ func TestPluginScans(t *testing.T) {
Convey("When reading app plugin definition", t, func() { Convey("When reading app plugin definition", t, func() {
setting.Cfg = ini.Empty() setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.nginx-app") sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
sec.NewKey("path", "../../examples/nginx-app") sec.NewKey("path", "../../tests/test-app")
err := Init() err := Init()
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(Apps), ShouldBeGreaterThan, 0) So(len(Apps), ShouldBeGreaterThan, 0)
So(Apps["nginx-app"].Info.Logos.Large, ShouldEqual, "public/plugins/nginx-app/img/logo_large.png") So(Apps["test-app"].Info.Logos.Large, ShouldEqual, "public/plugins/test-app/img/logo_large.png")
So(Apps["nginx-app"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/nginx-app/img/screenshot2.png") So(Apps["test-app"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/test-app/img/screenshot2.png")
}) })
} }

View File

@ -74,7 +74,7 @@ function (angular, _, kbn) {
if (urlValue !== void 0) { if (urlValue !== void 0) {
return self.setVariableFromUrl(variable, urlValue).then(lock.resolve); return self.setVariableFromUrl(variable, urlValue).then(lock.resolve);
} }
else if (variable.refresh === 'On Dashboard Load' || variable.refresh === 'On Time Change and Dashboard Load') { else if (variable.refresh === 1 || variable.refresh === 2) {
return self.updateOptions(variable).then(function() { return self.updateOptions(variable).then(function() {
if (_.isEmpty(variable.current) && variable.options.length) { if (_.isEmpty(variable.current) && variable.options.length) {
console.log("setting current for %s", variable.name); console.log("setting current for %s", variable.name);

View File

@ -143,7 +143,7 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
return this.awsRequest({ return this.awsRequest({
region: region, region: region,
action: 'DescribeInstances', action: 'DescribeInstances',
parameters: { filter: filters, instanceIds: instanceIds } parameters: { filters: filters, instanceIds: instanceIds }
}); });
}; };
@ -205,6 +205,28 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
}); });
} }
var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);
if (ec2InstanceAttributeQuery) {
region = templateSrv.replace(ec2InstanceAttributeQuery[1]);
var filterJson = JSON.parse(templateSrv.replace(ec2InstanceAttributeQuery[3]));
var filters = _.map(filterJson, function(values, name) {
return {
Name: name,
Values: values
};
});
var targetAttributeName = templateSrv.replace(ec2InstanceAttributeQuery[2]);
return this.performEC2DescribeInstances(region, filters, null).then(function(result) {
var attributes = _.chain(result.Reservations)
.map(function(reservations) {
return _.pluck(reservations.Instances, targetAttributeName);
})
.flatten().value();
return transformSuggestData(attributes);
});
}
return $q.when([]); return $q.when([]);
}; };

View File

@ -104,7 +104,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv)
this.metricFindQuery = function (query) { this.metricFindQuery = function (query) {
var interpolated; var interpolated;
try { try {
interpolated = templateSrv.replace(query); interpolated = templateSrv.replace(query, null, 'regex');
} catch (err) { } catch (err) {
return $q.reject(err); return $q.reject(err);
} }

View File

@ -258,6 +258,6 @@
"annotations": { "annotations": {
"enable": false "enable": false
}, },
"refresh": "Never", "refresh": 0,
"version": 6 "version": 6
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "app", "type": "app",
"name": "Nginx", "name": "Test App",
"id": "nginx-app", "id": "test-app",
"staticRoot": ".", "staticRoot": ".",
@ -16,12 +16,12 @@
}, },
"info": { "info": {
"description": "Official Grafana Nginx App & Dashboard bundle", "description": "Official Grafana Test App & Dashboard bundle",
"author": { "author": {
"name": "Nginx Inc.", "name": "Test Inc.",
"url": "http://nginx.com" "url": "http://test.com"
}, },
"keywords": ["nginx"], "keywords": ["test"],
"logos": { "logos": {
"small": "img/logo_small.png", "small": "img/logo_small.png",
"large": "img/logo_large.png" "large": "img/logo_large.png"