From 9f620cd32316f9062be51ecb6ac2ab47892f6dca Mon Sep 17 00:00:00 2001 From: Gaurav Trivedi Date: Tue, 7 Jul 2026 09:11:40 +0530 Subject: [PATCH] feat: promote Extend to top-level Antora module (JTBD Phase 3) Create modules/extend/ with 31 pages, 4 partials, 5 examples + 16 telemetry examples, and nav.adoc structured as two books: - "Customize workspace tooling" (21 pages) - "Add AI and analytics capabilities" (10 pages) Move IDE extension management, editor configuration, private registry deployment, AI provider registration, and custom telemetry plugin content from administration-guide into the new Extend module. New pages added (not present in previous admin-guide): - extending-devspaces.adoc (overview concept for Book 1) - deploy-open-vsx-with-prebuilt-image.adoc (prebuilt registry deploy) - ai-and-analytics-capabilities.adoc (overview concept for Book 2) - disabling-ai-providers.adoc (disable AI procedure) - telemetry-plugin-overview.adoc (concept, replaces monolithic page) - creating-a-telemetry-server.adoc (split from monolithic) - creating-a-telemetry-backend.adoc (split from monolithic) - implementing-telemetry-backend-event-handlers.adoc (split) - deploying-a-telemetry-plugin.adoc (split from monolithic) - configuring-workspaces-to-load-telemetry-plugin.adoc (split) Telemetry content split from single 412-line page into 6 focused pages matching downstream CQA-compliant modular structure. JTBD compliance: - Imperative procedure titles matching user goals - WHAT+WHY abstracts with title-verb consistency - Admin prerequisites on all procedures - [role="_abstract"] and [role="_additional-resources"] annotations - No generic persona snippets in topic files - Content body synced with downstream CQA source of truth - Nav structure matches TOC mapping spreadsheet (2 books, 6 sections) Co-authored-by: Cursor --- .gitignore | 1 + antora.yml | 7 +- ...ncealing-editors-additional-resources.adoc | 9 - .../snip_che-managing-extensions.adoc | 1 - modules/administration-guide/nav.adoc | 23 +- ...ster-custom-resource-fields-reference.adoc | 4 +- .../pages/concealing-editors.adoc | 36 -- .../pages/configuring-dashboard.adoc | 10 +- .../configuring-editors-download-urls.adoc | 43 -- ...uring-single-and-multiroot-workspaces.adoc | 91 ---- .../pages/configuring-visual-studio-code.adoc | 16 - .../pages/creating-a-telemetry-plugin.adoc | 411 ------------------ ...ions-for-microsoft-visual-studio-code.adoc | 176 -------- .../pages/manage-extension-installation.adoc | 153 ------- ...ions-for-microsoft-visual-studio-code.adoc | 68 --- ...or-removing-extensions-in-a-workspace.adoc | 96 ---- ...dding-or-removing-extensions-on-linux.adoc | 124 ------ ...ft-visual-studio-code-open-source-ide.adoc | 2 +- ...-code-extensions-at-workspace-startup.adoc | 4 +- .../AnalyticsManagerSkeleton.java | 0 .../Dockerfile.jvm | 0 .../Dockerfile.native | 0 .../MainConfiguration.java | 0 .../TelemetryService.java | 0 .../creating-a-telemetry-plugin/destroy.java | 0 .../increaseDuration.java | 0 .../isEnabled.java | 0 .../creating-a-telemetry-plugin/main.go | 0 .../onActivity.java | 0 .../creating-a-telemetry-plugin/onEvent.java | 0 .../creating-a-telemetry-plugin/plugin.yaml | 0 .../pom_snippet.xml | 0 .../project_scaffolding.sh | 0 .../creating-a-telemetry-plugin/settings.xml | 0 .../webserver.yaml | 0 .../snip_che-default-vsx-registry.adoc | 0 .../examples/snip_che-editor-id-list.adoc | 2 +- .../snip_che-managing-extensions.adoc | 1 + ...che-non-default-vsx-registry-instance.adoc | 0 .../devworkspace_telemetry_plugin.png | Bin .../images/devworkspace_telemetry_plugin.png | Bin 0 -> 78472 bytes modules/extend/nav.adoc | 39 ++ .../pages/ai-and-analytics-capabilities.adoc | 34 ++ .../ai-provider-api-key-secret-reference.adoc | 15 +- modules/extend/pages/concealing-editors.adoc | 54 +++ .../pages/configuring-ai-providers.adoc | 31 +- .../pages/configuring-default-editor.adoc | 23 +- .../configuring-editors-definitions.adoc | 55 ++- .../configuring-editors-download-urls.adoc | 57 +++ ...uring-single-and-multiroot-workspaces.adoc | 89 ++++ ...configuring-the-open-vsx-registry-url.adoc | 14 +- .../pages/configuring-visual-studio-code.adoc | 18 + ...g-workspaces-to-load-telemetry-plugin.adoc | 64 +++ .../pages/creating-a-telemetry-backend.adoc | 102 +++++ .../pages/creating-a-telemetry-server.adoc | 60 +++ ...ions-for-microsoft-visual-studio-code.adoc | 146 +++++++ .../deploy-open-vsx-with-prebuilt-image.adoc | 151 +++++++ .../pages/deploying-a-telemetry-plugin.adoc | 208 +++++++++ .../extend/pages/disabling-ai-providers.adoc | 53 +++ ...ions-for-microsoft-visual-studio-code.adoc | 73 ++-- modules/extend/pages/extending-devspaces.adoc | 41 ++ ...rosoft-visual-studio-code-open-source.adoc | 17 +- ...ting-telemetry-backend-event-handlers.adoc | 141 ++++++ .../pages/manage-extension-installation.adoc | 142 ++++++ .../pages/managing-ide-extensions.adoc | 10 +- ...oc_configure-internal-open-vsx-access.adoc | 10 +- ...ng-extension-from-postgresql-database.adoc | 24 +- ...ing-extension-using-openvsx-admin-api.adoc | 14 +- .../proc_deploy-open-vsx-from-source.adoc | 28 +- .../proc_deploy-open-vsx-from-workspace.adoc | 20 +- .../pages/show-deprecated-editors.adoc | 23 +- .../pages/telemetry-plugin-overview.adoc | 32 ++ ...ions-for-microsoft-visual-studio-code.adoc | 75 ++++ ...or-removing-extensions-in-a-workspace.adoc | 102 +++++ ...dding-or-removing-extensions-on-linux.adoc | 126 ++++++ ...lecting-an-open-vsx-registry-instance.adoc | 9 +- .../secure/pages/security-best-practices.adoc | 2 +- 77 files changed, 1946 insertions(+), 1434 deletions(-) delete mode 100644 modules/administration-guide/examples/snip_che-concealing-editors-additional-resources.adoc delete mode 100644 modules/administration-guide/examples/snip_che-managing-extensions.adoc delete mode 100644 modules/administration-guide/pages/concealing-editors.adoc delete mode 100644 modules/administration-guide/pages/configuring-editors-download-urls.adoc delete mode 100644 modules/administration-guide/pages/configuring-single-and-multiroot-workspaces.adoc delete mode 100644 modules/administration-guide/pages/configuring-visual-studio-code.adoc delete mode 100644 modules/administration-guide/pages/creating-a-telemetry-plugin.adoc delete mode 100644 modules/administration-guide/pages/default-extensions-for-microsoft-visual-studio-code.adoc delete mode 100644 modules/administration-guide/pages/manage-extension-installation.adoc delete mode 100644 modules/administration-guide/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc delete mode 100644 modules/administration-guide/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc delete mode 100644 modules/administration-guide/partials/proc_adding-or-removing-extensions-on-linux.adoc rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/Dockerfile.jvm (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/Dockerfile.native (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/MainConfiguration.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/TelemetryService.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/destroy.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/increaseDuration.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/isEnabled.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/main.go (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/onActivity.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/onEvent.java (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/plugin.yaml (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/pom_snippet.xml (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/project_scaffolding.sh (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/settings.xml (100%) rename modules/{administration-guide => extend}/examples/creating-a-telemetry-plugin/webserver.yaml (100%) rename modules/{administration-guide => extend}/examples/snip_che-default-vsx-registry.adoc (100%) rename modules/{administration-guide => extend}/examples/snip_che-editor-id-list.adoc (73%) create mode 100644 modules/extend/examples/snip_che-managing-extensions.adoc rename modules/{administration-guide => extend}/examples/snip_che-non-default-vsx-registry-instance.adoc (100%) rename modules/{administration-guide => extend}/images/creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png (100%) create mode 100644 modules/extend/images/devworkspace_telemetry_plugin.png create mode 100644 modules/extend/nav.adoc create mode 100644 modules/extend/pages/ai-and-analytics-capabilities.adoc rename modules/{administration-guide => extend}/pages/ai-provider-api-key-secret-reference.adoc (88%) create mode 100644 modules/extend/pages/concealing-editors.adoc rename modules/{administration-guide => extend}/pages/configuring-ai-providers.adoc (85%) rename modules/{administration-guide => extend}/pages/configuring-default-editor.adoc (51%) rename modules/{administration-guide => extend}/pages/configuring-editors-definitions.adoc (82%) create mode 100644 modules/extend/pages/configuring-editors-download-urls.adoc create mode 100644 modules/extend/pages/configuring-single-and-multiroot-workspaces.adoc rename modules/{administration-guide => extend}/pages/configuring-the-open-vsx-registry-url.adoc (81%) create mode 100644 modules/extend/pages/configuring-visual-studio-code.adoc create mode 100644 modules/extend/pages/configuring-workspaces-to-load-telemetry-plugin.adoc create mode 100644 modules/extend/pages/creating-a-telemetry-backend.adoc create mode 100644 modules/extend/pages/creating-a-telemetry-server.adoc create mode 100644 modules/extend/pages/default-extensions-for-microsoft-visual-studio-code.adoc create mode 100644 modules/extend/pages/deploy-open-vsx-with-prebuilt-image.adoc create mode 100644 modules/extend/pages/deploying-a-telemetry-plugin.adoc create mode 100644 modules/extend/pages/disabling-ai-providers.adoc rename modules/{administration-guide => extend}/pages/editor-configurations-for-microsoft-visual-studio-code.adoc (53%) create mode 100644 modules/extend/pages/extending-devspaces.adoc rename modules/{administration-guide => extend}/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc (55%) create mode 100644 modules/extend/pages/implementing-telemetry-backend-event-handlers.adoc create mode 100644 modules/extend/pages/manage-extension-installation.adoc rename modules/{administration-guide => extend}/pages/managing-ide-extensions.adoc (71%) rename modules/{administration-guide => extend}/pages/proc_configure-internal-open-vsx-access.adoc (83%) rename modules/{administration-guide => extend}/pages/proc_deleting-extension-from-postgresql-database.adoc (82%) rename modules/{administration-guide => extend}/pages/proc_deleting-extension-using-openvsx-admin-api.adoc (83%) rename modules/{administration-guide => extend}/pages/proc_deploy-open-vsx-from-source.adoc (90%) rename modules/{administration-guide => extend}/pages/proc_deploy-open-vsx-from-workspace.adoc (89%) rename modules/{administration-guide => extend}/pages/show-deprecated-editors.adoc (54%) create mode 100644 modules/extend/pages/telemetry-plugin-overview.adoc create mode 100644 modules/extend/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc create mode 100644 modules/extend/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc create mode 100644 modules/extend/partials/proc_adding-or-removing-extensions-on-linux.adoc rename modules/{administration-guide => extend}/partials/proc_selecting-an-open-vsx-registry-instance.adoc (86%) diff --git a/.gitignore b/.gitignore index bed5638306..d7a37026fe 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ yarn.lock # Do not commit diagrams generated for downstream crw* +CLAUDE.md diff --git a/antora.yml b/antora.yml index 61f46682e6..c1cf0cc378 100644 --- a/antora.yml +++ b/antora.yml @@ -12,6 +12,7 @@ nav: - modules/upgrade/nav.adoc - modules/secure/nav.adoc - modules/optimize/nav.adoc + - modules/extend/nav.adoc - modules/administration-guide/nav.adoc - modules/extensions/nav.adoc - modules/glossary/nav.adoc @@ -30,7 +31,7 @@ asciidoc: che-plugin-registry-directory: che-plugin-registry plugin-registry-repo-url: "https://github.com/redhat-developer/che-plugin-registry" devworkspace-operator-index-disconnected-install: quay.io/devfile/devworkspace-operator-index:release-digest - devworkspace-operator-version-patch: "0.42.0" + devworkspace-operator-version-patch: "0.41.0" devworkspace: DevWorkspace devworkspace-id: devworkspace docker-cli: docker @@ -102,8 +103,8 @@ asciidoc: prod-upstream: Eclipse Che prod-url: "https://__<che_fqdn>__" prod-ver-major: "7" - prod-ver-patch: "7.120.0" - prod-ver: "7.120" + prod-ver-patch: "7.119.0" + prod-ver: "7.119" prod-workspace: che-ws prod: Eclipse Che prod2: Eclipse Che diff --git a/modules/administration-guide/examples/snip_che-concealing-editors-additional-resources.adoc b/modules/administration-guide/examples/snip_che-concealing-editors-additional-resources.adoc deleted file mode 100644 index 3c98c8da29..0000000000 --- a/modules/administration-guide/examples/snip_che-concealing-editors-additional-resources.adoc +++ /dev/null @@ -1,9 +0,0 @@ -:_content-type: SNIPPET - -.Additional resources - -* xref:install:using-chectl-to-configure-the-checluster-custom-resource-during-installation.adoc[] - -* xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] - -* {editor-definition-samples-link} diff --git a/modules/administration-guide/examples/snip_che-managing-extensions.adoc b/modules/administration-guide/examples/snip_che-managing-extensions.adoc deleted file mode 100644 index c685aea45e..0000000000 --- a/modules/administration-guide/examples/snip_che-managing-extensions.adoc +++ /dev/null @@ -1 +0,0 @@ -More details about managing IDE extensions are available in link:https://eclipse.dev/che/docs/stable/administration-guide/extensions-for-microsoft-visual-studio-code-open-source/[the official documentation]. \ No newline at end of file diff --git a/modules/administration-guide/nav.adoc b/modules/administration-guide/nav.adoc index 844c4820c7..4ee52b32d6 100644 --- a/modules/administration-guide/nav.adoc +++ b/modules/administration-guide/nav.adoc @@ -23,7 +23,7 @@ *** xref:enabling-container-run-capabilities.adoc[] ** xref:configuring-observability.adoc[] **** xref:the-woopra-telemetry-plugin.adoc[] -**** xref:creating-a-telemetry-plugin.adoc[] +**** xref:extend:telemetry-plugin-overview.adoc[] *** xref:configuring-server-logging.adoc[] *** xref:collecting-logs-using-chectl.adoc[] **** xref:monitoring-the-dev-workspace-operator.adoc[] @@ -43,13 +43,6 @@ *** xref:con_persistent-user-home.adoc[] ** xref:configuring-dashboard.adoc[] *** xref:configuring-getting-started-samples.adoc[] -*** xref:configuring-editors-definitions.adoc[] -*** xref:show-deprecated-editors.adoc[] -*** xref:configuring-default-editor.adoc[] -*** xref:concealing-editors.adoc[] -*** xref:configuring-editors-download-urls.adoc[] -*** xref:configuring-ai-providers.adoc[] -**** xref:ai-provider-api-key-secret-reference.adoc[] *** xref:customizing-openshift-che-branding-images.adoc[] ** xref:configuring-oauth-for-git-providers.adoc[] *** xref:configuring-oauth-2-for-github.adoc[] @@ -64,18 +57,4 @@ ** xref:devworkspace-backup.adoc[] *** xref:devworkspace-backup-integrated-openshift-registry.adoc[] *** xref:devworkspace-backup-regular-oci-registry.adoc[] -* xref:managing-ide-extensions.adoc[] -** xref:extensions-for-microsoft-visual-studio-code-open-source.adoc[] -** xref:configuring-the-open-vsx-registry-url.adoc[] -** xref:proc_deploy-open-vsx-from-source.adoc[] -** xref:proc_deploy-open-vsx-from-workspace.adoc[] -** xref:proc_configure-internal-open-vsx-access.adoc[] -** xref:proc_deleting-extension-using-openvsx-admin-api.adoc[] -** xref:proc_deleting-extension-from-postgresql-database.adoc[] -* xref:configuring-visual-studio-code.adoc[] -** xref:configuring-single-and-multiroot-workspaces.adoc[] -** xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] -** xref:default-extensions-for-microsoft-visual-studio-code.adoc[] -** xref:manage-extension-installation.adoc[] -** xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] * xref:managing-workloads-using-the-che-server-api.adoc[] diff --git a/modules/administration-guide/pages/checluster-custom-resource-fields-reference.adoc b/modules/administration-guide/pages/checluster-custom-resource-fields-reference.adoc index 2d4a2989e3..6629d7d385 100644 --- a/modules/administration-guide/pages/checluster-custom-resource-fields-reference.adoc +++ b/modules/administration-guide/pages/checluster-custom-resource-fields-reference.adoc @@ -191,5 +191,5 @@ Optional. A JSON array of provider `id` values that are pre-selected in the AI S .Additional resources -* xref:configuring-ai-providers.adoc[] -* xref:ai-provider-api-key-secret-reference.adoc[] +* xref:extend:configuring-ai-providers.adoc[] +* xref:extend:ai-provider-api-key-secret-reference.adoc[] diff --git a/modules/administration-guide/pages/concealing-editors.adoc b/modules/administration-guide/pages/concealing-editors.adoc deleted file mode 100644 index e4c0160410..0000000000 --- a/modules/administration-guide/pages/concealing-editors.adoc +++ /dev/null @@ -1,36 +0,0 @@ -:_content-type: PROCEDURE -:description: Concealing editors in the Dashboard UI -:keywords: administration guide, concealing, dashboard, editors -:navtitle: Concealing editors - -[id="concealing-editors"] -= Concealing editors - -Learn how to conceal {prod-short} editors. This is useful when you want to hide selected editors from the Dashboard UI, for example hide the IntelliJ IDEA Ultimate and have only Visual Studio Code - Open Source visible. - -.Prerequisites - -* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. - -* `jq`. See link:https://stedolan.github.io/jq/download/[Downloading `jq`]. - -.Procedure - -include::example$snip_che-editor-id-list.adoc[] - -. Configure the `CheCluster` Custom Resource. See xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[]. -+ -[source,yaml] ----- -spec: - components: - dashboard: - deployment: - containers: - - env: - - name: CHE_HIDE_EDITORS_BY_ID - value: 'che-incubator/che-webstorm-server/latest, che-incubator/che-webstorm-server/next' <1> ----- -<1> A string containing comma-separated IDs of editors to hide. - -include::example$snip_{project-context}-concealing-editors-additional-resources.adoc[] diff --git a/modules/administration-guide/pages/configuring-dashboard.adoc b/modules/administration-guide/pages/configuring-dashboard.adoc index 56047f6105..503eafd2cf 100644 --- a/modules/administration-guide/pages/configuring-dashboard.adoc +++ b/modules/administration-guide/pages/configuring-dashboard.adoc @@ -9,15 +9,15 @@ * xref:configuring-getting-started-samples.adoc[] -* xref:configuring-editors-definitions.adoc[] +* xref:extend:configuring-editors-definitions.adoc[] -* xref:configuring-editors-download-urls.adoc[] +* xref:extend:configuring-editors-download-urls.adoc[] -* xref:show-deprecated-editors.adoc[] +* xref:extend:show-deprecated-editors.adoc[] -* xref:configuring-default-editor.adoc[] +* xref:extend:configuring-default-editor.adoc[] -* xref:concealing-editors.adoc[] +* xref:extend:concealing-editors.adoc[] * xref:customizing-openshift-che-branding-images.adoc[] diff --git a/modules/administration-guide/pages/configuring-editors-download-urls.adoc b/modules/administration-guide/pages/configuring-editors-download-urls.adoc deleted file mode 100644 index ea073a5620..0000000000 --- a/modules/administration-guide/pages/configuring-editors-download-urls.adoc +++ /dev/null @@ -1,43 +0,0 @@ -:_content-type: PROCEDURE -:description: Configuring editors download urls -:keywords: administration guide, dashboard, editors -:navtitle: Configuring editors download urls - -[id="configuring-editors-download-urls"] -= Configuring editors download URLs - -This procedure describes how to configure download URLs for editors. This feature is valuable in air-gapped environments where editors cannot be retrieved from the public internet. - -[IMPORTANT] -==== -This configuration option is currently supported only for JetBrains editors. Do not use this feature with other editor types. -==== - -.Prerequisites - -* You have an active `_orch-cli_` session with administrative permissions to the `{orch-name}` cluster. For more information, see {orch-cli-link}. - -* `jq`. See link:https://stedolan.github.io/jq/download/[Downloading `jq`]. - -.Procedure - -include::example$snip_che-editor-id-list.adoc[] - -. Configure the download URLs for editors: -+ -[source,subs="+quotes,+attributes"] ----- -{orch-cli} patch checluster/{prod-checluster} \ - --namespace {prod-namespace} \ - --type='merge' \ - -p '{ - "spec": { - "devEnvironments": { - "editorsDownloadUrls": [ - { "editor": "publisher1/editor-name1/version1", "url": "https://example.com/editor1.tar.gz" }, - { "editor": "publisher2/editor-name2/version2", "url": "https://example.com/editor2.tar.gz" } - ] - } - } - }' ----- diff --git a/modules/administration-guide/pages/configuring-single-and-multiroot-workspaces.adoc b/modules/administration-guide/pages/configuring-single-and-multiroot-workspaces.adoc deleted file mode 100644 index cd4004999c..0000000000 --- a/modules/administration-guide/pages/configuring-single-and-multiroot-workspaces.adoc +++ /dev/null @@ -1,91 +0,0 @@ -:_content-type: PROCEDURE -:description: Configuring single and multiroot workspaces -:keywords: singleroot, multiroot, workspace -:navtitle: Configuring single and multiroot workspaces -// :page-aliases: - -[id="configuring-single-and-multiroot-workspaces"] -= Configuring single and multi root workspaces - -With the multi-root workspace feature, you can work with multiple project folders in the same workspace. This is useful when you are working on several related projects at once, such as product documentation and product code repositories. - -TIP: See link:https://code.visualstudio.com/docs/editing/workspaces/workspaces[What is a VS Code workspace] for better understanding and authoring the workspace files. - -[NOTE] -==== -The workspace is set to open in multi-root mode by default. -==== - -Once workspace is started, the `/projects/.code-workspace` workspace file is generated. The workspace file will contain all the projects described in the devfile. - -[source,json] ----- -{ - "folders": [ - { - "name": "project-1", - "path": "/projects/project-1" - }, - { - "name": "project-2", - "path": "/projects/project-2" - } - ] -} ----- - -If the workspace file already exist, it will be updated and all missing projects will be taken from the devfile. -If you remove a project from the devfile, it will be left in the workspace file. - -You can change the default behavior and provide your own workspace file or switch to a single-root workspace. - -.Procedure - -* Provide your own workspace file. - -** Put a workspace file with the name `.code-workspace` into the root of your repository. After workspace creation, the Visual Studio Code - Open Source ("Code - OSS") will use the workspace file as it is. -+ -[source,json] ----- -{ - "folders": [ - { - "name": "project-name", - "path": "." - } - ] -} ----- -+ -[IMPORTANT] -==== -Be careful when creating a workspace file. In case of errors, an empty Visual Studio Code - Open Source ("Code - OSS") will be opened instead. -==== -+ -[IMPORTANT] -==== -If you have several projects, the workspace file will be taken from the first project. -If the workspace file does not exist in the first project, a new one will be created and placed in the `/projects` directory. -==== - -* Specify alternative workspace file. - -** Define the __VSCODE_DEFAULT_WORKSPACE__ environment variable in your devfile and specify the right location to the workspace file. -+ -[source,yaml] ----- - env: - - name: VSCODE_DEFAULT_WORKSPACE - value: "/projects/project-name/workspace-file" ----- - -* Open a workspace in a single-root mode. - -** Define __VSCODE_DEFAULT_WORKSPACE__ environment variable and set it to the root. -+ -[source,yaml] ----- - env: - - name: VSCODE_DEFAULT_WORKSPACE - value: "/" ----- diff --git a/modules/administration-guide/pages/configuring-visual-studio-code.adoc b/modules/administration-guide/pages/configuring-visual-studio-code.adoc deleted file mode 100644 index d25bd834d7..0000000000 --- a/modules/administration-guide/pages/configuring-visual-studio-code.adoc +++ /dev/null @@ -1,16 +0,0 @@ -:_content-type: CONCEPT -:description: Configuring Visual Studio Code - Open Source ("Code - OSS") -:keywords: vscode, workspace -:navtitle: Configuring Visual Studio Code - Open Source ("Code - OSS") -//:page-aliases: - -[id="configuring-visual-studio-code"] -= Configuring Visual Studio Code - Open Source ("Code - OSS") - -Learn how to configure Visual Studio Code - Open Source ("Code - OSS"). - -* xref:configuring-single-and-multiroot-workspaces.adoc[] -* xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] -* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] -* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] -* xref:manage-extension-installation.adoc[] \ No newline at end of file diff --git a/modules/administration-guide/pages/creating-a-telemetry-plugin.adoc b/modules/administration-guide/pages/creating-a-telemetry-plugin.adoc deleted file mode 100644 index 6dc82233db..0000000000 --- a/modules/administration-guide/pages/creating-a-telemetry-plugin.adoc +++ /dev/null @@ -1,411 +0,0 @@ -:_content-type: REFERENCE -:description: Creating a telemetry plugin -:keywords: extensions, telemetry -:navtitle: Creating a telemetry plugin -:page-aliases: extensions:creating-a-telemetry-plug-in.adoc - -[id="creating-a-telemetry-plugin"] -= Creating a telemetry plugin - -This section shows how to create an `AnalyticsManager` class that extends link:https://github.com/che-incubator/che-workspace-telemetry-client/blob/master/backend-base/src/main/java/org/eclipse/che/incubator/workspace/telemetry/base/AbstractAnalyticsManager.java[`AbstractAnalyticsManager`] and implements the following methods: - -* `isEnabled()` - determines whether the telemetry backend is functioning correctly. This can mean always returning `true`, or have more complex checks, for example, returning `false` when a connection property is missing. -* `destroy()` - cleanup method that is run before shutting down the telemetry backend. This method sends the `WORKSPACE_STOPPED` event. -* `onActivity()` - notifies that some activity is still happening for a given user. This is mainly used to send `WORKSPACE_INACTIVE` events. -* `onEvent()` - submits telemetry events to the telemetry server, such as `WORKSPACE_USED` or `WORKSPACE_STARTED`. -* `increaseDuration()` - increases the duration of a current event rather than sending many events in a small frame of time. - -The following sections cover: - -* Creating a telemetry server to echo events to standard output. -* Extending the {prod-short} telemetry client and implementing a user's custom backend. -* Creating a `plugin.yaml` file representing a {devworkspace} plugin for the custom backend. -* Specifying of a location of a custom plugin to {prod-short} by setting the `workspacesDefaultPlugins` attribute from the `CheCluster` custom resource. - -== Getting started - -This document describes the steps required to extend the {prod-short} telemetry system to communicate with to a custom backend: - -. Creating a server process that receives events -. Extending {prod-short} libraries to create a backend that sends events to the server -. Packaging the telemetry backend in a container and deploying it to an image registry -. Adding a plugin for your backend and instructing {prod-short} to load the plugin in your {devworkspace}s - -A finished example of the telemetry backend is available link:https://github.com/che-incubator/devworkspace-telemetry-example-plugin[here]. - -== Creating a server that receives events - -For demonstration purposes, this example shows how to create a server that receives events from our telemetry plugin and writes them to standard output. - -For production use cases, consider integrating with a third-party telemetry system (for example, Segment, Woopra) rather than creating your own telemetry server. In this case, use your provider's APIs to send events from your custom backend to their system. - -The following Go code starts a server on port `8080` and writes events to standard output: - -.`main.go` -==== -[source,go] ----- -include::example$creating-a-telemetry-plugin/main.go[] ----- -==== - -Create a container image based on this code and expose it as a deployment in OpenShift in the `{prod-namespace}` {orch-namespace}. The code for the example telemetry server is available at link:https://github.com/che-incubator/telemetry-server-example[telemetry-server-example]. To deploy the telemetry server, clone the repository and build the container: - -[subs="+attributes,+quotes"] ----- -$ git clone https://github.com/che-incubator/telemetry-server-example -$ cd telemetry-server-example -$ {docker-cli} build -t registry/organization/telemetry-server-example:latest . -$ {docker-cli} push registry/organization/telemetry-server-example:latest ----- - -Both `manifest_with_ingress.yaml` and `manifest_with_route` contain definitions for a Deployment and Service. The former also defines a {kubernetes} Ingress, while the latter defines an OpenShift Route. - -In the manifest file, replace the `image` and `host` fields to match the image you pushed, and the public hostname of your {platforms-name} cluster. Then run: - -[subs="+quotes,+attributes"] ----- -$ kubectl apply -f manifest_with_[ingress|route].yaml -n {prod-namespace} ----- - -== Creating the back-end project - -NOTE: For fast feedback when developing, it is recommended to do development inside a {devworkspace}. This way, you can run the application in a cluster and receive events from the front-end telemetry plugin. - -. Maven Quarkus project scaffolding: -+ ----- -include::example$creating-a-telemetry-plugin/project_scaffolding.sh[] ----- - -. Remove the files under `src/main/java/mygroup` and `src/test/java/mygroup`. - -. Consult the link:https://github.com/che-incubator/che-workspace-telemetry-client/packages[GitHub packages] for the latest version and Maven coordinates of `backend-base`. - -. Add the following dependencies to your `pom.xml`: -+ -.`pom.xml` -==== -[source,xml] ----- -include::example$creating-a-telemetry-plugin/pom_snippet.xml[] ----- -==== - -. Create a personal access token with `read:packages` permissions to download the `org.eclipse.che.incubator.workspace-telemetry:backend-base` dependency from link:https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages[GitHub packages]. - -. Add your GitHub username, personal access token and `che-incubator` repository details in your `~/.m2/settings.xml` file: -+ -.`settings.xml` -==== -[source,xml] ----- -include::example$creating-a-telemetry-plugin/settings.xml[] ----- -==== -+ - - -== Creating a concrete implementation of AnalyticsManager and adding specialized logic - -Create two files in your project under `src/main/java/mygroup`: - -* `MainConfiguration.java` - contains configuration provided to `AnalyticsManager`. -* `AnalyticsManager.java` - contains logic specific to the telemetry system. - -.`MainConfiguration.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/MainConfiguration.java[] ----- -==== -<1> A MicroProfile configuration annotation is used to inject the `welcome.message` configuration. - -For more details on how to set configuration properties specific to your backend, see the Quarkus link:https://quarkus.io/guides/config-reference[Configuration Reference Guide]. - -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java[] ----- -==== -<1> Log the welcome message if it was provided. -<2> Log the event received from the front-end plugin. - -Since `org.my.group.AnalyticsManager` and `org.my.group.MainConfiguration` are alternative beans, specify them using the `quarkus.arc.selected-alternatives` property in `src/main/resources/application.properties`. - -.`application.properties` -==== ----- -quarkus.arc.selected-alternatives=MainConfiguration,AnalyticsManager ----- -==== - -[id="running-the-application"] -== Running the application within a {devworkspace} - -. Set the `DEVWORKSPACE_TELEMETRY_BACKEND_PORT` environment variable in the {devworkspace}. Here, the value is set to `4167`. -+ ----- -spec: - template: - attributes: - workspaceEnv: - - name: DEVWORKSPACE_TELEMETRY_BACKEND_PORT - value: '4167' ----- - -. Restart the {devworkspace} from the {prod} dashboard. - -. Run the following command within a {devworkspace}'s terminal window to start the application. Use the `--settings` flag to specify path to the location of the `settings.xml` file that contains the GitHub access token. -+ ----- -$ mvn --settings=settings.xml quarkus:dev -Dquarkus.http.port=${DEVWORKSPACE_TELEMETRY_BACKEND_PORT} ----- -+ -The application now receives telemetry events through port `4167` from the front-end plugin. - -.Verification steps - -. Verify that the following output is logged: -+ ----- -INFO [org.ecl.che.inc.AnalyticsManager] (Quarkus Main Thread) No welcome message provided -INFO [io.quarkus] (Quarkus Main Thread) devworkspace-telemetry-example-plugin 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.7.2.Final) started in 0.323s. Listening on: http://localhost:4167 -INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. -INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, kubernetes-client, rest-client, rest-client-jackson, resteasy, resteasy-jsonb, smallrye-context-propagation, smallrye-openapi, swagger-ui, vertx] ----- - -. To verify that the `onEvent()` method of `AnalyticsManager` receives events from the front-end plugin, press the kbd:[l] key to disable Quarkus live coding and edit any file within the IDE. The following output should be logged: -+ ----- -INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Live reload disabled -INFO [org.ecl.che.inc.AnalyticsManager] (executor-thread-2) The received event is: Edit Workspace File in Che ----- - -== Implementing `isEnabled()` - -For the purposes of the example, this method always returns `true` whenever it is called. - -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/isEnabled.java[] ----- -==== - -It is possible to put more complex logic in `isEnabled()`. For example, the link:https://github.com/che-incubator/devworkspace-telemetry-woopra-plugin/blob/main/src/main/java/com/redhat/devworkspace/services/telemetry/woopra/AnalyticsManager.java[hosted {prod-short} Woopra backend] checks that a configuration property exists before determining if the backend is enabled. - -== Implementing `onEvent()` - -`onEvent()` sends the event received by the backend to the telemetry system. For the example application, it sends an HTTP POST payload to the `/event` endpoint from the telemetry server. - -=== Sending a POST request to the example telemetry server -For the following example, the telemetry server application is deployed to OpenShift at the following URL: `++http://little-telemetry-server-che.apps-crc.testing++`, where `apps-crc.testing` is the ingress domain name of the OpenShift cluster. - -. Set up the RESTEasy REST Client by creating `TelemetryService.java` -+ -.`TelemetryService.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/TelemetryService.java[] ----- -==== -<1> The endpoint to make the `POST` request to. - -. Specify the base URL for `TelemetryService` in the `src/main/resources/application.properties` file: -+ -.`application.properties` -==== ----- -org.my.group.TelemetryService/mp-rest/url=http://little-telemetry-server-che.apps-crc.testing ----- -==== - -. Inject `TelemetryService` into `AnalyticsManager` and send a `POST` request in `onEvent()` -+ -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/onEvent.java[] ----- -==== -+ -This sends an HTTP request to the telemetry server and automatically delays identical events for a small period of time. The default duration is 1500 milliseconds. - -== Implementing `increaseDuration()` - -Many telemetry systems recognize event duration. The `AbstractAnalyticsManager` merges similar events that happen in the same frame of time into one event. This implementation of `increaseDuration()` is a no-op. This method uses the APIs of the user's telemetry provider to alter the event or event properties to reflect the increased duration of an event. - -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/increaseDuration.java[] ----- -==== - -== Implementing `onActivity()` - -Set an inactive timeout limit, and use `onActivity()` to send a `WORKSPACE_INACTIVE` event if the last event time is longer than the timeout. - -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/onActivity.java[] ----- -==== - -== Implementing `destroy()` - -When `destroy()` is called, send a `WORKSPACE_STOPPED` event and shutdown any resources such as connection pools. - -.`AnalyticsManager.java` -==== -[source,java] ----- -include::example$creating-a-telemetry-plugin/destroy.java[] ----- -==== - -Running `mvn quarkus:dev` as described in xref:running-the-application[] and terminating the application with kbd:[Ctrl+C] sends a `WORKSPACE_STOPPED` event to the server. - -[id="packaging-the-quarkus-application"] -== Packaging the Quarkus application - -See link:https://quarkus.io/guides/building-native-image#using-a-multi-stage-docker-build[the Quarkus documentation] for the best instructions to package the application in a container. Build and push the container to a container registry of your choice. - -=== Sample Dockerfile for building a Quarkus image running with JVM - -.`Dockerfile.jvm` -==== -[source,yaml] ----- -include::example$creating-a-telemetry-plugin/Dockerfile.jvm[] ----- -==== -To build the image, run: - -[subs="+attributes,+quotes"] ----- -mvn package && \ -{docker-cli} build -f src/main/docker/Dockerfile.jvm -t image:tag . ----- - -=== Sample Dockerfile for building a Quarkus native image - -.`Dockerfile.native` -==== -[source,yaml] ----- -include::example$creating-a-telemetry-plugin/Dockerfile.native[] ----- -==== -To build the image, run: - -[subs="+attributes,+quotes"] ----- -mvn package -Pnative -Dquarkus.native.container-build=true && \ -{docker-cli} build -f src/main/docker/Dockerfile.native -t image:tag . ----- - -== Creating a `plugin.yaml` for your plugin - -Create a `plugin.yaml` devfile v2 file representing a {devworkspace} plugin that runs your custom backend in a {devworkspace} Pod. For more information about devfile v2, see link:https://devfile.io/docs/2.1.0/overview[Devfile v2 documentation] - -.`plugin.yaml` -==== -[source,yaml] ----- -include::example$creating-a-telemetry-plugin/plugin.yaml[] ----- -==== - -<1> Specify the container image built from xref:packaging-the-quarkus-application[]. -<2> Set the value for the `welcome.message` optional configuration property from Example 4. - -Typically, the user deploys this file to a corporate web server. This guide demonstrates how to create an Apache web server on OpenShift and host the plugin there. - -Create a `ConfigMap` object that references the new `plugin.yaml` file. - -[subs="+attributes"] ----- -$ oc create configmap --from-file=plugin.yaml -n {prod-namespace} telemetry-plugin-yaml ----- - -Create a deployment, a service, and a route to expose the web server. The deployment references this `ConfigMap` object and places it in the `/var/www/html` directory. - -.`manifest.yaml` -==== -[source,yaml,subs="+quotes,+attributes"] ----- -include::example$creating-a-telemetry-plugin/webserver.yaml[] ----- -==== - ----- -$ oc apply -f manifest.yaml ----- - -.Verification steps -* After the deployment has started, confirm that `plugin.yaml` is available in the web server: -+ ----- -$ curl apache-che.apps-crc.testing/plugin.yaml ----- - -[id="specifying-the-telemetry-plugin-in-a-devworkspace"] -== Specifying the telemetry plugin in a {devworkspace} -. Add the following to the `components` field of an existing {devworkspace}: -+ ----- -components: - ... - - name: telemetry-plugin - plugin: - uri: http://apache-che.apps-crc.testing/plugin.yaml ----- - -. Start the {devworkspace} from the {prod-short} dashboard. - - -.Verification steps -. Verify that the telemetry plugin container is running in the {devworkspace} pod. Here, this is verified by checking the Workspace view within the editor. -+ -image::creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png[{devworkspace} telemetry plugin] - -. Edit files within the editor and observe their events in the example telemetry server's logs. - -== Applying the telemetry plugin for all {devworkspace}s - -Set the telemetry plugin as a default plugin. Default plugins are applied on {devworkspace} startup for new and existing {devworkspace}s. - -* Configure the `CheCluster` Custom Resource. See xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[]. -+ ----- -spec: - devEnvironments: - defaultPlugins: - - editor: eclipse/che-theia/next <1> - plugins: <2> - - 'http://apache-che.apps-crc.testing/plugin.yaml' ----- -<1> The editor identification to set the default plugins for. -<2> List of URLs to devfile v2 plugins. - - -.Additional resources - -* xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[]. - -.Verification steps - -. Start a new or existing {devworkspace} from the {prod} dashboard. - -. Verify that the telemetry plugin is working by following the verification steps for xref:specifying-the-telemetry-plugin-in-a-devworkspace[]. diff --git a/modules/administration-guide/pages/default-extensions-for-microsoft-visual-studio-code.adoc b/modules/administration-guide/pages/default-extensions-for-microsoft-visual-studio-code.adoc deleted file mode 100644 index 06e7692b5e..0000000000 --- a/modules/administration-guide/pages/default-extensions-for-microsoft-visual-studio-code.adoc +++ /dev/null @@ -1,176 +0,0 @@ -:_content-type: PROCEDURE -:description: Configure default extensions -:keywords: extensions, workspace -:navtitle: Configure default extensions -// :page-aliases: - -[id="visual-studio-code-default-extensions"] -= Configuring default extensions - -Default extensions are a pre-installed set of extensions, specified by putting the extension binary `.vsix` file path to the __DEFAULT_EXTENSIONS__ environment variable. - -After startup, the editor checks for this environment variable, and if it is specified, takes the path to the extensions and installs it in the background without disturbing the user. - -Configuring default extensions is useful for installing .vsix extensions from the editor level. - -[NOTE] -==== -If you want to specify multiple extensions, separate them by semicolon. - -[source,yaml] ----- - DEFAULT_EXTENSIONS='/projects/extension-1.vsix;/projects/extension-2.vsix' ----- -==== - -Read on to learn how to define the DEFAULT_EXTENSIONS environment variable, including multiple examples of adding `.vsix` files to your workspace. - -There are three different ways to embed default `.vsix` extensions into your workspace: - -* Put the extension binary into the source repository. -* Use the devfile `postStart` event to fetch extension binaries from the network. -* Include the extensions' `.vsix` binaries in the `che-code` image. - -.Putting the extension binary into the source repository - -Putting the extension binary into the Git repository and defining the environment variable in the devfile is the easiest way to add default extensions to your workspace. -If the `extension.vsix` file exists in the repository root, you can set the __DEFAULT_EXTENSIONS__ for a tooling container. - -.Procedure -* Specify __DEFAULT_EXTENSIONS__ in your `.devfile.yaml` as shown in the following example: -+ -==== -[source,yaml] ----- -schemaVersion: 2.3.0 -metadata: - generateName: example-project -components: - - name: tools - container: - image: quay.io/devfile/universal-developer-image:ubi8-latest - env: - - name: 'DEFAULT_EXTENSIONS' - value: '/projects/example-project/extension.vsix' ----- -==== - -.Using the devfile *postStart* event to fetch extension binaries from the network - -You can use cURL or GNU Wget to download extensions to your workspace. -For that you need to: - --- -* specify a devfile command to download extensions to your workpace -* add a `postStart` event to run the command on workspace startup -* define the __DEFAULT_EXTENSIONS__ environment variable in the devfile --- - -.Procedure -* Add the values shown in the following example to the devfile: -+ -==== -[source,yaml] ----- -schemaVersion: 2.3.0 -metadata: - generateName: example-project -components: - - name: tools - container: - image: quay.io/devfile/universal-developer-image:ubi8-latest - env: - - name: DEFAULT_EXTENSIONS - value: '/tmp/extension-1.vsix;/tmp/extension-2.vsix' - -commands: - - id: add-default-extensions - exec: - # name of the tooling container - component: tools - # download several extensions using curl - commandLine: | - curl https://.../extension-1.vsix --location -o /tmp/extension-1.vsix - curl https://.../extension-2.vsix --location -o /tmp/extension-2.vsix - -events: - postStart: - - add-default-extensions ----- -==== -+ -[WARNING] -==== -In some cases curl may download a `.gzip` compressed file. This might make installing the extension impossible. -To fix that try to save the file as a *.vsix.gz* file and then decompress it with *gunzip*. This will replace the *.vsix.gz* file with an unpacked *.vsix* file. - -[source,yaml] ----- -curl https://some-extension-url --location -o /tmp/extension.vsix.gz -gunzip /tmp/extension.vsix.gz ----- -==== - -.Including the extensions `.vsix` binaries in the `che-code` image. - -With default extensions bundled in the editor image, and the __DEFAULT_EXTENSIONS__ environment variable defined in the ConfigMap, you can apply the default extensions without changing the devfile. - -Following the steps below to add the extensions you need to the editor image. - -.Procedure -* Create a directory and place your selected `.vsix` extensions in this directory. - -* Create a Dockerfile with the following content: -+ -==== -[source,] ----- -# inherit che-incubator/che-code:latest -FROM quay.io/che-incubator/che-code:latest -USER 0 - -# copy all .vsix files to /default-extensions directory -RUN mkdir --mode=775 /default-extensions -COPY --chmod=755 *.vsix /default-extensions/ - -# add instruction to the script to copy default extensions to the working container -RUN echo "cp -r /default-extensions /checode/" >> /entrypoint-init-container.sh ----- -==== - -* Build the image and then push it to a registry: -+ -==== -[,console] ----- -$ docker build -t yourname/che-code:next . -$ docker push yourname/che-code:next ----- -==== - -* Add the new ConfigMap to the user's {orch-namespace}, define the __DEFAULT_EXTENSIONS__ environment variable, and specify the absolute paths to the extensions. This ConfigMap sets the environment variable to all workspaces in the user's {orch-namespace}. -+ -==== -[source,yaml] ----- -kind: ConfigMap -apiVersion: v1 -metadata: - name: vscode-default-extensions - labels: - controller.devfile.io/mount-to-devworkspace: 'true' - controller.devfile.io/watch-configmap: 'true' - annotations: - controller.devfile.io/mount-as: env -data: - DEFAULT_EXTENSIONS: '/checode/default-extensions/extension1.vsix;/checode/default-extensions/extension2.vsix' ----- -==== - -* Create a workspace using `yourname/che-code:next` image. -First, open the dashboard and navigate to the *Create Workspace* tab on the left side. -+ --- -.. In the *Editor Selector* section, expand the *Use an Editor Definition* dropdown and set the editor URI to the *Editor Image*. -.. Create a workspace by clicking on any sample or by using a Git repository URL. --- diff --git a/modules/administration-guide/pages/manage-extension-installation.adoc b/modules/administration-guide/pages/manage-extension-installation.adoc deleted file mode 100644 index 73c80af147..0000000000 --- a/modules/administration-guide/pages/manage-extension-installation.adoc +++ /dev/null @@ -1,153 +0,0 @@ -:_content-type: PROCEDURE -:description: Manage extension installation with ConfigMap -:keywords: extensions, workspace -:navtitle: Manage extension installation with ConfigMap -// :page-aliases: - -[id="visual-studio-code-manage-extensions-installation"] -= Manage extension installation with ConfigMap - -This page describes how Code - OSS manages extension installation using a ConfigMap. -With these controls, you can enforce a fine-grained allow/deny list using the `AllowedExtensions` policy and block installs via the CLI, default extensions, and the `workbench.extensions.command.installFromVSIX` API command. -The sections below show how to enable and enforce these controls in Code - OSS. - - -The following items are currently supported: - -* BlockCliExtensionsInstallation property - when enabled, blocks installation of extensions via CLI -* BlockDefaultExtensionsInstallation property - when enabled, blocks installation of default extensions, see xref:default-extensions-for-microsoft-visual-studio-code.adoc[] -* BlockInstallFromVSIXCommandExtensionsInstallation property - when enabled, blocks installation of extensions via the workbench.extensions.command.installFromVSIX API command -* AllowedExtensions section - provides fine-grained control over Code - OSS extension installation; when this policy is applied, already installed extensions that are not allowed are disabled and show the warning `Some extensions are disabled because they are not allowed by your system administrator`. For conceptual background, see link:https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions/[Configure allowed extensions]. - -.Procedure - -* Add a new ConfigMap to the {prod-namespace} namespace and specify the properties you want to add. -+ -==== -[source,yaml] ----- -kind: ConfigMap -apiVersion: v1 -metadata: - name: vscode-editor-configurations - namespace: eclipse-che - - labels: - app.kubernetes.io/component: workspaces-config - app.kubernetes.io/part-of: che.eclipse.org - annotations: - controller.devfile.io/mount-as: subpath - controller.devfile.io/mount-path: /checode-config - controller.devfile.io/read-only: 'true' - -data: - policy.json: | - { - "BlockCliExtensionsInstallation": true, - "BlockDefaultExtensionsInstallation": true, - "BlockInstallFromVSIXCommandExtensionsInstallation": true, - "AllowedExtensions": { - "*": true, - "dbaeumer.vscode-eslint": false, - "ms-python.python": false, - "redhat": false - } - } ----- -==== - -[NOTE] -==== -Make sure that the Configmap contains data in a valid JSON format. -==== - -* Start or restart your workspace - - -TIP: To completely disable extension installation, set all extensions to disallowed: -==== -[source,yaml] ----- -kind: ConfigMap -apiVersion: v1 -metadata: - name: vscode-editor-configurations - namespace: eclipse-che - - labels: - app.kubernetes.io/component: workspaces-config - app.kubernetes.io/part-of: che.eclipse.org - annotations: - controller.devfile.io/mount-as: subpath - controller.devfile.io/mount-path: /checode-config - controller.devfile.io/read-only: 'true' - -data: - policy.json: | - { - "AllowedExtensions": { - "*": false - } - } ----- -==== - -* Optional: To add the ConfigMap in the user's namespace, use the following example: -+ -==== -[source,yaml] ----- -kind: ConfigMap -apiVersion: v1 -metadata: - name: vscode-editor-configurations - labels: - controller.devfile.io/mount-to-devworkspace: 'true' - controller.devfile.io/watch-configmap: 'true' - annotations: - controller.devfile.io/mount-as: subpath - controller.devfile.io/mount-path: /checode-config - controller.devfile.io/read-only: 'true' -data: - policy.json: | - { - "AllowedExtensions": { - "*": false - } - } ----- -==== -+ -[WARNING] -==== -When the ConfigMap is stored in the user's namespace, the user can edit its values. -==== - -.Verification -. Verify that the `BlockCliExtensionsInstallation` property is applied: -* Press `F1` → `Preferences: Open Settings (UI)`, and enter `BlockCliExtensionsInstallation` in the search field => The setting from the ConfigMap should appear in Settings. -* Provide a file with the `.vsix` extension (for example, `redhat.java-1.43.1.vsix`) in your workspace. -* Open a terminal and use the CLI to install the extension, for example: `/checode/checode-linux-libc/ubi9/bin/remote-cli/code-oss --install-extension /projects/web-nodejs-sample/redhat.java-1.43.1.vsix` -* The extension should not install; the terminal shows: `Installation of extensions via CLI has been blocked by an administrator`. - -. Verify that the `BlockDefaultExtensionsInstallation` property is applied: -* Press `F1` → `Preferences: Open Settings (UI)`, and enter `BlockDefaultExtensionsInstallation` in the search field => The setting from the ConfigMap should appear in Settings. -* Configure default extensions: see xref:default-extensions-for-microsoft-visual-studio-code.adoc[]. -* Open the Extensions view. -* Verify that default extensions are not installed when the workspace started/restarted. - -. Verify that the `BlockInstallFromVSIXCommandExtensionsInstallation` property is applied: -* Press `F1` → `Preferences: Open Settings (UI)`, and enter `BlockInstallFromVSIXCommandExtensionsInstallation` in the search field => The setting from the ConfigMap should appear in Settings. -* This property blocks installing extensions via the `workbench.extensions.command.installFromVSIX` API command. -* For example, an extension might call: `vscode.commands.executeCommand('workbench.extensions.command.installFromVSIX', URL);` -* It is not possible to install an extension from a `.vsix` when this property is set to `true`. - -. Verify that rules defined in the `AllowedExtensions` section are applied: -* Press `F1` → `Preferences: Open Settings (UI)`, and enter `extensions.allowed` in the search field. -* All settings from the `AllowedExtensions` section of the ConfigMap should be present in Settings. -* Use `F1 → Open View → Extensions` to open the Extensions view, disallowed extensions has `This extension cannot be installed because it is not in the allowed list` warning -* Try to install allowed and disallowed extensions to verify that the ConfigMap rules are enforced. - -. Verify that rules defined in the `AllowedExtensions` section are applied: -* Press `F1` → `Preferences: Open Settings (UI)`, and enter `extensions.allowed` in the search field. -* All settings from the `AllowedExtensions` section of the ConfigMap should be present in Settings. diff --git a/modules/administration-guide/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc b/modules/administration-guide/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc deleted file mode 100644 index af6c22ae7b..0000000000 --- a/modules/administration-guide/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc +++ /dev/null @@ -1,68 +0,0 @@ -:_content-type: PROCEDURE -:description: Configure trusted extensions for Microsoft Visual Studio Code -:keywords: extensions, vs-code, vsx, open-vsx, marketplace -:navtitle: Configure trusted extensions for Microsoft Visual Studio Code - -[id="visual-studio-code-trusted-extensions"] -= Configure trusted extensions for Microsoft Visual Studio Code - - -You can use the `trustedExtensionAuthAccess` field in the `product.json` file of Microsoft Visual Studio Code to specify which extensions are trusted to access authentication tokens. -[source,json] ----- - "trustedExtensionAuthAccess": [ - ".", - "." - ] ----- - -This is particularly useful when you have extensions that require access to services such as GitHub, Microsoft, or any other service that requires OAuth. By adding the extension IDs to this field, you are granting them the permission to access these tokens. - -You can define the variable in the devfile or in the ConfigMap. Pick the option that better suits your needs. -With a ConfigMap, the variable will be propagated on all your workspaces and you do not need to add the variable to each the devfile you are using. -[WARNING] -==== -Use the `trustedExtensionAuthAccess` field with caution as it could potentially lead to security risks if misused. Give access only to trusted extensions. -==== - -.Procedure -[IMPORTANT] -==== -Since the Microsoft Visual Studio Code editor is bundled within `che-code` image, you can only change the `product.json` file when the workspace is started up. -==== - - -. Define the __VSCODE_TRUSTED_EXTENSIONS__ environment variable. Choose between defining the variable in devfile.yaml or mounting a ConfigMap with the variable instead. -.. Define the __VSCODE_TRUSTED_EXTENSIONS__ environment variable in devfile.yaml: -+ -==== -[source,yaml] ----- - env: - - name: VSCODE_TRUSTED_EXTENSIONS - value: ".,." ----- -==== - -.. Mount a ConfigMap with __VSCODE_TRUSTED_EXTENSIONS__ environment variable: -+ -==== -[source,yaml] ----- - kind: ConfigMap - apiVersion: v1 - metadata: - name: trusted-extensions - labels: - controller.devfile.io/mount-to-devworkspace: 'true' - controller.devfile.io/watch-configmap: 'true' - annotations: - controller.devfile.io/mount-as: env - data: - VSCODE_TRUSTED_EXTENSIONS: '.,.' ----- -==== - -.Verification - -* The value of the variable will be parsed on the workspace startup and the corresponding `trustedExtensionAuthAccess` section will be added to the `product.json`. diff --git a/modules/administration-guide/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc b/modules/administration-guide/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc deleted file mode 100644 index bb051b047e..0000000000 --- a/modules/administration-guide/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc +++ /dev/null @@ -1,96 +0,0 @@ -:_content-type: PROCEDURE - -[id="adding-or-removing-extensions-in-a-workspace_{context}"] -= Adding or removing extensions by using a {prod-short} workspace - -[role="_abstract"] -You can add or remove extensions in the embedded Open VSX registry instance directly within a {prod-short} workspace to create a custom build for your organization. - -[IMPORTANT] -==== -The embedded plugin registry is deprecated. Setting up an internal, on-premises Open VSX registry provides full control over the extension lifecycle, enables offline use, and improves compliance. Refer to the xref:proc_deploy-open-vsx-from-source.adoc[] procedure for detailed setup instructions. -==== - -.Prerequisites - -* You have started a workspace using the link:{plugin-registry-repo-url}[plugin registry repository]. -* You are logged in to the cluster as a cluster administrator from the workspace terminal: `$ oc login --token= --server=` -* You have created a link:https://access.redhat.com/terms-based-registry/[Red Hat Registry Service Account] and have the username and token available. -* For IBM Power (`ppc64le`) and IBM Z (`s390x`) architectures, you must build the custom plugin registry locally on the corresponding hardware. -* (Optional) You can rebuild the container based on the latest tag or SHA to get the latest security fixes after a {prod-short} update. - -.Procedure - -. Identify the publisher and extension name for each extension you want to add: -.. Find the extension on the link:https://open-vsx.org/[Open VSX registry website]. -.. Copy the URL of the extension's listing page. -.. Extract the `` and `` from the URL: -+ -[subs="+quotes"] ----- -https://open-vsx.org/extension/____/____ ----- -+ -[TIP] -==== -If the extension is only available from link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace], but not link:https://open-vsx.org[Open VSX], you can ask the extension publisher to publish it on link:https://open-vsx.org[open-vsx.org] according to these link:https://github.com/eclipse/openvsx/wiki/Publishing-Extensions#how-to-publish-an-extension[instructions], potentially using this link:https://github.com/marketplace/actions/publish-vs-code-extension[GitHub action]. - -If the extension publisher is unavailable or unwilling to publish the extension to link:https://open-vsx.org[open-vsx.org], and if there is no Open VSX equivalent of the extension, consider link:https://github.com/open-vsx/publish-extensions/issues[reporting an issue] to the Open VSX team. -==== - -. Open the `openvsx-sync.json` file in the workspace. - -. Add or remove extensions using the following JSON syntax: -+ -[source,json,subs="+quotes"] ----- -{ - "id": "____.____", - "version": "____" -} ----- -+ -[TIP] -==== -If you have a closed-source extension or an extension developed only for internal use in your organization, you can add the extension directly from a `.vsix` file by using a URL accessible to your custom plugin registry container: - -[source,json,subs="+quotes"] ----- -{ - "id": "____.____", - "download": "____", - "version": "____" -} ----- - -Read the link:https://aka.ms/vsmarketplace-ToU[Terms of Use] for the link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] before using its resources. -==== - -. Log in to the Red Hat registry: -.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. -.. Run the *1. Login to registry.redhat.io* task. -.. Enter your Red Hat Registry Service Account credentials when prompted. - -. Build and publish the custom plugin registry: -.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. -.. Run the *2. Build and Publish a Custom Plugin Registry* task. -+ -[NOTE] -==== -Verify that the `CHE_CODE_VERSION` in the `build-config.json` file matches the version of the editor currently used with {prod-short}. Update it if necessary. -==== - -. Configure {prod-short} to use the custom plugin registry: -.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. -.. Run the *3. Configure Che to use the Custom Plugin Registry* task. - -.Verification - -. Check that the `plugin-registry` pod has restarted and is running. -. Restart your workspace. -. Open the *Extensions* view in the IDE and verify that your added extensions are available. - -.Additional resources - -* link:{plugin-registry-repo-url}[Plugin registry repository] -* xref:proc_deploy-open-vsx-from-source.adoc[] diff --git a/modules/administration-guide/partials/proc_adding-or-removing-extensions-on-linux.adoc b/modules/administration-guide/partials/proc_adding-or-removing-extensions-on-linux.adoc deleted file mode 100644 index 7c9333607f..0000000000 --- a/modules/administration-guide/partials/proc_adding-or-removing-extensions-on-linux.adoc +++ /dev/null @@ -1,124 +0,0 @@ -:_content-type: PROCEDURE - -[id="adding-or-removing-extensions-on-linux_{context}"] -= Adding or removing extensions by using a Linux operating system - -[role="_abstract"] -You can build and publish a custom plugin registry using the Linux command line. This results in a custom build of the Open VSX registry that can be used in your organization's workspaces. - -.Prerequisites - -* Podman is installed. -* Node.js version 18.20.3 or higher is installed. -* You have created a link:https://access.redhat.com/terms-based-registry/[Red Hat Registry Service Account] and have the username and token available. -* You have read the link:https://aka.ms/vsmarketplace-ToU[Terms of Use] for the link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] before using its resources. -* (Optional) You can rebuild the container based on the latest tag or SHA to get the latest security fixes after a {prod-short} update. - -.Procedure - -. Clone the plugin registry repository: -+ -[source,bash,subs="+attributes"] ----- -$ git clone {plugin-registry-repo-url}.git ----- - -. Change to the plugin registry directory: -+ -[source,bash] ----- -$ cd che-plugin-registry ----- - -. Log in to the Red Hat registry: -+ -[source,bash] ----- -$ podman login registry.redhat.io ----- - -. Identify the publisher and extension name for each extension you want to add: -.. Find the extension on the link:https://open-vsx.org/[Open VSX registry website]. -.. Copy the URL of the extension's listing page. -.. Extract the `` and `` from the URL: -+ -[subs="+quotes"] ----- -https://open-vsx.org/extension/____/____ ----- -+ -[TIP] -==== -If the extension is only available from link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace], but not link:https://open-vsx.org[Open VSX], you can ask the extension publisher to publish it on link:https://open-vsx.org[open-vsx.org] according to these link:https://github.com/eclipse/openvsx/wiki/Publishing-Extensions#how-to-publish-an-extension[instructions], potentially using this link:https://github.com/marketplace/actions/publish-vs-code-extension[GitHub action]. - -If the extension publisher is unavailable or unwilling to publish the extension to link:https://open-vsx.org[open-vsx.org], and if there is no Open VSX equivalent of the extension, consider link:https://github.com/open-vsx/publish-extensions/issues[reporting an issue] to the Open VSX team. -==== - -. Open the `openvsx-sync.json` file. - -. Add or remove extensions using the following JSON syntax: -+ -[source,json,subs="+quotes"] ----- -{ - "id": "____.____", - "version": "____" -} ----- -+ -[TIP] -==== -If you have a closed-source extension or an extension developed only for internal use in your organization, you can add the extension directly from a `.vsix` file by using a URL accessible to your custom plugin registry container: - -[source,json,subs="+quotes"] ----- -{ - "id": "____.____", - "download": "____", - "version": "____" -} ----- -==== - -. Build the plugin registry container image: -+ -[source,bash,subs="+quotes"] ----- -$ ./build.sh -o ____ -r quay.io -t custom ----- -+ -[NOTE] -==== -Verify that the `CHE_CODE_VERSION` in the `build-config.json` file matches the version of the editor currently used with {prod-short}. Update it if necessary. -==== - -. Push the image to a container registry such as link:https://quay.io/[quay.io]: -+ -[source,bash,subs="+quotes"] ----- -$ podman push quay.io/____/plugin_registry:custom ----- - -. Edit the `CheCluster` custom resource in your organization's cluster to point to the image and save the changes: -+ -[source,yaml,subs="+quotes"] ----- -spec: - components: - pluginRegistry: - deployment: - containers: - - image: quay.io/____/plugin_registry:custom - openVSXURL: '' ----- - -.Verification - -. Check that the `plugin-registry` pod has restarted and is running. -. Restart your workspace. -. Open the *Extensions* view in the IDE and verify that your added extensions are available. - -.Additional resources - -* link:{plugin-registry-repo-url}[Plugin registry repository] -* xref:proc_deploy-open-vsx-from-source.adoc[] diff --git a/modules/end-user-guide/pages/microsoft-visual-studio-code-open-source-ide.adoc b/modules/end-user-guide/pages/microsoft-visual-studio-code-open-source-ide.adoc index 046b3d954c..c5a2bde08a 100644 --- a/modules/end-user-guide/pages/microsoft-visual-studio-code-open-source-ide.adoc +++ b/modules/end-user-guide/pages/microsoft-visual-studio-code-open-source-ide.adoc @@ -9,7 +9,7 @@ The {prod-short} build of link:https://github.com/microsoft/vscode[Microsoft Visual Studio Code - Open Source] is the default IDE of a new workspace. -You can automate installation of Microsoft Visual Studio Code extensions from the xref:administration-guide:extensions-for-microsoft-visual-studio-code-open-source.adoc[Open VSX registry] at workspace startup. See _Automating installation of Microsoft Visual Studio Code extensions at workspace startup_. +You can automate installation of Microsoft Visual Studio Code extensions from the xref:extend:extensions-for-microsoft-visual-studio-code-open-source.adoc[Open VSX registry] at workspace startup. See _Automating installation of Microsoft Visual Studio Code extensions at workspace startup_. [TIP] ==== diff --git a/modules/end-user-guide/partials/proc_automating-installation-of-microsoft-visual-studio-code-extensions-at-workspace-startup.adoc b/modules/end-user-guide/partials/proc_automating-installation-of-microsoft-visual-studio-code-extensions-at-workspace-startup.adoc index a00e2c5f11..a4ce43957f 100644 --- a/modules/end-user-guide/partials/proc_automating-installation-of-microsoft-visual-studio-code-extensions-at-workspace-startup.adoc +++ b/modules/end-user-guide/partials/proc_automating-installation-of-microsoft-visual-studio-code-extensions-at-workspace-startup.adoc @@ -5,13 +5,13 @@ To have the Microsoft Visual Studio Code - Open Source IDE automatically install chosen extensions, you can add an `extensions.json` file to the remote Git repository that contains your project source code and that will be cloned into workspaces. .Prerequisites -* The public OpenVSX registry at link:https://open-vsx.org[open-vsx.org] is selected and accessible over the internet. See xref:administration-guide:extensions-for-microsoft-visual-studio-code-open-source.adoc[Selecting an Open VSX registry instance]. +* The public OpenVSX registry at link:https://open-vsx.org[open-vsx.org] is selected and accessible over the internet. See xref:extend:extensions-for-microsoft-visual-studio-code-open-source.adoc[Selecting an Open VSX registry instance]. + [TIP] ==== To install recommended extensions in a restricted environment, consider the following options instead: -* xref:administration-guide:configuring-the-open-vsx-registry-url.adoc[] to point to your OpenVSX registry. +* xref:extend:configuring-the-open-vsx-registry-url.adoc[] to point to your OpenVSX registry. * xref:defining-a-common-ide.adoc[]. diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java b/modules/extend/examples/creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java rename to modules/extend/examples/creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/Dockerfile.jvm b/modules/extend/examples/creating-a-telemetry-plugin/Dockerfile.jvm similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/Dockerfile.jvm rename to modules/extend/examples/creating-a-telemetry-plugin/Dockerfile.jvm diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/Dockerfile.native b/modules/extend/examples/creating-a-telemetry-plugin/Dockerfile.native similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/Dockerfile.native rename to modules/extend/examples/creating-a-telemetry-plugin/Dockerfile.native diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/MainConfiguration.java b/modules/extend/examples/creating-a-telemetry-plugin/MainConfiguration.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/MainConfiguration.java rename to modules/extend/examples/creating-a-telemetry-plugin/MainConfiguration.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/TelemetryService.java b/modules/extend/examples/creating-a-telemetry-plugin/TelemetryService.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/TelemetryService.java rename to modules/extend/examples/creating-a-telemetry-plugin/TelemetryService.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/destroy.java b/modules/extend/examples/creating-a-telemetry-plugin/destroy.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/destroy.java rename to modules/extend/examples/creating-a-telemetry-plugin/destroy.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/increaseDuration.java b/modules/extend/examples/creating-a-telemetry-plugin/increaseDuration.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/increaseDuration.java rename to modules/extend/examples/creating-a-telemetry-plugin/increaseDuration.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/isEnabled.java b/modules/extend/examples/creating-a-telemetry-plugin/isEnabled.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/isEnabled.java rename to modules/extend/examples/creating-a-telemetry-plugin/isEnabled.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/main.go b/modules/extend/examples/creating-a-telemetry-plugin/main.go similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/main.go rename to modules/extend/examples/creating-a-telemetry-plugin/main.go diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/onActivity.java b/modules/extend/examples/creating-a-telemetry-plugin/onActivity.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/onActivity.java rename to modules/extend/examples/creating-a-telemetry-plugin/onActivity.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/onEvent.java b/modules/extend/examples/creating-a-telemetry-plugin/onEvent.java similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/onEvent.java rename to modules/extend/examples/creating-a-telemetry-plugin/onEvent.java diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/plugin.yaml b/modules/extend/examples/creating-a-telemetry-plugin/plugin.yaml similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/plugin.yaml rename to modules/extend/examples/creating-a-telemetry-plugin/plugin.yaml diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/pom_snippet.xml b/modules/extend/examples/creating-a-telemetry-plugin/pom_snippet.xml similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/pom_snippet.xml rename to modules/extend/examples/creating-a-telemetry-plugin/pom_snippet.xml diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/project_scaffolding.sh b/modules/extend/examples/creating-a-telemetry-plugin/project_scaffolding.sh similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/project_scaffolding.sh rename to modules/extend/examples/creating-a-telemetry-plugin/project_scaffolding.sh diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/settings.xml b/modules/extend/examples/creating-a-telemetry-plugin/settings.xml similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/settings.xml rename to modules/extend/examples/creating-a-telemetry-plugin/settings.xml diff --git a/modules/administration-guide/examples/creating-a-telemetry-plugin/webserver.yaml b/modules/extend/examples/creating-a-telemetry-plugin/webserver.yaml similarity index 100% rename from modules/administration-guide/examples/creating-a-telemetry-plugin/webserver.yaml rename to modules/extend/examples/creating-a-telemetry-plugin/webserver.yaml diff --git a/modules/administration-guide/examples/snip_che-default-vsx-registry.adoc b/modules/extend/examples/snip_che-default-vsx-registry.adoc similarity index 100% rename from modules/administration-guide/examples/snip_che-default-vsx-registry.adoc rename to modules/extend/examples/snip_che-default-vsx-registry.adoc diff --git a/modules/administration-guide/examples/snip_che-editor-id-list.adoc b/modules/extend/examples/snip_che-editor-id-list.adoc similarity index 73% rename from modules/administration-guide/examples/snip_che-editor-id-list.adoc rename to modules/extend/examples/snip_che-editor-id-list.adoc index a6146c6d09..2312d652cc 100644 --- a/modules/administration-guide/examples/snip_che-editor-id-list.adoc +++ b/modules/extend/examples/snip_che-editor-id-list.adoc @@ -1,6 +1,6 @@ :_content-type: SNIPPET -. An editor ID has the following format: `publisher/name/version`. Find out the IDs of the available editors: +. Determine the IDs of the available editors. An editor ID has the following format: `publisher/name/version`. + [source,subs="+quotes,+attributes"] ---- diff --git a/modules/extend/examples/snip_che-managing-extensions.adoc b/modules/extend/examples/snip_che-managing-extensions.adoc new file mode 100644 index 0000000000..f04f4effcc --- /dev/null +++ b/modules/extend/examples/snip_che-managing-extensions.adoc @@ -0,0 +1 @@ +More details about managing IDE extensions are available in link:https://eclipse.dev/che/docs/stable/extend/extensions-for-microsoft-visual-studio-code-open-source/[the official documentation]. \ No newline at end of file diff --git a/modules/administration-guide/examples/snip_che-non-default-vsx-registry-instance.adoc b/modules/extend/examples/snip_che-non-default-vsx-registry-instance.adoc similarity index 100% rename from modules/administration-guide/examples/snip_che-non-default-vsx-registry-instance.adoc rename to modules/extend/examples/snip_che-non-default-vsx-registry-instance.adoc diff --git a/modules/administration-guide/images/creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png b/modules/extend/images/creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png similarity index 100% rename from modules/administration-guide/images/creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png rename to modules/extend/images/creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png diff --git a/modules/extend/images/devworkspace_telemetry_plugin.png b/modules/extend/images/devworkspace_telemetry_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..150aee9539b1bc29b46b3645c01fa912b17fe34b GIT binary patch literal 78472 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 |r#1?=dlMz| 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 Mounts the Secret data keys as environment variables (not as files). <6> The data key is the environment variable name. The value is base64-encoded. The variable is injected directly into all workspace containers. -== Label and annotation reference +== Required labels and annotations [cols="1,1,2",options="header"] |=== @@ -59,7 +59,7 @@ data: | Used by the {prod-short} dashboard to identify and list AI provider key Secrets when rendering the AI Selector widget. |=== -== Secret naming convention +== How to name your Secret Secret names follow the pattern: @@ -77,11 +77,11 @@ Example: | `ai-provider-openai-api-key` |=== -== Manual Secret creation +== Create the Secret manually Advanced users can create AI provider key Secrets manually using `{orch-cli}` instead of the dashboard UI. Use the same labels and annotations as shown in the schema above: -[subs="+quotes,+attributes"] +[source,bash,subs="+quotes,+attributes"] ---- $ {orch-cli} apply -f - < +---- +<1> A string containing comma-separated IDs of editors to hide. + +.Verification + +* In the {prod-short} Dashboard, go to *Create Workspace* and verify that the concealed editors are no longer visible. + +[role="_additional-resources"] +.Additional resources + +* xref:install:using-chectl-to-configure-the-checluster-custom-resource-during-installation.adoc[] + +* xref:administration-guide:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] + +* {editor-definition-samples-link} diff --git a/modules/administration-guide/pages/configuring-ai-providers.adoc b/modules/extend/pages/configuring-ai-providers.adoc similarity index 85% rename from modules/administration-guide/pages/configuring-ai-providers.adoc rename to modules/extend/pages/configuring-ai-providers.adoc index 8be4bd5f88..5ceaa39753 100644 --- a/modules/administration-guide/pages/configuring-ai-providers.adoc +++ b/modules/extend/pages/configuring-ai-providers.adoc @@ -1,19 +1,19 @@ +:page-aliases: administration-guide:configuring-ai-providers.adoc :_content-type: PROCEDURE -:description: Register AI providers in Eclipse Che so that developers can select and use AI coding assistants when creating workspaces. +:description: Register one or more AI providers in Eclipse Che so that developers can select and use AI coding assistants when creating workspaces. :keywords: administration, ai, ai provider, opencode, configmap, ai-tool-registry, api key, injector image, dockerfile -:navtitle: Configuring AI providers -:page-aliases: +:navtitle: Register an AI provider [id="configuring-ai-providers"] -= Configure AI providers += Register an AI provider :FeatureName: The AI provider feature -include::example$snip_che-technology-preview.adoc[] +include::administration-guide:example$snip_che-technology-preview.adoc[] [role="_abstract"] Register one or more AI providers in {prod-short} so that developers can select and use AI coding assistants when creating workspaces. -The AI tool registry is stored in a {kubernetes} `ConfigMap` with specific labels. When the ConfigMap exists and contains at least one provider with a matching tool, the AI Selector widget is displayed on the dashboard. When the ConfigMap is absent or empty, the widget is hidden. +The AI tool registry is stored in a {orch-name} `ConfigMap` with specific labels. When the ConfigMap exists and contains at least one provider with a matching tool, the AI Selector widget is displayed on the dashboard. When the ConfigMap is absent or empty, the widget is hidden. .Prerequisites @@ -66,20 +66,22 @@ LABEL org.opencontainers.image.description="OpenCode CLI tool for DevWorkspace i + Key design points: + +-- ** *Multi-stage build*: the `builder` stage downloads the architecture-specific binary; the minimal runtime stage keeps the final image small. -** *Wrapper script*: redirects `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, and related variables to writable paths under `/tmp`, allowing the tool to run as an arbitrary UID on OpenShift. -** *Multi-arch*: pass `--platform linux/amd64,linux/arm64` to `docker build` to produce a multi-arch image. -+ +** *Wrapper script*: redirects `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, and related variables to writable paths under `/tmp`, allowing the tool to run as an arbitrary UID on {orch-name}. +** *Multi-arch*: pass `--platform linux/amd64,linux/arm64` to `{docker-cli} build` to produce a multi-arch image. + Build and push the image: -+ -[subs="+quotes"] + +[source,bash,subs="+quotes"] ---- $ docker build --platform linux/amd64,linux/arm64 \ -t ____/____/opencode:next \ --push . ---- -+ + See link:https://github.com/che-incubator/che-ai-tool-images[che-incubator/che-ai-tool-images] for maintained injector image examples. +-- . Create a `registry.json` file that defines providers, tools, and optional defaults: + @@ -117,12 +119,12 @@ See link:https://github.com/che-incubator/che-ai-tool-images[che-incubator/che-a <4> Binary name that must be available in `PATH` inside the workspace after injection. <5> Injection pattern: `init` copies a single binary into the shared volume; `bundle` copies a full runtime directory and creates a symlink. <6> Container image that carries the tool binary. Run as an init container at workspace start. -<7> Environment variable name for the API key. The dashboard creates a {kubernetes} Secret using this name as the data key. +<7> Environment variable name for the API key. The dashboard creates a {orch-name} Secret using this name as the data key. <8> Optional. Provider IDs pre-selected in the AI Selector widget for new workspaces. . Create the `ConfigMap` in the `{prod-namespace}` namespace with the required labels: + -[subs="+quotes,+attributes"] +[source,bash,subs="+quotes,+attributes"] ---- $ {orch-cli} create configmap ai-tool-registry \ --from-file=registry.json=registry.json \ @@ -148,6 +150,7 @@ To disable the AI Selector widget, delete the `ai-tool-registry` ConfigMap. User When you update the registry (for example, to remove a tool or change the injector image tag), existing workspaces are updated automatically before their next start. The dashboard removes stale tool injectors and replaces outdated image tags without user intervention. ==== +[role="_additional-resources"] .Additional resources * xref:ai-provider-api-key-secret-reference.adoc[] diff --git a/modules/administration-guide/pages/configuring-default-editor.adoc b/modules/extend/pages/configuring-default-editor.adoc similarity index 51% rename from modules/administration-guide/pages/configuring-default-editor.adoc rename to modules/extend/pages/configuring-default-editor.adoc index 3db3f7276f..aeb1a72b0b 100644 --- a/modules/administration-guide/pages/configuring-default-editor.adoc +++ b/modules/extend/pages/configuring-default-editor.adoc @@ -1,12 +1,14 @@ +:page-aliases: administration-guide:configuring-default-editor.adoc :_content-type: PROCEDURE -:description: Configuring default editor +:description: Set the default IDE for new workspaces :keywords: administration guide, dashboard, editors -:navtitle: Configuring default editor +:navtitle: Set the default IDE for new workspaces [id="configuring-default-editor"] -= Configuring default editor += Set the default IDE for new workspaces -Learn how to configure {prod-short} default editor. +[role="_abstract"] +Set the default editor that {prod-short} uses when creating new workspaces to ensure a consistent development experience. The default editor is specified by its plugin ID in the `publisher/name/version` format. .Prerequisites @@ -25,10 +27,18 @@ include::example$snip_che-editor-id-list.adoc[] {orch-cli} patch checluster/{prod-checluster} \ --namespace {prod-namespace} \ --type='merge' \ - -p '{"spec":{"devEnvironments":{"defaultEditor": "____"}}}'# <1> + -p '{"spec":{"devEnvironments":{"defaultEditor": "____"}}}' ---- -<1> The default editor for creating a workspace can be specified using either a plugin ID or a URI. The plugin ID should follow the format: `publisher/name/version`. See available editors IDs in the first step. ++ +where: ++ +`____`:: The default editor specified as a plugin ID in `publisher/name/version` format or as a URI. + +.Verification +* Create a new workspace from the {prod-short} Dashboard and verify that the configured default editor opens. + +[role="_additional-resources"] .Additional resources * xref:configuring-editors-definitions.adoc[] @@ -36,4 +46,3 @@ include::example$snip_che-editor-id-list.adoc[] * xref:concealing-editors.adoc[] * {editor-definition-samples-link} - diff --git a/modules/administration-guide/pages/configuring-editors-definitions.adoc b/modules/extend/pages/configuring-editors-definitions.adoc similarity index 82% rename from modules/administration-guide/pages/configuring-editors-definitions.adoc rename to modules/extend/pages/configuring-editors-definitions.adoc index 998f0cebf8..6795882765 100644 --- a/modules/administration-guide/pages/configuring-editors-definitions.adoc +++ b/modules/extend/pages/configuring-editors-definitions.adoc @@ -1,13 +1,14 @@ +:page-aliases: administration-guide:configuring-editors-definitions.adoc, installation-guide:configuring-editors-definitions.adoc :_content-type: PROCEDURE -:description: Configuring editors definitions +:description: Add custom editors to the dashboard :keywords: administration guide, configuring, dashboard, editors -:navtitle: Configuring editors definitions -:page-aliases: installation-guide:configuring-editors-definitions.adoc +:navtitle: Add custom editors to the dashboard [id="configuring-editors-definitions"] -= Configuring editors definitions += Add custom editors to the dashboard -Learn how to configure {prod-short} editor definitions. +[role="_abstract"] +Add custom editor definitions to {prod-short} by creating a devfile with the editor configuration and storing it in a ConfigMap to offer additional IDE options to your users. .Prerequisites @@ -15,19 +16,13 @@ Learn how to configure {prod-short} editor definitions. .Procedure -. Create the `my-editor-definition-devfile.yaml` YAML file with the editor definition configuration. +. Create the `my-editor-definition-devfile.yaml` YAML file with the editor definition configuration. Provide actual values for `publisher` and `version` under `metadata.attributes` because these construct the editor ID in the format `publisher/name/version`. + -[IMPORTANT] -==== -Make sure you provide the actual values for `publisher` and `version` under `metadata.attributes`. -They are used to construct the editor id along with editor name in the following format `publisher/name/version`. -==== -+ -Below you can find the supported values, including optional ones: +For example: + [source,yaml,subs="+quotes,+attributes"] ---- -# Version of the devile schema +# Version of the devfile schema schemaVersion: 2.2.2 # Meta information of the editor metadata: @@ -35,7 +30,7 @@ metadata: # Must consist of lower case alphanumeric characters, '-' or '.' name: editor-name displayName: Display Name - description: Run Editor Foo on top of Eclipse Che + description: Run Editor Foo on top of {prod-short} # (OPTIONAL) Array of tags of the current editor. The Tech-Preview tag means the option is considered experimental and is not recommended for production environments. While it can include new features and improvements, it may still contain bugs or undergo significant changes before reaching a stable version. tags: - Tech-Preview @@ -91,13 +86,13 @@ components: # Defines a container component as a "container contribution". If a flattened DevWorkspace has a container component with the merge-contribution attribute, then any container contributions are merged into that container component controller.devfile.io/container-contribution: true container: - # Can be a dummy image because the component is expected to be injected into workspace dev component + # Can be a placeholder image because the component is expected to be injected into workspace dev component image: quay.io/devfile/universal-developer-image:latest # (OPTIONAL) List of volume mounts that should be mounted in this container volumeMounts: # The name of the mount - name: checode - # (OPTIONAL) The path in the component container where the volume should be mounted. If no path is defined, the default path is the is / + # (OPTIONAL) The path in the component container where the volume should be mounted. If no path is defined, the default path is / path: /checode # (OPTIONAL) The memory limit of the container memoryLimit: 1024Mi @@ -123,7 +118,7 @@ components: cookiesAuthEnabled: true # Defines an endpoint as "discoverable", meaning that a service should be created using the endpoint name (i.e. instead of generating a service name for all endpoints, this endpoint should be statically accessible) discoverable: false - # Used to secure the endpoint with authorization on OpenShift, so that not anyone on the cluster can access the endpoint, the attribute enables authentication. + # Used to secure the endpoint with authorization on {orch-name}, so that not anyone on the cluster can access the endpoint, the attribute enables authentication. urlRewriteSupported: true # Port number to be used within the container component targetPort: 3100 @@ -162,6 +157,10 @@ commands: commandLine: 'nohup /checode/entrypoint-volume.sh > /checode/entrypoint-logs.txt 2>&1 &' ---- ++ +where: ++ +`____`:: The SVG icon data for the editor, displayed in the {prod-short} Dashboard editor selector. . Create a ConfigMap with the editor definition content: + @@ -177,23 +176,23 @@ commands: {orch-cli} label configmap my-editor-definition app.kubernetes.io/part-of=che.eclipse.org app.kubernetes.io/component=editor-definition -n {prod-namespace} ---- -. Refresh the {prod-short} Dashboard page to see new available editor. +.Verification -== Retrieving the editor definition - -The editor definition is also served by the {prod-short} dashboard API from the following URL: +* Refresh the {prod-short} Dashboard page and verify that the new editor is available. +* Verify the editor definition through the {prod-short} Dashboard API: ++ `pass:c,a,q[{prod-url}]/dashboard/api/editors` - -For the example from xref:configuring-editors-definitions.adoc[], the editor definition can be retrieved by accessing the following URL: - ++ +To retrieve a specific editor definition, use the publisher, name, and version values: ++ `pass:c,a,q[{prod-url}]/dashboard/api/editors/devfile?che-editor=publisher/editor-name/version` ++ +When retrieving the editor definition from within the {orch-name} cluster, access the {prod-short} Dashboard API through the dashboard service: `pass:c,a,q[http://{prod-id-short}-dashboard.{prod-namespace}.svc.cluster.local:8080]/dashboard/api/editors` -TIP: When retrieving the editor definition from within the {orch-name} cluster, the {prod-short} dashboard API can be accessed via the dashboard service: `pass:c,a,q[http://{prod-id-short}-dashboard.{prod-namespace}.svc.cluster.local:8080]/dashboard/api/editors` - +[role="_additional-resources"] .Additional resources * link:https://devfile.io/docs/2.2.2/what-is-a-devfile[Devfile documentation] * {editor-definition-samples-link} - diff --git a/modules/extend/pages/configuring-editors-download-urls.adoc b/modules/extend/pages/configuring-editors-download-urls.adoc new file mode 100644 index 0000000000..09974e336c --- /dev/null +++ b/modules/extend/pages/configuring-editors-download-urls.adoc @@ -0,0 +1,57 @@ +:page-aliases: administration-guide:configuring-editors-download-urls.adoc +:_content-type: PROCEDURE +:description: Host editor binaries internally for air-gapped clusters +:keywords: administration guide, dashboard, editors +:navtitle: Host editor binaries internally for air-gapped clusters + +[id="configuring-editors-download-urls"] += Host editor binaries internally for air-gapped clusters + +[role="_abstract"] +Host editor binaries internally by configuring custom download URLs for editors in air-gapped {prod-short} environments where editors cannot be retrieved from the public internet. This option applies only to JetBrains editors. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. + +* `jq`. See link:https://stedolan.github.io/jq/download/[Downloading `jq`]. + +.Procedure + +include::example$snip_che-editor-id-list.adoc[] + +. Configure the download URLs for editors: ++ +[source,subs="+quotes,+attributes"] +---- +{orch-cli} patch checluster/{prod-checluster} \ + --namespace {prod-namespace} \ + --type='merge' \ + -p '{ + "spec": { + "devEnvironments": { + "editorsDownloadUrls": [ + { "editor": "publisher1/editor-name1/version1", "url": "https://example.com/editor1.tar.gz" }, + { "editor": "publisher2/editor-name2/version2", "url": "https://example.com/editor2.tar.gz" } + ] + } + } + }' +---- ++ +where: ++ +`editor`:: The editor ID in the format `publisher/name/version`. Determine the IDs by running the command in step 1. ++ +`url`:: The URL of the editor archive to download. + +.Verification + +* Verify that the editor download URLs appear in the `CheCluster` Custom Resource specification. + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-editors-definitions.adoc[] + +* xref:show-deprecated-editors.adoc[] diff --git a/modules/extend/pages/configuring-single-and-multiroot-workspaces.adoc b/modules/extend/pages/configuring-single-and-multiroot-workspaces.adoc new file mode 100644 index 0000000000..b30f9c286e --- /dev/null +++ b/modules/extend/pages/configuring-single-and-multiroot-workspaces.adoc @@ -0,0 +1,89 @@ +:page-aliases: administration-guide:configuring-single-and-multiroot-workspaces.adoc +:_content-type: PROCEDURE +:description: Work with multiple projects in one workspace +:keywords: singleroot, multiroot, workspace +:navtitle: Work with multiple projects in one workspace + +[id="configuring-single-and-multiroot-workspaces"] += Work with multiple projects in one workspace + + +[role="_abstract"] +Work with multiple project folders in the same workspace by using the multi-root workspace feature. This is useful when you are working on several related projects at once, such as product documentation and product code repositories. + +By default, workspaces open in multi-root mode. After a workspace starts, the `/projects/.code-workspace` workspace file is generated. The workspace file contains all the projects described in the devfile. + +[source,json] +---- +{ + "folders": [ + { + "name": "project-1", + "path": "/projects/project-1" + }, + { + "name": "project-2", + "path": "/projects/project-2" + } + ] +} +---- + +If the workspace file already exists, it is updated and all missing projects are taken from the devfile. If you remove a project from the devfile, it remains in the workspace file. + +You can change the default behavior and provide your own workspace file or switch to a single-root workspace. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. + +.Procedure + +. Add a workspace file with the name `.code-workspace` to the root of your repository. After workspace creation, the Visual Studio Code - Open Source ("Code - OSS") uses the workspace file as it is. ++ +[source,json] +---- +{ + "folders": [ + { + "name": "project-name", + "path": "." + } + ] +} +---- ++ +[IMPORTANT] +==== +Be careful when creating a workspace file. In case of errors, an empty Visual Studio Code - Open Source ("Code - OSS") opens instead. If you have several projects, the workspace file is taken from the first project. If the workspace file does not exist in the first project, a new one is created and placed in the `/projects` directory. +==== + +. Define the `VSCODE_DEFAULT_WORKSPACE` environment variable in your devfile with the path to an alternative workspace file. ++ +[source,yaml] +---- + env: + - name: VSCODE_DEFAULT_WORKSPACE + value: "/projects/project-name/workspace-file" +---- + +. Define the `VSCODE_DEFAULT_WORKSPACE` environment variable and set it to `/` to open a workspace in single-root mode. ++ +[source,yaml] +---- + env: + - name: VSCODE_DEFAULT_WORKSPACE + value: "/" +---- + +.Verification + +* Start or restart the workspace and verify that Code - OSS opens with the expected workspace mode (single-root or multi-root). + +[role="_additional-resources"] +.Additional resources + +* link:https://code.visualstudio.com/docs/editing/workspaces/workspaces[What is a VS Code workspace] +* xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] diff --git a/modules/administration-guide/pages/configuring-the-open-vsx-registry-url.adoc b/modules/extend/pages/configuring-the-open-vsx-registry-url.adoc similarity index 81% rename from modules/administration-guide/pages/configuring-the-open-vsx-registry-url.adoc rename to modules/extend/pages/configuring-the-open-vsx-registry-url.adoc index 5d76369db7..06baa4e82f 100644 --- a/modules/administration-guide/pages/configuring-the-open-vsx-registry-url.adoc +++ b/modules/extend/pages/configuring-the-open-vsx-registry-url.adoc @@ -1,14 +1,14 @@ +:page-aliases: administration-guide:configuring-the-open-vsx-registry-url.adoc :_content-type: PROCEDURE -:description: Configure the Open VSX registry URL for {prod} workspaces +:description: Use an alternative extension registry in {prod-short} :keywords: administration guide, configuring, openvsx, registry, extensions -:navtitle: Configure the Open VSX registry URL -:page-aliases: +:navtitle: Use an alternative extension registry [id="configuring-the-open-vsx-registry-url"] -= Configure the Open VSX registry URL += Use an alternative extension registry [role="_abstract"] -Configure {prod-short} to use an alternative Open VSX registry instance instead of the default embedded registry. Switch to the public open-vsx.org registry for internet-connected environments, or to a standalone on-premises instance for full control over available extensions. +Use an alternative Open VSX registry instance instead of the default embedded registry. Switch to the public open-vsx.org registry for internet-connected environments, or to a standalone on-premises instance for full control over available extensions. The default is the embedded instance of the Open VSX registry. @@ -20,7 +20,7 @@ If the default Open VSX registry instance does not meet your requirements, you c .Prerequisites -* You have administrator access to the cluster where {prod-short} is deployed. +* You have administrator access to the {orch-name} cluster where {prod-short} is deployed. * You have an active `{orch-cli}` session with administrative permissions. See {orch-cli-link}. .Procedure @@ -68,5 +68,5 @@ Due to the dedicated Microsoft link:https://cdn.vsassets.io/v/M190_20210811.1/_c [role="_additional-resources"] .Additional resources -* xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] +* xref:administration-guide:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] * link:https://open-vsx.org/[Open VSX registry] diff --git a/modules/extend/pages/configuring-visual-studio-code.adoc b/modules/extend/pages/configuring-visual-studio-code.adoc new file mode 100644 index 0000000000..27ee37cff9 --- /dev/null +++ b/modules/extend/pages/configuring-visual-studio-code.adoc @@ -0,0 +1,18 @@ +:page-aliases: administration-guide:configuring-visual-studio-code.adoc +:_content-type: CONCEPT +:description: Customize the default IDE +:keywords: vscode, workspace +:navtitle: Customize the default IDE + +[id="configuring-visual-studio-code"] += Customize the default IDE + + +[role="_abstract"] +Customize Visual Studio Code - Open Source for {prod-short} workspaces, including multi-root project layout, trusted and default extensions, and editor settings so that developers get a consistent IDE experience. + +* xref:configuring-single-and-multiroot-workspaces.adoc[] +* xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] +* xref:manage-extension-installation.adoc[] \ No newline at end of file diff --git a/modules/extend/pages/configuring-workspaces-to-load-telemetry-plugin.adoc b/modules/extend/pages/configuring-workspaces-to-load-telemetry-plugin.adoc new file mode 100644 index 0000000000..f27d03e44e --- /dev/null +++ b/modules/extend/pages/configuring-workspaces-to-load-telemetry-plugin.adoc @@ -0,0 +1,64 @@ +:_content-type: PROCEDURE +:description: Configure DevWorkspaces to load the telemetry plugin for event collection +:keywords: telemetry, plugin, devworkspace, defaultPlugins, CheCluster +:navtitle: Configure workspaces to load a telemetry plugin + +[id="configuring-workspaces-to-load-telemetry-plugin"] += Configure workspaces to load a telemetry plugin + +[role="_abstract"] +Configure {devworkspace}s to load the telemetry plugin so that workspace activity events are sent to your telemetry backend for collection and analysis. + + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the destination {orch-name} cluster. See {orch-cli-link}. +* You have a running instance of {prod}. +* You have a telemetry plugin deployed and hosted on a web server. See xref:deploying-a-telemetry-plugin.adoc[]. + +.Procedure + +. Add the telemetry plugin to the `components` field of an existing {devworkspace}: ++ +[source,yaml,subs="+quotes"] +---- +components: + ... + - name: telemetry-plugin + plugin: + uri: ____ +---- + +. Start the {devworkspace} from the {prod-short} dashboard. + +. Optional: Configure the `CheCluster` Custom Resource to apply the telemetry plugin as a default for all {devworkspace}s. Default plugins are applied on {devworkspace} startup for new and existing {devworkspace}s. ++ +[source,yaml,subs="+quotes"] +---- +spec: + devEnvironments: + defaultPlugins: + - editor: che-incubator/che-code/latest + plugins: + - '____' +---- ++ +where: ++ +`editor`:: The editor identification to set the default plugins for. ++ +`plugins`:: List of URLs to devfile v2 plugins. + +.Verification + +. Verify that the telemetry plugin container is running in the {devworkspace} pod by checking the Workspace view within the editor. ++ +image::creating-a-telemetry-plugin/devworkspace_telemetry_plugin.png[{devworkspace} telemetry plugin] + +. Edit files within the editor and observe their events in the example telemetry server's logs. + +[role="_additional-resources"] +.Additional resources + +* xref:administration-guide:using-the-cli-to-configure-the-checluster-custom-resource.adoc[Configure the CheCluster Custom Resource using the CLI] +* xref:telemetry-plugin-overview.adoc[] diff --git a/modules/extend/pages/creating-a-telemetry-backend.adoc b/modules/extend/pages/creating-a-telemetry-backend.adoc new file mode 100644 index 0000000000..3d7ca6fcb5 --- /dev/null +++ b/modules/extend/pages/creating-a-telemetry-backend.adoc @@ -0,0 +1,102 @@ +:_content-type: PROCEDURE +:description: Create a Quarkus-based telemetry backend that extends the telemetry client +:keywords: telemetry, backend, quarkus, maven, java, AnalyticsManager +:navtitle: Create a telemetry backend + +[id="creating-a-telemetry-backend"] += Create a telemetry backend + +[role="_abstract"] +Create a Quarkus-based telemetry backend that extends the {prod-short} telemetry client and implements custom event handling logic. + + +[NOTE] +==== +For fast feedback when developing, develop inside a {devworkspace}. This way, you can run the application in a cluster and receive events from the front-end telemetry plugin. +==== + +.Prerequisites + +* You have a running instance of {prod}. +* You have a telemetry server deployed to receive events. See xref:creating-a-telemetry-server.adoc[]. + +.Procedure + +. Create a Maven Quarkus project: ++ +[source,bash] +---- +include::example$creating-a-telemetry-plugin/project_scaffolding.sh[] +---- + +. Remove the files under `src/main/java/mygroup` and `src/test/java/mygroup`. + +. Consult the link:https://github.com/che-incubator/che-workspace-telemetry-client/packages[GitHub packages] for the latest version of `backend-base` and add the following dependencies to your `pom.xml`: ++ +.`pom.xml` +==== +[source,xml] +---- +include::example$creating-a-telemetry-plugin/pom_snippet.xml[] +---- +==== + +. Create a personal access token with `read:packages` permissions from link:https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages[GitHub packages] and add your GitHub username, the token, and `che-incubator` repository details in your `~/.m2/settings.xml` file: ++ +.`settings.xml` +==== +[source,xml] +---- +include::example$creating-a-telemetry-plugin/settings.xml[] +---- +==== + +. Create `MainConfiguration.java` under `src/main/java/mygroup`. This file contains configuration provided to `AnalyticsManager`: ++ +.`MainConfiguration.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/MainConfiguration.java[] +---- +==== +<1> A MicroProfile configuration annotation is used to inject the `welcome.message` configuration. ++ +For more details on how to set configuration properties specific to your backend, see the Quarkus link:https://quarkus.io/guides/config-reference[Configuration Reference Guide]. + +. Create `AnalyticsManager.java` under `src/main/java/mygroup`. This file contains logic specific to the telemetry system: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/AnalyticsManagerSkeleton.java[] +---- +==== +<1> Log the welcome message if it was provided. +<2> Log the event received from the front-end plugin. + +. Add the `quarkus.arc.selected-alternatives` property to `src/main/resources/application.properties` to specify the alternative beans: ++ +.`application.properties` +==== +---- +quarkus.arc.selected-alternatives=MainConfiguration,AnalyticsManager +---- +==== + +.Verification + +* Run the Quarkus application and verify that it starts without errors: ++ +[source,bash] +---- +mvn quarkus:dev +---- + +[role="_additional-resources"] +.Additional resources + +* link:https://quarkus.io/guides/config-reference[Quarkus Configuration Reference Guide] +* xref:implementing-telemetry-backend-event-handlers.adoc[] +* xref:telemetry-plugin-overview.adoc[] diff --git a/modules/extend/pages/creating-a-telemetry-server.adoc b/modules/extend/pages/creating-a-telemetry-server.adoc new file mode 100644 index 0000000000..83b210c055 --- /dev/null +++ b/modules/extend/pages/creating-a-telemetry-server.adoc @@ -0,0 +1,60 @@ +:_content-type: PROCEDURE +:description: Create a server that receives telemetry events and writes them to standard output +:keywords: telemetry, server, go, events, stdout +:navtitle: Create a telemetry server + +[id="creating-a-telemetry-server"] += Create a telemetry server + +[role="_abstract"] +Create a server that receives telemetry events from the {prod-short} telemetry plugin and writes them to standard output. For production, consider integrating with a third-party telemetry system such as Segment or Woopra. + + +.Prerequisites + +* You have a running instance of {prod}. + +.Procedure + +. Create a `main.go` file for a Go application that starts a server on port `8080` and writes events to standard output: ++ +[source,go] +---- +include::example$creating-a-telemetry-plugin/main.go[] +---- + +. Create a container image based on this code and expose it as a deployment in {orch-name} in the `{prod-namespace}` {orch-namespace}. Clone the repository and build the container: ++ +[source,bash,subs="+attributes,+quotes"] +---- +$ git clone https://github.com/che-incubator/telemetry-server-example +$ cd telemetry-server-example +$ {docker-cli} build -t registry/organization/telemetry-server-example:latest . +$ {docker-cli} push registry/organization/telemetry-server-example:latest +---- + +. Deploy the telemetry server to {orch-name}. ++ +Both `manifest_with_ingress.yaml` and `manifest_with_route` contain definitions for a Deployment and Service. The former also defines a {kubernetes} Ingress, while the latter defines an {orch-name} Route. ++ +In the manifest file, replace the `image` and `host` fields to match the image you pushed, and the public hostname of your {orch-name} cluster. Then run: ++ +[source,bash,subs="+quotes,+attributes"] +---- +$ {orch-cli} apply -f manifest_with_[ingress|route].yaml -n {prod-namespace} +---- + +.Verification + +* Verify that the telemetry server pod is running: ++ +[source,bash,subs="+attributes"] +---- +{orch-cli} get pods -n {prod-namespace} -l app=telemetry-server-example +---- + +[role="_additional-resources"] +.Additional resources + +* link:https://github.com/che-incubator/telemetry-server-example[telemetry-server-example] +* xref:telemetry-plugin-overview.adoc[] diff --git a/modules/extend/pages/default-extensions-for-microsoft-visual-studio-code.adoc b/modules/extend/pages/default-extensions-for-microsoft-visual-studio-code.adoc new file mode 100644 index 0000000000..594a0fcbd3 --- /dev/null +++ b/modules/extend/pages/default-extensions-for-microsoft-visual-studio-code.adoc @@ -0,0 +1,146 @@ +:page-aliases: administration-guide:default-extensions-for-microsoft-visual-studio-code.adoc +:_content-type: PROCEDURE +:description: Preinstall extensions in every workspace +:keywords: extensions, workspace +:navtitle: Pre-install extensions in every workspace + +[id="visual-studio-code-default-extensions"] += Pre-install extensions in every workspace + + +[role="_abstract"] +Preinstall Visual Studio Code extensions in {prod-short} workspaces by configuring the `DEFAULT_EXTENSIONS` environment variable to provide a consistent set of editor extensions on workspace startup. + +After startup, the editor checks for the `DEFAULT_EXTENSIONS` environment variable and installs the specified extensions in the background. To specify multiple extensions, separate the paths with a semicolon. + +There are three ways to embed default `.vsix` extensions into your workspace: + +* Add the extension binary to the source repository. +* Use the devfile `postStart` event to fetch extension binaries from the network. +* Include the extensions' `.vsix` binaries in the `che-code` image. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. + +.Procedure + +. Add the extension binary to the source repository. ++ +Adding the extension binary to the Git repository and defining the environment variable in the devfile is the easiest way to add default extensions to your workspace. +If the `extension.vsix` file exists in the repository root, set the `DEFAULT_EXTENSIONS` environment variable for the tooling container in your `.devfile.yaml`: ++ +[source,yaml] +---- +schemaVersion: 2.3.0 +metadata: + generateName: example-project +components: + - name: tools + container: + image: quay.io/devfile/universal-developer-image:ubi8-latest + env: + - name: 'DEFAULT_EXTENSIONS' + value: '/projects/example-project/extension.vsix' +---- + +. Use the devfile `postStart` event to fetch extension binaries from the network. ++ +Use cURL or GNU Wget to download extensions to your workspace. Specify a devfile command to download extensions and add a `postStart` event to run the command on workspace startup. Define the `DEFAULT_EXTENSIONS` environment variable in the devfile: ++ +[source,yaml] +---- +schemaVersion: 2.3.0 +metadata: + generateName: example-project +components: + - name: tools + container: + image: quay.io/devfile/universal-developer-image:ubi8-latest + env: + - name: DEFAULT_EXTENSIONS + value: '/tmp/extension-1.vsix;/tmp/extension-2.vsix' + +commands: + - id: add-default-extensions + exec: + # name of the tooling container + component: tools + # download several extensions using curl + commandLine: | + curl https://.../extension-1.vsix --location -o /tmp/extension-1.vsix + curl https://.../extension-2.vsix --location -o /tmp/extension-2.vsix + +events: + postStart: + - add-default-extensions +---- ++ +[WARNING] +==== +In some cases curl may download a `.gzip` compressed file. This might make installing the extension impossible. To fix that, save the file as a *.vsix.gz* file and then decompress it with *gunzip*. This replaces the *.vsix.gz* file with an unpacked *.vsix* file: `curl https://some-extension-url --location -o /tmp/extension.vsix.gz && gunzip /tmp/extension.vsix.gz` +==== + +. Include the extensions `.vsix` binaries in the `che-code` image. ++ +Bundling extensions in the editor image and defining the `DEFAULT_EXTENSIONS` environment variable in a ConfigMap applies default extensions without changing the devfile. + +.. Create a directory and place your selected `.vsix` extensions in this directory. + +.. Create a Dockerfile with the following content: ++ +[source,dockerfile] +---- +# inherit che-incubator/che-code:latest +FROM quay.io/che-incubator/che-code:latest +USER 0 + +# copy all .vsix files to /default-extensions directory +RUN mkdir --mode=775 /default-extensions +COPY --chmod=755 *.vsix /default-extensions/ + +# add instruction to the script to copy default extensions to the working container +RUN echo "cp -r /default-extensions /checode/" >> /entrypoint-init-container.sh +---- + +.. Build the image and then push it to a registry: ++ +[source,bash] +---- +$ docker build -t yourname/che-code:next . +$ docker push yourname/che-code:next +---- + +.. Add the new ConfigMap to the user's {orch-namespace}, define the `DEFAULT_EXTENSIONS` environment variable, and specify the absolute paths to the extensions. This ConfigMap sets the environment variable to all workspaces in the user's {orch-namespace}. ++ +[source,yaml] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: vscode-default-extensions + labels: + controller.devfile.io/mount-to-devworkspace: 'true' + controller.devfile.io/watch-configmap: 'true' + annotations: + controller.devfile.io/mount-as: env +data: + DEFAULT_EXTENSIONS: '/checode/default-extensions/extension1.vsix;/checode/default-extensions/extension2.vsix' +---- + +.. Open the {prod-short} Dashboard and navigate to the *Create Workspace* tab on the left side. + +.. In the *Editor Selector* section, expand the *Use an Editor Definition* dropdown and set the editor URI to `yourname/che-code:next`. + +.. Create a workspace by selecting a sample or entering a Git repository URL. + +.Verification + +* Verify that the extensions are installed in the workspace by checking the *Extensions* panel in the editor. + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-single-and-multiroot-workspaces.adoc[] +* xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] diff --git a/modules/extend/pages/deploy-open-vsx-with-prebuilt-image.adoc b/modules/extend/pages/deploy-open-vsx-with-prebuilt-image.adoc new file mode 100644 index 0000000000..2d47509d6a --- /dev/null +++ b/modules/extend/pages/deploy-open-vsx-with-prebuilt-image.adoc @@ -0,0 +1,151 @@ +:_content-type: PROCEDURE +:description: Deploy a standalone Open VSX extension registry from a prebuilt container image +:keywords: openvsx, extension, registry, deploy, prebuilt, image, air-gapped +:navtitle: Deploy from a prebuilt image + +[id="deploy-open-vsx-with-prebuilt-image"] += Deploy from a prebuilt image + +[role="_abstract"] +Deploy a standalone Open VSX extension registry by using an existing container image. Use a private, on-premises registry to control which extensions are available in your {prod-short} workspaces without building from source. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the destination {orch-name} cluster. See {orch-cli-link}. +* You have `jq` installed. + +.Procedure + +. Create a new {orch-name} {orch-namespace} for Open VSX: ++ +[source,bash,subs="+attributes,+quotes"] +---- +{orch-cli} new-project openvsx +---- + +. Save the link:https://github.com/eclipse-openvsx/openvsx/blob/master/deploy/openshift/openvsx-deployment-no-es.yml[openvsx-deployment-no-es.yml] deployment template file on your file system. + +. Deploy Open VSX from the directory where you saved the file: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} process -f openvsx-deployment-no-es.yml \ + -p OPENVSX_SERVER_IMAGE=____ \ + | {orch-cli} apply -f - +---- + +. Verify that all pods in the `openvsx` {orch-namespace} are running and ready: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +\{orch-cli} get pods -n openvsx \ + -o jsonpath='\{range .items[*]}\{@.metadata.name}\{"\t"}\{@.status.phase}\{"\t"}\{.status.containerStatuses[*].ready}\{"\n"}\{end}' +---- + +. Add an Open VSX user with a Personal Access Token (PAT) to the database. + +.. Find the PostgreSQL pod: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +export POSTGRESQL_POD_NAME=$(\{orch-cli} get pods -n openvsx \ + -o jsonpath="\{.items[*].metadata.name}" | tr ' ' '\n' | grep '^postgresql' | head -n 1) +---- + +.. Insert the username into the Open VSX database: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{POSTGRESQL_POD_NAME}" -- bash -c \ + "psql -d openvsx -c \"INSERT INTO user_data (id, login_name, role) VALUES (1001, 'eclipse-che', 'privileged');\"" +---- + +.. Insert the user PAT into the Open VSX database: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{POSTGRESQL_POD_NAME}" -- bash -c \ + "psql -d openvsx -c \"INSERT INTO personal_access_token (id, user_data, value, active, created_timestamp, accessed_timestamp, description, notified) VALUES (1001, 1001, 'eclipse_che_token', true, current_timestamp, current_timestamp, 'extensions publisher', false);\"" +---- ++ +[IMPORTANT] +==== +The user PAT must match the decoded value of `OVSX_PAT_BASE64` specified in the deployment file. If you update `OVSX_PAT_BASE64`, use the new decoded value as the user PAT. +==== + +. Configure {prod-short} to use the internal Open VSX registry: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +export CHECLUSTER_NAME="$(\{orch-cli} get checluster --all-namespaces -o json | jq -r '.items[0].metadata.name')" && +export CHECLUSTER_NAMESPACE="$(\{orch-cli} get checluster --all-namespaces -o json | jq -r '.items[0].metadata.namespace')" && +export OPENVSX_ROUTE_URL="$(\{orch-cli} get route internal -n openvsx -o jsonpath='\{.spec.host}')" && +export PATCH='\{"spec":\{"components":\{"pluginRegistry":\{"openVSXURL":"https://'"$OPENVSX_ROUTE_URL"'"\}\}\}\}' && +\{orch-cli} patch checluster "$\{CHECLUSTER_NAME}" --type=merge --patch "$\{PATCH}" -n "$\{CHECLUSTER_NAMESPACE}" +---- ++ +[TIP] +==== +For detailed instructions on configuring the Open VSX registry URL, see xref:configuring-the-open-vsx-registry-url.adoc[]. +==== + +. Publish a Visual Studio Code extension from a `.vsix` file. The Open VSX registry does not provide any extension by default. You need the extension publisher name and the download URL of the `.vsix` package. + +.. Retrieve the name of the pod running the Open VSX server: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +export OVSX_POD_NAME=$(\{orch-cli} get pods -n openvsx -o jsonpath="\{.items[*].metadata.name}" | tr ' ' '\n' | grep ^openvsx-server) +---- + +.. Download the `.vsix` extension: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "wget -O /tmp/extension.vsix ____" +---- + +.. Create an extension publisher: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "ovsx create-namespace ____" || true +---- + +.. Publish the extension: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "ovsx publish /tmp/extension.vsix" +---- + +.. Delete the downloaded extension file: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "rm /tmp/extension.vsix" +---- + +. Optional: Publish multiple extensions from a list. Update the `deploy/openshift/extensions.txt` file with the download URLs of each `.vsix` file, then publish all listed extensions: ++ +[source,bash,subs="+attributes,+quotes",options="nowrap"] +---- +while IFS= read -r url; do + {orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "wget -O /tmp/extension.vsix '$url' && ovsx publish /tmp/extension.vsix && rm /tmp/extension.vsix" +done < deploy/openshift/extensions.txt +---- + +.Verification + +* Start any workspace and verify the published extensions are available in the Extensions view of the workspace IDE. +* Navigate to the Open VSX route URL to verify the registry UI displays the published extensions. + +[role="_additional-resources"] +.Additional resources + +* xref:proc_configure-internal-open-vsx-access.adoc[] +* xref:proc_deleting-extension-using-openvsx-admin-api.adoc[] +* link:https://github.com/eclipse-openvsx/openvsx/[Eclipse Open VSX sources] +* link:https://github.com/eclipse-openvsx/openvsx/wiki/[Eclipse Open VSX documentation] +* xref:proc_deploy-open-vsx-from-source.adoc[] +* xref:configuring-the-open-vsx-registry-url.adoc[] diff --git a/modules/extend/pages/deploying-a-telemetry-plugin.adoc b/modules/extend/pages/deploying-a-telemetry-plugin.adoc new file mode 100644 index 0000000000..f570fd9beb --- /dev/null +++ b/modules/extend/pages/deploying-a-telemetry-plugin.adoc @@ -0,0 +1,208 @@ +:_content-type: PROCEDURE +:description: Deploy the telemetry backend as a container image and host the plugin on a web server +:keywords: telemetry, deploy, plugin, devfile, container, apache +:navtitle: Deploy a telemetry plugin + +[id="deploying-a-telemetry-plugin"] += Deploy a telemetry plugin + +[role="_abstract"] +Deploy the telemetry backend as a container image, create a devfile v2 plugin, and host the plugin on a web server so that {devworkspace}s can load it. + + +This guide demonstrates hosting the plugin on an Apache web server on {orch-name}. In production, deploy the plugin file to a corporate web server. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the destination {orch-name} cluster. See {orch-cli-link}. +* You have a running instance of {prod}. +* You have a telemetry backend created and tested. See xref:creating-a-telemetry-backend.adoc[]. + +.Procedure + +. Package the Quarkus application as a container image and push it to a container registry by using one of the following options. See link:https://quarkus.io/guides/building-native-image#using-a-multi-stage-docker-build[the Quarkus documentation] for details. ++ +-- +Option A: JVM image:: ++ +.. Create a `Dockerfile.jvm`: ++ +[source,dockerfile] +---- +FROM registry.access.redhat.com/ubi8/openjdk-11:1.11 + +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' + +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 + +ENTRYPOINT ["java", "-Dquarkus.http.host=0.0.0.0", "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", "-Dquarkus.http.port=${DEVWORKSPACE_TELEMETRY_BACKEND_PORT}", "-jar", "/deployments/quarkus-run.jar"] +---- ++ +.. Build and push the image: ++ +[source,bash,subs="+attributes,+quotes"] +---- +mvn package && \ +{docker-cli} build -f src/main/docker/Dockerfile.jvm -t image:tag . +---- + +Option B: Native image:: ++ +.. Create a `Dockerfile.native`: ++ +[source,dockerfile] +---- +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Dquarkus.http.port=${DEVWORKSPACE_TELEMETRY_BACKEND_PORT}"] +---- ++ +.. Build and push the image: ++ +[source,bash,subs="+attributes,+quotes"] +---- +mvn package -Pnative -Dquarkus.native.container-build=true && \ +{docker-cli} build -f src/main/docker/Dockerfile.native -t image:tag . +---- +-- + +. Create a `plugin.yaml` devfile v2 file representing a {devworkspace} plugin that runs your custom backend in a {devworkspace} Pod. For more information about devfile v2, see link:https://devfile.io/docs/2.1.0/overview[Devfile v2 documentation]. ++ +[source,yaml,subs="+quotes"] +---- +schemaVersion: 2.1.0 +metadata: + name: devworkspace-telemetry-backend-plugin + version: 0.0.1 + description: A Demo telemetry backend + displayName: Devworkspace Telemetry Backend +components: + - name: devworkspace-telemetry-backend-plugin + attributes: + workspaceEnv: + - name: DEVWORKSPACE_TELEMETRY_BACKEND_PORT + value: '4167' + container: + image: ____ + env: + - name: WELCOME_MESSAGE + value: 'hello world!' +---- ++ +where: ++ +`____`:: The container image built in the previous step. ++ +`WELCOME_MESSAGE`:: Set the value for the `welcome.message` optional configuration property. + +. Create a `ConfigMap` object that references the `plugin.yaml` file: ++ +[source,bash,subs="+attributes"] +---- +$ {orch-cli} create configmap --from-file=plugin.yaml -n {prod-namespace} telemetry-plugin-yaml +---- + +. Create a `manifest.yaml` file with a Deployment, a Service, and a Route to expose the Apache web server. The Deployment references this `ConfigMap` object and places the `plugin.yaml` in the `/var/www/html` directory. ++ +[source,yaml,subs="+quotes,+attributes"] +---- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: apache +spec: + replicas: 1 + selector: + matchLabels: + app: apache + template: + metadata: + labels: + app: apache + spec: + volumes: + - name: plugin-yaml + configMap: + name: telemetry-plugin-yaml + defaultMode: 420 + containers: + - name: apache + image: 'registry.redhat.io/rhscl/httpd-24-rhel7:latest' + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + volumeMounts: + - name: plugin-yaml + mountPath: /var/www/html + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 25% + maxSurge: 25% + revisionHistoryLimit: 10 + progressDeadlineSeconds: 600 +--- +kind: Service +apiVersion: v1 +metadata: + name: apache +spec: + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 + selector: + app: apache + type: ClusterIP +--- +kind: Route +apiVersion: route.openshift.io/v1 +metadata: + name: apache +spec: + host: apache-che.apps-crc.testing + to: + kind: Service + name: apache + weight: 100 + port: + targetPort: 8080 + wildcardPolicy: None +---- + +. Apply the manifest: ++ +[source,bash,subs="+attributes"] +---- +$ {orch-cli} apply -f manifest.yaml +---- + +.Verification + +* After the deployment has started, confirm that `plugin.yaml` is available in the web server: ++ +[source,bash] +---- +$ curl apache-che.apps-crc.testing/plugin.yaml +---- + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-workspaces-to-load-telemetry-plugin.adoc[] +* xref:administration-guide:using-the-cli-to-configure-the-checluster-custom-resource.adoc[Configure the CheCluster Custom Resource using the CLI] diff --git a/modules/extend/pages/disabling-ai-providers.adoc b/modules/extend/pages/disabling-ai-providers.adoc new file mode 100644 index 0000000000..79c09fa898 --- /dev/null +++ b/modules/extend/pages/disabling-ai-providers.adoc @@ -0,0 +1,53 @@ +:_content-type: PROCEDURE +:description: Disable AI coding assistants by deleting the ai-tool-registry ConfigMap +:keywords: ai, disable, configmap, ai-tool-registry, providers +:navtitle: Disable AI coding assistants + +[id="disabling-ai-providers"] += Disable AI coding assistants + +[role="_abstract"] +Disable AI coding assistants by deleting the `ai-tool-registry` ConfigMap. The AI Provider section on the *Create Workspace* page is hidden when the ConfigMap is absent. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the destination {orch-name} cluster. See {orch-cli-link}. + +.Procedure + +. Delete the `ai-tool-registry` ConfigMap: ++ +[source,bash,subs="+quotes,+attributes"] +---- +$ {orch-cli} delete configmap ai-tool-registry -n {prod-namespace} +---- + +. Verify that the AI provider registry is empty: ++ +[source,bash,subs="+quotes,+attributes"] +---- +$ {orch-cli} exec deploy/{prod-id-short}-dashboard -n {prod-namespace} \ + -- curl -s http://localhost:8080/dashboard/api/ai-registry +---- ++ +Expected output: ++ +[source,json] +---- +{"providers":[],"tools":[],"defaultAiProviders":[]} +---- + +.Verification + +* Open the {prod-short} dashboard and navigate to *Create Workspace*. Verify that the *AI Provider* section is no longer visible. + +[NOTE] +==== +Deleting the ConfigMap does not remove existing API key Secrets from user namespaces. Users who previously stored API keys retain those Secrets until they manually delete them or an administrator removes them. +==== + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-ai-providers.adoc[] +* xref:ai-provider-api-key-secret-reference.adoc[] diff --git a/modules/administration-guide/pages/editor-configurations-for-microsoft-visual-studio-code.adoc b/modules/extend/pages/editor-configurations-for-microsoft-visual-studio-code.adoc similarity index 53% rename from modules/administration-guide/pages/editor-configurations-for-microsoft-visual-studio-code.adoc rename to modules/extend/pages/editor-configurations-for-microsoft-visual-studio-code.adoc index dee52b966e..c7ac96ae5f 100644 --- a/modules/administration-guide/pages/editor-configurations-for-microsoft-visual-studio-code.adoc +++ b/modules/extend/pages/editor-configurations-for-microsoft-visual-studio-code.adoc @@ -1,50 +1,50 @@ +:page-aliases: administration-guide:editor-configurations-for-microsoft-visual-studio-code.adoc :_content-type: PROCEDURE -:description: Applying editor configurations +:description: Apply IDE settings to all workspaces :keywords: settings, extensions, configurations -:navtitle: Applying editor configurations -// :page-aliases: +:navtitle: Apply IDE settings to all workspaces [id="visual-studio-code-editor-configs"] -= Applying editor configurations += Apply IDE settings to all workspaces -You can configure Visual Studio Code - Open Source editor by adding configurations to a ConfigMap. -These configurations are applied to any workspace you open. -Once a workspace is started, the editor checks for this ConfigMap and stores configurations to the corresponding config files. + +[role="_abstract"] +Apply a {orch-name} ConfigMap to standardize the Code - OSS editor across all workspaces, giving every developer the same settings, recommended extensions, and product properties at startup. The following sections are currently supported: -* settings.json -* extensions.json -* product.json -* configurations.json -* policy.json +`settings.json`:: Contains various settings with which you can customize different parts of the Code - OSS editor. + +`extensions.json`:: Contains recommended extensions that are installed when a workspace is started. + +`product.json`:: Contains properties that you need to add to the editor's *product.json* file. If the property already exists, its value is updated. -The *settings.json* section contains various settings with which you can customize different parts of the Code - OSS editor. + -The *extensions.json* section contains recommended extensions that are installed when a workspace is started. + -The *product.json* section contains properties that you need to add to the editor's *product.json* file. If the property already exists, its value will be updated. + -The *configurations.json* section contains properties for Code - OSS editor configuration. For example, you can use the `extensions.install-from-vsix-enabled` property to disable `Install from VSIX` menu item in the Extensions panel. +`configurations.json`:: Contains properties for Code - OSS editor configuration. For example, you can use the `extensions.install-from-vsix-enabled` property to disable the `Install from VSIX` menu item in the Extensions panel. ++ [NOTE] ==== -The `extensions.install-from-vsix-enabled` property disables only the UI action. Extensions can still be installed via the `workbench.extensions.command.installFromVSIX` API command or the CLI. To block these paths as well, see xref:manage-extension-installation.adoc[]. +The `extensions.install-from-vsix-enabled` property disables only the UI action. Extensions can still be installed by using the `workbench.extensions.command.installFromVSIX` API command or the CLI. To block these paths as well, see xref:manage-extension-installation.adoc[]. ==== +`policy.json`:: Controls Code - OSS extension installation by using the `AllowedExtensions` policy and the ability to fully block extension installation. See xref:manage-extension-installation.adoc[]. + +.Prerequisites -The *policy.json* section allows to control over Code - OSS extension installation using the AllowedExtensions policy, as well as the ability to fully block extension installation. See xref:manage-extension-installation.adoc[]. +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. .Procedure -* Add a new ConfigMap to the user's {orch-namespace}, define the supported sections, and specify the properties you want to add. +. Add a new ConfigMap in valid JSON format to the user's {orch-namespace}, define the supported sections, and specify the properties you want to add. + -==== -[source,yaml] +[source,yaml,subs="+quotes"] ---- apiVersion: v1 kind: ConfigMap metadata: name: vscode-editor-configurations labels: - app.kubernetes.io/component: workspaces-config app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: workspaces-config data: extensions.json: | { @@ -71,8 +71,8 @@ data: ] }, "trustedExtensionAuthAccess": [ - ".", - "." + "____.____", + "____.____" ] } configurations.json: | @@ -80,20 +80,19 @@ data: "extensions.install-from-vsix-enabled": false } ---- -==== ++ +where: ++ +`____.____`, `____.____`:: The publisher and extension name pairs for extensions that are granted trusted authentication access. Use the format `publisher.extensionName`. -* Start or restart your workspace +. Optional: To replicate the ConfigMap across all user {orch-namespace}s while preventing user modifications, add the ConfigMap to the {prod-namespace} namespace instead of individual user {orch-namespace}s. -[NOTE] -==== -Make sure that the Configmap contains data in a valid JSON format. -==== - -TIP: Consider adding the ConfigMap to the {prod-namespace} namespace. By adding it to the namespace, you replicate the ConfigMap across all user namespaces while preventing modifications within user's namespaces. See xref:configuring-a-user-namespace.adoc[]. +. Start or restart your workspace. .Verification + . Verify that settings defined in the ConfigMap are applied using one of the following methods: -* Use `F1 → Preferences: Open Remote Settings` to check if the defined settings are applied. +* Use `F1 → Preferences: Open Remote Settings` to check if the defined settings are applied. * Ensure that the settings from the ConfigMap are present in the `/checode/remote/data/Machine/settings.json` file by using the `F1 → File: Open File...` command to inspect the file's content. . Verify that extensions defined in the ConfigMap are applied: @@ -109,3 +108,11 @@ TIP: Consider adding the ConfigMap to the {prod-namespace} namespace. By adding * Open the Command Palette (use `F1`) to check that `Install from VSIX` command is not present in the list of commands. * Use `F1 → Open View → Extensions` to open the `Extensions` panel, then click `...` on the view (`Views and More Actions` tooltip) to check that `Install from VSIX` action is absent in the list of actions. * Go to the Explorer, find a file with the `vsix` extension (redhat.vscode-yaml-1.17.0.vsix, for example), open menu for that file. `Install from VSIX` action should be absent in the menu. + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-single-and-multiroot-workspaces.adoc[] +* xref:trusted-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:manage-extension-installation.adoc[] diff --git a/modules/extend/pages/extending-devspaces.adoc b/modules/extend/pages/extending-devspaces.adoc new file mode 100644 index 0000000000..371ad99d2f --- /dev/null +++ b/modules/extend/pages/extending-devspaces.adoc @@ -0,0 +1,41 @@ +:_content-type: CONCEPT +:description: What you can customize in workspace tooling +:keywords: extend, customize, editors, extensions, registry, IDE +:navtitle: What you can customize in workspace tooling + +[id="extending-devspaces"] += What you can customize in workspace tooling + +[role="_abstract"] +{prod-short} creates a Cloud Development Environment (CDE) for every project. The dashboard and CLI refer to each CDE as a *workspace*. You can customize every layer of the developer tooling to match your organization's requirements. + +By default, {prod-short} provides: + +* Visual Studio Code - Open Source as the workspace editor. +* An embedded Open VSX registry running in the `plugin-registry` pod. This registry contains a curated subset of extensions from the public open-vsx.org registry and supports air-gapped environments. + +As a Platform administrator, you can customize the following areas: + +.What do you need to customize? +[cols="1,2", options="header"] +|=== +| Goal | Description + +| Choose which editors are available +| Add custom editor definitions, set the default IDE, hide or restore editors, and host editor binaries internally for air-gapped clusters. + +| Customize the default IDE +| Configure multi-root project layout, trusted and default extensions, editor settings, and extension installation policies for Visual Studio Code - Open Source. + +| Add or remove IDE extensions +| Change which extensions are available by pointing to a different Open VSX registry, editing workspace devfiles, or using the command line. + +| Deploy a private extension registry +| Run an on-premises Open VSX instance for air-gapped environments where workspaces cannot reach the public internet. +|=== + +[role="_additional-resources"] +.Additional resources + +* xref:ai-and-analytics-capabilities.adoc[Add AI and analytics capabilities] +* xref:install:installing-che-on-openshift-using-cli.adoc[Install {prod-short}] diff --git a/modules/administration-guide/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc b/modules/extend/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc similarity index 55% rename from modules/administration-guide/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc rename to modules/extend/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc index bf8486108d..e2c04e152b 100644 --- a/modules/administration-guide/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc +++ b/modules/extend/pages/extensions-for-microsoft-visual-studio-code-open-source.adoc @@ -1,17 +1,20 @@ +:page-aliases: administration-guide:extensions-for-microsoft-visual-studio-code-open-source.adoc :_content-type: ASSEMBLY -:description: Extensions for Microsoft Visual Studio Code - Open Source +:description: How the Open VSX extension registry works :keywords: extensions, vs-code, vsx, open-vsx, marketplace -:navtitle: Extensions for Microsoft Visual Studio Code - Open Source -//:page-aliases: +:navtitle: How the Open VSX extension registry works [id="extensions-for-microsoft-visual-studio-code-open-source"] -= Extensions for Microsoft Visual Studio Code - Open Source += How the Open VSX extension registry works -To manage extensions, {prod-short} uses one of the following Open VSX registry instances: +[role="_abstract"] +The Open VSX extension registry works as the extension source for Microsoft Visual Studio Code - Open Source in {prod-short}, providing IDE extensions from one of several configurable registry instances. -* The embedded instance of the Open VSX registry that runs in the `plugin-registry` pod of {prod-short} to support air-gapped, offline, and proxy-restricted environments. The embedded Open VSX registry contains only a subset of the extensions published on link:https://open-vsx.org[open-vsx.org]. You can customize this subset xref:adding-or-removing-extensions-in-a-workspace_{context}[using a workspace] or xref:adding-or-removing-extensions-on-linux_{context}[using a Linux operating system]. +To manage extensions, this IDE uses one of the Open VSX registry instances: -* The public link:https://open-vsx.org[open-vsx.org] registry that is accessed over the internet. +* The embedded instance of the Open VSX registry that runs in the `plugin-registry` pod of {prod-short} to support air-gapped, offline, and proxy-restricted environments. The embedded Open VSX registry contains only a subset of the extensions published on the public open-vsx.org registry. This subset is customizable. + +* The public open-vsx.org registry that is accessed over the internet. * A standalone Open VSX registry instance that is deployed on a network accessible from {prod-short} workspace pods. diff --git a/modules/extend/pages/implementing-telemetry-backend-event-handlers.adoc b/modules/extend/pages/implementing-telemetry-backend-event-handlers.adoc new file mode 100644 index 0000000000..7f6b42b201 --- /dev/null +++ b/modules/extend/pages/implementing-telemetry-backend-event-handlers.adoc @@ -0,0 +1,141 @@ +:_content-type: PROCEDURE +:description: Implement AnalyticsManager event handling methods and test the backend +:keywords: telemetry, backend, isEnabled, onEvent, onActivity, destroy, increaseDuration +:navtitle: Implement and test telemetry backend event handlers + +[id="implementing-telemetry-backend-event-handlers"] += Implement and test telemetry backend event handlers + +[role="_abstract"] +Implement the `AnalyticsManager` event handling methods in your telemetry backend and test the backend in a running {devworkspace} to verify that events are received from the front-end plugin. + + +.Prerequisites + +* You have a running instance of {prod}. +* You have a telemetry backend project created. See xref:creating-a-telemetry-backend.adoc[]. + +.Procedure + +. Set the `DEVWORKSPACE_TELEMETRY_BACKEND_PORT` environment variable in the {devworkspace}. Here, the value is set to `4167`. ++ +[source,yaml] +---- +spec: + template: + attributes: + workspaceEnv: + - name: DEVWORKSPACE_TELEMETRY_BACKEND_PORT + value: '4167' +---- + +. Restart the {devworkspace} from the {prod} dashboard. + +. Run the following command within a {devworkspace}'s terminal window to start the application. Use the `--settings` flag to specify the path to the `settings.xml` file that contains the GitHub access token. ++ +[source,bash] +---- +$ mvn --settings=settings.xml quarkus:dev -Dquarkus.http.port=${DEVWORKSPACE_TELEMETRY_BACKEND_PORT} +---- ++ +The application now receives telemetry events through port `4167` from the front-end plugin. Verify that the following output is logged: ++ +---- +INFO [org.ecl.che.inc.AnalyticsManager] (Quarkus Main Thread) No welcome message provided +INFO [io.quarkus] (Quarkus Main Thread) devworkspace-telemetry-example-plugin 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.7.2.Final) started in 0.323s. Listening on: http://localhost:4167 +INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. +INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, kubernetes-client, rest-client, rest-client-jackson, resteasy, resteasy-jsonb, smallrye-context-propagation, smallrye-openapi, swagger-ui, vertx] +---- + +. Customize `isEnabled()` in `AnalyticsManager.java`. For this example, the method always returns `true`: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/isEnabled.java[] +---- +==== ++ +The link:https://github.com/che-incubator/devworkspace-telemetry-woopra-plugin/blob/main/src/main/java/com/redhat/devworkspace/services/telemetry/woopra/AnalyticsManager.java[hosted {prod-short} Woopra backend] demonstrates a more advanced `isEnabled()` implementation that checks for a configuration property before enabling the backend. + +. Implement `onEvent()` to send events to the telemetry server. For the example application, it sends an HTTP POST payload to the `/event` endpoint. + +.. Configure the RESTEasy REST Client by creating a `TelemetryService.java` interface: ++ +.`TelemetryService.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/TelemetryService.java[] +---- +==== +<1> The endpoint to make the `POST` request to. + +.. Specify the base URL for `TelemetryService` in `src/main/resources/application.properties`: ++ +.`application.properties` +==== +---- +org.my.group.TelemetryService/mp-rest/url=http://little-telemetry-server-che.apps-crc.testing +---- +==== + +.. Inject `TelemetryService` into `AnalyticsManager.java` and send a `POST` request in `onEvent()`: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/onEvent.java[] +---- +==== ++ +This sends an HTTP request to the telemetry server and automatically delays identical events for a small period of time. The default duration is 1500 milliseconds. + +. Implement `increaseDuration()` in `AnalyticsManager.java`. Many telemetry systems recognize event duration. The `AbstractAnalyticsManager` merges similar events that happen in the same frame of time into one event. This implementation is a no-op: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/increaseDuration.java[] +---- +==== + +. Implement `onActivity()` in `AnalyticsManager.java`. Set an inactive timeout limit and send a `WORKSPACE_INACTIVE` event if the last event time exceeds the timeout: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/onActivity.java[] +---- +==== + +. Implement `destroy()` in `AnalyticsManager.java`. When called, send a `WORKSPACE_STOPPED` event and shut down any resources such as connection pools: ++ +.`AnalyticsManager.java` +==== +[source,java] +---- +include::example$creating-a-telemetry-plugin/destroy.java[] +---- +==== + +.Verification + +. To verify that the `onEvent()` method receives events from the front-end plugin, press the kbd:[l] key to disable Quarkus live coding and edit any file within the IDE. The following output should be logged: ++ +---- +INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Live reload disabled +INFO [org.ecl.che.inc.AnalyticsManager] (executor-thread-2) The received event is: Edit Workspace File in Che +---- + +. Stop the application with kbd:[Ctrl+C] and verify that a `WORKSPACE_STOPPED` event is sent to the server. + +[role="_additional-resources"] +.Additional resources + +* xref:deploying-a-telemetry-plugin.adoc[] +* xref:telemetry-plugin-overview.adoc[] diff --git a/modules/extend/pages/manage-extension-installation.adoc b/modules/extend/pages/manage-extension-installation.adoc new file mode 100644 index 0000000000..5e41d1b6c5 --- /dev/null +++ b/modules/extend/pages/manage-extension-installation.adoc @@ -0,0 +1,142 @@ +:page-aliases: administration-guide:manage-extension-installation.adoc +:_content-type: PROCEDURE +:description: Control which extensions users can install +:keywords: extensions, workspace +:navtitle: Control which extensions users can install + +[id="visual-studio-code-manage-extensions-installation"] += Control which extensions users can install + + +[role="_abstract"] +Control Code - OSS extension installation by using a ConfigMap. Enforce a fine-grained allow or deny list by using the `AllowedExtensions` policy. + +You can also block installs through the CLI, default extensions, and the `workbench.extensions.command.installFromVSIX` API command. The following properties are supported: + +* `BlockCliExtensionsInstallation` -- when enabled, blocks installation of extensions through the CLI. +* `BlockDefaultExtensionsInstallation` -- when enabled, blocks installation of default extensions. See xref:default-extensions-for-microsoft-visual-studio-code.adoc[]. +* `BlockInstallFromVSIXCommandExtensionsInstallation` -- when enabled, blocks installation of extensions through the `workbench.extensions.command.installFromVSIX` API command. +* `AllowedExtensions` -- provides fine-grained control over Code - OSS extension installation. When this policy is applied, already installed extensions that are not allowed are disabled and display a warning. For conceptual background, see link:https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions[Configure allowed extensions]. + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. + +.Procedure + +. Add a new ConfigMap to the {prod-namespace} namespace and specify the properties you want to add: ++ +[source,yaml,subs="+attributes,+quotes"] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: vscode-editor-configurations + namespace: {prod-namespace} + labels: + app.kubernetes.io/component: workspaces-config + app.kubernetes.io/part-of: che.eclipse.org + annotations: + controller.devfile.io/mount-as: subpath + controller.devfile.io/mount-path: /checode-config + controller.devfile.io/read-only: 'true' +data: + policy.json: | + { + "BlockCliExtensionsInstallation": true, + "BlockDefaultExtensionsInstallation": true, + "BlockInstallFromVSIXCommandExtensionsInstallation": true, + "AllowedExtensions": { + "*": true, + "dbaeumer.vscode-eslint": false, + "ms-python.python": false, + "redhat": false + } + } +---- ++ +[NOTE] +==== +Ensure that the ConfigMap contains data in a valid JSON format. +==== + +. Optional: To completely disable extension installation instead of using fine-grained control, set all extensions to disallowed: ++ +[source,yaml,subs="+attributes,+quotes"] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: vscode-editor-configurations + namespace: {prod-namespace} + labels: + app.kubernetes.io/component: workspaces-config + app.kubernetes.io/part-of: che.eclipse.org + annotations: + controller.devfile.io/mount-as: subpath + controller.devfile.io/mount-path: /checode-config + controller.devfile.io/read-only: 'true' +data: + policy.json: | + { + "AllowedExtensions": { + "*": false + } + } +---- + +. Start or restart your workspace. + +. Optional: Add the ConfigMap in the user's {orch-namespace}: ++ +[source,yaml,subs="+attributes,+quotes"] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: vscode-editor-configurations + labels: + controller.devfile.io/mount-to-devworkspace: 'true' + controller.devfile.io/watch-configmap: 'true' + annotations: + controller.devfile.io/mount-as: subpath + controller.devfile.io/mount-path: /checode-config + controller.devfile.io/read-only: 'true' +data: + policy.json: | + { + "AllowedExtensions": { + "*": false + } + } +---- ++ +[NOTE] +==== +When the ConfigMap is stored in the user's {orch-namespace}, the user can edit its values. +==== + +.Verification + +. Verify that the `BlockCliExtensionsInstallation` property is applied: +* Press F1, select *Preferences: Open Settings (UI)*, and enter `BlockCliExtensionsInstallation` in search. +* Provide a `.vsix` file and try CLI install. The installation fails with "Installation of extensions via CLI has been blocked by an administrator". + +. Verify that the `BlockDefaultExtensionsInstallation` property is applied: +* Check Settings for the property. +* Configure default extensions and verify they are not installed on workspace start or restart. + +. Verify that the `BlockInstallFromVSIXCommandExtensionsInstallation` property is applied: +* Check Settings for the property. +* The `workbench.extensions.command.installFromVSIX` API command is blocked. + +. Verify that rules defined in the `AllowedExtensions` section are applied: +* Check *Settings* -> `extensions.allowed`. +* Disallowed extensions display a "This extension cannot be installed because it is not in the allowed list" warning. + +[role="_additional-resources"] +.Additional resources + +* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] +* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] +* link:https://code.visualstudio.com/docs/setup/enterprise#_configure-allowed-extensions[Configure allowed extensions] diff --git a/modules/administration-guide/pages/managing-ide-extensions.adoc b/modules/extend/pages/managing-ide-extensions.adoc similarity index 71% rename from modules/administration-guide/pages/managing-ide-extensions.adoc rename to modules/extend/pages/managing-ide-extensions.adoc index 5ddb74e2b4..0e7a42616f 100644 --- a/modules/administration-guide/pages/managing-ide-extensions.adoc +++ b/modules/extend/pages/managing-ide-extensions.adoc @@ -1,14 +1,14 @@ +:page-aliases: administration-guide:managing-ide-extensions.adoc :_content-type: CONCEPT -:description: Options for managing IDE extensions in {prod} workspaces +:description: How IDE extensions work in {prod-short} workspaces :keywords: extensions, plugins, openvsx, registry, ide -:navtitle: Managing IDE extensions -:page-aliases: +:navtitle: How IDE extensions work in workspaces [id="managing-ide-extensions"] -= Managing IDE extensions += How IDE extensions work in workspaces [role="_abstract"] -Manage IDE extensions in {prod-short} workspaces to control which extensions are available, trusted, and pre-installed for users across different IDE types. Extension management is essential for air-gapped environments, security-conscious organizations, and teams that need consistent developer tooling. +IDE extensions in {prod-short} workspaces work through an Open VSX registry instance that controls which extensions are available, trusted, and pre-installed for users across different IDE types. Extension management is essential for air-gapped environments, security-conscious organizations, and teams that need consistent developer tooling. IDEs in {prod-short} use extensions to provide language support, debugging, linting, and other development tools. The Microsoft Visual Studio Code - Open Source editor manages extensions through the Open VSX registry. diff --git a/modules/administration-guide/pages/proc_configure-internal-open-vsx-access.adoc b/modules/extend/pages/proc_configure-internal-open-vsx-access.adoc similarity index 83% rename from modules/administration-guide/pages/proc_configure-internal-open-vsx-access.adoc rename to modules/extend/pages/proc_configure-internal-open-vsx-access.adoc index 51318e6f6b..3fb28c0a29 100644 --- a/modules/administration-guide/pages/proc_configure-internal-open-vsx-access.adoc +++ b/modules/extend/pages/proc_configure-internal-open-vsx-access.adoc @@ -1,18 +1,18 @@ +:page-aliases: administration-guide:proc_configure-internal-open-vsx-access.adoc :_content-type: PROCEDURE -:description: Restrict the Open VSX registry to internal cluster access +:description: Restrict the extension registry to internal cluster traffic :keywords: administration guide, openvsx, security, internal, routing -:navtitle: Configure internal Open VSX access -:page-aliases: +:navtitle: Restrict the extension registry to internal traffic [id="proc_configure-internal-open-vsx-access"] -= Configure internal access to the Open VSX registry += Restrict the extension registry to internal traffic [role="_abstract"] Restrict your Open VSX registry to internal cluster traffic by removing the public route and configuring {prod-short} to use the internal service URL. Internal routing keeps extension registry traffic within the cluster and avoids public exposure. .Prerequisites -* You have Open VSX deployed in the `openvsx` namespace. +* You have Open VSX deployed in the `openvsx` {orch-namespace}. * You have the `{orch-cli}` tool installed. * You are logged in to the cluster as a cluster administrator. * You have `jq` installed. diff --git a/modules/administration-guide/pages/proc_deleting-extension-from-postgresql-database.adoc b/modules/extend/pages/proc_deleting-extension-from-postgresql-database.adoc similarity index 82% rename from modules/administration-guide/pages/proc_deleting-extension-from-postgresql-database.adoc rename to modules/extend/pages/proc_deleting-extension-from-postgresql-database.adoc index 1785fc5f42..41fb89e82d 100644 --- a/modules/administration-guide/pages/proc_deleting-extension-from-postgresql-database.adoc +++ b/modules/extend/pages/proc_deleting-extension-from-postgresql-database.adoc @@ -1,20 +1,20 @@ +:page-aliases: administration-guide:proc_deleting-extension-from-postgresql-database.adoc :_content-type: PROCEDURE -:description: Delete an extension directly from the PostgreSQL database +:description: Remove an extension directly from the PostgreSQL database :keywords: administration guide, openvsx, delete, extension, postgresql, database -:navtitle: Delete an extension from the PostgreSQL database -:page-aliases: +:navtitle: Remove an extension directly from the database [id="proc_deleting-extension-from-postgresql-database"] -= Delete an extension from the PostgreSQL database += Remove an extension directly from the database [role="_abstract"] Remove extension records and related data directly from the PostgreSQL database when the administrator API is not available or when you need to clean up specific data. If the extension uses local storage, you must also remove its files from the Open VSX server pod. .Prerequisites -* You have access to the cluster where the Open VSX registry is deployed in the `openvsx` namespace. -* You have the `{orch-cli}` tool installed. -* You know the namespace name and extension name that you want to delete. +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. +* The Open VSX registry is deployed in the `openvsx` {orch-namespace}. +* You know the publisher name and extension name that you want to delete. .Procedure @@ -43,20 +43,20 @@ At the `postgres=#` prompt, connect to the database: + You are now connected to the `openvsx` database as the `postgres` user. -. Find the namespace ID and extension ID: +. Find the publisher ID and extension ID: + -Identify the extension publisher. Replace `____` with the actual publisher name: +Identify the extension publisher. Replace `____` with the actual publisher name: + [source,sql,subs="+quotes",options="nowrap"] ---- -SELECT id, name FROM namespace WHERE name = '____'; +SELECT id, name FROM namespace WHERE name = '____'; ---- + -Identify the extension ID. Replace `____` and `____` with the values from the previous query: +Identify the extension ID. Replace `____` and `____` with the values from the previous query: + [source,sql,subs="+quotes",options="nowrap"] ---- -SELECT id, name, namespace_id FROM extension WHERE namespace_id = ____ AND name = '____'; +SELECT id, name, namespace_id FROM extension WHERE namespace_id = ____ AND name = '____'; ---- + Note the ID of the extension to use as `____` in the next steps. diff --git a/modules/administration-guide/pages/proc_deleting-extension-using-openvsx-admin-api.adoc b/modules/extend/pages/proc_deleting-extension-using-openvsx-admin-api.adoc similarity index 83% rename from modules/administration-guide/pages/proc_deleting-extension-using-openvsx-admin-api.adoc rename to modules/extend/pages/proc_deleting-extension-using-openvsx-admin-api.adoc index c8c5003474..4b61b0941f 100644 --- a/modules/administration-guide/pages/proc_deleting-extension-using-openvsx-admin-api.adoc +++ b/modules/extend/pages/proc_deleting-extension-using-openvsx-admin-api.adoc @@ -1,19 +1,19 @@ +:page-aliases: administration-guide:proc_deleting-extension-using-openvsx-admin-api.adoc :_content-type: PROCEDURE -:description: Delete an extension from the Open VSX registry using the administrator API +:description: Remove an extension from the Open VSX registry using the administrator API :keywords: administration guide, openvsx, delete, extension, api -:navtitle: Delete an extension using the Open VSX API -:page-aliases: +:navtitle: Remove an extension through the registry API [id="proc_deleting-extension-using-openvsx-admin-api"] -= Delete an extension by using the Open VSX administrator API += Remove an extension through the registry API [role="_abstract"] -Delete an extension from your private Open VSX registry by calling the administrator API with an administrator user and a Personal Access Token (PAT). +Remove an extension from your private Open VSX registry by calling the administrator API with an administrator user and a Personal Access Token (PAT). .Prerequisites -* You have access to the cluster where the Open VSX registry is deployed in the `openvsx` namespace. -* You have the `{orch-cli}` tool installed. +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. +* The Open VSX registry is deployed in the `openvsx` {orch-namespace}. .Procedure diff --git a/modules/administration-guide/pages/proc_deploy-open-vsx-from-source.adoc b/modules/extend/pages/proc_deploy-open-vsx-from-source.adoc similarity index 90% rename from modules/administration-guide/pages/proc_deploy-open-vsx-from-source.adoc rename to modules/extend/pages/proc_deploy-open-vsx-from-source.adoc index d56f233112..d16a51baf3 100644 --- a/modules/administration-guide/pages/proc_deploy-open-vsx-from-source.adoc +++ b/modules/extend/pages/proc_deploy-open-vsx-from-source.adoc @@ -1,19 +1,19 @@ +:page-aliases: administration-guide:proc_deploy-open-vsx-from-source.adoc :_content-type: PROCEDURE -:description: Build Open VSX from source and deploy a customized instance +:description: Build a custom extension registry from source code and deploy it to your cluster :keywords: administration guide, openvsx, build, deploy, registry, extensions -:navtitle: Deploy Open VSX from source -:page-aliases: +:navtitle: Build a custom extension registry from source [id="proc_deploy-open-vsx-from-source"] -= Deploy Open VSX from source += Build a custom extension registry from source [role="_abstract"] -Build custom Open VSX server and CLI images from source and deploy them to your cluster. Use this method when you need a specific Open VSX version or custom modifications to the registry. +Build custom Open VSX server and CLI images from source and deploy them to your cluster. A source build gives you full control over the Open VSX version and allows custom modifications to the registry. .Prerequisites * You have the `{orch-cli}` tool installed. -* You are logged in to the cluster where {prod-short} is deployed as a cluster administrator. +* You are logged in to the {orch-name} cluster where {prod-short} is deployed as a cluster administrator. + [TIP] ==== @@ -25,7 +25,7 @@ Build custom Open VSX server and CLI images from source and deploy them to your .Procedure -. Create a new namespace for Open VSX: +. Create a new {orch-namespace} for Open VSX: + [source,bash,subs="+attributes,+quotes"] ---- @@ -94,7 +94,7 @@ where: | {orch-cli} apply -f - ---- -. Verify that all pods in the `openvsx` namespace are running and ready: +. Verify that all pods in the `openvsx` {orch-namespace} are running and ready: + [source,bash,subs="+attributes,+quotes",options="nowrap"] ---- @@ -160,11 +160,11 @@ export OVSX_POD_NAME=$({orch-cli} get pods -n openvsx -o jsonpath="\{.items[*].m {orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "wget -O /tmp/extension.vsix ____" ---- -.. Create an extension namespace: +.. Create an extension publisher: + [source,bash,subs="+attributes,+quotes",options="nowrap"] ---- -{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "ovsx create-namespace ____" || true +{orch-cli} exec -n openvsx "$\{OVSX_POD_NAME}" -- bash -c "ovsx create-namespace ____" || true ---- .. Publish the extension: @@ -195,16 +195,12 @@ done < deploy/openshift/extensions.txt * Start any workspace and verify the published extensions are available in the Extensions view of the workspace IDE. * Navigate to the Open VSX route URL to verify the registry UI displays the published extensions. -.Next steps - -* To restrict the registry to internal cluster traffic, see xref:proc_configure-internal-open-vsx-access.adoc[]. -* To remove a published extension, see xref:proc_deleting-extension-using-openvsx-admin-api.adoc[]. - [role="_additional-resources"] .Additional resources +* To restrict the registry to internal cluster traffic, see xref:proc_configure-internal-open-vsx-access.adoc[]. +* To remove a published extension, see xref:proc_deleting-extension-using-openvsx-admin-api.adoc[]. * link:https://github.com/eclipse-openvsx/openvsx/[Eclipse Open VSX sources] * link:https://github.com/eclipse-openvsx/openvsx/releases[Eclipse Open VSX releases] * link:https://github.com/eclipse-openvsx/openvsx/wiki/[Eclipse Open VSX documentation] * xref:proc_deploy-open-vsx-from-workspace.adoc[] -* xref:proc_configure-internal-open-vsx-access.adoc[] diff --git a/modules/administration-guide/pages/proc_deploy-open-vsx-from-workspace.adoc b/modules/extend/pages/proc_deploy-open-vsx-from-workspace.adoc similarity index 89% rename from modules/administration-guide/pages/proc_deploy-open-vsx-from-workspace.adoc rename to modules/extend/pages/proc_deploy-open-vsx-from-workspace.adoc index d08cc4a012..ef0c4be6bf 100644 --- a/modules/administration-guide/pages/proc_deploy-open-vsx-from-workspace.adoc +++ b/modules/extend/pages/proc_deploy-open-vsx-from-workspace.adoc @@ -1,14 +1,14 @@ +:page-aliases: administration-guide:proc_deploy-open-vsx-from-workspace.adoc :_content-type: PROCEDURE -:description: Deploy Open VSX from a Che workspace using devfile tasks +:description: Build the Open VSX extension registry inside a workspace using devfile tasks :keywords: administration guide, openvsx, deploy, workspace, devfile -:navtitle: Deploy Open VSX from a workspace -:page-aliases: +:navtitle: Build the extension registry inside a workspace [id="proc_deploy-open-vsx-from-workspace"] -= Deploy Open VSX from a {prod-short} workspace += Build the extension registry inside a workspace [role="_abstract"] -Deploy an on-premises Open VSX extension registry by using predefined devfile tasks in a {prod-short} workspace. The workspace environment includes all necessary tools and commands defined in the `.devfile.yaml` file of the Open VSX repository. +Build an on-premises Open VSX extension registry by using predefined devfile tasks in a {prod-short} workspace. The workspace environment includes all necessary tools and commands defined in the `.devfile.yaml` file of the Open VSX repository. .Prerequisites @@ -31,7 +31,7 @@ The environment, including all necessary commands, is defined in the `.devfile.y . Login to the cluster as a cluster administrator from the workspace terminal: .. Navigate to *Terminal* -> *New Terminal* -.. Execute: `$ oc login --token= --server=` +.. Execute: `$ oc login --token=____ --server=____` . Create a new project for Open VSX. + Run the `2.1. Create Namespace for OpenVSX` task in the workspace: @@ -116,15 +116,11 @@ Update the `deploy/openshift/extensions.txt` file with the download URLs of each * Start any workspace and verify the published extensions are available in the Extensions view of the workspace IDE. * Open the `internal` route in the `openvsx` project to verify the registry UI displays the published extensions. -.Next steps - -* To restrict the registry to internal cluster traffic, see xref:proc_configure-internal-open-vsx-access.adoc[]. -* To remove a published extension, see xref:proc_deleting-extension-using-openvsx-admin-api.adoc[]. - [role="_additional-resources"] .Additional resources +* To restrict the registry to internal cluster traffic, see xref:proc_configure-internal-open-vsx-access.adoc[]. +* To remove a published extension, see xref:proc_deleting-extension-using-openvsx-admin-api.adoc[]. * link:https://github.com/eclipse-openvsx/openvsx/[Eclipse Open VSX sources] * link:https://github.com/eclipse-openvsx/openvsx/blob/master/.devfile.yaml[Open VSX devfile] * xref:proc_deploy-open-vsx-from-source.adoc[] -* xref:proc_configure-internal-open-vsx-access.adoc[] diff --git a/modules/administration-guide/pages/show-deprecated-editors.adoc b/modules/extend/pages/show-deprecated-editors.adoc similarity index 54% rename from modules/administration-guide/pages/show-deprecated-editors.adoc rename to modules/extend/pages/show-deprecated-editors.adoc index 54eb992c5b..c400cea9a9 100644 --- a/modules/administration-guide/pages/show-deprecated-editors.adoc +++ b/modules/extend/pages/show-deprecated-editors.adoc @@ -1,13 +1,14 @@ +:page-aliases: administration-guide:show-deprecated-editors.adoc, installation-guide:show-deprecated-editors.adoc :_content-type: PROCEDURE -:description: Show deprecated editors +:description: Restore access to deprecated editors :keywords: administration guide, show deprecated, dashboard, editors -:navtitle: Show deprecated editors -:page-aliases: installation-guide:show-deprecated-editors.adoc +:navtitle: Restore access to deprecated editors [id="show-deprecated-editors"] -= Show deprecated editors += Restore access to deprecated editors -Learn how to show deprecated {prod-short} editors on the Dashboard. By default, the Dashboard UI hides them. +[role="_abstract"] +Restore access to deprecated {prod-short} editors on the Dashboard to support users who need them during migration to a supported editor. By default, the Dashboard UI hides them. .Prerequisites @@ -17,7 +18,7 @@ Learn how to show deprecated {prod-short} editors on the Dashboard. By default, .Procedure -. An editor ID has the following format: `publisher/name/version`. Find out the IDs of the deprecated editors: +. Determine the IDs of the deprecated editors. An editor ID has the following format: `publisher/name/version`. + [source,subs="+quotes,+attributes"] ---- @@ -25,7 +26,12 @@ Learn how to show deprecated {prod-short} editors on the Dashboard. By default, -- curl -s http://localhost:8080/dashboard/api/editors | jq -r '[.[] | select(.metadata.tags != null) | select(.metadata.tags[] | contains("Deprecate")) | "\(.metadata.attributes.publisher)/\(.metadata.name)/\(.metadata.attributes.version)"]' ---- -. Configure the `CheCluster` Custom Resource. See xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[]. +. Edit the `CheCluster` Custom Resource on the cluster: ++ +[source,subs="+attributes"] +---- +{orch-cli} edit checluster/{prod-checluster} -n {prod-namespace} +---- + [source,yaml] ---- @@ -39,8 +45,9 @@ spec: value: 'true' ---- +[role="_additional-resources"] .Additional resources * xref:install:using-chectl-to-configure-the-checluster-custom-resource-during-installation.adoc[] -* xref:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] +* xref:administration-guide:using-the-cli-to-configure-the-checluster-custom-resource.adoc[] diff --git a/modules/extend/pages/telemetry-plugin-overview.adoc b/modules/extend/pages/telemetry-plugin-overview.adoc new file mode 100644 index 0000000000..1d9e4f2e05 --- /dev/null +++ b/modules/extend/pages/telemetry-plugin-overview.adoc @@ -0,0 +1,32 @@ +:page-aliases: administration-guide:creating-a-telemetry-plugin.adoc, extensions:creating-a-telemetry-plug-in.adoc +:_content-type: CONCEPT +:description: How telemetry plugins work in workspaces +:keywords: telemetry, plugin, analytics, backend, AbstractAnalyticsManager +:navtitle: How telemetry plugins work + +[id="telemetry-plugin-overview"] += How telemetry plugins work + +[role="_abstract"] +A telemetry plugin for {prod-short} collects workspace usage data and sends it to your analytics backend. The plugin extends the `AbstractAnalyticsManager` class with methods for event handling, activity tracking, and shutdown. + + +The `AbstractAnalyticsManager` class requires the following method implementations: + +* `isEnabled()` - determines whether the telemetry backend is functioning correctly. This can mean always returning `true`, or have more complex checks, for example, returning `false` when a connection property is missing. +* `destroy()` - cleanup method that is run before shutting down the telemetry backend. This method sends the `WORKSPACE_STOPPED` event. +* `onActivity()` - notifies that some activity is still happening for a given user. This is mainly used to send `WORKSPACE_INACTIVE` events. +* `onEvent()` - submits telemetry events to the telemetry server, such as `WORKSPACE_USED` or `WORKSPACE_STARTED`. +* `increaseDuration()` - increases the duration of a current event rather than sending many events in a small frame of time. + +A finished example of the telemetry backend is available in the `devworkspace-telemetry-example-plugin` repository. For procedures to implement each method and the complete example code, see Additional resources. + +[role="_additional-resources"] +.Additional resources + +* xref:creating-a-telemetry-server.adoc[] +* xref:creating-a-telemetry-backend.adoc[] +* xref:implementing-telemetry-backend-event-handlers.adoc[] +* xref:deploying-a-telemetry-plugin.adoc[] +* xref:configuring-workspaces-to-load-telemetry-plugin.adoc[] +* link:https://github.com/che-incubator/devworkspace-telemetry-example-plugin[devworkspace-telemetry-example-plugin] diff --git a/modules/extend/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc b/modules/extend/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc new file mode 100644 index 0000000000..f452222d6c --- /dev/null +++ b/modules/extend/pages/trusted-extensions-for-microsoft-visual-studio-code.adoc @@ -0,0 +1,75 @@ +:page-aliases: administration-guide:trusted-extensions-for-microsoft-visual-studio-code.adoc +:_content-type: PROCEDURE +:description: Grant extensions access to OAuth tokens +:keywords: extensions, vs-code, vsx, open-vsx, marketplace +:navtitle: Grant extensions access to OAuth tokens + +[id="visual-studio-code-trusted-extensions"] += Grant extensions access to OAuth tokens + + +[role="_abstract"] +Grant specific extensions access to OAuth authentication tokens in Microsoft Visual Studio Code by configuring the `trustedExtensionAuthAccess` field. Extensions that access services such as GitHub or Microsoft can then authenticate without manual intervention. + +[source,json,subs="+quotes"] +---- + "trustedExtensionAuthAccess": [ + "__.__", + "__.__" + ] +---- + +Define the variable in the devfile or in a ConfigMap. + +[WARNING] +==== +Use the `trustedExtensionAuthAccess` field with caution as it could potentially lead to security risks if misused. Give access only to trusted extensions. +==== + +[IMPORTANT] +==== +Since the Microsoft Visual Studio Code editor is bundled within `che-code` image, you can only change the `product.json` file when the workspace is started up. +==== + +.Prerequisites + +* An active `{orch-cli}` session with administrative permissions to the {orch-name} cluster. See {orch-cli-link}. + +.Procedure + +. Define the `VSCODE_TRUSTED_EXTENSIONS` environment variable in devfile.yaml: ++ +[source,yaml,subs="+quotes"] +---- + env: + - name: VSCODE_TRUSTED_EXTENSIONS + value: "__.__,__.__" +---- + +. Alternatively, mount a ConfigMap with the `VSCODE_TRUSTED_EXTENSIONS` environment variable. With a ConfigMap, the variable is propagated to all your workspaces and you do not need to add the variable to each devfile you are using. ++ +[source,yaml,subs="+quotes"] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: trusted-extensions + labels: + controller.devfile.io/mount-to-devworkspace: 'true' + controller.devfile.io/watch-configmap: 'true' + annotations: + controller.devfile.io/mount-as: env +data: + VSCODE_TRUSTED_EXTENSIONS: '__.__,__.__' +---- + +.Verification + +* Start or restart the workspace and verify that the `trustedExtensionAuthAccess` section is added to the `product.json` file. + +[role="_additional-resources"] +.Additional resources + +* xref:configuring-single-and-multiroot-workspaces.adoc[] +* xref:default-extensions-for-microsoft-visual-studio-code.adoc[] +* xref:editor-configurations-for-microsoft-visual-studio-code.adoc[] diff --git a/modules/extend/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc b/modules/extend/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc new file mode 100644 index 0000000000..b0205daefd --- /dev/null +++ b/modules/extend/partials/proc_adding-or-removing-extensions-in-a-workspace.adoc @@ -0,0 +1,102 @@ +:_content-type: PROCEDURE + +[id="adding-or-removing-extensions-in-a-workspace_{context}"] += Add or remove extensions in a workspace + +[role="_abstract"] +Customize the embedded Open VSX registry instance by adding or removing extensions directly within a {prod-short} workspace to create a custom extension catalog for your organization. + +[IMPORTANT] +==== +The embedded plugin registry is deprecated; the Open VSX Registry is its successor. Setting up an internal, on-premises Open VSX Registry provides full control over the extension lifecycle, enables offline use, and improves compliance. See xref:proc_deploy-open-vsx-from-source.adoc[] for detailed setup instructions. +==== + +.Prerequisites + +* You are logged in to the {orch-name} cluster from the workspace terminal with cluster administrator permissions: ++ +[source,bash,subs="+quotes,+attributes"] +---- +{orch-cli} login --token=____ --server=____ +---- +* You have started a workspace using the link:{plugin-registry-repo-url}[plugin registry repository]. +* You have created a link:https://access.redhat.com/RegistryAuthentication[Red Hat Registry Service Account] and have the username and token available. +* You have the custom plugin registry built locally on the corresponding hardware for IBM Power (`ppc64le`) and IBM Z (`s390x`) architectures. +* You have a container image based on the latest tag or SHA to include the latest security fixes. + +.Procedure + +. Identify the publisher and extension name for each extension you want to add: +.. Find the extension on the link:https://open-vsx.org/[Open VSX registry website]. +.. Copy the URL of the extension's listing page. +.. Extract the ____ and ____ from the URL: ++ +[source,text,subs="+quotes"] +---- +https://open-vsx.org/extension/____/____ +---- ++ +[TIP] +==== +If the extension is only available from link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] and not link:https://open-vsx.org[Open VSX], ask the extension publisher to publish it on link:https://open-vsx.org[open-vsx.org]. See the link:https://github.com/eclipse-openvsx/openvsx/wiki/Publishing-Extensions#how-to-publish-an-extension[publishing instructions] and the link:https://github.com/marketplace/actions/publish-vs-code-extension[GitHub action]. + +If the publisher is unavailable or unwilling, and no Open VSX equivalent exists, consider link:https://github.com/open-vsx/publish-extensions/issues[reporting an issue] to the Open VSX team. +==== + +. Open the link:{plugin-registry-repo-url}/blob/main/openvsx-sync.json[`openvsx-sync.json`] file in the workspace. + +. Add or remove extensions using the following JSON syntax: ++ +[source,json,subs="+quotes"] +---- + { + "id": "____.____", + "version": "____" + } +---- ++ +[TIP] +==== +If you have a closed-source or internal-only extension, you can add it directly from a `.vsix` file. Use a URL accessible to your custom plugin registry container: + +[source,json,subs="+quotes"] +---- + { + "id": "____.____", + "download": "____", + "version": "____" + } +---- + +Read the link:https://aka.ms/vsmarketplace-ToU[Terms of Use] for the link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] before using its resources. +==== + +. Log in to the Red Hat registry: +.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. +.. Run the *1. Login to registry.redhat.io* task. +.. Enter your Red Hat Registry Service Account credentials when prompted. + +. Build and publish the custom plugin registry: +.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. +.. Run the *2. Build and Publish a Custom Plugin Registry* task. ++ +[NOTE] +==== +Verify that the `CHE_CODE_VERSION` in the link:{plugin-registry-repo-url}/blob/main/build-config.json[`build-config.json`] file matches the version of the editor currently used with {prod-short}. Update it if necessary. +==== + +. Configure {prod-short} to use the custom plugin registry: +.. Navigate to *Terminal* -> *Run Task...* -> *devfile*. +.. Run the *3. Configure Che to use the Custom Plugin Registry* task. + +.Verification + +. Check that the `plugin-registry` pod has restarted and is running. +. Restart your workspace. +. Open the *Extensions* view in the IDE and verify that your added extensions are available. + +[role="_additional-resources"] +.Additional resources + +* link:{plugin-registry-repo-url}[Plugin registry repository] +* xref:proc_deploy-open-vsx-from-source.adoc[] diff --git a/modules/extend/partials/proc_adding-or-removing-extensions-on-linux.adoc b/modules/extend/partials/proc_adding-or-removing-extensions-on-linux.adoc new file mode 100644 index 0000000000..c7b00e3c40 --- /dev/null +++ b/modules/extend/partials/proc_adding-or-removing-extensions-on-linux.adoc @@ -0,0 +1,126 @@ +:_content-type: PROCEDURE + +[id="adding-or-removing-extensions-on-linux_{context}"] += Add or remove extensions from the Linux command line + +[role="_abstract"] +Build and publish a custom plugin registry from the Linux command line to create a tailored Open VSX registry with the specific extensions your organization needs. + +.Prerequisites + +* You have {docker-cli} installed. +* You have Node.js version 18.20.3 or higher installed. +* You have created a link:https://access.redhat.com/RegistryAuthentication[Red Hat Registry Service Account] and have the username and token available. +* You have a container image based on the latest tag or SHA to include the latest security fixes. + +.Procedure + +. Clone the plugin registry repository: ++ +[source,terminal,subs="+attributes"] +---- +$ git clone {plugin-registry-repo-url}.git +---- + +. Change to the plugin registry directory: ++ +[source,terminal] +---- +$ cd che-plugin-registry/ +---- + +. Log in to the Red Hat registry: ++ +[source,terminal,subs="+attributes"] +---- +$ {docker-cli} login registry.redhat.io +---- + +. Identify the publisher and extension name for each extension you want to add: +.. Find the extension on the link:https://open-vsx.org/[Open VSX registry website]. +.. Copy the URL of the extension's listing page. +.. Extract the ____ and ____ from the URL: ++ +[source,text,subs="+quotes"] +---- +https://open-vsx.org/extension/____/____ +---- ++ +[TIP] +==== +If the extension is only available from link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] and not link:https://open-vsx.org[Open VSX], ask the extension publisher to publish it on link:https://open-vsx.org[open-vsx.org]. See the link:https://github.com/eclipse-openvsx/openvsx/wiki/Publishing-Extensions#how-to-publish-an-extension[publishing instructions] and the link:https://github.com/marketplace/actions/publish-vs-code-extension[GitHub action]. + +If the publisher is unavailable or unwilling, and no Open VSX equivalent exists, consider link:https://github.com/open-vsx/publish-extensions/issues[reporting an issue] to the Open VSX team. +==== + +. Open the link:{plugin-registry-repo-url}/blob/main/openvsx-sync.json[`openvsx-sync.json`] file. + +. Add or remove extensions using the following JSON syntax: ++ +[source,json,subs="+quotes"] +---- + { + "id": "____.____", + "version": "____" + } +---- ++ +[TIP] +==== +If you have a closed-source or internal-only extension, you can add it directly from a `.vsix` file. Use a URL accessible to your custom plugin registry container: + +[source,json,subs="+quotes"] +---- + { + "id": "____.____", + "download": "____", + "version": "____" + } +---- + +Read the link:https://aka.ms/vsmarketplace-ToU[Terms of Use] for the link:https://marketplace.visualstudio.com/VSCode[Microsoft Visual Studio Marketplace] before using its resources. +==== + +. Build the plugin registry container image: ++ +[source,bash,subs="+attributes,+quotes"] +---- +$ ./build.sh -o ____ -r quay.io -t custom +---- ++ +[NOTE] +==== +Verify that the `CHE_CODE_VERSION` in the link:{plugin-registry-repo-url}/blob/main/build-config.json[`build-config.json`] file matches the version of the editor currently used with {prod-short}. Update it if necessary. +==== + +. Push the image to a container registry such as link:https://quay.io/[quay.io]: ++ +[source,bash,subs="+attributes,+quotes"] +---- +$ {docker-cli} push quay.io/____/plugin_registry:custom +---- + +. Edit the `CheCluster` custom resource in your organization's cluster to point to the image and save the changes: ++ +[source,yaml,subs="+quotes"] +---- +spec: + components: + pluginRegistry: + deployment: + containers: + - image: quay.io/____/plugin_registry:custom + openVSXURL: '' +---- + +.Verification + +. Check that the `plugin-registry` pod has restarted and is running. +. Restart your workspace. +. Open the *Extensions* view in the IDE and verify that your added extensions are available. + +[role="_additional-resources"] +.Additional resources + +* link:{plugin-registry-repo-url}[Plugin registry repository] +* xref:proc_deploy-open-vsx-from-source.adoc[] diff --git a/modules/administration-guide/partials/proc_selecting-an-open-vsx-registry-instance.adoc b/modules/extend/partials/proc_selecting-an-open-vsx-registry-instance.adoc similarity index 86% rename from modules/administration-guide/partials/proc_selecting-an-open-vsx-registry-instance.adoc rename to modules/extend/partials/proc_selecting-an-open-vsx-registry-instance.adoc index 62fc185ed5..12b9799241 100644 --- a/modules/administration-guide/partials/proc_selecting-an-open-vsx-registry-instance.adoc +++ b/modules/extend/partials/proc_selecting-an-open-vsx-registry-instance.adoc @@ -1,7 +1,7 @@ :_content-type: PROCEDURE [id="selecting-an-open-vsx-registry-instance_{context}"] -= Selecting an Open VSX registry instance += Choose which registry to use include::example$snip_{project-context}-default-vsx-registry.adoc[] @@ -13,12 +13,11 @@ include::example$snip_{project-context}-non-default-vsx-registry-instance.adoc[] .Prerequisites -* You have administrator access to the cluster where {prod-short} is deployed. -* You have the `oc` CLI tool installed if you are using the command line. +* You have administrator access to the {orch-name} cluster where {prod-short} is deployed. +* You have an active `{orch-cli}` session with administrative permissions. See {orch-cli-link}. .Procedure -. Log in to the cluster as an administrator. . Edit the `CheCluster` custom resource to update the `openVSXURL` value: + [source,yaml,subs="+quotes"] @@ -30,7 +29,7 @@ spec: ---- <1> For example: `openVSXURL: "pass:c,a,q[https://open-vsx.org]"`. -.Save the changes to the custom resource. +. Save the changes to the custom resource. [IMPORTANT] ==== diff --git a/modules/secure/pages/security-best-practices.adoc b/modules/secure/pages/security-best-practices.adoc index 20fa72080c..a237877e85 100644 --- a/modules/secure/pages/security-best-practices.adoc +++ b/modules/secure/pages/security-best-practices.adoc @@ -262,7 +262,7 @@ Open Source editor. Alternatively, cluster administrators can specify a different plugin registry in the Custom Resource, for example https://open-vsx.org that contains thousands of extensions. They can also build a custom Open VSX registry. -include::administration-guide:example$snip_che-managing-extensions.adoc[] +include::extend:example$snip_che-managing-extensions.adoc[] [IMPORTANT] ====