Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
ryantxu committed May 7, 2024
2 parents de35b2b + 21f2303 commit 9dda07d
Show file tree
Hide file tree
Showing 85 changed files with 6,342 additions and 1,082 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check-grafana-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ jobs:
- uses: actions/setup-go@v5
with:
cache: false
go-version: '~1.22'
check-latest: true
go-version-file: 'grafana-plugin-sdk-go/go.mod'

- name: Check if branch exists in Grafana
working-directory: './grafana'
Expand All @@ -42,7 +42,7 @@ jobs:
- name: Link sdk
working-directory: './grafana'
run: echo 'replace github.com/grafana/grafana-plugin-sdk-go => ../grafana-plugin-sdk-go' >> go.mod
run: go mod edit -replace github.com/grafana/grafana-plugin-sdk-go=../grafana-plugin-sdk-go

- name: Automatic updates
working-directory: './grafana'
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 4 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to -1 to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
days-before-close: 14
# Number of days of inactivity before an Issue or Pull Request becomes stale
days-before-stale: 90
exempt-issue-labels: no stalebot
exempt-pr-labels: no stalebot
operations-per-run: 100
stale-issue-label: stale
stale-pr-label: stale
stale-pr-message: >
This pull request has been automatically marked as stale because it has not had
activity in the last 90 days. It will be closed in 2 weeks if no further activity occurs. Please
feel free to give a status update now, ping for review, or re-open when it's ready.
Thank you for your contributions!
close-pr-message: >
This pull request has been automatically closed because it has not had
activity in the last 2 weeks. Please feel free to give a status update now, ping for review, or re-open when it's ready.
Thank you for your contributions!
14 changes: 14 additions & 0 deletions backend/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,15 @@ func propagateTenantIDIfPresent(ctx context.Context) context.Context {
return ctx
}

func (s *DataSourceInstanceSettings) ProxyOptionsFromContext(ctx context.Context) (*proxy.Options, error) {
cfg := GrafanaConfigFromContext(ctx)
p, err := cfg.proxy()
if err != nil {
return nil, err
}
return s.ProxyOptions(p.clientCfg)
}

func (s *DataSourceInstanceSettings) ProxyOptions(clientCfg *proxy.ClientCfg) (*proxy.Options, error) {
opts := &proxy.Options{}

Expand Down Expand Up @@ -302,3 +311,8 @@ func (s *DataSourceInstanceSettings) ProxyClient(ctx context.Context) (proxy.Cli

return proxy.New(proxyOpts), nil
}

// WithTenant injects supplied tenant ID into context.
func WithTenant(ctx context.Context, tenantID string) context.Context {
return tenant.WithTenant(ctx, tenantID)
}
172 changes: 170 additions & 2 deletions backend/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func TestProxyOptions(t *testing.T) {
proxyClientCfg: &proxy.ClientCfg{
ClientCert: "<client-cert>",
ClientKey: "123abc",
RootCA: "<root-ca-cert>",
RootCAs: []string{"<root-ca-cert>"},
ProxyAddress: "10.1.2.3",
ServerName: "grafana-server",
},
Expand All @@ -381,7 +381,7 @@ func TestProxyOptions(t *testing.T) {
ClientCfg: &proxy.ClientCfg{
ClientCert: "<client-cert>",
ClientKey: "123abc",
RootCA: "<root-ca-cert>",
RootCAs: []string{"<root-ca-cert>"},
ProxyAddress: "10.1.2.3",
ServerName: "grafana-server",
},
Expand All @@ -398,3 +398,171 @@ func TestProxyOptions(t *testing.T) {
}
})
}

