diff --git a/api/v1alpha1/frontendenvironment_types.go b/api/v1alpha1/frontendenvironment_types.go index bcfb248f..dfc034dc 100644 --- a/api/v1alpha1/frontendenvironment_types.go +++ b/api/v1alpha1/frontendenvironment_types.go @@ -88,8 +88,11 @@ type FrontendEnvironmentSpec struct { EnableAkamaiCacheBust bool `json:"enableAkamaiCacheBust,omitempty"` // Set Akamai Cache Bust Image AkamaiCacheBustImage string `json:"akamaiCacheBustImage,omitempty"` - // Set Akamai Cache Bust URL that the files will hang off of + // Deprecated: Users should move to AkamaiCacheBustURLs + // Preserving for backwards compatibility AkamaiCacheBustURL string `json:"akamaiCacheBustURL,omitempty"` + // Set Akamai Cache Bust URL that the files will hang off of + AkamaiCacheBustURLs []string `json:"akamaiCacheBustURLs,omitempty"` // The name of the secret we will use to get the akamai credentials AkamaiSecretName string `json:"akamaiSecretName,omitempty"` // List of namespaces that should receive a copy of the frontend configuration as a config map @@ -100,6 +103,8 @@ type FrontendEnvironmentSpec struct { // Custom HTTP Headers // These populate an ENV var that is then added into the caddy config as a header block HTTPHeaders map[string]string `json:"httpHeaders,omitempty"` + + DefaultReplicas *int32 `json:"defaultReplicas,omitempty" yaml:"defaultReplicas,omitempty"` } type MonitoringConfig struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index e5d43c6c..cf9e3ee5 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -480,6 +480,11 @@ func (in *FrontendEnvironmentSpec) DeepCopyInto(out *FrontendEnvironmentSpec) { *out = new(MonitoringConfig) **out = **in } + if in.AkamaiCacheBustURLs != nil { + in, out := &in.AkamaiCacheBustURLs, &out.AkamaiCacheBustURLs + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.TargetNamespaces != nil { in, out := &in.TargetNamespaces, &out.TargetNamespaces *out = make([]string, len(*in)) @@ -503,6 +508,11 @@ func (in *FrontendEnvironmentSpec) DeepCopyInto(out *FrontendEnvironmentSpec) { (*out)[key] = val } } + if in.DefaultReplicas != nil { + in, out := &in.DefaultReplicas, &out.DefaultReplicas + *out = new(int32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FrontendEnvironmentSpec. diff --git a/config/crd/bases/cloud.redhat.com_frontendenvironments.yaml b/config/crd/bases/cloud.redhat.com_frontendenvironments.yaml index ac046100..193019a8 100644 --- a/config/crd/bases/cloud.redhat.com_frontendenvironments.yaml +++ b/config/crd/bases/cloud.redhat.com_frontendenvironments.yaml @@ -53,13 +53,23 @@ spec: description: Set Akamai Cache Bust Image type: string akamaiCacheBustURL: + description: |- + Deprecated: Users should move to AkamaiCacheBustURLs + Preserving for backwards compatibility + type: string + akamaiCacheBustURLs: description: Set Akamai Cache Bust URL that the files will hang off of - type: string + items: + type: string + type: array akamaiSecretName: description: The name of the secret we will use to get the akamai credentials type: string + defaultReplicas: + format: int32 + type: integer enableAkamaiCacheBust: description: Enable Akamai Cache Bust type: boolean diff --git a/controllers/reconcile.go b/controllers/reconcile.go index 757a4491..53be5957 100644 --- a/controllers/reconcile.go +++ b/controllers/reconcile.go @@ -216,30 +216,64 @@ func makeAkamaiEdgercFileFromSecret(secret *v1.Secret) string { } func createCachePurgePathList(frontend *crd.Frontend, frontendEnvironment *crd.FrontendEnvironment) []string { - // Set purgeHost by ensuring the URL begins with https:// and has no trailing / - purgeHost := strings.TrimSuffix(fmt.Sprintf("https://%s", strings.TrimPrefix(frontendEnvironment.Spec.AkamaiCacheBustURL, "https://")), "/") + var purgePaths []string - // Initialize with a default path if AkamaiCacheBustPaths is nil - purgePaths := []string{fmt.Sprintf("%s/apps/%s/fed-mods.json", purgeHost, frontend.Name)} + // Helper function to check if a path is already in the list + contains := func(slice []string, item string) bool { + for _, existing := range slice { + if existing == item { + return true + } + } + return false + } + + cacheBustUrls := frontendEnvironment.Spec.AkamaiCacheBustURLs + + if frontendEnvironment.Spec.AkamaiCacheBustURL != "" { + cacheBustUrls = append(cacheBustUrls, frontendEnvironment.Spec.AkamaiCacheBustURL) + } - if frontend.Spec.AkamaiCacheBustPaths == nil { + // Return early if we have no cache bust URLs of any kind to process + if len(cacheBustUrls) == 0 { return purgePaths } - purgePaths = make([]string, 0, len(frontend.Spec.AkamaiCacheBustPaths)) - for _, path := range frontend.Spec.AkamaiCacheBustPaths { - // Check if path is a full URL (starts with "http://" or "https://") - if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { - // Add full URL path directly - purgePaths = append(purgePaths, path) - } else { - // Ensure each path has a leading slash but no double slashes - if !strings.HasPrefix(path, "/") { - path = "/" + path + for _, cacheBustURL := range cacheBustUrls { + // Ensure the URL begins with https:// and has no trailing / + purgeHost := strings.TrimSuffix(fmt.Sprintf("https://%s", strings.TrimPrefix(cacheBustURL, "https://")), "/") + + // Add default path if AkamaiCacheBustPaths is nil + if frontend.Spec.AkamaiCacheBustPaths == nil { + defaultPath := fmt.Sprintf("%s/apps/%s/fed-mods.json", purgeHost, frontend.Name) + if !contains(purgePaths, defaultPath) { + purgePaths = append(purgePaths, defaultPath) + } + continue + } + + // Append paths based on AkamaiCacheBustPaths + for _, path := range frontend.Spec.AkamaiCacheBustPaths { + var fullPath string + + if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { + // Add full URL path directly + fullPath = path + } else { + // Ensure each path has a leading slash but no double slashes + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + fullPath = purgeHost + path + } + + // Append the fullPath only if it doesn't already exist in purgePaths + if !contains(purgePaths, fullPath) { + purgePaths = append(purgePaths, fullPath) } - purgePaths = append(purgePaths, purgeHost+path) } } + return purgePaths } @@ -439,7 +473,11 @@ func (r *FrontendReconciliation) createFrontendDeployment(annotationHashes []map if r.Frontend.Spec.Replicas != nil { d.Spec.Replicas = r.Frontend.Spec.Replicas } else { - d.Spec.Replicas = utils.Int32Ptr(1) + if r.FrontendEnvironment.Spec.DefaultReplicas != nil { + d.Spec.Replicas = r.FrontendEnvironment.Spec.DefaultReplicas + } else { + d.Spec.Replicas = utils.Int32Ptr(1) + } } populateVolumes(d, r.Frontend, r.FrontendEnvironment) diff --git a/deploy.yml b/deploy.yml index 79fc0561..f0a25363 100644 --- a/deploy.yml +++ b/deploy.yml @@ -256,13 +256,23 @@ objects: description: Set Akamai Cache Bust Image type: string akamaiCacheBustURL: + description: 'Deprecated: Users should move to AkamaiCacheBustURLs + + Preserving for backwards compatibility' + type: string + akamaiCacheBustURLs: description: Set Akamai Cache Bust URL that the files will hang off of - type: string + items: + type: string + type: array akamaiSecretName: description: The name of the secret we will use to get the akamai credentials type: string + defaultReplicas: + format: int32 + type: integer enableAkamaiCacheBust: description: Enable Akamai Cache Bust type: boolean diff --git a/docs/antora/modules/ROOT/pages/api_reference.adoc b/docs/antora/modules/ROOT/pages/api_reference.adoc index 2316ca25..8ec8fd46 100644 --- a/docs/antora/modules/ROOT/pages/api_reference.adoc +++ b/docs/antora/modules/ROOT/pages/api_reference.adoc @@ -458,13 +458,16 @@ parts should be generated for the bundles. We want to do + do this in epehemeral environments but not in production + | | | *`enableAkamaiCacheBust`* __boolean__ | Enable Akamai Cache Bust + | | | *`akamaiCacheBustImage`* __string__ | Set Akamai Cache Bust Image + | | -| *`akamaiCacheBustURL`* __string__ | Set Akamai Cache Bust URL that the files will hang off of + | | +| *`akamaiCacheBustURL`* __string__ | Deprecated: Users should move to AkamaiCacheBustURLs + +Preserving for backwards compatibility + | | +| *`akamaiCacheBustURLs`* __string array__ | Set Akamai Cache Bust URL that the files will hang off of + | | | *`akamaiSecretName`* __string__ | The name of the secret we will use to get the akamai credentials + | | | *`targetNamespaces`* __string array__ | List of namespaces that should receive a copy of the frontend configuration as a config map + By configurations we mean the fed-modules.json, navigation files, etc. + | | | *`serviceCategories`* __xref:{anchor_prefix}-github-com-redhatinsights-frontend-operator-api-v1alpha1-frontendservicecategory[$$FrontendServiceCategory$$]__ | For the ChromeUI to render additional global components + | | | *`httpHeaders`* __object (keys:string, values:string)__ | Custom HTTP Headers + These populate an ENV var that is then added into the caddy config as a header block + | | +| *`defaultReplicas`* __integer__ | | | |=== diff --git a/tests/e2e/cachebust-multiple-urls/00-create-namespace.yaml b/tests/e2e/cachebust-multiple-urls/00-create-namespace.yaml new file mode 100644 index 00000000..b2ce11ca --- /dev/null +++ b/tests/e2e/cachebust-multiple-urls/00-create-namespace.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: test-cachebust-multiple-urls +spec: + finalizers: + - kubernetes +--- +kind: Secret +apiVersion: v1 +metadata: + name: akamai + namespace: test-cachebust-multiple-urls +data: + access_token: "YWNjZXNzX3Rva2Vu" + client_secret: "Y2xpZW50X3NlY3JldA==" + client_token: "Y2xpZW50X3Rva2Vu" + host: "aG9zdA==" +type: Opaque + diff --git a/tests/e2e/cachebust-multiple-urls/01-create-resources.yaml b/tests/e2e/cachebust-multiple-urls/01-create-resources.yaml new file mode 100644 index 00000000..b641b187 --- /dev/null +++ b/tests/e2e/cachebust-multiple-urls/01-create-resources.yaml @@ -0,0 +1,83 @@ +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: FrontendEnvironment +metadata: + name: test-cachebust-multiple-urls-environment +spec: + generateNavJSON: false + ssl: false + hostname: foo.redhat.com + sso: https://sso.foo.redhat.com + enableAkamaiCacheBust: true + akamaiCacheBustImage: "quay.io/rh_ee_addrew/hi_true_bye:add_alias" + akamaiCacheBustURLs: + - "console.doesntexist.redhat.com" + - "us.console.doesntexist.redhat.com" +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: Frontend +metadata: + name: chrome-test-filelist + namespace: test-cachebust-multiple-urls +spec: + API: + versions: + - v1 + frontend: + paths: + - / + akamaiCacheBustPaths: + - /config/chrome/fed-modules.json + - apps/chrome/index.html + - https://app.company.com + deploymentRepo: https://github.com/RedHatInsights/insights-chrome + envName: test-cachebust-multiple-urls-environment + image: quay.io/cloudservices/insights-chrome-frontend:720317c + module: + config: + ssoUrl: 'https://' + manifestLocation: /apps/chrome/js/fed-mods.json + title: Chrome +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: Frontend +metadata: + name: chrome-test-defaults + namespace: test-cachebust-multiple-urls +spec: + API: + versions: + - v1 + frontend: + paths: + - /chrome/defaults + deploymentRepo: https://github.com/RedHatInsights/insights-chrome + envName: test-cachebust-multiple-urls-environment + image: quay.io/cloudservices/insights-chrome-frontend:720317c + module: + config: + ssoUrl: 'https://' + manifestLocation: /apps/chrome/js/fed-mods.json + title: Chrome +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: Frontend +metadata: + name: chrome-test-optout + namespace: test-cachebust-multiple-urls +spec: + akamaiCacheBustDisable: true + API: + versions: + - v1 + frontend: + paths: + - /chrome2 + deploymentRepo: https://github.com/RedHatInsights/insights-chrome + envName: test-cachebust-multiple-urls-environment + image: quay.io/cloudservices/insights-chrome-frontend:720317c + module: + config: + ssoUrl: 'https://' + manifestLocation: /apps/chrome/js/fed-mods.json + title: Chrome diff --git a/tests/e2e/cachebust-multiple-urls/02-assert.yaml b/tests/e2e/cachebust-multiple-urls/02-assert.yaml new file mode 100644 index 00000000..d74c5d04 --- /dev/null +++ b/tests/e2e/cachebust-multiple-urls/02-assert.yaml @@ -0,0 +1,198 @@ +kind: Deployment +apiVersion: apps/v1 +metadata: + name: chrome-test-filelist-frontend + namespace: test-cachebust-multiple-urls + labels: + frontend: chrome-test-filelist + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome-test-filelist +spec: + selector: + matchLabels: + frontend: chrome-test-filelist + template: + metadata: + labels: + frontend: chrome-test-filelist + spec: + volumes: + - name: config + configMap: + name: test-cachebust-multiple-urls-environment + defaultMode: 420 + containers: + - name: fe-image + image: quay.io/cloudservices/insights-chrome-frontend:720317c + ports: + - name: web + containerPort: 80 + protocol: TCP + - name: metrics + containerPort: 9000 + protocol: TCP + resources: {} + volumeMounts: + - name: config + mountPath: /opt/app-root/src/build/stable/operator-generated + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: IfNotPresent +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: chrome-test-filelist-frontend-cachebust + namespace: test-cachebust-multiple-urls + labels: + frontend: chrome-test-filelist + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome-test-filelist +spec: + template: + metadata: + annotations: + frontend-image: quay.io/cloudservices/insights-chrome-frontend:720317c + spec: + volumes: + - name: akamai-edgerc + configMap: + name: akamai-edgerc + defaultMode: 420 + containers: + - name: akamai-cache-bust + image: quay.io/rh_ee_addrew/hi_true_bye:add_alias + command: + - /bin/bash + - '-c' + - 'sleep 120; /cli/.akamai-cli/src/cli-purge/bin/akamai-purge --edgerc /opt/app-root/edgerc delete https://console.doesntexist.redhat.com/config/chrome/fed-modules.json https://console.doesntexist.redhat.com/apps/chrome/index.html https://app.company.com https://us.console.doesntexist.redhat.com/config/chrome/fed-modules.json https://us.console.doesntexist.redhat.com/apps/chrome/index.html' + resources: {} + volumeMounts: + - name: akamai-edgerc + mountPath: /opt/app-root/edgerc + subPath: edgerc + restartPolicy: Never +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: chrome-test-defaults-frontend + namespace: test-cachebust-multiple-urls + labels: + frontend: chrome-test-defaults + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome-test-defaults +spec: + selector: + matchLabels: + frontend: chrome-test-defaults + template: + metadata: + labels: + frontend: chrome-test-defaults + spec: + volumes: + - name: config + configMap: + name: test-cachebust-multiple-urls-environment + defaultMode: 420 + containers: + - name: fe-image + image: quay.io/cloudservices/insights-chrome-frontend:720317c + ports: + - name: web + containerPort: 80 + protocol: TCP + - name: metrics + containerPort: 9000 + protocol: TCP + resources: {} + volumeMounts: + - name: config + mountPath: /opt/app-root/src/build/stable/operator-generated + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + imagePullPolicy: IfNotPresent +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: chrome-test-defaults-frontend-cachebust + namespace: test-cachebust-multiple-urls + labels: + frontend: chrome-test-defaults + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome-test-defaults +spec: + template: + metadata: + annotations: + frontend-image: quay.io/cloudservices/insights-chrome-frontend:720317c + spec: + volumes: + - name: akamai-edgerc + configMap: + name: akamai-edgerc + defaultMode: 420 + containers: + - name: akamai-cache-bust + image: quay.io/rh_ee_addrew/hi_true_bye:add_alias + command: + - /bin/bash + - '-c' + - 'sleep 120; /cli/.akamai-cli/src/cli-purge/bin/akamai-purge --edgerc /opt/app-root/edgerc delete https://console.doesntexist.redhat.com/apps/chrome-test-defaults/fed-mods.json https://us.console.doesntexist.redhat.com/apps/chrome-test-defaults/fed-mods.json' + resources: {} + volumeMounts: + - name: akamai-edgerc + mountPath: /opt/app-root/edgerc + subPath: edgerc + restartPolicy: Never +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: chrome-test-optout-frontend + namespace: test-cachebust-multiple-urls + labels: + frontend: chrome-test-optout + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome-test-optout +spec: + replicas: 1 + selector: + matchLabels: + frontend: chrome-test-optout + template: + metadata: + labels: + frontend: chrome-test-optout + spec: + volumes: + - name: config + configMap: + name: test-cachebust-multiple-urls-environment + defaultMode: 420 + containers: + - name: fe-image + image: quay.io/cloudservices/insights-chrome-frontend:720317c + ports: + - name: web + containerPort: 80 + protocol: TCP + - name: metrics + containerPort: 9000 + protocol: TCP + resources: {} + volumeMounts: + - name: config + mountPath: /opt/app-root/src/build/stable/operator-generated diff --git a/tests/e2e/cachebust/03-update-resources.yaml b/tests/e2e/cachebust/03-update-resources.yaml index e2ac0d0a..b08f5025 100644 --- a/tests/e2e/cachebust/03-update-resources.yaml +++ b/tests/e2e/cachebust/03-update-resources.yaml @@ -10,7 +10,8 @@ spec: sso: https://sso.foo.redhat.com enableAkamaiCacheBust: true akamaiCacheBustImage: "quay.io/rh_ee_addrew/hi_true_bye:add_alias" - akamaiCacheBustURL: "console.doesntexist.redhat.com" + akamaiCacheBustURLs: + - "console.doesntexist.redhat.com" --- apiVersion: cloud.redhat.com/v1alpha1 kind: Frontend diff --git a/tests/e2e/default-replicas/00-create-namespace.yaml b/tests/e2e/default-replicas/00-create-namespace.yaml new file mode 100644 index 00000000..82d0f1ef --- /dev/null +++ b/tests/e2e/default-replicas/00-create-namespace.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: test-default-replicas +spec: + finalizers: + - kubernetes diff --git a/tests/e2e/default-replicas/01-create-resources.yaml b/tests/e2e/default-replicas/01-create-resources.yaml new file mode 100644 index 00000000..48db4fac --- /dev/null +++ b/tests/e2e/default-replicas/01-create-resources.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: FrontendEnvironment +metadata: + name: test-default-replicas-environment +spec: + generateNavJSON: false + ssl: false + hostname: foo.redhat.com + sso: https://sso.foo.redhat.com + defaultReplicas: 3 +--- +apiVersion: cloud.redhat.com/v1alpha1 +kind: Frontend +metadata: + name: chrome + namespace: test-default-replicas +spec: + API: + versions: + - v1 + frontend: + paths: + - / + deploymentRepo: https://github.com/RedHatInsights/insights-chrome + envName: test-default-replicas-environment + image: quay.io/cloudservices/insights-chrome-frontend:720317c + module: + config: + ssoUrl: 'https://' + manifestLocation: /apps/chrome/js/fed-mods.json + title: Chrome + diff --git a/tests/e2e/default-replicas/02-assert.yaml b/tests/e2e/default-replicas/02-assert.yaml new file mode 100644 index 00000000..59dd5e68 --- /dev/null +++ b/tests/e2e/default-replicas/02-assert.yaml @@ -0,0 +1,37 @@ +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: chrome-frontend + namespace: test-default-replicas + labels: + frontend: chrome + ownerReferences: + - apiVersion: cloud.redhat.com/v1alpha1 + kind: Frontend + name: chrome +spec: + replicas: 3 + selector: + matchLabels: + frontend: chrome + template: + spec: + volumes: + - name: config + configMap: + name: test-default-replicas-environment + defaultMode: 420 + containers: + - name: fe-image + image: 'quay.io/cloudservices/insights-chrome-frontend:720317c' + ports: + - name: web + containerPort: 80 + protocol: TCP + - name: metrics + containerPort: 9000 + protocol: TCP + volumeMounts: + - name: config + mountPath: /opt/app-root/src/build/stable/operator-generated \ No newline at end of file