Feat: Add cloud plugin cost management to admin section (#76547)

* feat: add cost management to admin and put adaptive metrics and log volume under it

* test: fix applinks test

* chore: fix lint error

* remove "new" from feature toggle description

---------

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
Adam Bannach
2023-10-17 17:15:51 +01:00
committed by GitHub
co-authored by Ashley Harrison
parent 0f7233bdf6
commit de1ed216f4
10 changed files with 75 additions and 3 deletions
@@ -152,6 +152,7 @@ Experimental features might be changed or removed without prior notice.
| `awsDatasourcesNewFormStyling` | Applies new form styling for configuration and query editors in AWS plugins |
| `cachingOptimizeSerializationMemoryUsage` | If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses. |
| `pluginsInstrumentationStatusSource` | Include a status source label for plugin request metrics and logs |
| `costManagementUi` | Toggles the display of the cost management ui plugin |
## Development feature toggles
@@ -146,4 +146,5 @@ export interface FeatureToggles {
cachingOptimizeSerializationMemoryUsage?: boolean;
panelTitleSearchInV1?: boolean;
pluginsInstrumentationStatusSource?: boolean;
costManagementUi?: boolean;
}
+1
View File
@@ -26,4 +26,5 @@ const (
grafanaOperatorExperienceSquad codeowner = "@grafana/grafana-operator-experience-squad"
enterpriseDatasourcesSquad codeowner = "@grafana/enterprise-datasources"
grafanaSharingSquad codeowner = "@grafana/sharing-squad"
grafanaDatabasesFrontend codeowner = "@grafana/databases-frontend"
)
+7
View File
@@ -897,5 +897,12 @@ var (
Stage: FeatureStageExperimental,
Owner: grafanaPluginsPlatformSquad,
},
{
Name: "costManagementUi",
Description: "Toggles the display of the cost management ui plugin",
Stage: FeatureStageExperimental,
FrontendOnly: false,
Owner: grafanaDatabasesFrontend,
},
}
)
+1
View File
@@ -127,3 +127,4 @@ awsDatasourcesNewFormStyling,experimental,@grafana/aws-datasources,false,false,f
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,false,false,false,false
panelTitleSearchInV1,experimental,@grafana/backend-platform,true,false,false,false
pluginsInstrumentationStatusSource,experimental,@grafana/plugins-platform-backend,false,false,false,false
costManagementUi,experimental,@grafana/databases-frontend,false,false,false,false
1 Name Stage Owner requiresDevMode RequiresLicense RequiresRestart FrontendOnly
127 cachingOptimizeSerializationMemoryUsage experimental @grafana/grafana-operator-experience-squad false false false false
128 panelTitleSearchInV1 experimental @grafana/backend-platform true false false false
129 pluginsInstrumentationStatusSource experimental @grafana/plugins-platform-backend false false false false
130 costManagementUi experimental @grafana/databases-frontend false false false false
+4
View File
@@ -518,4 +518,8 @@ const (
// FlagPluginsInstrumentationStatusSource
// Include a status source label for plugin request metrics and logs
FlagPluginsInstrumentationStatusSource = "pluginsInstrumentationStatusSource"
// FlagCostManagementUi
// Toggles the display of the cost management ui plugin
FlagCostManagementUi = "costManagementUi"
)
+37 -1
View File
@@ -90,7 +90,9 @@ func (root *NavTreeRoot) RemoveSection(node *NavLink) {
func (root *NavTreeRoot) FindById(id string) *NavLink {
return FindById(root.Children, id)
}
func (root *NavTreeRoot) FindByURL(url string) *NavLink {
return FindByURL(root.Children, url)
}
func (root *NavTreeRoot) Sort() {
Sort(root.Children)
}
@@ -192,6 +194,26 @@ func (root *NavTreeRoot) ApplyAdminIA(navAdminSubsectionsEnabled bool) {
authenticationNode.IsSection = true
adminNodeLinks = append(adminNodeLinks, authenticationNode)
}
costManagementNode := root.FindById("plugin-page-grafana-costmanagementui-app")
if costManagementNode != nil {
adminNodeLinks = append(adminNodeLinks, costManagementNode)
}
costManagementMetricsNode := root.FindByURL("/a/grafana-costmanagementui-app/metrics")
adaptiveMetricsNode := root.FindById("plugin-page-grafana-adaptive-metrics-app")
if costManagementMetricsNode != nil && adaptiveMetricsNode != nil {
costManagementMetricsNode.Children = append(costManagementMetricsNode.Children, adaptiveMetricsNode)
}
costManagementLogsNode := root.FindByURL("/a/grafana-costmanagementui-app/logs")
logVolumeExplorerNode := root.FindById("plugin-page-grafana-logvolumeexplorer-app")
if costManagementLogsNode != nil && logVolumeExplorerNode != nil {
costManagementLogsNode.Children = append(costManagementLogsNode.Children, logVolumeExplorerNode)
}
} else {
adminNodeLinks = AppendIfNotNil(adminNodeLinks, root.FindById("datasources"))
adminNodeLinks = AppendIfNotNil(adminNodeLinks, root.FindById("plugins"))
@@ -245,3 +267,17 @@ func FindById(nodes []*NavLink, id string) *NavLink {
return nil
}
func FindByURL(nodes []*NavLink, url string) *NavLink {
for _, child := range nodes {
if child.Url == url {
return child
} else if len(child.Children) > 0 {
if found := FindByURL(child.Children, url); found != nil {
return found
}
}
}
return nil
}
+11
View File
@@ -33,4 +33,15 @@ func TestNavTreeRoot(t *testing.T) {
require.Equal(t, "1", treeRoot.Children[0].Id)
require.Equal(t, "4", treeRoot.Children[1].Id)
})
t.Run("FindByURL is able to find a navItem by url", func(t *testing.T) {
treeRoot := NavTreeRoot{
Children: []*NavLink{
{Id: "1", Url: "/"},
{Id: "2", Url: "/org"},
{Id: "3", Url: "/org/users"},
},
}
require.Equal(t, "2", treeRoot.FindByURL("/org").Id)
require.Equal(t, "3", treeRoot.FindByURL("/org/users").Id)
})
}
@@ -262,10 +262,18 @@ func (s *ServiceImpl) readNavigationSettings() {
"grafana-incident-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 2, Text: "Incidents"},
"grafana-ml-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 3, Text: "Machine Learning"},
"grafana-cloud-link-app": {SectionID: navtree.NavIDCfg},
"grafana-costmanagementui-app": {SectionID: navtree.NavIDCfg, Text: "Cost management"},
"grafana-easystart-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightApps + 1, Text: "Connections", Icon: "adjust-circle"},
"k6-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightAlertsAndIncidents + 1, Text: "Performance testing", Icon: "k6"},
}
if s.features.IsEnabled(featuremgmt.FlagNavAdminSubsections) && s.features.IsEnabled(featuremgmt.FlagCostManagementUi) {
// if cost management is enabled we want to nest adaptive metrics and log volume explorer under that plugin
// in the admin section
s.navigationAppConfig["grafana-adaptive-metrics-app"] = NavigationAppConfig{SectionID: navtree.NavIDCfg}
s.navigationAppConfig["grafana-logvolumeexplorer-app"] = NavigationAppConfig{SectionID: navtree.NavIDCfg}
}
s.navigationAppPathConfig = map[string]NavigationAppConfig{
"/a/grafana-auth-app": {SectionID: navtree.NavIDCfg, SortWeight: 7},
}
@@ -360,7 +360,8 @@ func TestAddAppLinks(t *testing.T) {
func TestReadingNavigationSettings(t *testing.T) {
t.Run("Should include defaults", func(t *testing.T) {
service := ServiceImpl{
cfg: setting.NewCfg(),
cfg: setting.NewCfg(),
features: featuremgmt.WithFeatures(),
}
_, _ = service.cfg.Raw.NewSection("navigation.app_sections")
@@ -371,7 +372,8 @@ func TestReadingNavigationSettings(t *testing.T) {
t.Run("Can add additional overrides via ini system", func(t *testing.T) {
service := ServiceImpl{
cfg: setting.NewCfg(),
cfg: setting.NewCfg(),
features: featuremgmt.WithFeatures(),
}
appSections, _ := service.cfg.Raw.NewSection("navigation.app_sections")