func TestProxyOptionsFromContext(t *testing.T) {
tcs := []struct {
name string
instanceSettings *DataSourceInstanceSettings
grafanaCfg *GrafanaCfg
expectedClientOptions *proxy.Options
err error
}{
{
name: "Proxy options are configured when enableSecureSocksProxy is true",
instanceSettings: &DataSourceInstanceSettings{
Name: "ds-name",
Type: "example-datasource",
JSONData: []byte("{ \"enableSecureSocksProxy\": true, \"timeout\": 10, \"keepAlive\": 15, \"secureSocksProxyUsername\": \"user\" }"),
DecryptedSecureJSONData: map[string]string{"secureSocksProxyPassword": "pass"},
},
grafanaCfg: NewGrafanaCfg(
map[string]string{
proxy.PluginSecureSocksProxyEnabled: "true",
proxy.PluginSecureSocksProxyClientCert: "/path/to/client-cert",
proxy.PluginSecureSocksProxyClientCertContents: "client-cert-contents",
proxy.PluginSecureSocksProxyClientKey: "/path/to/client-key",
proxy.PluginSecureSocksProxyClientKeyContents: "client-key-contents",
proxy.PluginSecureSocksProxyRootCAs: "/path/to/root-ca",
proxy.PluginSecureSocksProxyRootCAsContents: "root-ca-contents",
proxy.PluginSecureSocksProxyProxyAddress: "localhost:1234",
proxy.PluginSecureSocksProxyServerName: "proxy-server",
proxy.PluginSecureSocksProxyAllowInsecure: "true",
},
),
expectedClientOptions: &proxy.Options{
Enabled: true,
DatasourceName: "ds-name",
DatasourceType: "example-datasource",
Auth: &proxy.AuthOptions{
Username: "user",
Password: "pass",
},
Timeouts: &proxy.TimeoutOptions{
Timeout: time.Second * 10,
KeepAlive: time.Second * 15,
},
ClientCfg: &proxy.ClientCfg{
ClientCert: "/path/to/client-cert",
ClientKey: "/path/to/client-key",
RootCAs: []string{"/path/to/root-ca"},
ClientCertVal: "client-cert-contents",
ClientKeyVal: "client-key-contents",
RootCAsVals: []string{"root-ca-contents"},
ProxyAddress: "localhost:1234",
ServerName: "proxy-server",
AllowInsecure: true,
},
},
},
{
name: "Datasource UID becomes user name when secureSocksProxyUsername is not set",
instanceSettings: &DataSourceInstanceSettings{
Name: "ds-name",
UID: "ds-uid",
Type: "example-datasource",
JSONData: []byte("{ \"enableSecureSocksProxy\": true, \"timeout\": 10, \"keepAlive\": 15 }"),
DecryptedSecureJSONData: map[string]string{"secureSocksProxyPassword": "pass"},
},
grafanaCfg: NewGrafanaCfg(
map[string]string{
proxy.PluginSecureSocksProxyEnabled: "true",
proxy.PluginSecureSocksProxyClientCert: "/path/to/client-cert",
proxy.PluginSecureSocksProxyClientCertContents: "client-cert-contents",
proxy.PluginSecureSocksProxyClientKey: "/path/to/client-key",
proxy.PluginSecureSocksProxyClientKeyContents: "client-key-contents",
proxy.PluginSecureSocksProxyRootCAs: "/path/to/root-ca",
proxy.PluginSecureSocksProxyRootCAsContents: "root-ca-contents",
proxy.PluginSecureSocksProxyProxyAddress: "localhost:1234",
proxy.PluginSecureSocksProxyServerName: "proxy-server",
proxy.PluginSecureSocksProxyAllowInsecure: "true",
},
),
expectedClientOptions: &proxy.Options{
Enabled: true,
DatasourceName: "ds-name",
DatasourceType: "example-datasource",
Auth: &proxy.AuthOptions{
Username: "ds-uid",
Password: "pass",
},
Timeouts: &proxy.TimeoutOptions{
Timeout: time.Second * 10,
KeepAlive: time.Second * 15,
},
ClientCfg: &proxy.ClientCfg{
ClientCert: "/path/to/client-cert",
ClientKey: "/path/to/client-key",
RootCAs: []string{"/path/to/root-ca"},
ClientCertVal: "client-cert-contents",
ClientKeyVal: "client-key-contents",
RootCAsVals: []string{"root-ca-contents"},
ProxyAddress: "localhost:1234",
ServerName: "proxy-server",
AllowInsecure: true,
},
},
},
{
name: "Datasource UID becomes user name when secureSocksProxyUsername is not set",
instanceSettings: &DataSourceInstanceSettings{
Name: "ds-name",
UID: "ds-uid",
Type: "example-datasource",
JSONData: []byte("{ \"enableSecureSocksProxy\": false }"),
},
grafanaCfg: NewGrafanaCfg(
map[string]string{
proxy.PluginSecureSocksProxyEnabled: "true",
proxy.PluginSecureSocksProxyClientCert: "/path/to/client-cert",
proxy.PluginSecureSocksProxyClientCertContents: "client-cert-contents",
proxy.PluginSecureSocksProxyClientKey: "/path/to/client-key",
proxy.PluginSecureSocksProxyClientKeyContents: "client-key-contents",
proxy.PluginSecureSocksProxyRootCAs: "/path/to/root-ca",
proxy.PluginSecureSocksProxyRootCAsContents: "root-ca-contents",
proxy.PluginSecureSocksProxyProxyAddress: "localhost:1234",
proxy.PluginSecureSocksProxyServerName: "proxy-server",
proxy.PluginSecureSocksProxyAllowInsecure: "true",
},
),
expectedClientOptions: nil,
},
{
name: "Proxy options client configuration is not set when proxy.PluginSecureSocksProxyEnabled is false",
instanceSettings: &DataSourceInstanceSettings{
Name: "ds-name",
UID: "ds-uid",
Type: "example-datasource",
JSONData: []byte("{ \"enableSecureSocksProxy\": true }"),
},
grafanaCfg: NewGrafanaCfg(
map[string]string{
proxy.PluginSecureSocksProxyEnabled: "false",
},
),
expectedClientOptions: &proxy.Options{
Enabled: true,
DatasourceName: "ds-name",
DatasourceType: "example-datasource",
Auth: &proxy.AuthOptions{
Username: "ds-uid",
},
Timeouts: &proxy.TimeoutOptions{
Timeout: time.Second * 30,
KeepAlive: time.Second * 30,
},
ClientCfg: nil,
},
},
}

for _, tc := range tcs {
ctx := WithGrafanaConfig(context.Background(), tc.grafanaCfg)
opts, err := tc.instanceSettings.ProxyOptionsFromContext(ctx)
if tc.err != nil {
require.ErrorIs(t, err, tc.err)
continue
}
require.NoError(t, err)
require.Equal(t, tc.expectedClientOptions, opts)
}
}
44 changes: 42 additions & 2 deletions backend/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"os"
"strconv"
"strings"

