From 31ae531683639f52d1787786ef8e20b5084b01eb Mon Sep 17 00:00:00 2001 From: Joanna Bodora Date: Thu, 6 Feb 2025 15:10:57 +0100 Subject: [PATCH] Add view for apirule v2 --- .../apirules-v1beta1/dataSources | 18 + config/ui-extensions/apirules-v1beta1/details | 170 +++++++ config/ui-extensions/apirules-v1beta1/form | 329 ++++++++++++++ config/ui-extensions/apirules-v1beta1/general | 10 + .../{apirules => apirules-v1beta1}/injections | 0 .../apirules-v1beta1/kustomization.yaml | 18 + config/ui-extensions/apirules-v1beta1/list | 20 + config/ui-extensions/apirules-v1beta1/presets | 11 + .../apirules-v1beta1/translations | 55 +++ config/ui-extensions/apirules/details | 224 ++++++---- config/ui-extensions/apirules/form | 420 +++++++----------- config/ui-extensions/apirules/general | 8 +- .../ui-extensions/apirules/kustomization.yaml | 1 - config/ui-extensions/apirules/list | 16 +- config/ui-extensions/apirules/presets | 8 +- config/ui-extensions/apirules/translations | 78 ++-- config/ui-extensions/kustomization.yaml | 3 +- tests/ui/tests/support/navigation.ts | 4 +- 18 files changed, 968 insertions(+), 425 deletions(-) create mode 100644 config/ui-extensions/apirules-v1beta1/dataSources create mode 100644 config/ui-extensions/apirules-v1beta1/details create mode 100644 config/ui-extensions/apirules-v1beta1/form create mode 100644 config/ui-extensions/apirules-v1beta1/general rename config/ui-extensions/{apirules => apirules-v1beta1}/injections (100%) create mode 100644 config/ui-extensions/apirules-v1beta1/kustomization.yaml create mode 100644 config/ui-extensions/apirules-v1beta1/list create mode 100644 config/ui-extensions/apirules-v1beta1/presets create mode 100644 config/ui-extensions/apirules-v1beta1/translations diff --git a/config/ui-extensions/apirules-v1beta1/dataSources b/config/ui-extensions/apirules-v1beta1/dataSources new file mode 100644 index 000000000..55eafa108 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/dataSources @@ -0,0 +1,18 @@ +relatedGateways: + resource: + kind: Gateway + group: networking.istio.io + version: v1beta1 + namespace: null +relatedServices: + resource: + kind: Service + version: v1 + namespace: null +virtualServices: + resource: + kind: VirtualService + group: networking.istio.io + version: v1beta1 + namespace: null + filter: '$item.metadata.labels."apirule.gateway.kyma-project.io/v1beta1" = $root.metadata.name & "." & $root.metadata.namespace' \ No newline at end of file diff --git a/config/ui-extensions/apirules-v1beta1/details b/config/ui-extensions/apirules-v1beta1/details new file mode 100644 index 000000000..601111701 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/details @@ -0,0 +1,170 @@ +header: + - name: status + widget: Badge + highlights: + positive: + - 'OK' + negative: + - 'ERROR' + critical: + - 'SKIPPED' + source: 'status.APIRuleStatus.code ? status.APIRuleStatus.code : "UNKNOWN"' + description: status.APIRuleStatus.desc + - name: host + widget: ExternalLink + source: 'spec.host' + link: 'status.APIRuleStatus.code = "OK" ? "https://" & $virtualServices().items[0].spec.hosts[0] : ""' +body: + - simple: true + widget: Alert + severity: warning + source: '"alert.spec.jwks_url_http"' + visibility: '$count(spec.rules.accessStrategies.config.jwks_urls)>0 and $reduce(spec.rules.accessStrategies.config.jwks_urls,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' + - simple: true + widget: Alert + severity: warning + source: '"alert.spec.trusted_issuers_http"' + visibility: '$count(spec.rules.accessStrategies.config.trusted_issuers)>0 and $reduce(spec.rules.accessStrategies.config.trusted_issuers,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' + - simple: true + widget: Alert + severity: warning + source: '"alert.corsPolicy"' + visibility: '$not($exists(spec.corsPolicy))' + - simple: true + widget: Alert + severity: warning + source: '"alert.gateway.details"' + visibility: '$not($exists($match(spec.gateway,/^[a-z0-9_]+(-[a-z0-9_]+)*\/[a-z0-9_]+(-[a-z0-9_]+)*$/))[0])' + - widget: Panel + name: corsPolicy + source: spec.corsPolicy + visibility: $exists(spec.corsPolicy) + children: + - name: corsAllowMethods + source: allowMethods + widget: JoinedArray + visibility: '$not($count(allowMethods)=0)' + - name: corsAllowOriginsRegex + source: $join(allowOrigins.regex,",") + widget: Text + visibility: '$not($count(allowOrigins.regex)=0)' + - name: corsAllowOriginsPrefix + source: $join(allowOrigins.prefix,",") + widget: Text + visibility: '$not($count(allowOrigins.prefix)=0)' + - name: corsAllowOriginsExact + source: $join(allowOrigins.exact,",") + widget: Text + visibility: '$not($count(allowOrigins.exact)=0)' + - name: corsExposeHeaders + source: exposeHeaders + widget: JoinedArray + visibility: '$not($count(exposeHeaders)=0)' + - name: corsAllowHeaders + source: allowHeaders + widget: JoinedArray + visibility: '$not($count(allowHeaders)=0)' + - name: corsAllowCredentials + source: allowCredentials + widget: Badge + visibility: '$exists(allowCredentials)' + - name: corsMaxAge + source: maxAge + widget: Text + visibility: '$exists(maxAge)' + - name: general + source: spec + widget: Panel + visibility: $exists(spec.timeout) + children: + - source: timeout + name: details.timeout + - name: service + source: spec.service + widget: Panel + children: + - name: service.name + source: name + widget: ResourceLink + resource: + name: $root.spec.service.name + namespace: $root.metadata.namespace + kind: '"Service"' + - name: service.port + source: port + - source: spec.rules + widget: Table + name: rules + children: + - source: $item.path + name: rules.path + - source: $item.methods + name: rules.methods + widget: Badge + collapsible: + - name: general + source: $item + widget: Panel + visibility: $exists($item.timeout) + children: + - source: $item.timeout + name: details.timeout + - source: $item.accessStrategies + widget: Table + disablePadding: true + name: accessStrategies + children: + - source: $item.handler + name: accessStrategies.handlers + widget: Badge + - source: $item.config.required_scope + name: accessStrategies.required_scope + widget: JoinedArray + - source: $item.config.jwks_urls + name: accessStrategies.jwks_urls + widget: JoinedArray + - source: $item.config.trusted_issuers + name: accessStrategies.trusted_issuers + widget: JoinedArray + - source: $item.config.introspection_url + name: accessStrategies.introspection_url + widget: Text + - source: $item.config.introspection_request_headers + name: accessStrategies.introspection_request_headers + widget: Labels + - source: $item.config.token_from + name: accessStrategies.token_from + widget: Labels + - source: $item.mutators + widget: Table + disablePadding: true + name: mutators + visibility: $exists($item.mutators) + children: + - source: $item.handler + name: mutators.handlers + widget: Badge + - source: $item.config + name: mutators.config + widget: CodeViewer + description: "Configuration for {{[Ory Oathkeeper Rule mutators]https://www.ory.sh/docs/oathkeeper/pipeline/mutator}}" + language: "'yaml'" + visibility: '$exists($value)' + - name: service + source: $item.service + widget: Panel + visibility: $exists($item.service) + children: + - name: service.name + source: $item.service.name + widget: ResourceLink + resource: + name: $item.service.name + namespace: $root.metadata.namespace + kind: '"Service"' + - name: service.port + source: $item.service.port + - widget: ResourceList + source: $virtualServices() + name: virtualService + disableCreate: true diff --git a/config/ui-extensions/apirules-v1beta1/form b/config/ui-extensions/apirules-v1beta1/form new file mode 100644 index 000000000..251e7cb42 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/form @@ -0,0 +1,329 @@ +- simple: true + required: false + path: spec.timeout + name: timeout + inputInfo: inputInfo.timeout + value: + type: number +- simple: true + required: false + path: spec.service + name: service + widget: FormGroup + defaultExpanded: true + children: + - simple: true + required: false + path: name + name: service-name + widget: Resource + resource: + kind: Service + version: v1 + scope: namespace + trigger: [port] + - simple: true + required: false + path: port + name: service.port + subscribe: + port: "$filter($relatedServices().items, function ($v) { $v.metadata.name = $root.spec.service.name and $v.metadata.namespace = $root.metadata.namespace }).spec.ports[0].port" +- simple: true + widget: Alert + severity: warning + alert: '"alert.gateway.form"' + visibility: '$not($exists($match(spec.gateway,/^[a-z0-9_]+(-[a-z0-9_]+)*\/[a-z0-9_]+(-[a-z0-9_]+)*$/))[0])' +- simple: true + required: true + path: spec.gateway + name: gateway + widget: ResourceRef + defaultExpanded: true + visibility: $canI('networking.istio.io/v1beta1', 'Gateway') + resource: + kind: Gateway + group: networking.istio.io + version: v1beta1 + overwrite: false + toInternal: '($values := $split($, "/"); { "namespace": $values[0], "name": $values[1] })' + toExternal: 'namespace & "/" & name' + trigger: [host] +- simple: true + var: separator + value: "" +- simple: true + required: true + path: spec.gateway + name: gateway + visibility: $not($canI('networking.istio.io/v1beta1', 'Gateway')) + inputInfo: inputInfo.gateway + overwrite: false + trigger: [host] +- simple: true + widget: Alert + severity: warning + alert: '"alert.corsPolicy"' + visibility: '$not($useCorsPolicy)' +- var: useCorsPolicy + name: useCorsPolicy + simple: true + type: boolean + dynamicValue: '$boolean(spec.corsPolicy)' +- simple: true + visibility: '$useCorsPolicy' + required: false + path: spec.corsPolicy + name: corsPolicy + defaultExpanded: true + inputInfo: inputInfo.corsPolicy + widget: FormGroup + children: + - simple: true + required: false + path: allowMethods + name: corsAllowMethods + widget: MultiCheckbox + options: + - key: GET + - key: POST + - key: PUT + - key: DELETE + - key: PATCH + - key: HEAD + - key: OPTIONS + - key: CONNECT + - key: TRACE + - simple: true + required: false + path: allowOrigins + name: corsAllowOrigins + widget: GenericList + children: + - path: '[]' + widget: KeyValuePair + keyEnum: + - exact + - prefix + - regex + simple: true + - simple: true + required: false + path: allowHeaders + name: corsAllowHeaders + widget: SimpleList + children: + - path: '[]' + simple: true + - simple: true + required: false + path: exposeHeaders + name: corsExposeHeaders + widget: SimpleList + children: + - path: '[]' + simple: true + - simple: true + required: false + path: allowCredentials + name: corsAllowCredentials + value: + type: boolean + - simple: true + required: false + path: maxAge + name: corsMaxAge + placeholder: 300s + value: + type: string +- simple: true + required: true + path: spec.host + name: host + enum: "$distinct($filter($relatedGateways().items, function ($v) { $v.metadata.namespace = $substringBefore($root.spec.gateway, '/') and $v.metadata.name = $substringAfter($root.spec.gateway, '/')}).spec.servers.hosts)" + subscribe: + host: "$string($filter($relatedGateways().items, function ($v) { $v.metadata.namespace = $substringBefore($root.spec.gateway, '/') and $v.metadata.name = $substringAfter($root.spec.gateway, '/')}).spec.servers[0].hosts[0])" +- simple: true + widget: Alert + severity: error + alert: '"alert.spec.host"' + visibility: '$substring(spec.host, 0, 1)="*"' +- simple: true + required: true + path: spec.rules + name: rules + widget: GenericList + defaultExpanded: true + template: + path: '/.*' + methods: ['GET'] + accessStrategies: + - handler: 'no_auth' + children: + - simple: true + required: false + path: '[].timeout' + name: timeout + inputInfo: inputInfo.timeout + value: + type: number + - simple: true + required: true + path: '[].path' + name: path + inputInfo: inputInfo.path + - required: true + simple: true + path: '[].accessStrategies' + name: accessStrategies + widget: GenericList + defaultExpanded: true + template: + handler: 'no_auth' + children: + - required: true + simple: true + path: '[].handler' + name: accessStrategies.handler + enum: + - allow + - no_auth + - noop + - jwt + - oauth2_introspection + - path: '[].config' + simple: true + name: accessStrategies.config + type: object + properties: + jwks_urls: + type: array + items: + type: string + pattern: ^(https://|file://).*$ + trusted_issuers: + type: array + items: + type: string + pattern: ^(https://|file://).*$ + required_scope: + type: array + items: + type: string + introspection_url: + type: string + pattern: ^(https://|http://).*$ + introspection_request_headers: + type: map + token_from: + type: map + children: + - simple: true + widget: Alert + severity: warning + alert: '"alert.spec.jwks_url_http"' + visibility: '$reduce($item.config.jwks_urls,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' + - path: jwks_urls + name: accessStrategies.jwks_urls + inputInfo: inputInfo.jwks_urls + simple: true + widget: SimpleList + visibility: '$item.handler="jwt"' + children: + - path: '[]' + simple: true + - simple: true + widget: Alert + severity: warning + alert: '"alert.spec.trusted_issuers_http"' + visibility: '$reduce($item.config.trusted_issuers,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' + - path: trusted_issuers + name: accessStrategies.trusted_issuers + inputInfo: inputInfo.trusted_issuers + simple: true + widget: SimpleList + visibility: '$item.handler="jwt"' + children: + - path: '[]' + simple: true + - path: introspection_url + name: accessStrategies.introspection_url + inputInfo: inputInfo.introspection_url + simple: true + visibility: '$item.handler="oauth2_introspection"' + - path: introspection_request_headers + name: accessStrategies.introspection_request_headers + simple: true + inputInfo: inputInfo.introspection_request_headers + widget: KeyValuePair + visibility: '$item.handler="oauth2_introspection"' + - path: required_scope + name: accessStrategies.required_scope + simple: true + widget: SimpleList + visibility: '$item.handler="oauth2_introspection" or $item.handler="oauth2_client_credentials" or $item.handler="jwt"' + children: + - path: '[]' + simple: true + - path: token_from + name: accessStrategies.token_from + simple: true + inputInfo: inputInfo.token_from + widget: KeyValuePair + visibility: '$item.handler!="allow" and $item.handler!="no_auth" and $item.handler!="noop"' + keyEnum: + - header + - query_parameter + - cookie + - required: true + simple: true + path: '[].methods' + name: rules.methods + widget: MultiCheckbox + options: + - key: GET + - key: POST + - key: PUT + - key: DELETE + - key: PATCH + - key: HEAD + - key: OPTIONS + - key: CONNECT + - key: TRACE + - path: '[].mutators' + name: mutators + widget: GenericList + children: + - path: '[].config' + widget: CodeEditor + description: "Configuration for {{[Ory Oathkeeper Rule mutators]https://www.ory.sh/docs/oathkeeper/pipeline/mutator}}" + language: "'yaml'" + - required: true + path: '[].handler' + name: accessStrategies.handler + enum: + - noop + - id_token + - header + - cookie + - path: '[].service' + simple: true + name: service + widget: FormGroup + required: false + children: + - simple: true + required: false + path: name + name: service-name + widget: Resource + resource: + kind: Service + version: v1 + scope: namespace + trigger: [accessStrategyPort] + - simple: true + required: false + path: port + name: service.port + subscribe: + accessStrategyPort: "$filter($relatedServices().items, function ($v) { $v.metadata.name = $item.service.name and $v.metadata.namespace = $root.metadata.namespace }).spec.ports[0].port" diff --git a/config/ui-extensions/apirules-v1beta1/general b/config/ui-extensions/apirules-v1beta1/general new file mode 100644 index 000000000..ec7cb0f96 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/general @@ -0,0 +1,10 @@ +resource: + kind: APIRule + group: gateway.kyma-project.io + version: v1beta1 +name: API Rules v1beta1 +category: Discovery and Network +scope: namespace +description: '{{[APIRule](https://kyma-project.io/#/api-gateway/user/custom-resources/apirule/v1beta1/04-10-apirule-custom-resource)}} allows for exposing a service externally.' +urlPath: apirules +filter: "$filter(data, function($data) {$not($data.metadata.annotations.'gateway.kyma-project.io/original-version' = 'v2alpha1')})" \ No newline at end of file diff --git a/config/ui-extensions/apirules/injections b/config/ui-extensions/apirules-v1beta1/injections similarity index 100% rename from config/ui-extensions/apirules/injections rename to config/ui-extensions/apirules-v1beta1/injections diff --git a/config/ui-extensions/apirules-v1beta1/kustomization.yaml b/config/ui-extensions/apirules-v1beta1/kustomization.yaml new file mode 100644 index 000000000..a02f720d4 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/kustomization.yaml @@ -0,0 +1,18 @@ +configMapGenerator: + - name: apirule-v1beta1-ui.operator.kyma-project.io + namespace: kyma-system + files: + - general + - form + - list + - details + - translations + - presets + - dataSources + - injections + options: + disableNameSuffixHash: true + labels: + app.kubernetes.io/component: operator + busola.io/extension: resource + busola.io/extension-version: "0.5" diff --git a/config/ui-extensions/apirules-v1beta1/list b/config/ui-extensions/apirules-v1beta1/list new file mode 100644 index 000000000..d7b8cb329 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/list @@ -0,0 +1,20 @@ +- name: host + source: spec.host +- name: service-name + source: '$string(spec.service.name) ? ($string(spec.service.name) & " (port: " & $string(spec.service.port) & ")") : ""' + widget: ResourceLink + resource: + name: $root.spec.service.name + namespace: $root.metadata.namespace + kind: '"Service"' +- name: status + widget: Badge + highlights: + positive: + - 'OK' + negative: + - 'ERROR' + critical: + - 'SKIPPED' + source: 'status.APIRuleStatus.code ? status.APIRuleStatus.code : "UNKNOWN"' + description: status.APIRuleStatus.desc \ No newline at end of file diff --git a/config/ui-extensions/apirules-v1beta1/presets b/config/ui-extensions/apirules-v1beta1/presets new file mode 100644 index 000000000..1f40884a6 --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/presets @@ -0,0 +1,11 @@ +- name: Default gateway + default: true + value: + spec: + gateway: kyma-system/kyma-gateway + rules: + - path: /.* + methods: + - GET + accessStrategies: + - handler: no_auth \ No newline at end of file diff --git a/config/ui-extensions/apirules-v1beta1/translations b/config/ui-extensions/apirules-v1beta1/translations new file mode 100644 index 000000000..9957310ad --- /dev/null +++ b/config/ui-extensions/apirules-v1beta1/translations @@ -0,0 +1,55 @@ +en: + accessStrategies: Access Strategies + accessStrategies.config: Config + accessStrategies.handler: Handler + accessStrategies.handlers: Handlers + accessStrategies.jwks_urls: JWKS URLs + accessStrategies.trusted_issuers: Trusted Issuers + accessStrategies.required_scope: Required Scope + accessStrategies.introspection_url: Introspection URL + accessStrategies.introspection_request_headers: Introspection Request Headers + accessStrategies.token_from: Token From + gateway: Gateway + useCorsPolicy: Use custom CORS policy + corsPolicy: CORS Policy + corsAllowMethods: CORS Allow Methods + corsAllowOrigins: CORS Allow Origins + corsAllowHeaders: CORS Allow Headers + corsExposeHeaders: CORS Expose Headers + corsAllowCredentials: CORS Allow Credentials + corsAllowOriginsRegex: CORS origins allowed with regex match + corsAllowOriginsPrefix: CORS origins allowed with prefix match + corsAllowOriginsExact: CORS origins allowed with exact match + corsMaxAge: CORS Max Age + host: Host + inputInfo.corsPolicy: Defines what CORS headers will be sent on CORS preflight and response + inputInfo.gateway: Gateway structure, '{NAMESPACE}/{NAME}' + inputInfo.path: "Path can contain alphanumeric characters and '/', '.', '*', '?', '!', '-', '(', and ')'." + inputInfo.jwks_urls: "JWKS URLs must start with 'https://', 'http://' or 'file://'. HTTP protocol is not recommended." + inputInfo.trusted_issuers: "Trusted Issuers must start with 'https://', 'http://' or 'file://'. HTTP protocol is not recommended." + inputInfo.introspection_url: "URL endpoint used for validating the Bearer token. Should start with 'http://' or 'https://'" + inputInfo.introspection_request_headers: "Headers sent alongside the introspection request" + inputInfo.token_from: "Defines where Ory Oathkeeper should look for access token" + inputInfo.timeout: "Timeout for HTTP requests in seconds. The timeout can be configured for a maximum of 3900 seconds (65 minutes)." + mutators: Mutators + mutators.config: Config + mutators.handlers: Handlers + path: Path + rules: Rules + rules.methods: Methods + rules.path: Path + service: Service + service.name: Name + service.port: Port + service-name: Service Name + status: Status + general: General + timeout: HTTP Request Timeout + details.timeout: HTTP Request Timeout (seconds) + virtualService: Virtual Service + alert.spec.host: Host can not be a wildcard, replace * with subdomain name + alert.spec.jwks_url_http: "JWKS URL: HTTP protocol is not secure, consider using HTTPS" + alert.spec.trusted_issuers_http: "Trusted Issuers: HTTP protocol is not secure, consider using HTTPS" + alert.corsPolicy: "Disabling custom CORS Policy is not recommended. Consider setting up CORS yourself" + alert.gateway.details: "Gateway must be in the format '{NAMESPACE}/{NAME}'" + alert.gateway.form: "Gateway must exist, specify both Namespace and Name" diff --git a/config/ui-extensions/apirules/details b/config/ui-extensions/apirules/details index 601111701..800b92226 100644 --- a/config/ui-extensions/apirules/details +++ b/config/ui-extensions/apirules/details @@ -3,38 +3,40 @@ header: widget: Badge highlights: positive: - - 'OK' + - 'Ready' negative: - - 'ERROR' - critical: - - 'SKIPPED' - source: 'status.APIRuleStatus.code ? status.APIRuleStatus.code : "UNKNOWN"' - description: status.APIRuleStatus.desc - - name: host - widget: ExternalLink - source: 'spec.host' - link: 'status.APIRuleStatus.code = "OK" ? "https://" & $virtualServices().items[0].spec.hosts[0] : ""' + - 'Error' + warning: + - 'Warning' + source: 'status.state ? status.state : "Unknown"' + description: status.description + body: - - simple: true - widget: Alert - severity: warning - source: '"alert.spec.jwks_url_http"' - visibility: '$count(spec.rules.accessStrategies.config.jwks_urls)>0 and $reduce(spec.rules.accessStrategies.config.jwks_urls,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' - - simple: true - widget: Alert - severity: warning - source: '"alert.spec.trusted_issuers_http"' - visibility: '$count(spec.rules.accessStrategies.config.trusted_issuers)>0 and $reduce(spec.rules.accessStrategies.config.trusted_issuers,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' - - simple: true - widget: Alert - severity: warning - source: '"alert.corsPolicy"' - visibility: '$not($exists(spec.corsPolicy))' - - simple: true - widget: Alert - severity: warning - source: '"alert.gateway.details"' - visibility: '$not($exists($match(spec.gateway,/^[a-z0-9_]+(-[a-z0-9_]+)*\/[a-z0-9_]+(-[a-z0-9_]+)*$/))[0])' + - source: spec + name: general + widget: Panel + children: + - source: timeout + name: timeout + visibility: $exists(spec.timeout) + - source: gateway + name: gateway + - source: hosts + name: host + widget: JoinedArray + - source: spec.service + name: service + widget: Panel + children: + - source: name + name: service.name + widget: ResourceLink + resource: + name: $root.spec.service.name + namespace: $root.metadata.namespace + kind: '"Service"' + - source: port + name: port - widget: Panel name: corsPolicy source: spec.corsPolicy @@ -72,84 +74,116 @@ body: source: maxAge widget: Text visibility: '$exists(maxAge)' - - name: general - source: spec - widget: Panel - visibility: $exists(spec.timeout) - children: - - source: timeout - name: details.timeout - - name: service - source: spec.service - widget: Panel - children: - - name: service.name - source: name - widget: ResourceLink - resource: - name: $root.spec.service.name - namespace: $root.metadata.namespace - kind: '"Service"' - - name: service.port - source: port - source: spec.rules widget: Table name: rules children: - source: $item.path - name: rules.path - source: $item.methods - name: rules.methods + widget: Badge + - source: '$boolean($item.noAuth) ? "No Auth" : ($exists($item.jwt) ? "JWT" : ($exists($item.extAuth) ? "Ext Auth" : ""))' widget: Badge collapsible: - - name: general - source: $item - widget: Panel + - source: $item.timeout + name: timeout visibility: $exists($item.timeout) + - source: $item.jwt + name: jwt + widget: Panel + visibility: $exists($item.jwt) children: - - source: $item.timeout - name: details.timeout - - source: $item.accessStrategies - widget: Table - disablePadding: true - name: accessStrategies + - source: $item.jwt.authentications + name: authentications + widget: Table + children: + - source: $item.issuer + name: issuer + - source: $item.jwksUri + name: jwksUri + collapsible: + - source: $item.fromHeaders + name: fromHeaders + visibility: $exists($item.fromHeaders) + widget: Table + children: + - source: $item.name + name: fromHeaders.name + - source: $item.prefix + name: prefix + - source: $item.fromParams + name: fromParams + visibility: $exists($item.fromParams) + widget: JoinedArray + - source: $item.jwt.authorizations + name: authorizations + visibility: $exists($item.jwt.authorizations) + widget: Table + collapsible: + - source: $item.requiredScopes + name: requiredScopes + widget: JoinedArray + - source: $item.audiences + name: audiences + widget: JoinedArray + - source: $item.extAuth + name: extAuth + widget: Panel + visibility: $exists($item.extAuth) children: - - source: $item.handler - name: accessStrategies.handlers - widget: Badge - - source: $item.config.required_scope - name: accessStrategies.required_scope - widget: JoinedArray - - source: $item.config.jwks_urls - name: accessStrategies.jwks_urls - widget: JoinedArray - - source: $item.config.trusted_issuers - name: accessStrategies.trusted_issuers + - source: $item.extAuth.authorizers + name: authorizers widget: JoinedArray - - source: $item.config.introspection_url - name: accessStrategies.introspection_url - widget: Text - - source: $item.config.introspection_request_headers - name: accessStrategies.introspection_request_headers + - source: $item.extAuth.restrictions + name: restrictions + widget: Panel + visibility: $exists($item.extAuth.restrictions) + children: + - source: $item.extAuth.restrictions.authentications + name: authentications + widget: Table + children: + - source: $item.issuer + name: issuer + - source: $item.jwksUri + name: jwksUri + collapsible: + - source: $item.fromHeaders + name: fromHeaders + visibility: $exists($item.fromHeaders) + widget: Table + children: + - source: $item.name + name: fromHeaders.name + - source: $item.prefix + name: prefix + - source: $item.fromParams + name: fromParams + visibility: $exists($item.fromParams) + widget: JoinedArray + - source: $item.extAuth.restrictions.authorizations + name: authorizations + visibility: $exists($item.extAuth.restrictions.authorizations) + widget: Table + collapsible: + - source: $item.requiredScopes + name: requiredScopes + widget: JoinedArray + - source: $item.audiences + name: audiences + widget: JoinedArray + - source: $item.request + name: request + widget: Panel + visibility: $exists($item.request) + children: + - source: $item.request.cookies + name: cookies widget: Labels - - source: $item.config.token_from - name: accessStrategies.token_from + visibility: $exists($item.request.cookies) + - source: $item.request.headers + name: headers widget: Labels - - source: $item.mutators - widget: Table - disablePadding: true - name: mutators - visibility: $exists($item.mutators) - children: - - source: $item.handler - name: mutators.handlers - widget: Badge - - source: $item.config - name: mutators.config - widget: CodeViewer - description: "Configuration for {{[Ory Oathkeeper Rule mutators]https://www.ory.sh/docs/oathkeeper/pipeline/mutator}}" - language: "'yaml'" - visibility: '$exists($value)' + visibility: $exists($item.request.headers) - name: service source: $item.service widget: Panel @@ -162,7 +196,7 @@ body: name: $item.service.name namespace: $root.metadata.namespace kind: '"Service"' - - name: service.port + - name: port source: $item.service.port - widget: ResourceList source: $virtualServices() diff --git a/config/ui-extensions/apirules/form b/config/ui-extensions/apirules/form index 251e7cb42..8fba6f62d 100644 --- a/config/ui-extensions/apirules/form +++ b/config/ui-extensions/apirules/form @@ -1,42 +1,21 @@ -- simple: true - required: false - path: spec.timeout - name: timeout - inputInfo: inputInfo.timeout +- path: spec.timeout value: type: number -- simple: true - required: false - path: spec.service - name: service +- path: spec.service widget: FormGroup defaultExpanded: true children: - - simple: true - required: false - path: name - name: service-name + - path: name widget: Resource resource: kind: Service version: v1 scope: namespace trigger: [port] - - simple: true - required: false - path: port - name: service.port + - path: port subscribe: port: "$filter($relatedServices().items, function ($v) { $v.metadata.name = $root.spec.service.name and $v.metadata.namespace = $root.metadata.namespace }).spec.ports[0].port" -- simple: true - widget: Alert - severity: warning - alert: '"alert.gateway.form"' - visibility: '$not($exists($match(spec.gateway,/^[a-z0-9_]+(-[a-z0-9_]+)*\/[a-z0-9_]+(-[a-z0-9_]+)*$/))[0])' -- simple: true - required: true - path: spec.gateway - name: gateway +- path: spec.gateway widget: ResourceRef defaultExpanded: true visibility: $canI('networking.istio.io/v1beta1', 'Gateway') @@ -47,41 +26,38 @@ overwrite: false toInternal: '($values := $split($, "/"); { "namespace": $values[0], "name": $values[1] })' toExternal: 'namespace & "/" & name' - trigger: [host] - simple: true var: separator value: "" -- simple: true - required: true - path: spec.gateway - name: gateway +- path: spec.gateway visibility: $not($canI('networking.istio.io/v1beta1', 'Gateway')) - inputInfo: inputInfo.gateway overwrite: false - trigger: [host] -- simple: true - widget: Alert - severity: warning - alert: '"alert.corsPolicy"' - visibility: '$not($useCorsPolicy)' -- var: useCorsPolicy - name: useCorsPolicy +- var: host + type: string simple: true - type: boolean - dynamicValue: '$boolean(spec.corsPolicy)' -- simple: true - visibility: '$useCorsPolicy' - required: false - path: spec.corsPolicy + dynamicValue: '$exists(spec.hosts) ? spec.hosts[0] : ""' + name: Host + required: true + description: "Host must be a lowercase RFC 1123 label (must consist of lowercase alphanumeric characters or '-', and must start and end with an lowercase alphanumeric character) or a fully qualified domain name." + trigger: [hostChanged] +- path: spec.hosts + visibility: false + overwrite: false + subscribe: + hostChanged: "[$host]" +- var: customCorsPolicy + type: string + simple: true + dynamicValue: '$exists(spec.corsPolicy) ? "Yes" : "No"' + name: 'Custom CORS Policy' + enum: [Yes, No] +- path: spec.corsPolicy + visibility: '$customCorsPolicy = "Yes"' name: corsPolicy defaultExpanded: true - inputInfo: inputInfo.corsPolicy widget: FormGroup children: - - simple: true - required: false - path: allowMethods - name: corsAllowMethods + - path: allowMethods widget: MultiCheckbox options: - key: GET @@ -93,10 +69,7 @@ - key: OPTIONS - key: CONNECT - key: TRACE - - simple: true - required: false - path: allowOrigins - name: corsAllowOrigins + - path: allowOrigins widget: GenericList children: - path: '[]' @@ -105,225 +78,150 @@ - exact - prefix - regex - simple: true - - simple: true - required: false - path: allowHeaders - name: corsAllowHeaders + - path: allowHeaders widget: SimpleList children: - path: '[]' - simple: true - - simple: true - required: false - path: exposeHeaders - name: corsExposeHeaders + - path: exposeHeaders widget: SimpleList children: - path: '[]' - simple: true - - simple: true - required: false - path: allowCredentials - name: corsAllowCredentials + - path: allowCredentials value: type: boolean - - simple: true - required: false - path: maxAge - name: corsMaxAge - placeholder: 300s + - path: maxAge + placeholder: 300 value: type: string -- simple: true - required: true - path: spec.host - name: host - enum: "$distinct($filter($relatedGateways().items, function ($v) { $v.metadata.namespace = $substringBefore($root.spec.gateway, '/') and $v.metadata.name = $substringAfter($root.spec.gateway, '/')}).spec.servers.hosts)" - subscribe: - host: "$string($filter($relatedGateways().items, function ($v) { $v.metadata.namespace = $substringBefore($root.spec.gateway, '/') and $v.metadata.name = $substringAfter($root.spec.gateway, '/')}).spec.servers[0].hosts[0])" -- simple: true - widget: Alert - severity: error - alert: '"alert.spec.host"' - visibility: '$substring(spec.host, 0, 1)="*"' -- simple: true - required: true - path: spec.rules - name: rules +- path: spec.rules widget: GenericList defaultExpanded: true template: - path: '/.*' + path: '/*' methods: ['GET'] - accessStrategies: - - handler: 'no_auth' children: - - simple: true - required: false - path: '[].timeout' - name: timeout - inputInfo: inputInfo.timeout - value: - type: number - - simple: true - required: true - path: '[].path' - name: path - inputInfo: inputInfo.path - - required: true - simple: true - path: '[].accessStrategies' - name: accessStrategies - widget: GenericList - defaultExpanded: true - template: - handler: 'no_auth' - children: - - required: true - simple: true - path: '[].handler' - name: accessStrategies.handler - enum: - - allow - - no_auth - - noop - - jwt - - oauth2_introspection - - path: '[].config' - simple: true - name: accessStrategies.config - type: object - properties: - jwks_urls: - type: array - items: - type: string - pattern: ^(https://|file://).*$ - trusted_issuers: - type: array - items: - type: string - pattern: ^(https://|file://).*$ - required_scope: - type: array - items: - type: string - introspection_url: - type: string - pattern: ^(https://|http://).*$ - introspection_request_headers: - type: map - token_from: - type: map - children: - - simple: true - widget: Alert - severity: warning - alert: '"alert.spec.jwks_url_http"' - visibility: '$reduce($item.config.jwks_urls,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' - - path: jwks_urls - name: accessStrategies.jwks_urls - inputInfo: inputInfo.jwks_urls - simple: true - widget: SimpleList - visibility: '$item.handler="jwt"' - children: - - path: '[]' - simple: true - - simple: true - widget: Alert - severity: warning - alert: '"alert.spec.trusted_issuers_http"' - visibility: '$reduce($item.config.trusted_issuers,function($i, $j){$i or $substringBefore($j,"://")="http"},false)' - - path: trusted_issuers - name: accessStrategies.trusted_issuers - inputInfo: inputInfo.trusted_issuers - simple: true - widget: SimpleList - visibility: '$item.handler="jwt"' - children: - - path: '[]' - simple: true - - path: introspection_url - name: accessStrategies.introspection_url - inputInfo: inputInfo.introspection_url - simple: true - visibility: '$item.handler="oauth2_introspection"' - - path: introspection_request_headers - name: accessStrategies.introspection_request_headers - simple: true - inputInfo: inputInfo.introspection_request_headers - widget: KeyValuePair - visibility: '$item.handler="oauth2_introspection"' - - path: required_scope - name: accessStrategies.required_scope - simple: true - widget: SimpleList - visibility: '$item.handler="oauth2_introspection" or $item.handler="oauth2_client_credentials" or $item.handler="jwt"' - children: - - path: '[]' - simple: true - - path: token_from - name: accessStrategies.token_from - simple: true - inputInfo: inputInfo.token_from - widget: KeyValuePair - visibility: '$item.handler!="allow" and $item.handler!="no_auth" and $item.handler!="noop"' - keyEnum: - - header - - query_parameter - - cookie - - required: true - simple: true - path: '[].methods' - name: rules.methods - widget: MultiCheckbox - options: - - key: GET - - key: POST - - key: PUT - - key: DELETE - - key: PATCH - - key: HEAD - - key: OPTIONS - - key: CONNECT - - key: TRACE - - path: '[].mutators' - name: mutators - widget: GenericList - children: - - path: '[].config' - widget: CodeEditor - description: "Configuration for {{[Ory Oathkeeper Rule mutators]https://www.ory.sh/docs/oathkeeper/pipeline/mutator}}" - language: "'yaml'" - - required: true - path: '[].handler' - name: accessStrategies.handler - enum: - - noop - - id_token - - header - - cookie - - path: '[].service' - simple: true - name: service - widget: FormGroup - required: false + - path: '[]' children: - - simple: true - required: false - path: name - name: service-name - widget: Resource - resource: - kind: Service - version: v1 - scope: namespace - trigger: [accessStrategyPort] - - simple: true - required: false - path: port - name: service.port - subscribe: - accessStrategyPort: "$filter($relatedServices().items, function ($v) { $v.metadata.name = $item.service.name and $v.metadata.namespace = $root.metadata.namespace }).spec.ports[0].port" + - path: 'timeout' + value: + type: number + - path: 'path' + - path: 'methods' + widget: MultiCheckbox + options: + - key: GET + - key: POST + - key: PUT + - key: DELETE + - key: PATCH + - key: HEAD + - key: OPTIONS + - key: CONNECT + - key: TRACE + - var: accessStrategy + required: true + type: string + simple: true + dynamicValue: '$boolean($item.noAuth) ? "No Auth" : ($exists($item.jwt) ? "JWT" : ($exists($item.extAuth) ? "Ext Auth" : ""))' + name: 'Access Strategy' + enum: ['No Auth', JWT, 'Ext Auth', ''] + trigger: [accessStrategyChanged] + - path: 'noAuth' + visibility: "$accessStrategy = 'No Auth'" + type: boolean + visibility: false + overwrite: false + subscribe: + accessStrategyChanged: "$accessStrategy = 'No Auth' ? true : false" + - path: 'jwt' + name: jwt + visibility: "$accessStrategy = 'JWT'" + widget: FormGroup + children: + - path: 'authentications' + required: true + widget: GenericList + children: + - path: '[].issuer' + - path: '[].jwksUri' + name: jwksUri + - path: '[].fromHeaders' + widget: GenericList + children: + - path: '[].name' + - path: '[].prefix' + - path: '[].fromParams' + widget: SimpleList + children: + - path: '[]' + - path: 'authorizations' + widget: GenericList + children: + - path: '[].requiredScopes' + widget: SimpleList + children: + - path: '[]' + - path: '[].audiences' + widget: SimpleList + children: + - path: '[]' + - path: 'extAuth' + name: extAuth + visibility: "$accessStrategy = 'Ext Auth'" + widget: FormGroup + children: + - path: 'authorizers' + widget: SimpleList + children: + - path: '[]' + - path: 'restrictions' + widget: FormGroup + children: + - path: 'authentications' + required: true + widget: GenericList + children: + - path: '[].issuer' + - path: '[].jwksUri' + name: jwksUri + - path: '[].fromHeaders' + widget: GenericList + children: + - path: '[].name' + - path: '[].prefix' + - path: '[].fromParams' + widget: SimpleList + children: + - path: '[]' + - path: 'authorizations' + widget: GenericList + children: + - path: '[].requiredScopes' + widget: SimpleList + children: + - path: '[]' + - path: '[].audiences' + widget: SimpleList + children: + - path: '[]' + - path: 'request' + widget: FormGroup + children: + - path: cookies + widget: KeyValuePair + - path: headers + widget: KeyValuePair + - path: 'service' + widget: FormGroup + required: false + children: + - path: name + widget: Resource + resource: + kind: Service + version: v1 + scope: namespace + trigger: [ruleServicePort] + - path: port + subscribe: + ruleServicePort: "$filter($relatedServices().items, function ($v) { $v.metadata.name = $item.service.name and $v.metadata.namespace = $root.metadata.namespace }).spec.ports[0].port" \ No newline at end of file diff --git a/config/ui-extensions/apirules/general b/config/ui-extensions/apirules/general index cf336f6d0..81bdfc0b6 100644 --- a/config/ui-extensions/apirules/general +++ b/config/ui-extensions/apirules/general @@ -1,10 +1,10 @@ resource: kind: APIRule group: gateway.kyma-project.io - version: v1beta1 + version: v2alpha1 name: API Rules category: Discovery and Network scope: namespace -description: '{{[APIRule](https://kyma-project.io/#/api-gateway/user/custom-resources/apirule/04-10-apirule-custom-resource)}} allows for exposing a service externally.' -urlPath: apirules -filter: "$filter(data, function($data) {$not($data.metadata.annotations.'gateway.kyma-project.io/original-version' = 'v2alpha1')})" \ No newline at end of file +description: '{{[APIRule](https://kyma-project.io/#/api-gateway/user/custom-resources/apirule/v2/04-10-apirule-custom-resource)}} allows for exposing a service externally.' +urlPath: apirules-v2 +filter: "$filter(data, function($data) {$data.metadata.annotations.'gateway.kyma-project.io/original-version' = 'v2alpha1'})" \ No newline at end of file diff --git a/config/ui-extensions/apirules/kustomization.yaml b/config/ui-extensions/apirules/kustomization.yaml index f28be2dc8..b5ef7d6e6 100644 --- a/config/ui-extensions/apirules/kustomization.yaml +++ b/config/ui-extensions/apirules/kustomization.yaml @@ -9,7 +9,6 @@ configMapGenerator: - translations - presets - dataSources - - injections options: disableNameSuffixHash: true labels: diff --git a/config/ui-extensions/apirules/list b/config/ui-extensions/apirules/list index d7b8cb329..17ca9fa39 100644 --- a/config/ui-extensions/apirules/list +++ b/config/ui-extensions/apirules/list @@ -1,6 +1,4 @@ -- name: host - source: spec.host -- name: service-name +- name: service source: '$string(spec.service.name) ? ($string(spec.service.name) & " (port: " & $string(spec.service.port) & ")") : ""' widget: ResourceLink resource: @@ -11,10 +9,10 @@ widget: Badge highlights: positive: - - 'OK' + - 'Ready' negative: - - 'ERROR' - critical: - - 'SKIPPED' - source: 'status.APIRuleStatus.code ? status.APIRuleStatus.code : "UNKNOWN"' - description: status.APIRuleStatus.desc \ No newline at end of file + - 'Error' + warning: + - 'Warning' + source: 'status.state ? status.state : "Unknown"' + description: status.description \ No newline at end of file diff --git a/config/ui-extensions/apirules/presets b/config/ui-extensions/apirules/presets index 1f40884a6..727e71ad2 100644 --- a/config/ui-extensions/apirules/presets +++ b/config/ui-extensions/apirules/presets @@ -1,11 +1,9 @@ -- name: Default gateway +- name: Default default: true value: spec: gateway: kyma-system/kyma-gateway rules: - - path: /.* + - path: /* methods: - - GET - accessStrategies: - - handler: no_auth \ No newline at end of file + - GET \ No newline at end of file diff --git a/config/ui-extensions/apirules/translations b/config/ui-extensions/apirules/translations index 9957310ad..31e884c02 100644 --- a/config/ui-extensions/apirules/translations +++ b/config/ui-extensions/apirules/translations @@ -1,55 +1,39 @@ en: - accessStrategies: Access Strategies - accessStrategies.config: Config - accessStrategies.handler: Handler - accessStrategies.handlers: Handlers - accessStrategies.jwks_urls: JWKS URLs - accessStrategies.trusted_issuers: Trusted Issuers - accessStrategies.required_scope: Required Scope - accessStrategies.introspection_url: Introspection URL - accessStrategies.introspection_request_headers: Introspection Request Headers - accessStrategies.token_from: Token From - gateway: Gateway useCorsPolicy: Use custom CORS policy corsPolicy: CORS Policy - corsAllowMethods: CORS Allow Methods - corsAllowOrigins: CORS Allow Origins - corsAllowHeaders: CORS Allow Headers - corsExposeHeaders: CORS Expose Headers - corsAllowCredentials: CORS Allow Credentials - corsAllowOriginsRegex: CORS origins allowed with regex match - corsAllowOriginsPrefix: CORS origins allowed with prefix match - corsAllowOriginsExact: CORS origins allowed with exact match - corsMaxAge: CORS Max Age - host: Host - inputInfo.corsPolicy: Defines what CORS headers will be sent on CORS preflight and response - inputInfo.gateway: Gateway structure, '{NAMESPACE}/{NAME}' - inputInfo.path: "Path can contain alphanumeric characters and '/', '.', '*', '?', '!', '-', '(', and ')'." - inputInfo.jwks_urls: "JWKS URLs must start with 'https://', 'http://' or 'file://'. HTTP protocol is not recommended." - inputInfo.trusted_issuers: "Trusted Issuers must start with 'https://', 'http://' or 'file://'. HTTP protocol is not recommended." - inputInfo.introspection_url: "URL endpoint used for validating the Bearer token. Should start with 'http://' or 'https://'" - inputInfo.introspection_request_headers: "Headers sent alongside the introspection request" - inputInfo.token_from: "Defines where Ory Oathkeeper should look for access token" - inputInfo.timeout: "Timeout for HTTP requests in seconds. The timeout can be configured for a maximum of 3900 seconds (65 minutes)." - mutators: Mutators - mutators.config: Config - mutators.handlers: Handlers - path: Path + corsAllowMethods: Allow Methods + corsAllowOrigins: Allow Origins + corsAllowHeaders: Allow Headers + corsExposeHeaders: Expose Headers + corsAllowCredentials: Allow Credentials + corsAllowOriginsRegex: origins allowed with regex match + corsAllowOriginsPrefix: origins allowed with prefix match + corsAllowOriginsExact: origins allowed with exact match + gateway: Gateway rules: Rules - rules.methods: Methods - rules.path: Path + noAuth: No Auth + jwt: JWT + authentications: Authentications + issuer: Issuer + jwksUri: JWKS URI + fromHeaders: From Headers + fromHeaders.name: Name + prefix: Prefix + fromParams: From Params + authorizations: Authorizations + requiredScopes: Required Scopes + audiences: Audiences + extAuth: External Authorization + authorizers: Authorizers + restrictions: Restrictions + request: Request + cookies: Cookies + headers: Headers service: Service service.name: Name - service.port: Port - service-name: Service Name + port: Port status: Status general: General - timeout: HTTP Request Timeout - details.timeout: HTTP Request Timeout (seconds) - virtualService: Virtual Service - alert.spec.host: Host can not be a wildcard, replace * with subdomain name - alert.spec.jwks_url_http: "JWKS URL: HTTP protocol is not secure, consider using HTTPS" - alert.spec.trusted_issuers_http: "Trusted Issuers: HTTP protocol is not secure, consider using HTTPS" - alert.corsPolicy: "Disabling custom CORS Policy is not recommended. Consider setting up CORS yourself" - alert.gateway.details: "Gateway must be in the format '{NAMESPACE}/{NAME}'" - alert.gateway.form: "Gateway must exist, specify both Namespace and Name" + host: Host + timeout: Timeout + virtualService: Virtual Service \ No newline at end of file diff --git a/config/ui-extensions/kustomization.yaml b/config/ui-extensions/kustomization.yaml index c96f4754b..aaf5cd29b 100644 --- a/config/ui-extensions/kustomization.yaml +++ b/config/ui-extensions/kustomization.yaml @@ -1,4 +1,5 @@ resources: - apigateways - - apirules + - apirules-v1beta1 - apirules-v2alpha1 + - apirules diff --git a/tests/ui/tests/support/navigation.ts b/tests/ui/tests/support/navigation.ts index a45129054..831881b30 100644 --- a/tests/ui/tests/support/navigation.ts +++ b/tests/ui/tests/support/navigation.ts @@ -10,7 +10,7 @@ export interface NavigationCommands { Cypress.Commands.add('navigateToApiRule', (name: string, namespace: string) : void => { cy.wrap(getK8sCurrentContext()).then((context) => { - cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/apirules/${name}`) + cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/apirules-v1beta1/${name}`) }); // Waiting to avoid dashboard rendering timing issues cy.wait(5000); @@ -18,7 +18,7 @@ Cypress.Commands.add('navigateToApiRule', (name: string, namespace: string) : vo Cypress.Commands.add('navigateToApiRuleList', (namespace: string) : void => { cy.wrap(getK8sCurrentContext()).then((context) => { - cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/apirules`) + cy.visit(`${config.clusterAddress}/cluster/${context}/namespaces/${namespace}/apirules-v1beta1`) }); // Waiting to avoid dashboard rendering timing issues cy.wait(5000);