Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nav bundles to frontend config #229

Merged
merged 3 commits into from
Dec 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/v1alpha1/frontend_types.go
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ type WidgetEntry struct {
}

type NavigationSegment struct {
SectionID string `json:"sectionId" yaml:"sectionId"`
SegmentID string `json:"segmentId" yaml:"segmentId"`
// Id of the bundle to which the segment should be injected
BundleID string `json:"bundleId" yaml:"bundleId"`
// A position of the segment within the bundle
16 changes: 16 additions & 0 deletions api/v1alpha1/frontendenvironment_types.go
Original file line number Diff line number Diff line change
@@ -24,6 +24,20 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)

// FrontendBundles defines the bundles specific to an environment that will be used to
// construct navigation
type FrontendBundles struct {
ID string `json:"id" yaml:"id"`
Title string `json:"title" yaml:"title"`
}

// The frontend bundles but with the nav items filled with chrome nav items
type FrontendBundlesGenerated struct {
ID string `json:"id" yaml:"id"`
Title string `json:"title" yaml:"title"`
NavItems *[]ChromeNavItem `json:"navItems" yaml:"navItems"`
}

type FrontendServiceCategoryGroup struct {
ID string `json:"id" yaml:"id"`
Title string `json:"title" yaml:"title"`
@@ -105,6 +119,8 @@ type FrontendEnvironmentSpec struct {
HTTPHeaders map[string]string `json:"httpHeaders,omitempty"`

DefaultReplicas *int32 `json:"defaultReplicas,omitempty" yaml:"defaultReplicas,omitempty"`
// For the ChromeUI to render navigation bundles
Bundles *[]FrontendBundles `json:"bundles,omitempty" yaml:"bundles,omitempty"`
}

type MonitoringConfig struct {
50 changes: 50 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions config/crd/bases/cloud.redhat.com_frontendenvironments.yaml
Original file line number Diff line number Diff line change
@@ -67,6 +67,22 @@ spec:
description: The name of the secret we will use to get the akamai
credentials
type: string
bundles:
description: For the ChromeUI to render navigation bundles
items:
description: |-
FrontendBundles defines the bundles specific to an environment that will be used to
construct navigation
properties:
id:
type: string
title:
type: string
required:
- id
- title
type: object
type: array
defaultReplicas:
format: int32
type: integer
4 changes: 2 additions & 2 deletions config/crd/bases/cloud.redhat.com_frontends.yaml
Original file line number Diff line number Diff line change
@@ -376,13 +376,13 @@ spec:
0 is the first position
The position "steps" should be at least 100 to make sure there is enough space in case some segments should be injected between existing ones
type: integer
sectionId:
segmentId:
type: string
required:
- bundleId
- navItems
- position
- sectionId
- segmentId
type: object
type: array
replicas:
65 changes: 65 additions & 0 deletions controllers/reconcile.go
Original file line number Diff line number Diff line change
@@ -988,6 +988,56 @@ func setupServiceTilesData(feList *crd.FrontendList, feEnvironment crd.FrontendE
return categories, skippedTiles
}

func getNavItemPath(feName string, bundleID string, segmentID string) string {
return fmt.Sprintf("%s-%s-%s", feName, bundleID, segmentID)
}

func setupBundlesData(feList *crd.FrontendList, feEnvironment crd.FrontendEnvironment) ([]crd.FrontendBundlesGenerated, []string) {
bundles := []crd.FrontendBundlesGenerated{}
if feEnvironment.Spec.Bundles == nil {
// skip if we do not have bundles in fe environment
return bundles, []string{}
}

skippedNavItemsMap := make(map[string][]string)
bundleNavSegmentMap := make(map[string][]crd.NavigationSegment)
for _, frontend := range feList.Items {
if frontend.Spec.FeoConfigEnabled && frontend.Spec.NavigationSegments != nil {
for _, navSegment := range frontend.Spec.NavigationSegments {
bundleNavSegmentMap[navSegment.BundleID] = append(bundleNavSegmentMap[navSegment.BundleID], *navSegment)
skippedNavItemsMap[navSegment.BundleID] = append(skippedNavItemsMap[navSegment.BundleID], getNavItemPath(frontend.Name, navSegment.BundleID, navSegment.SegmentID))
}
}
}

for _, bundle := range *feEnvironment.Spec.Bundles {
delete(skippedNavItemsMap, bundle.ID)
sort.Slice(bundleNavSegmentMap[bundle.ID], func(i, j int) bool {
if (bundleNavSegmentMap[bundle.ID])[i].Position == (bundleNavSegmentMap[bundle.ID])[j].Position {
return (bundleNavSegmentMap[bundle.ID])[i].SegmentID[0] < (bundleNavSegmentMap[bundle.ID])[j].SegmentID[0]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Hyperkid123 in hindsight - I'm not sure if this is what you meant by falling back to "alphabetical" ordering in case of position number conflict (I'm sorting alphabetically on the SegmentID). Could you take a look here? I did update the kuttl test and it is working.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If needed we can revert my recent commit for the alphabetical ordering and deliver

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this is good enough, I see this just as a fallback to make sure the nav items are "somewhat" in the right place. We won't have many of them as the services will probably very quickly notice the items are not in the right place.

}
return (bundleNavSegmentMap[bundle.ID])[i].Position < (bundleNavSegmentMap[bundle.ID])[j].Position
})
navItems := []crd.ChromeNavItem{}
for _, navSegment := range bundleNavSegmentMap[bundle.ID] {
navItems = append(navItems, *navSegment.NavItems...)
}
newBundle := crd.FrontendBundlesGenerated{
ID: bundle.ID,
Title: bundle.Title,
NavItems: &navItems,
}
bundles = append(bundles, newBundle)
}

skippedNavItems := []string{}
for _, skipped := range skippedNavItemsMap {
skippedNavItems = append(skippedNavItems, skipped...)
}

return bundles, skippedNavItems
}

func (r *FrontendReconciliation) setupBundleData(_ *v1.ConfigMap, _ map[string]crd.Frontend) error {
bundleList := &crd.BundleList{}

@@ -1127,6 +1177,8 @@ func (r *FrontendReconciliation) populateConfigMap(cfgMap *v1.ConfigMap, cacheMa

serviceCategories, skippedTiles := setupServiceTilesData(feList, *r.FrontendEnvironment)

bundles, skippedBundles := setupBundlesData(feList, *r.FrontendEnvironment)

fedModulesJSONData, err := json.Marshal(fedModules)
if err != nil {
return err
@@ -1149,10 +1201,19 @@ func (r *FrontendReconciliation) populateConfigMap(cfgMap *v1.ConfigMap, cacheMa
return err
}

bundlesJSONData, err := json.Marshal(bundles)
if err != nil {
return err
}

if len(skippedTiles) > 0 {
r.Log.Info("Unable to find service categories for tiles:", strings.Join(skippedTiles, ","))
}

if len(skippedBundles) > 0 {
r.Log.Info("Unable to find bundle for nav items:", "skippedBundles", strings.Join(skippedBundles, ","))
}

cfgMap.Data["fed-modules.json"] = string(fedModulesJSONData)
if len(searchIndex) > 0 {
cfgMap.Data["search-index.json"] = string(searchIndexJSONData)
@@ -1166,6 +1227,10 @@ func (r *FrontendReconciliation) populateConfigMap(cfgMap *v1.ConfigMap, cacheMa
cfgMap.Data["service-tiles.json"] = string(serviceCategoriesJSONData)
}

if len(bundles) > 0 {
cfgMap.Data["bundles.json"] = string(bundlesJSONData)
}

return nil
}

21 changes: 19 additions & 2 deletions deploy.yml
Original file line number Diff line number Diff line change
@@ -270,6 +270,23 @@ objects:
description: The name of the secret we will use to get the akamai
credentials
type: string
bundles:
description: For the ChromeUI to render navigation bundles
items:
description: 'FrontendBundles defines the bundles specific to
an environment that will be used to

construct navigation'
properties:
id:
type: string
title:
type: string
required:
- id
- title
type: object
type: array
defaultReplicas:
format: int32
type: integer
@@ -780,13 +797,13 @@ objects:
there is enough space in case some segments should be injected
between existing ones'
type: integer
sectionId:
segmentId:
type: string
required:
- bundleId
- navItems
- position
- sectionId
- segmentId
type: object
type: array
replicas:
7 changes: 7 additions & 0 deletions examples/feenvironment.yaml
Original file line number Diff line number Diff line change
@@ -22,3 +22,10 @@ spec:
groups:
- id: iam
title: IAM
bundles:
- id: rhel
title: Red Hat Enterprise Linux
- id: ansible
title: Ansible
- id: settings
title: Settings
18 changes: 18 additions & 0 deletions examples/landing.yaml
Original file line number Diff line number Diff line change
@@ -77,3 +77,21 @@ spec:
description: Some Iam thing
icon: IAMIcon
isExternal: false
navigationSegments:
- segmentId: inventory-partial
bundleId: insights
position: 100
navItems:
- id: landing
title: Landing section
href: /apps/landing
- id: bar
title: Some new link
expandable: true
routes:
- id: foo
title: Foo
href: /nested/bar
- id: baz
title: Some new link
href: /baz
8 changes: 8 additions & 0 deletions tests/e2e/generate-bundles/00-create-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: test-generate-bundles
spec:
finalizers:
- kubernetes
Loading