mirror of https://github.com/grafana/grafana
commit
e5c9a24c33
@ -1,4 +1,4 @@ |
||||
{ |
||||
"stable": "2.6.0", |
||||
"testing": "3.0.0" |
||||
"stable": "2.6.0", |
||||
"testing": "3.0.0-beta2" |
||||
} |
||||
|
@ -1,22 +1,22 @@ |
||||
#! /usr/bin/env bash |
||||
|
||||
deb_ver=3.0.0-beta21459801392 |
||||
rpm_ver=3.0.0-beta21459801392 |
||||
deb_ver=3.0.0-beta31460467884 |
||||
rpm_ver=3.0.0-beta31460467884 |
||||
|
||||
#rpm_ver=3.0.0-1 |
||||
|
||||
#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb |
||||
# wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb |
||||
|
||||
#package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb |
||||
#package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb |
||||
|
||||
#package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb |
||||
#package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb |
||||
# package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb |
||||
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb |
||||
|
||||
#wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm |
||||
wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm |
||||
|
||||
package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm |
||||
#package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm |
||||
ackage_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm |
||||
|
||||
# package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm |
||||
# package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm |
||||
|
@ -0,0 +1,46 @@ |
||||
package api |
||||
|
||||
import ( |
||||
"crypto/tls" |
||||
"net" |
||||
"net/http" |
||||
"net/http/httputil" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/middleware" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
var gNetProxyTransport = &http.Transport{ |
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, |
||||
Proxy: http.ProxyFromEnvironment, |
||||
Dial: (&net.Dialer{ |
||||
Timeout: 30 * time.Second, |
||||
KeepAlive: 30 * time.Second, |
||||
}).Dial, |
||||
TLSHandshakeTimeout: 10 * time.Second, |
||||
} |
||||
|
||||
func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy { |
||||
director := func(req *http.Request) { |
||||
req.URL.Scheme = "https" |
||||
req.URL.Host = "grafana.net" |
||||
req.Host = "grafana.net" |
||||
|
||||
req.URL.Path = util.JoinUrlFragments("https://grafana.net/api", proxyPath) |
||||
|
||||
// clear cookie headers
|
||||
req.Header.Del("Cookie") |
||||
req.Header.Del("Set-Cookie") |
||||
} |
||||
|
||||
return &httputil.ReverseProxy{Director: director} |
||||
} |
||||
|
||||
func ProxyGnetRequest(c *middleware.Context) { |
||||
proxyPath := c.Params("*") |
||||
proxy := ReverseProxyGnetReq(proxyPath) |
||||
proxy.Transport = gNetProxyTransport |
||||
proxy.ServeHTTP(c.Resp, c.Req.Request) |
||||
c.Resp.Header().Del("Set-Cookie") |
||||
} |
@ -0,0 +1,119 @@ |
||||
package plugins |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/log" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
type GrafanaNetPlugin struct { |
||||
Slug string `json:"slug"` |
||||
Version string `json:"version"` |
||||
} |
||||
|
||||
type GithubLatest struct { |
||||
Stable string `json:"stable"` |
||||
Testing string `json:"testing"` |
||||
} |
||||
|
||||
func StartPluginUpdateChecker() { |
||||
if !setting.CheckForUpdates { |
||||
return |
||||
} |
||||
|
||||
// do one check directly
|
||||
go checkForUpdates() |
||||
|
||||
ticker := time.NewTicker(time.Minute * 10) |
||||
for { |
||||
select { |
||||
case <-ticker.C: |
||||
checkForUpdates() |
||||
} |
||||
} |
||||
} |
||||
|
||||
func getAllExternalPluginSlugs() string { |
||||
str := "" |
||||
|
||||
for _, plug := range Plugins { |
||||
if plug.IsCorePlugin { |
||||
continue |
||||
} |
||||
|
||||
str += plug.Id + "," |
||||
} |
||||
|
||||
return str |
||||
} |
||||
|
||||
func checkForUpdates() { |
||||
log.Trace("Checking for updates") |
||||
|
||||
client := http.Client{Timeout: time.Duration(5 * time.Second)} |
||||
|
||||
pluginSlugs := getAllExternalPluginSlugs() |
||||
resp, err := client.Get("https://grafana.net/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion) |
||||
|
||||
if err != nil { |
||||
log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
defer resp.Body.Close() |
||||
|
||||
body, err := ioutil.ReadAll(resp.Body) |
||||
if err != nil { |
||||
log.Trace("Update check failed, reading response from grafana.net, %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
gNetPlugins := []GrafanaNetPlugin{} |
||||
err = json.Unmarshal(body, &gNetPlugins) |
||||
if err != nil { |
||||
log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
for _, plug := range Plugins { |
||||
for _, gplug := range gNetPlugins { |
||||
if gplug.Slug == plug.Id { |
||||
plug.GrafanaNetVersion = gplug.Version |
||||
plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion |
||||
} |
||||
} |
||||
} |
||||
|
||||
resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json") |
||||
if err != nil { |
||||
log.Trace("Failed to get lates.json repo from github: %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
defer resp2.Body.Close() |
||||
body, err = ioutil.ReadAll(resp2.Body) |
||||
if err != nil { |
||||
log.Trace("Update check failed, reading response from github.net, %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
var githubLatest GithubLatest |
||||
err = json.Unmarshal(body, &githubLatest) |
||||
if err != nil { |
||||
log.Trace("Failed to unmarshal github latest, reading response from github: %v", err.Error()) |
||||
return |
||||
} |
||||
|
||||
if strings.Contains(setting.BuildVersion, "-") { |
||||
GrafanaLatestVersion = githubLatest.Testing |
||||
GrafanaHasUpdate = strings.HasPrefix(setting.BuildVersion, githubLatest.Testing) |
||||
} else { |
||||
GrafanaLatestVersion = githubLatest.Stable |
||||
GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion |
||||
} |
||||
} |
@ -0,0 +1,21 @@ |
||||
<div class="modal-body"> |
||||
<div class="modal-header"> |
||||
<h2 class="modal-header-title"> |
||||
<i class="fa fa-cloud-download"></i> |
||||
<span class="p-l-1">Update Plugin</span> |
||||
</h2> |
||||
|
||||
<a class="modal-header-close" ng-click="dismiss();"> |
||||
<i class="fa fa-remove"></i> |
||||
</a> |
||||
</div> |
||||
|
||||
<div class="modal-content"> |
||||
<div class="gf-form-group"> |
||||
<p>Type the following on the command line to update {{plugin.name}}.</p> |
||||
<pre><code>grafana-cli plugins update {{plugin.id}}</code></pre> |
||||
<span class="small">Check out {{plugin.name}} on <a href="http://grafana/net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span> |
||||
</div> |
||||
<p class="pluginlist-none-installed code--line"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div> |
||||
</div> |
||||
</div> |
@ -1,40 +1,32 @@ |
||||
<div class="gf-form-group"> |
||||
<div class="gf-form-inline"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Mode</span> |
||||
<div class="gf-form-select-wrapper max-width-10"> |
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ctrl.modes" ng-change="ctrl.refresh()"></select> |
||||
</div> |
||||
</div> |
||||
<div class="gf-form" ng-show="ctrl.panel.mode === 'recently viewed'"> |
||||
<span class="gf-form-label"> |
||||
<i class="grafana-tip fa fa-question-circle ng-scope" bs-tooltip="'WARNING: This list will be cleared when clearing browser cache'" data-original-title="" title=""></i> |
||||
</span> |
||||
</div> |
||||
</div> |
||||
<div> |
||||
<div class="section gf-form-group"> |
||||
<h5 class="section-heading">Options</h5> |
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.panel.mode === 'search'"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Search options</span> |
||||
<span class="gf-form-label">Query</span> |
||||
<gf-form-switch class="gf-form" label="Starred" label-class="width-9" checked="ctrl.panel.starred" on-change="ctrl.refresh()"></gf-form-switch> |
||||
<gf-form-switch class="gf-form" label="Recently viewed" label-class="width-9" checked="ctrl.panel.recent" on-change="ctrl.refresh()"></gf-form-switch> |
||||
<gf-form-switch class="gf-form" label="Search" label-class="width-9" checked="ctrl.panel.search" on-change="ctrl.refresh()"></gf-form-switch> |
||||
|
||||
<input type="text" class="gf-form-input" placeholder="title query" |
||||
ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur> |
||||
<gf-form-switch class="gf-form" label="Show headings" label-class="width-9" checked="ctrl.panel.headings" on-change="ctrl.refresh()"></gf-form-switch> |
||||
|
||||
</div> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-9">Max items</span> |
||||
<input class="gf-form-input max-width-5" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()"> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="gf-form"> |
||||
<span class="gf-form-label">Tags</span> |
||||
<div class="section gf-form-group"> |
||||
<h5 class="section-heading">Search</h5> |
||||
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()"> |
||||
</bootstrap-tagsinput> |
||||
</div> |
||||
</div> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-6">Query</span> |
||||
<input type="text" class="gf-form-input" placeholder="title query" ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur> |
||||
</div> |
||||
|
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-6">Tags</span> |
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()"> |
||||
</bootstrap-tagsinput> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="gf-form-inline"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Limit number to</span> |
||||
<input class="gf-form-input" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
@ -1,12 +1,17 @@ |
||||
<div class="dashlist"> |
||||
<div class="dashlist-item" ng-repeat="dash in ctrl.dashList"> |
||||
<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}"> |
||||
<span class="dashlist-title"> |
||||
{{dash.title}} |
||||
</span> |
||||
<span class="dashlist-star"> |
||||
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i> |
||||
</span> |
||||
</a> |
||||
</div> |
||||
<div class="dashlist" ng-repeat="group in ctrl.groups"> |
||||
<div class="dashlist-section" ng-if="group.show"> |
||||
<h6 class="dashlist-section-header" ng-show="ctrl.panel.headings"> |
||||
{{group.header}} |
||||
</h6> |
||||
<div class="dashlist-item" ng-repeat="dash in group.list"> |
||||
<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}"> |
||||
<span class="dashlist-title"> |
||||
{{dash.title}} |
||||
</span> |
||||
<span class="dashlist-star"> |
||||
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i> |
||||
</span> |
||||
</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
@ -0,0 +1,2 @@ |
||||
# Plugin List Panel - Native Plugin |
||||
|
@ -0,0 +1,40 @@ |
||||
<div class="gf-form-group"> |
||||
<div class="gf-form-inline"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Mode</span> |
||||
<div class="gf-form-select-wrapper max-width-10"> |
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ctrl.modes" ng-change="ctrl.refresh()"></select> |
||||
</div> |
||||
</div> |
||||
<div class="gf-form" ng-show="ctrl.panel.mode === 'recently viewed'"> |
||||
<span class="gf-form-label"> |
||||
<i class="grafana-tip fa fa-question-circle ng-scope" bs-tooltip="'WARNING: This list will be cleared when clearing browser cache'" data-original-title="" title=""></i> |
||||
</span> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="gf-form-inline" ng-if="ctrl.panel.mode === 'search'"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Search options</span> |
||||
<span class="gf-form-label">Query</span> |
||||
|
||||
<input type="text" class="gf-form-input" placeholder="title query" |
||||
ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur> |
||||
|
||||
</div> |
||||
|
||||
<div class="gf-form"> |
||||
<span class="gf-form-label">Tags</span> |
||||
|
||||
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()"> |
||||
</bootstrap-tagsinput> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="gf-form-inline"> |
||||
<div class="gf-form"> |
||||
<span class="gf-form-label width-10">Limit number to</span> |
||||
<input class="gf-form-input" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()"> |
||||
</div> |
||||
</div> |
||||
</div> |
After Width: | Height: | Size: 8.8 KiB |
@ -0,0 +1,30 @@ |
||||
<div class="pluginlist"> |
||||
<div class="pluginlist-section" ng-repeat="category in ctrl.viewModel"> |
||||
<h6 class="pluginlist-section-header"> |
||||
{{category.header}} |
||||
</h6> |
||||
<div class="pluginlist-item" ng-repeat="plugin in category.list"> |
||||
<div class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" ng-click="ctrl.gotoPlugin(plugin)"> |
||||
<a href="plugins/{{plugin.id}}/edit"> |
||||
<img ng-src="{{plugin.info.logos.small}}" class="pluginlist-image"> |
||||
<span class="pluginlist-title">{{plugin.name}}</span> |
||||
<span class="pluginlist-version">v{{plugin.info.version}}</span> |
||||
</a> |
||||
<a class="pluginlist-message pluginlist-message--update" ng-show="plugin.hasUpdate" ng-click="ctrl.updateAvailable(plugin, $event)" bs-tooltip="plugin.latestVersion"> |
||||
Update available! |
||||
</a> |
||||
<span class="pluginlist-message pluginlist-message--enable" ng-show="!plugin.enabled && !plugin.hasUpdate"> |
||||
Enable now |
||||
</span> |
||||
<span class="pluginlist-message pluginlist-message--no-update" ng-show="plugin.enabled && !plugin.hasUpdate"> |
||||
Up to date |
||||
</span> |
||||
</div> |
||||
</div> |
||||
<div class="pluginlist-item" ng-show="category.list.length === 0"> |
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="http://grafana/net/plugins/"> |
||||
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span> |
||||
</a> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,72 @@ |
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash'; |
||||
import config from 'app/core/config'; |
||||
import {PanelCtrl} from '../../../features/panel/panel_ctrl'; |
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = { |
||||
}; |
||||
|
||||
class PluginListCtrl extends PanelCtrl { |
||||
static templateUrl = 'module.html'; |
||||
|
||||
pluginList: any[]; |
||||
viewModel: any; |
||||
|
||||
/** @ngInject */ |
||||
constructor($scope, $injector, private backendSrv, private $location) { |
||||
super($scope, $injector); |
||||
_.defaults(this.panel, panelDefaults); |
||||
|
||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); |
||||
this.pluginList = []; |
||||
this.viewModel = [ |
||||
{header: "Installed Apps", list: [], type: 'app'}, |
||||
{header: "Installed Panels", list: [], type: 'panel'}, |
||||
{header: "Installed Datasources", list: [], type: 'datasource'}, |
||||
]; |
||||
|
||||
this.update(); |
||||
} |
||||
|
||||
onInitEditMode() { |
||||
this.editorTabIndex = 1; |
||||
this.addEditorTab('Options', 'public/app/plugins/panel/pluginlist/editor.html'); |
||||
} |
||||
|
||||
gotoPlugin(plugin) { |
||||
this.$location.path(`plugins/${plugin.id}/edit`); |
||||
} |
||||
|
||||
updateAvailable(plugin, $event) { |
||||
$event.stopPropagation(); |
||||
|
||||
var modalScope = this.$scope.$new(true); |
||||
modalScope.plugin = plugin; |
||||
|
||||
this.publishAppEvent('show-modal', { |
||||
src: 'public/app/features/plugins/partials/update_instructions.html', |
||||
scope: modalScope |
||||
}); |
||||
} |
||||
|
||||
update() { |
||||
this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => { |
||||
this.pluginList = plugins; |
||||
this.viewModel[0].list = _.filter(plugins, {type: 'app'}); |
||||
this.viewModel[1].list = _.filter(plugins, {type: 'panel'}); |
||||
this.viewModel[2].list = _.filter(plugins, {type: 'datasource'}); |
||||
|
||||
for (let plugin of this.pluginList) { |
||||
if (plugin.hasUpdate) { |
||||
plugin.state = 'has-update'; |
||||
} else if (!plugin.enabled) { |
||||
plugin.state = 'not-enabled'; |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
export {PluginListCtrl, PluginListCtrl as PanelCtrl} |
@ -0,0 +1,16 @@ |
||||
{ |
||||
"type": "panel", |
||||
"name": "Plugin list", |
||||
"id": "pluginlist", |
||||
|
||||
"info": { |
||||
"author": { |
||||
"name": "Grafana Project", |
||||
"url": "http://grafana.org" |
||||
}, |
||||
"logos": { |
||||
"small": "img/icn-dashlist-panel.svg", |
||||
"large": "img/icn-dashlist-panel.svg" |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,75 @@ |
||||
.pluginlist-section-header { |
||||
margin-bottom: $spacer; |
||||
color: $text-color-weak; |
||||
} |
||||
|
||||
.pluginlist-section { |
||||
margin-bottom: $spacer; |
||||
} |
||||
|
||||
.pluginlist-link { |
||||
display: block; |
||||
margin: 5px; |
||||
padding: 7px; |
||||
background-color: $tight-form-bg; |
||||
|
||||
&:hover { |
||||
background-color: $tight-form-func-bg; |
||||
} |
||||
} |
||||
|
||||
.pluginlist-icon { |
||||
vertical-align: sub; |
||||
font-size: $font-size-h1; |
||||
margin-right: $spacer / 2; |
||||
} |
||||
|
||||
.pluginlist-image { |
||||
width: 20px; |
||||
} |
||||
|
||||
.pluginlist-title { |
||||
margin-right: $spacer / 3; |
||||
} |
||||
|
||||
.pluginlist-version { |
||||
font-size: $font-size-sm; |
||||
color: $text-color-weak; |
||||
} |
||||
|
||||
.pluginlist-message { |
||||
float: right; |
||||
font-size: $font-size-sm; |
||||
} |
||||
|
||||
.pluginlist-message--update { |
||||
&:hover { |
||||
border-bottom: 1px solid $text-color; |
||||
} |
||||
} |
||||
|
||||
.pluginlist-message--enable{ |
||||
color: $external-link-color; |
||||
&:hover { |
||||
border-bottom: 1px solid $external-link-color; |
||||
} |
||||
} |
||||
|
||||
.pluginlist-message--no-update { |
||||
color: $text-color-weak; |
||||
} |
||||
|
||||
.pluginlist-emphasis { |
||||
font-weight: 600; |
||||
} |
||||
|
||||
.pluginlist-none-installed { |
||||
color: $text-color-weak; |
||||
font-size: $font-size-sm; |
||||
} |
||||
|
||||
.pluginlist-inline-logo { |
||||
vertical-align: sub; |
||||
margin-right: $spacer / 3; |
||||
width: 16px; |
||||
} |
Loading…
Reference in new issue