Expand All @@ -20,6 +21,8 @@ const (
SQLMaxOpenConnsDefault = "GF_SQL_MAX_OPEN_CONNS_DEFAULT"
SQLMaxIdleConnsDefault = "GF_SQL_MAX_IDLE_CONNS_DEFAULT"
SQLMaxConnLifetimeSecondsDefault = "GF_SQL_MAX_CONN_LIFETIME_SECONDS_DEFAULT"
ResponseLimit = "GF_RESPONSE_LIMIT"
AppClientSecret = "GF_PLUGIN_APP_CLIENT_SECRET" // nolint:gosec
)

type configKey struct{}
Expand Down Expand Up @@ -122,11 +125,19 @@ func (c *GrafanaCfg) proxy() (Proxy, error) {
}
}

var rootCaVals []string
if v = c.Get(proxy.PluginSecureSocksProxyRootCAsContents); v != "" {
rootCaVals = strings.Split(c.Get(proxy.PluginSecureSocksProxyRootCAsContents), ",")
}

return Proxy{
clientCfg: &proxy.ClientCfg{
ClientCert: c.Get(proxy.PluginSecureSocksProxyClientCert),
ClientCertVal: c.Get(proxy.PluginSecureSocksProxyClientCertContents),
ClientKey: c.Get(proxy.PluginSecureSocksProxyClientKey),
RootCA: c.Get(proxy.PluginSecureSocksProxyRootCACert),
ClientKeyVal: c.Get(proxy.PluginSecureSocksProxyClientKeyContents),
RootCAs: strings.Split(c.Get(proxy.PluginSecureSocksProxyRootCAs), " "),
RootCAsVals: rootCaVals,
ProxyAddress: c.Get(proxy.PluginSecureSocksProxyProxyAddress),
ServerName: c.Get(proxy.PluginSecureSocksProxyServerName),
AllowInsecure: allowInsecure,
Expand All @@ -140,7 +151,11 @@ func (c *GrafanaCfg) proxy() (Proxy, error) {
func (c *GrafanaCfg) AppURL() (string, error) {
url, ok := c.config[AppURL]
if !ok {
return "", fmt.Errorf("app URL not set in config. A more recent version of Grafana may be required")
// Fallback to environment variable for backwards compatibility
url = os.Getenv(AppURL)
if url == "" {
return "", errors.New("app URL not set in config. A more recent version of Grafana may be required")
}
}
return url, nil
}
Expand Down Expand Up @@ -225,6 +240,31 @@ func (c *GrafanaCfg) UserFacingDefaultError() (string, error) {
return value, nil
}

func (c *GrafanaCfg) ResponseLimit() int64 {
count, ok := c.config[ResponseLimit]
if !ok {
return 0
}
i, err := strconv.ParseInt(count, 10, 64)
if err != nil {
return 0
}
return i
}

func (c *GrafanaCfg) PluginAppClientSecret() (string, error) {
value, ok := c.config[AppClientSecret]
if !ok {
// Fallback to environment variable for backwards compatibility
value = os.Getenv(AppClientSecret)
if value == "" {
return "", errors.New("PluginAppClientSecret not set in config")
}
}

return value, nil
}

type userAgentKey struct{}

// UserAgentFromContext returns user agent from context.
Expand Down
Loading

0 comments on commit 9dda07d

Please sign in to comment.