diff --git a/controllers/frontend_controller.go b/controllers/frontend_controller.go index 34b8af62..5fcff58d 100644 --- a/controllers/frontend_controller.go +++ b/controllers/frontend_controller.go @@ -145,7 +145,17 @@ func (r *FrontendReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c cache := resCache.NewObjectCache(ctx, r.Client, cacheConfig) - err = runReconciliation(ctx, r.Client, &frontend, fe, &cache) + reconciliation := FrontendReconciliation{ + Log: log, + Recorder: r.Recorder, + Cache: cache, + FRE: r, + FrontendEnvironment: fe, + Ctx: ctx, + Frontend: &frontend, + } + + err = reconciliation.run() if err != nil { SetFrontendConditions(ctx, r.Client, &frontend, crd.ReconciliationFailed, err) diff --git a/controllers/frontend_controller_suite_test.go b/controllers/frontend_controller_suite_test.go index f9a465be..74a8899a 100644 --- a/controllers/frontend_controller_suite_test.go +++ b/controllers/frontend_controller_suite_test.go @@ -37,7 +37,10 @@ var _ = Describe("Frontend controller with image", func() { ctx := context.Background() var customConfig apiextensions.JSON - customConfig.UnmarshalJSON([]byte(`{"apple": "pie"}`)) + customConfig.UnmarshalJSON([]byte(`{"apple":"pie"}`)) + + var customConfig2 apiextensions.JSON + customConfig2.UnmarshalJSON([]byte(`{"cheese":"pasty"}`)) frontend := &crd.Frontend{ TypeMeta: metav1.TypeMeta{ @@ -114,7 +117,7 @@ var _ = Describe("Frontend controller with image", func() { }}, }}, }, - CustomConfig: &customConfig, + CustomConfig: &customConfig2, }, } Expect(k8sClient.Create(ctx, frontend2)).Should(Succeed()) @@ -197,7 +200,7 @@ var _ = Describe("Frontend controller with image", func() { }, timeout, interval).Should(BeTrue()) Expect(createdConfigMap.Name).Should(Equal(FrontendEnvName)) Expect(createdConfigMap.Data).Should(Equal(map[string]string{ - "fed-modules.json": "{\"testFrontend\":{\"manifestLocation\":\"/apps/inventory/fed-mods.json\",\"modules\":[{\"id\":\"test\",\"module\":\"./RootApp\",\"routes\":[{\"pathname\":\"/test/href\"}]}],\"config\":{\"apple\":\"pie\"}},\"testFrontend2\":{\"manifestLocation\":\"/apps/inventory/fed-mods.json\",\"modules\":[{\"id\":\"test\",\"module\":\"./RootApp\",\"routes\":[{\"pathname\":\"/test/href\"}]}],\"config\":{\"apple\":\"pie\"}}}", + "fed-modules.json": "{\"testFrontend\":{\"manifestLocation\":\"/apps/inventory/fed-mods.json\",\"modules\":[{\"id\":\"test\",\"module\":\"./RootApp\",\"routes\":[{\"pathname\":\"/test/href\"}]}],\"config\":{\"apple\":\"pie\"}},\"testFrontend2\":{\"manifestLocation\":\"/apps/inventory/fed-mods.json\",\"modules\":[{\"id\":\"test\",\"module\":\"./RootApp\",\"routes\":[{\"pathname\":\"/test/href\"}]}],\"config\":{\"cheese\":\"pasty\"}}}", "test-env.json": "{\"id\":\"test-bundle\",\"title\":\"\",\"navItems\":[{\"title\":\"Test\",\"href\":\"/test/href\"},{\"title\":\"Test\",\"href\":\"/test/href\"}]}", })) Expect(createdConfigMap.ObjectMeta.OwnerReferences[0].Name).Should(Equal(FrontendEnvName)) diff --git a/controllers/reconcile.go b/controllers/reconcile.go index fd16eeaa..fa6ef52b 100644 --- a/controllers/reconcile.go +++ b/controllers/reconcile.go @@ -12,6 +12,7 @@ import ( localUtil "github.com/RedHatInsights/frontend-operator/controllers/utils" resCache "github.com/RedHatInsights/rhc-osdk-utils/resource_cache" "github.com/RedHatInsights/rhc-osdk-utils/utils" + "github.com/go-logr/logr" apps "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" @@ -19,31 +20,42 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" ) -func runReconciliation(context context.Context, pClient client.Client, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, cache *resCache.ObjectCache) error { - hash, err := createConfigConfigMap(context, pClient, frontend, frontendEnvironment, cache) +type FrontendReconciliation struct { + Log logr.Logger + Recorder record.EventRecorder + Cache resCache.ObjectCache + FRE *FrontendReconciler + FrontendEnvironment *crd.FrontendEnvironment + Frontend *crd.Frontend + Ctx context.Context +} + +func (r *FrontendReconciliation) run() error { + hash, err := r.setupConfigMap() if err != nil { return err } - ssoHash, err := createSSOConfigMap(context, pClient, frontend, frontendEnvironment, cache) + ssoHash, err := r.createSSOConfigMap() if err != nil { return err } - if frontend.Spec.Image != "" { - if err := createFrontendDeployment(context, pClient, frontend, frontendEnvironment, hash, ssoHash, cache); err != nil { + if r.Frontend.Spec.Image != "" { + if err := r.createFrontendDeployment(hash, ssoHash); err != nil { return err } - if err := createFrontendService(frontend, cache); err != nil { + if err := r.createFrontendService(); err != nil { return err } } - if err := createFrontendIngress(frontend, frontendEnvironment, cache); err != nil { + if err := r.createFrontendIngress(); err != nil { return err } @@ -85,7 +97,6 @@ func populateContainer(d *apps.Deployment, frontend *crd.Frontend, frontendEnvir Value: frontendEnvironment.Spec.SSO, }}, }} - } func populateVolumes(d *apps.Deployment, frontend *crd.Frontend) { @@ -113,30 +124,30 @@ func populateVolumes(d *apps.Deployment, frontend *crd.Frontend) { } } -func createFrontendDeployment(context context.Context, pClient client.Client, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, hash string, ssoHash string, cache *resCache.ObjectCache) error { +func (r *FrontendReconciliation) createFrontendDeployment(hash, ssoHash string) error { // Create new empty struct d := &apps.Deployment{} // Define name of resource nn := types.NamespacedName{ - Name: frontend.Name, - Namespace: frontend.Namespace, + Name: r.Frontend.Name, + Namespace: r.Frontend.Namespace, } // Create object in cache (will populate cache if exists) - if err := cache.Create(CoreDeployment, nn, d); err != nil { + if err := r.Cache.Create(CoreDeployment, nn, d); err != nil { return err } // Label with the right labels - labels := frontend.GetLabels() + labels := r.Frontend.GetLabels() - labeler := utils.GetCustomLabeler(labels, nn, frontend) + labeler := utils.GetCustomLabeler(labels, nn, r.Frontend) labeler(d) - populateContainer(d, frontend, frontendEnvironment) - populateVolumes(d, frontend) + populateContainer(d, r.Frontend, r.FrontendEnvironment) + populateVolumes(d, r.Frontend) d.Spec.Template.ObjectMeta.Labels = labels @@ -153,39 +164,16 @@ func createFrontendDeployment(context context.Context, pClient client.Client, fr d.Spec.Template.SetAnnotations(annotations) // Inform the cache that our updates are complete - if err := cache.Update(CoreDeployment, d); err != nil { + if err := r.Cache.Update(CoreDeployment, d); err != nil { return err } return nil } -//Will need to create a service resource ident in provider like CoreDeployment -func createFrontendService(frontend *crd.Frontend, cache *resCache.ObjectCache) error { - // Create empty service - s := &v1.Service{} - - // Define name of resource - nn := types.NamespacedName{ - Name: frontend.Name, - Namespace: frontend.Namespace, - } - - // Create object in cache (will populate cache if exists) - if err := cache.Create(CoreService, nn, s); err != nil { - return err - } - +func createPorts() []v1.ServicePort { appProtocol := "http" - - labels := make(map[string]string) - labels["frontend"] = frontend.Name - labeler := utils.GetCustomLabeler(labels, nn, frontend) - labeler(s) - // We should also set owner reference to the pod - s.SetOwnerReferences([]metav1.OwnerReference{frontend.MakeOwnerReference()}) - - ports := []v1.ServicePort{ + return []v1.ServicePort{ { Name: "public", Port: 8000, @@ -201,166 +189,148 @@ func createFrontendService(frontend *crd.Frontend, cache *resCache.ObjectCache) AppProtocol: &appProtocol, }, } +} + +//Will need to create a service resource ident in provider like CoreDeployment +func (r *FrontendReconciliation) createFrontendService() error { + // Create empty service + s := &v1.Service{} + + // Define name of resource + nn := types.NamespacedName{ + Name: r.Frontend.Name, + Namespace: r.Frontend.Namespace, + } + + // Create object in cache (will populate cache if exists) + if err := r.Cache.Create(CoreService, nn, s); err != nil { + return err + } + + labels := make(map[string]string) + labels["frontend"] = r.Frontend.Name + labeler := utils.GetCustomLabeler(labels, nn, r.Frontend) + labeler(s) + // We should also set owner reference to the pod + s.SetOwnerReferences([]metav1.OwnerReference{r.Frontend.MakeOwnerReference()}) + + ports := createPorts() s.Spec.Selector = labels - utils.MakeService(s, nn, labels, ports, frontend, false) + utils.MakeService(s, nn, labels, ports, r.Frontend, false) // Inform the cache that our updates are complete - if err := cache.Update(CoreService, s); err != nil { + if err := r.Cache.Update(CoreService, s); err != nil { return err } return nil } -func createFrontendIngress(frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, cache *resCache.ObjectCache) error { +func (r *FrontendReconciliation) createFrontendIngress() error { netobj := &networking.Ingress{} nn := types.NamespacedName{ - Name: frontend.Name, - Namespace: frontend.Namespace, + Name: r.Frontend.Name, + Namespace: r.Frontend.Namespace, } - if err := cache.Create(WebIngress, nn, netobj); err != nil { + if err := r.Cache.Create(WebIngress, nn, netobj); err != nil { return err } - labels := frontend.GetLabels() - labler := utils.GetCustomLabeler(labels, nn, frontend) + labels := r.Frontend.GetLabels() + labler := utils.GetCustomLabeler(labels, nn, r.Frontend) labler(netobj) - netobj.SetOwnerReferences([]metav1.OwnerReference{frontend.MakeOwnerReference()}) + netobj.SetOwnerReferences([]metav1.OwnerReference{r.Frontend.MakeOwnerReference()}) + + r.createAnnotationsAndPopulate(nn, netobj) + + if err := r.Cache.Update(WebIngress, netobj); err != nil { + return err + } + + return nil +} - ingressClass := frontendEnvironment.Spec.IngressClass +func (r *FrontendReconciliation) createAnnotationsAndPopulate(nn types.NamespacedName, netobj *networking.Ingress) { + ingressClass := r.FrontendEnvironment.Spec.IngressClass if ingressClass == "" { ingressClass = "nginx" } - if len(frontendEnvironment.Spec.Whitelist) != 0 { + if len(r.FrontendEnvironment.Spec.Whitelist) != 0 { annotations := netobj.GetAnnotations() if annotations == nil { annotations = map[string]string{} } - annotations["haproxy.router.openshift.io/ip_whitelist"] = strings.Join(frontendEnvironment.Spec.Whitelist, " ") - annotations["nginx.ingress.kubernetes.io/whitelist-source-range"] = strings.Join(frontendEnvironment.Spec.Whitelist, ",") + annotations["haproxy.router.openshift.io/ip_whitelist"] = strings.Join(r.FrontendEnvironment.Spec.Whitelist, " ") + annotations["nginx.ingress.kubernetes.io/whitelist-source-range"] = strings.Join(r.FrontendEnvironment.Spec.Whitelist, ",") netobj.SetAnnotations(annotations) } - if frontend.Spec.Image != "" { - populateConesoleDotIngress(nn, frontend, frontendEnvironment, netobj, ingressClass) + if r.Frontend.Spec.Image != "" { + r.populateConsoleDotIngress(nn, netobj, ingressClass, nn.Name) } else { - populateHACIngress(nn, frontend, frontendEnvironment, netobj, ingressClass) + r.populateConsoleDotIngress(nn, netobj, ingressClass, r.Frontend.Spec.Service) } - - if err := cache.Update(WebIngress, netobj); err != nil { - return err - } - - return nil } -func populateConesoleDotIngress(nn types.NamespacedName, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, netobj *networking.Ingress, ingressClass string) { - frontendPath := frontend.Spec.Frontend.Paths - defaultPath := fmt.Sprintf("/apps/%s", frontend.Name) - defaultBetaPath := fmt.Sprintf("/beta/apps/%s", frontend.Name) - if frontend.Spec.AssetsPrefix != "" { - defaultPath = fmt.Sprintf("/%s/%s", frontend.Spec.AssetsPrefix, frontend.Name) - defaultBetaPath = fmt.Sprintf("/beta/%s/%s", frontend.Spec.AssetsPrefix, frontend.Name) +func (r *FrontendReconciliation) getFrontendPaths() []string { + frontendPaths := r.Frontend.Spec.Frontend.Paths + defaultPath := fmt.Sprintf("/apps/%s", r.Frontend.Name) + defaultBetaPath := fmt.Sprintf("/beta/apps/%s", r.Frontend.Name) + if r.Frontend.Spec.AssetsPrefix != "" { + defaultPath = fmt.Sprintf("/%s/%s", r.Frontend.Spec.AssetsPrefix, r.Frontend.Name) + defaultBetaPath = fmt.Sprintf("/beta/%s/%s", r.Frontend.Spec.AssetsPrefix, r.Frontend.Name) } - if !frontend.Spec.Frontend.HasPath(defaultPath) { - frontendPath = append(frontendPath, defaultPath) + if !r.Frontend.Spec.Frontend.HasPath(defaultPath) { + frontendPaths = append(frontendPaths, defaultPath) } - if !frontend.Spec.Frontend.HasPath(defaultBetaPath) { - frontendPath = append(frontendPath, defaultBetaPath) + if !r.Frontend.Spec.Frontend.HasPath(defaultBetaPath) { + frontendPaths = append(frontendPaths, defaultBetaPath) } + return frontendPaths +} - prefixType := "Prefix" +func (r *FrontendReconciliation) populateConsoleDotIngress(nn types.NamespacedName, netobj *networking.Ingress, ingressClass, serviceName string) { + frontendPaths := r.getFrontendPaths() var ingressPaths []networking.HTTPIngressPath - for _, a := range frontendPath { - newPath := networking.HTTPIngressPath{ - Path: a, - PathType: (*networking.PathType)(&prefixType), - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: nn.Name, - Port: networking.ServiceBackendPort{ - Number: 8000, - }, - }, - }, - } + for _, a := range frontendPaths { + newPath := createNewIngressPath(a, serviceName) ingressPaths = append(ingressPaths, newPath) } - host := frontendEnvironment.Spec.Hostname + host := r.FrontendEnvironment.Spec.Hostname if host == "" { - host = frontend.Spec.EnvName + host = r.Frontend.Spec.EnvName } // we need to add /api fallback here as well - netobj.Spec = networking.IngressSpec{ - TLS: []networking.IngressTLS{{ - Hosts: []string{}, - }}, - IngressClassName: &ingressClass, - Rules: []networking.IngressRule{ - { - Host: host, - IngressRuleValue: networking.IngressRuleValue{ - HTTP: &networking.HTTPIngressRuleValue{ - Paths: ingressPaths, - }, - }, - }, - }, - } + netobj.Spec = defaultNetSpec(ingressClass, host, ingressPaths) } -func populateHACIngress(nn types.NamespacedName, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, netobj *networking.Ingress, ingressClass string) { - frontendPath := frontend.Spec.Frontend.Paths - defaultPath := fmt.Sprintf("/apps/%s", frontend.Name) - defaultBetaPath := fmt.Sprintf("/beta/apps/%s", frontend.Name) - if frontend.Spec.AssetsPrefix != "" { - defaultPath = fmt.Sprintf("/%s/%s", frontend.Spec.AssetsPrefix, frontend.Name) - defaultBetaPath = fmt.Sprintf("/beta/%s/%s", frontend.Spec.AssetsPrefix, frontend.Name) - } - - if !frontend.Spec.Frontend.HasPath(defaultPath) { - frontendPath = append(frontendPath, defaultPath) - } - - if !frontend.Spec.Frontend.HasPath(defaultBetaPath) { - frontendPath = append(frontendPath, defaultBetaPath) - } - +func createNewIngressPath(a, serviceName string) networking.HTTPIngressPath { prefixType := "Prefix" - - var ingressPaths []networking.HTTPIngressPath - for _, a := range frontendPath { - newPath := networking.HTTPIngressPath{ - Path: a, - PathType: (*networking.PathType)(&prefixType), - Backend: networking.IngressBackend{ - Service: &networking.IngressServiceBackend{ - Name: frontend.Spec.Service, - Port: networking.ServiceBackendPort{ - Number: 8000, - }, + return networking.HTTPIngressPath{ + Path: a, + PathType: (*networking.PathType)(&prefixType), + Backend: networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: serviceName, + Port: networking.ServiceBackendPort{ + Number: 8000, }, }, - } - ingressPaths = append(ingressPaths, newPath) - } - - host := frontendEnvironment.Spec.Hostname - if host == "" { - host = frontend.Spec.EnvName + }, } +} - // we need to add /api fallback here as well - netobj.Spec = networking.IngressSpec{ +func defaultNetSpec(ingressClass, host string, ingressPaths []networking.HTTPIngressPath) networking.IngressSpec { + return networking.IngressSpec{ TLS: []networking.IngressTLS{{ Hosts: []string{}, }}, @@ -378,44 +348,78 @@ func populateHACIngress(nn types.NamespacedName, frontend *crd.Frontend, fronten } } -func createConfigConfigMap(ctx context.Context, pClient client.Client, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, cache *resCache.ObjectCache) (string, error) { - // Will need to interact directly with the client here, and not the cache because - // we need to read ALL the Frontend CRDs in the Env that we care about - - frontendList := &crd.FrontendList{} +func setupCustomNav(bundle *crd.Bundle, cfgMap *v1.ConfigMap) error { + newBundleObject := bundle.Spec.CustomNav - if err := pClient.List(ctx, frontendList, client.MatchingFields{"spec.envName": frontend.Spec.EnvName}); err != nil { - return "", err + jsonData, err := json.Marshal(newBundleObject) + if err != nil { + return err } - cacheMap := make(map[string]crd.Frontend) - for _, frontend := range frontendList.Items { - cacheMap[frontend.Name] = frontend - } + cfgMap.Data[fmt.Sprintf("%s.json", bundle.Name)] = string(jsonData) + return nil +} - bundleList := &crd.BundleList{} +func setupNormalNav(bundle *crd.Bundle, cacheMap map[string]crd.Frontend, cfgMap *v1.ConfigMap) error { + newBundleObject := crd.ComputedBundle{ + ID: bundle.Spec.ID, + Title: bundle.Spec.Title, + NavItems: []crd.BundleNavItem{}, + } - if err := pClient.List(ctx, bundleList, client.MatchingFields{"spec.envName": frontend.Spec.EnvName}); err != nil { - return "", err + bundleCacheMap := make(map[string]crd.BundleNavItem) + for _, extraItem := range bundle.Spec.ExtraNavItems { + bundleCacheMap[extraItem.Name] = extraItem.NavItem } - cfgMap := &v1.ConfigMap{} + for _, app := range bundle.Spec.AppList { + if retrievedFrontend, ok := cacheMap[app]; ok { + if retrievedFrontend.Spec.NavItems != nil { + for _, navItem := range retrievedFrontend.Spec.NavItems { + newBundleObject.NavItems = append(newBundleObject.NavItems, *navItem) + } + } + } + if bundleNavItem, ok := bundleCacheMap[app]; ok { + newBundleObject.NavItems = append(newBundleObject.NavItems, bundleNavItem) + } + } - nn := types.NamespacedName{ - Name: frontend.Spec.EnvName, - Namespace: frontend.Namespace, + jsonData, err := json.Marshal(newBundleObject) + if err != nil { + return err } - if err := cache.Create(CoreConfig, nn, cfgMap); err != nil { - return "", err + cfgMap.Data[fmt.Sprintf("%s.json", bundle.Name)] = string(jsonData) + return nil +} + +func setupFedModules(frontendList *crd.FrontendList, fedModules map[string]crd.FedModule) { + for _, frontend := range frontendList.Items { + if frontend.Spec.Module != nil { + // module names in fed-modules.json must be camelCase + // K8s does not allow camelCase names, only + // whatever-this-case-is, so we convert. + modName := localUtil.ToCamelCase(frontend.GetName()) + if frontend.Spec.Module.ModuleID != "" { + modName = frontend.Spec.Module.ModuleID + } + fedModules[modName] = *frontend.Spec.Module + if frontend.Spec.CustomConfig != nil { + module := fedModules[modName] + module.Config = frontend.Spec.CustomConfig + fedModules[modName] = module + } + } } +} - labels := frontendEnvironment.GetLabels() - labler := utils.GetCustomLabeler(labels, nn, frontendEnvironment) - labler(cfgMap) - cfgMap.SetOwnerReferences([]metav1.OwnerReference{frontendEnvironment.MakeOwnerReference()}) +func (r *FrontendReconciliation) setupBundleData(cfgMap *v1.ConfigMap, cacheMap map[string]crd.Frontend) error { + bundleList := &crd.BundleList{} - cfgMap.Data = map[string]string{} + if err := r.FRE.Client.List(r.Ctx, bundleList, client.MatchingFields{"spec.envName": r.Frontend.Spec.EnvName}); err != nil { + return err + } keys := []string{} nBundleMap := map[string]crd.Bundle{} @@ -428,120 +432,122 @@ func createConfigConfigMap(ctx context.Context, pClient client.Client, frontend for _, key := range keys { bundle := nBundleMap[key] - var jsonData []byte - var err error if bundle.Spec.CustomNav != nil { - newBundleObject := bundle.Spec.CustomNav - - jsonData, err = json.Marshal(newBundleObject) - if err != nil { - return "", err + if err := setupCustomNav(&bundle, cfgMap); err != nil { + return err } - - cfgMap.Data[fmt.Sprintf("%s.json", bundle.Name)] = string(jsonData) } else { - newBundleObject := crd.ComputedBundle{ - ID: bundle.Spec.ID, - Title: bundle.Spec.Title, - NavItems: []crd.BundleNavItem{}, + if err := setupNormalNav(&bundle, cacheMap, cfgMap); err != nil { + return err } + } + } + return nil +} - bundleCacheMap := make(map[string]crd.BundleNavItem) - for _, extraItem := range bundle.Spec.ExtraNavItems { - bundleCacheMap[extraItem.Name] = extraItem.NavItem - } +func createHash(cfgMap *v1.ConfigMap) (string, error) { + hashData, err := json.Marshal(cfgMap.Data) + if err != nil { + return "", err + } - for _, app := range bundle.Spec.AppList { - if retrievedFrontend, ok := cacheMap[app]; ok { - if retrievedFrontend.Spec.NavItems != nil { - for _, navItem := range retrievedFrontend.Spec.NavItems { - newBundleObject.NavItems = append(newBundleObject.NavItems, *navItem) - } - } - } - if bundleNavItem, ok := bundleCacheMap[app]; ok { - newBundleObject.NavItems = append(newBundleObject.NavItems, bundleNavItem) - } - } + h := sha256.New() + h.Write([]byte(hashData)) + hash := fmt.Sprintf("%x", h.Sum(nil)) + return hash, nil +} - jsonData, err = json.Marshal(newBundleObject) - if err != nil { - return "", err - } +func (r *FrontendReconciliation) setupConfigMap() (string, error) { + // Will need to interact directly with the client here, and not the cache because + // we need to read ALL the Frontend CRDs in the Env that we care about - cfgMap.Data[fmt.Sprintf("%s.json", bundle.Name)] = string(jsonData) - } - } + frontendList := &crd.FrontendList{} - fedModules := make(map[string]crd.FedModule) + if err := r.FRE.Client.List(r.Ctx, frontendList, client.MatchingFields{"spec.envName": r.Frontend.Spec.EnvName}); err != nil { + return "", err + } + cacheMap := make(map[string]crd.Frontend) for _, frontend := range frontendList.Items { - if frontend.Spec.Module != nil { - // module names in fed-modules.json must be camelCase - // K8s does not allow camelCase names, only - // whatever-this-case-is, so we convert. - modName := localUtil.ToCamelCase(frontend.GetName()) - if frontend.Spec.Module.ModuleID != "" { - modName = frontend.Spec.Module.ModuleID - } - fedModules[modName] = *frontend.Spec.Module - if frontend.Spec.CustomConfig != nil { - module := fedModules[modName] - fmt.Printf("--%v\n", frontend.Spec.CustomConfig) - fmt.Printf("--%v\n", fedModules[modName].Config) - fmt.Printf("--%v\n", fedModules[modName]) - module.Config = frontend.Spec.CustomConfig - fedModules[modName] = module - } - } + cacheMap[frontend.Name] = frontend } - jsonData, err := json.Marshal(fedModules) - if err != nil { + cfgMap := &v1.ConfigMap{} + + if err := r.createConfigMap(cfgMap, cacheMap, frontendList); err != nil { return "", err } - cfgMap.Data["fed-modules.json"] = string(jsonData) + return createHash(cfgMap) +} - if err := cache.Update(CoreConfig, cfgMap); err != nil { - return "", err +func (r *FrontendReconciliation) createConfigMap(cfgMap *v1.ConfigMap, cacheMap map[string]crd.Frontend, feList *crd.FrontendList) error { + nn := types.NamespacedName{ + Name: r.Frontend.Spec.EnvName, + Namespace: r.Frontend.Namespace, } - hashData, err := json.Marshal(cfgMap.Data) - if err != nil { - return "", err + if err := r.Cache.Create(CoreConfig, nn, cfgMap); err != nil { + return err } - h := sha256.New() - h.Write([]byte(hashData)) - hash := fmt.Sprintf("%x", h.Sum(nil)) + labels := r.FrontendEnvironment.GetLabels() + labler := utils.GetCustomLabeler(labels, nn, r.FrontendEnvironment) + labler(cfgMap) - return hash, nil + if err := r.populateConfigMap(cfgMap, cacheMap, feList); err != nil { + return err + } + + if err := r.Cache.Update(CoreConfig, cfgMap); err != nil { + return err + } + return nil +} + +func (r *FrontendReconciliation) populateConfigMap(cfgMap *v1.ConfigMap, cacheMap map[string]crd.Frontend, feList *crd.FrontendList) error { + cfgMap.SetOwnerReferences([]metav1.OwnerReference{r.FrontendEnvironment.MakeOwnerReference()}) + cfgMap.Data = map[string]string{} + + if err := r.setupBundleData(cfgMap, cacheMap); err != nil { + return err + } + + fedModules := make(map[string]crd.FedModule) + setupFedModules(feList, fedModules) + + jsonData, err := json.Marshal(fedModules) + if err != nil { + return err + } + + cfgMap.Data["fed-modules.json"] = string(jsonData) + return nil } -func createSSOConfigMap(ctx context.Context, pClient client.Client, frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment, cache *resCache.ObjectCache) (string, error) { +func (r *FrontendReconciliation) createSSOConfigMap() (string, error) { // Will need to interact directly with the client here, and not the cache because // we need to read ALL the Frontend CRDs in the Env that we care about cfgMap := &v1.ConfigMap{} nn := types.NamespacedName{ - Name: fmt.Sprintf("%s-sso", frontend.Spec.EnvName), - Namespace: frontend.Namespace, + Name: fmt.Sprintf("%s-sso", r.Frontend.Spec.EnvName), + Namespace: r.Frontend.Namespace, } - if err := cache.Create(SSOConfig, nn, cfgMap); err != nil { + if err := r.Cache.Create(SSOConfig, nn, cfgMap); err != nil { return "", err } hashString := "" - labels := frontendEnvironment.GetLabels() - labler := utils.GetCustomLabeler(labels, nn, frontend) + labels := r.FrontendEnvironment.GetLabels() + labler := utils.GetCustomLabeler(labels, nn, r.Frontend) labler(cfgMap) - cfgMap.SetOwnerReferences([]metav1.OwnerReference{frontend.MakeOwnerReference()}) + cfgMap.SetOwnerReferences([]metav1.OwnerReference{r.Frontend.MakeOwnerReference()}) - ssoData := fmt.Sprintf(`"use strict";(self.webpackChunkinsights_chrome=self.webpackChunkinsights_chrome||[]).push([[172],{30701:(s,e,h)=>{h.r(e),h.d(e,{default:()=>c});const c="%s"}}]);`, frontendEnvironment.Spec.SSO) + ssoData := fmt.Sprintf(`"use strict";(self.webpackChunkinsights_chrome=self.webpackChunkinsights_chrome||[]).push([[172],{30701:(s,e,h)=>{h.r(e),h.d(e,{default:()=>c});const c="%s"}}]);`, r.FrontendEnvironment.Spec.SSO) cfgMap.Data = map[string]string{ "sso-url.js": ssoData, @@ -555,7 +561,7 @@ func createSSOConfigMap(ctx context.Context, pClient client.Client, frontend *cr h.Write([]byte(hashString)) hash := fmt.Sprintf("%x", h.Sum(nil)) - if err := cache.Update(SSOConfig, cfgMap); err != nil { + if err := r.Cache.Update(SSOConfig, cfgMap); err != nil { return "", err }