diff --git a/.github/workflows/helm-lint-test.yaml b/.github/workflows/helm-lint-test.yaml index 452994e..ff36287 100644 --- a/.github/workflows/helm-lint-test.yaml +++ b/.github/workflows/helm-lint-test.yaml @@ -4,37 +4,76 @@ on: pull_request: paths: - 'deployment/**/*' + workflow_dispatch: + inputs: + force_install_unchanged: + description: 'Force install even if chart is unchanged' + required: false + default: 'false' + type: choice + options: + - 'true' + - 'false' + target_branch: + description: 'Target branch to compare against (leave empty to use current branch)' + required: false + default: '' + type: string jobs: lint-test: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v1 + uses: azure/setup-helm@v4 with: - version: v3.7.2 + version: v3.19.0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.13' - name: Set up chart-testing - uses: helm/chart-testing-action@v2.2.0 + uses: helm/chart-testing-action@v2.8.0 + + - name: Determine if install is needed + id: should-install + run: | + if [[ -n "${{ github.event.inputs.target_branch }}" ]]; then + TARGET_BRANCH="${{ github.event.inputs.target_branch }}" + elif [[ -n "${{ github.base_ref }}" ]]; then + TARGET_BRANCH="${{ github.base_ref }}" + else + TARGET_BRANCH="${{ github.ref_name }}" + fi + if [[ "${{ github.event.inputs.force_install_unchanged }}" == "true" ]] || ct list-changed --config deployment/ct.yaml --target-branch "$TARGET_BRANCH" | grep -q .; then + echo "result=true" >> $GITHUB_OUTPUT + else + echo "result=false" >> $GITHUB_OUTPUT + fi + echo "target_branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT - name: Run chart-testing (lint) - run: ct lint --config deployment/ct.yaml --target-branch ${{ github.base_ref }} --check-version-increment=false + if: steps.should-install.outputs.result == 'true' + run: ct lint --config deployment/ct.yaml --check-version-increment=false --target-branch ${{ steps.should-install.outputs.target_branch }} ${{ github.event.inputs.force_install_unchanged == 'true' && '--all' || '' }} - name: Create kind cluster - uses: helm/kind-action@v1.2.0 + if: steps.should-install.outputs.result == 'true' + uses: helm/kind-action@v1.13.0 + with: + version: v0.31.0 + node_image: kindest/node:v1.32.0 - - uses: azure/setup-kubectl@v2.0 + - uses: azure/setup-kubectl@v4 + if: steps.should-install.outputs.result == 'true' id: install - name: Setup OpenShift dependencies + if: steps.should-install.outputs.result == 'true' run: | kubectl apply -f deployment/hack/crds/*.yaml kubectl wait --for condition="established" -f deployment/hack/crds/*.yaml @@ -50,4 +89,5 @@ jobs: kubectl create -n console-plugin-nvidia-gpu secret tls plugin-serving-cert --key ca.key --cert ca.crt - name: Run chart-testing (install) - run: ct install --namespace console-plugin-nvidia-gpu --config deployment/ct.yaml --target-branch ${{ github.base_ref }} + if: steps.should-install.outputs.result == 'true' + run: ct install --target-branch ${{ steps.should-install.outputs.target_branch }} --namespace console-plugin-nvidia-gpu --config deployment/ct.yaml ${{ github.event.inputs.force_install_unchanged == 'true' && '--all' || '' }} diff --git a/deployment/hack/crds/consoleplugin.customresourcedefinition.yaml b/deployment/hack/crds/consoleplugin.customresourcedefinition.yaml index 438411a..0637294 100644 --- a/deployment/hack/crds/consoleplugin.customresourcedefinition.yaml +++ b/deployment/hack/crds/consoleplugin.customresourcedefinition.yaml @@ -1,6 +1,15 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + annotations: + api-approved.openshift.io: https://github.com/openshift/api/pull/1186 + api.openshift.io/merged-by-featuregates: "true" + capability.openshift.io/name: Console + description: Extension for configuring openshift web console plugins. + displayName: ConsolePlugin + include.release.openshift.io/ibm-cloud-managed: "true" + include.release.openshift.io/self-managed-high-availability: "true" + service.beta.openshift.io/inject-cabundle: "true" name: consoleplugins.console.openshift.io spec: conversion: @@ -16,43 +25,49 @@ spec: - name: v1 schema: openAPIV3Schema: - description: "ConsolePlugin is an extension for customizing OpenShift web - console by dynamically loading code from another service running on the - cluster. \n Compatibility level 1: Stable within a major release for a minimum - of 12 months or 3 minor releases (whichever is longer)." + description: |- + ConsolePlugin is an extension for customizing OpenShift web console by + dynamically loading code from another service running on the cluster. + + Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer). properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: - description: ConsolePluginSpec is the desired plugin configuration. + description: spec contains the desired configuration for the console plugin. properties: backend: description: backend holds the configuration of backend which is serving console's plugin . properties: service: - description: service is a Kubernetes Service that exposes the - plugin using a deployment with an HTTP server. The Service must - use HTTPS and Service serving certificate. The console backend - will proxy the plugins assets from the Service using the service - CA bundle. + description: |- + service is a Kubernetes Service that exposes the plugin using a + deployment with an HTTP server. The Service must use HTTPS and + Service serving certificate. The console backend will proxy the + plugins assets from the Service using the service CA bundle. properties: basePath: default: / - description: basePath is the path to the plugin's assets. - The primary asset it the manifest file called `plugin-manifest.json`, - which is a JSON document that contains metadata about the - plugin and the extensions. + description: |- + basePath is the path to the plugin's assets. The primary asset it the + manifest file called `plugin-manifest.json`, which is a JSON document + that contains metadata about the plugin and the extensions. maxLength: 256 minLength: 1 pattern: ^[a-zA-Z0-9.\-_~!$&'()*+,;=:@\/]*$ @@ -81,17 +96,141 @@ spec: - port type: object type: - description: "type is the backend type which servers the console's - plugin. Currently only \"Service\" is supported. \n ---" + description: | + type is the backend type which servers the console's plugin. Currently only "Service" is supported. enum: - Service type: string required: - type type: object + contentSecurityPolicy: + description: |- + contentSecurityPolicy is a list of Content-Security-Policy (CSP) directives for the plugin. + Each directive specifies a list of values, appropriate for the given directive type, + for example a list of remote endpoints for fetch directives such as ScriptSrc. + Console web application uses CSP to detect and mitigate certain types of attacks, + such as cross-site scripting (XSS) and data injection attacks. + Dynamic plugins should specify this field if need to load assets from outside + the cluster or if violation reports are observed. Dynamic plugins should always prefer + loading their assets from within the cluster, either by vendoring them, or fetching + from a cluster service. + CSP violation reports can be viewed in the browser's console logs during development and + testing of the plugin in the OpenShift web console. + Available directive types are DefaultSrc, ScriptSrc, StyleSrc, ImgSrc, FontSrc and ConnectSrc. + Each of the available directives may be defined only once in the list. + The value 'self' is automatically included in all fetch directives by the OpenShift web + console's backend. + For more information about the CSP directives, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + + The OpenShift web console server aggregates the CSP directives and values across + its own default values and all enabled ConsolePlugin CRs, merging them into a single + policy string that is sent to the browser via `Content-Security-Policy` HTTP response header. + + Example: + ConsolePlugin A directives: + script-src: https://script1.com/, https://script2.com/ + font-src: https://font1.com/ + + ConsolePlugin B directives: + script-src: https://script2.com/, https://script3.com/ + font-src: https://font2.com/ + img-src: https://img1.com/ + + Unified set of CSP directives, passed to the OpenShift web console server: + script-src: https://script1.com/, https://script2.com/, https://script3.com/ + font-src: https://font1.com/, https://font2.com/ + img-src: https://img1.com/ + + OpenShift web console server CSP response header: + Content-Security-Policy: default-src 'self'; base-uri 'self'; script-src 'self' https://script1.com/ https://script2.com/ https://script3.com/; font-src 'self' https://font1.com/ https://font2.com/; img-src 'self' https://img1.com/; style-src 'self'; frame-src 'none'; object-src 'none' + items: + description: ConsolePluginCSP holds configuration for a specific + CSP directive + properties: + directive: + description: |- + directive specifies which Content-Security-Policy directive to configure. + Available directive types are DefaultSrc, ScriptSrc, StyleSrc, ImgSrc, FontSrc and ConnectSrc. + DefaultSrc directive serves as a fallback for the other CSP fetch directives. + For more information about the DefaultSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src + ScriptSrc directive specifies valid sources for JavaScript. + For more information about the ScriptSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src + StyleSrc directive specifies valid sources for stylesheets. + For more information about the StyleSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src + ImgSrc directive specifies a valid sources of images and favicons. + For more information about the ImgSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/img-src + FontSrc directive specifies valid sources for fonts loaded using @font-face. + For more information about the FontSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/font-src + ConnectSrc directive restricts the URLs which can be loaded using script interfaces. + For more information about the ConnectSrc directive, see: + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src + enum: + - DefaultSrc + - ScriptSrc + - StyleSrc + - ImgSrc + - FontSrc + - ConnectSrc + type: string + values: + description: |- + values defines an array of values to append to the console defaults for this directive. + Each ConsolePlugin may define their own directives with their values. These will be set + by the OpenShift web console's backend, as part of its Content-Security-Policy header. + The array can contain at most 16 values. Each directive value must have a maximum length + of 1024 characters and must not contain whitespace, commas (,), semicolons (;) or single + quotes ('). The value '*' is not permitted. + Each value in the array must be unique. + items: + description: |- + CSPDirectiveValue is single value for a Content-Security-Policy directive. + Each directive value must have a maximum length of 1024 characters and must not contain + whitespace, commas (,), semicolons (;) or single quotes ('). The value '*' is not permitted. + maxLength: 1024 + minLength: 1 + type: string + x-kubernetes-validations: + - message: CSP directive value cannot contain a quote + rule: '!self.contains("''")' + - message: CSP directive value cannot contain a whitespace + rule: '!self.matches(''\\s'')' + - message: CSP directive value cannot contain a comma + rule: '!self.contains('','')' + - message: CSP directive value cannot contain a semi-colon + rule: '!self.contains('';'')' + - message: CSP directive value cannot be a wildcard + rule: self != '*' + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: each CSP directive value must be unique + rule: self.all(x, self.exists_one(y, x == y)) + required: + - directive + - values + type: object + maxItems: 5 + type: array + x-kubernetes-list-map-keys: + - directive + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: the total combined size of values of all directives must + not exceed 8192 (8kb) + rule: self.map(x, x.values.map(y, y.size()).sum()).sum() < 8192 displayName: - description: displayName is the display name of the plugin. The dispalyName - should be between 1 and 128 characters. + description: |- + displayName is the display name of the plugin. + The dispalyName should be between 1 and 128 characters. maxLength: 128 minLength: 1 type: string @@ -99,13 +238,12 @@ spec: description: i18n is the configuration of plugin's localization resources. properties: loadType: - description: loadType indicates how the plugin's localization - resource should be loaded. Valid values are Preload, Lazy and - the empty string. When set to Preload, all localization resources - are fetched when the plugin is loaded. When set to Lazy, localization - resources are lazily loaded as and when they are required by - the console. When omitted or set to the empty string, the behaviour - is equivalent to Lazy type. + description: |- + loadType indicates how the plugin's localization resource should be loaded. + Valid values are Preload, Lazy and the empty string. + When set to Preload, all localization resources are fetched when the plugin is loaded. + When set to Lazy, localization resources are lazily loaded as and when they are required by the console. + When omitted or set to the empty string, the behaviour is equivalent to Lazy type. enum: - Preload - Lazy @@ -115,34 +253,43 @@ spec: - loadType type: object proxy: - description: proxy is a list of proxies that describe various service - type to which the plugin needs to connect to. + description: |- + proxy is a list of proxies that describe various service type + to which the plugin needs to connect to. items: - description: ConsolePluginProxy holds information on various service - types to which console's backend will proxy the plugin's requests. + description: |- + ConsolePluginProxy holds information on various service types + to which console's backend will proxy the plugin's requests. properties: alias: - description: "alias is a proxy name that identifies the plugin's - proxy. An alias name should be unique per plugin. The console - backend exposes following proxy endpoint: \n /api/proxy/plugin///? - \n Request example path: \n /api/proxy/plugin/acm/search/pods?namespace=openshift-apiserver" + description: |- + alias is a proxy name that identifies the plugin's proxy. An alias name + should be unique per plugin. The console backend exposes following + proxy endpoint: + + /api/proxy/plugin///? + + Request example path: + + /api/proxy/plugin/acm/search/pods?namespace=openshift-apiserver maxLength: 128 minLength: 1 pattern: ^[A-Za-z0-9-_]+$ type: string authorization: default: None - description: authorization provides information about authorization - type, which the proxied request should contain + description: |- + authorization provides information about authorization type, + which the proxied request should contain enum: - UserToken - None type: string caCertificate: - description: caCertificate provides the cert authority certificate - contents, in case the proxied Service is using custom service - CA. By default, the service CA bundle provided by the service-ca - operator is used. + description: |- + caCertificate provides the cert authority certificate contents, + in case the proxied Service is using custom service CA. + By default, the service CA bundle provided by the service-ca operator is used. pattern: ^-----BEGIN CERTIFICATE-----([\s\S]*)-----END CERTIFICATE-----\s?$ type: string endpoint: @@ -150,12 +297,12 @@ spec: which the request is proxied to. properties: service: - description: 'service is an in-cluster Service that the - plugin will connect to. The Service must use HTTPS. The - console backend exposes an endpoint in order to proxy - communication between the plugin and the Service. Note: - service field is required for now, since currently only - "Service" type is supported.' + description: |- + service is an in-cluster Service that the plugin will connect to. + The Service must use HTTPS. The console backend exposes an endpoint + in order to proxy communication between the plugin and the Service. + Note: service field is required for now, since currently only "Service" + type is supported. properties: name: description: name of Service that the plugin needs to @@ -170,8 +317,9 @@ spec: minLength: 1 type: string port: - description: port on which the Service that the plugin - needs to connect to is listening on. + description: |- + port on which the Service that the plugin needs to connect to + is listening on. format: int32 maximum: 65535 minimum: 1 @@ -182,8 +330,8 @@ spec: - port type: object type: - description: "type is the type of the console plugin's proxy. - Currently only \"Service\" is supported. \n ---" + description: | + type is the type of the console plugin's proxy. Currently only "Service" is supported. enum: - Service type: string @@ -195,6 +343,7 @@ spec: - endpoint type: object type: array + x-kubernetes-list-type: atomic required: - backend - displayName