diff --git a/conf/defaults.ini b/conf/defaults.ini index 67388c213cf0..ff0901874f7f 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -66,6 +66,9 @@ cert_key = # Unix socket path socket = /tmp/grafana.sock +# CDN Url +cdn_url = + #################################### Database ############################ [database] # You can configure the database connection by specifying type, host, name, user and password diff --git a/conf/sample.ini b/conf/sample.ini index aa3d66df1ff4..b0a182d03c67 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -67,6 +67,9 @@ # Unix socket path ;socket = +# CDN Url +;cdn_url = + #################################### Database #################################### [database] # You can configure the database connection by specifying type, host, name, user and password diff --git a/docs/sources/administration/configuration.md b/docs/sources/administration/configuration.md index dd52fa763ee5..5d05b4890841 100644 --- a/docs/sources/administration/configuration.md +++ b/docs/sources/administration/configuration.md @@ -259,6 +259,15 @@ Path to the certificate key file (if `protocol` is set to `https` or `h2`). Path where the socket should be created when `protocol=socket`. Make sure that Grafana has appropriate permissions before you change this setting. +### cdn_url + +> **Note**: Available in Grafana v7.4 and later versions. + +Specify a full HTTP URL address to the root of your Grafana CDN assets. Grafana will add edition and version paths. + +For example, given a cdn url like `https://cdn.myserver.com` grafana will try to load a javascript file from +`http://cdn.myserver.com/grafana-oss/v7.4.0/public/build/app..js`. +
## [database] diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index ff96f1813189..00ddf796241d 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -25,6 +25,7 @@ type IndexViewData struct { AppleTouchIcon template.URL AppTitle string Sentry *setting.Sentry + ContentDeliveryURL string // Nonce is a cryptographic identifier for use with Content Security Policy. Nonce string } diff --git a/pkg/api/index.go b/pkg/api/index.go index 7c72e389eb49..f017b3b027f9 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -428,6 +428,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat NavTree: navTree, Sentry: &hs.Cfg.Sentry, Nonce: c.RequestNonce, + ContentDeliveryURL: hs.Cfg.GetContentDeliveryURL((hs.License.Edition())), } if setting.DisableGravatar { diff --git a/pkg/models/licensing.go b/pkg/models/licensing.go index c4de1f15f1e8..90b3f78f7789 100644 --- a/pkg/models/licensing.go +++ b/pkg/models/licensing.go @@ -13,6 +13,9 @@ type Licensing interface { // Return edition Edition() string + // Used to build content delivery URL + ContentDeliveryPrefix() string + LicenseURL(user *SignedInUser) string StateInfo() string diff --git a/pkg/plugins/backendplugin/manager_test.go b/pkg/plugins/backendplugin/manager_test.go index 8dbcadad3690..eae2e01a8314 100644 --- a/pkg/plugins/backendplugin/manager_test.go +++ b/pkg/plugins/backendplugin/manager_test.go @@ -403,6 +403,10 @@ func (t *testLicensingService) StateInfo() string { return "" } +func (t *testLicensingService) ContentDeliveryPrefix() string { + return "" +} + func (t *testLicensingService) LicenseURL(user *models.SignedInUser) string { return "" } diff --git a/pkg/services/licensing/oss.go b/pkg/services/licensing/oss.go index aacdc989d944..174ddf56c96a 100644 --- a/pkg/services/licensing/oss.go +++ b/pkg/services/licensing/oss.go @@ -32,6 +32,10 @@ func (*OSSLicensingService) StateInfo() string { return "" } +func (*OSSLicensingService) ContentDeliveryPrefix() string { + return "grafana-oss" +} + func (l *OSSLicensingService) LicenseURL(user *models.SignedInUser) string { if user.IsGrafanaAdmin { return l.Cfg.AppSubURL + "/admin/upgrading" diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 6b882dbc7ac5..b17699d519e1 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -197,6 +197,7 @@ type Cfg struct { SocketPath string RouterLogging bool Domain string + CDNRootURL *url.URL // build BuildVersion string @@ -767,7 +768,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { provisioning := valueAsString(iniFile.Section("paths"), "provisioning", "") cfg.ProvisioningPath = makeAbsolute(provisioning, HomePath) - if err := readServerSettings(iniFile, cfg); err != nil { + if err := cfg.readServerSettings(iniFile); err != nil { return err } @@ -1251,7 +1252,7 @@ func readSnapshotsSettings(cfg *Cfg, iniFile *ini.File) error { return nil } -func readServerSettings(iniFile *ini.File, cfg *Cfg) error { +func (cfg *Cfg) readServerSettings(iniFile *ini.File) error { server := iniFile.Section("server") var err error AppUrl, AppSubUrl, err = parseAppUrlAndSubUrl(server) @@ -1263,8 +1264,8 @@ func readServerSettings(iniFile *ini.File, cfg *Cfg) error { cfg.AppURL = AppUrl cfg.AppSubURL = AppSubUrl cfg.ServeFromSubPath = ServeFromSubPath - cfg.Protocol = HTTPScheme + protocolStr := valueAsString(server, "protocol", "http") if protocolStr == "https" { @@ -1297,9 +1298,34 @@ func readServerSettings(iniFile *ini.File, cfg *Cfg) error { return err } + cdnURL := valueAsString(server, "cdn_url", "") + if cdnURL != "" { + cfg.CDNRootURL, err = url.Parse(cdnURL) + if err != nil { + return err + } + } + return nil } +// GetContentDeliveryURL returns full content delivery URL with // added to URL +func (cfg *Cfg) GetContentDeliveryURL(prefix string) string { + if cfg.CDNRootURL != nil { + url := *cfg.CDNRootURL + preReleaseFolder := "" + + if strings.Contains(cfg.BuildVersion, "pre") || strings.Contains(cfg.BuildVersion, "alpha") { + preReleaseFolder = "pre-releases" + } + + url.Path = path.Join(url.Path, prefix, preReleaseFolder, cfg.BuildVersion) + return url.String() + } + + return "" +} + func (cfg *Cfg) readDataSourcesSettings() { datasources := cfg.Raw.Section("datasources") cfg.DataSourceLimit = datasources.Key("datasource_limit").MustInt(5000) diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index ece5bb839789..762bfafa601d 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -2,6 +2,7 @@ package setting import ( "bufio" + "net/url" "os" "path" "path/filepath" @@ -389,3 +390,35 @@ func TestAuthDurationSettings(t *testing.T) { require.NoError(t, err) require.Equal(t, maxLifetimeDurationTest, cfg.LoginMaxLifetime) } + +func TestGetCDNPath(t *testing.T) { + var err error + cfg := NewCfg() + cfg.BuildVersion = "v7.5.0-11124" + cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com") + require.NoError(t, err) + + require.Equal(t, "http://cdn.grafana.com/grafana-oss/v7.5.0-11124", cfg.GetContentDeliveryURL("grafana-oss")) + require.Equal(t, "http://cdn.grafana.com/grafana/v7.5.0-11124", cfg.GetContentDeliveryURL("grafana")) +} + +func TestGetCDNPathWithPreReleaseVersionAndSubPath(t *testing.T) { + var err error + cfg := NewCfg() + cfg.BuildVersion = "v7.5.0-11124pre" + cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com/sub") + require.NoError(t, err) + require.Equal(t, "http://cdn.grafana.com/sub/grafana-oss/pre-releases/v7.5.0-11124pre", cfg.GetContentDeliveryURL("grafana-oss")) + require.Equal(t, "http://cdn.grafana.com/sub/grafana/pre-releases/v7.5.0-11124pre", cfg.GetContentDeliveryURL("grafana")) +} + +// Adding a case for this in case we switch to proper semver version strings +func TestGetCDNPathWithAlphaVersion(t *testing.T) { + var err error + cfg := NewCfg() + cfg.BuildVersion = "v7.5.0-alpha.11124" + cfg.CDNRootURL, err = url.Parse("http://cdn.grafana.com") + require.NoError(t, err) + require.Equal(t, "http://cdn.grafana.com/grafana-oss/pre-releases/v7.5.0-alpha.11124", cfg.GetContentDeliveryURL("grafana-oss")) + require.Equal(t, "http://cdn.grafana.com/grafana/pre-releases/v7.5.0-alpha.11124", cfg.GetContentDeliveryURL("grafana")) +} diff --git a/public/app/index.ts b/public/app/index.ts index c55f28504641..852a47358561 100644 --- a/public/app/index.ts +++ b/public/app/index.ts @@ -1,4 +1,12 @@ -import app from './app'; +declare let __webpack_public_path__: string; + +/** + * Check if we are hosting files on cdn and set webpack public path + */ +if ((window as any).public_cdn_path) { + __webpack_public_path__ = (window as any).public_cdn_path; +} +import app from './app'; app.initEchoSrv(); app.init(); diff --git a/public/views/index-template.html b/public/views/index-template.html index 40ae00a62aee..546bedfa0260 100644 --- a/public/views/index-template.html +++ b/public/views/index-template.html @@ -3,16 +3,15 @@ @@ -25,16 +24,15 @@ - - - + + [[if .GoogleTagManagerId]] - - - - - - [[end]] + + + + + + [[end]] - <% - for (key in htmlWebpackPlugin.files.chunks) { %><% - if (htmlWebpackPlugin.files.jsIntegrity) { %> - <% - } else { %> - <% - } %><% - } %> + <% for (key in htmlWebpackPlugin.files.chunks) { %> + <% if (htmlWebpackPlugin.files.jsIntegrity) { %> + + <% } else { %> + + <% } %> + <% } %>