├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── dependency-review.yml │ ├── format-check.yml │ ├── publish-retro.yml │ ├── publish-v2.yml │ ├── restrict-pr-size.yml │ ├── scorecards.yml │ └── website.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs ├── README.md ├── book │ ├── .gitignore │ ├── Makefile │ ├── book.toml │ └── src │ │ ├── README.md │ │ ├── SUMMARY.md │ │ ├── contributing.md │ │ ├── development │ │ ├── development.md │ │ └── webview-development.md │ │ ├── features │ │ ├── aks-compare-cluster.md │ │ ├── aks-diagnostics.md │ │ ├── aks-fleet-manager.md │ │ ├── aks-periscope.md │ │ ├── aks-plugins-github-copilot.md │ │ ├── create-gh-workflow.md │ │ ├── draft-integration.md │ │ ├── features.md │ │ ├── image-cleaner-eraser-tool.md │ │ ├── inspektor-gadget.md │ │ ├── install-azureserviceoperator.md │ │ ├── k8s-api-health-points.md │ │ ├── kaito-install-deploy.md │ │ ├── kaito-manage-test.md │ │ ├── manage-cluster-operations.md │ │ ├── merge-save-kubeconfig.md │ │ ├── retina-capture.md │ │ ├── run-kubectl-command.md │ │ ├── show-properties-azureportal-start-stop.md │ │ └── tcp-dumps.md │ │ ├── installation.md │ │ ├── release │ │ └── releasing.md │ │ ├── resources │ │ ├── Sign-in.png │ │ ├── aks-compare-cluster-from.png │ │ ├── aks-compare-cluster-result.png │ │ ├── aks-compare-cluster-with.png │ │ ├── aks-diagnostics-webview.png │ │ ├── aks-fleet-create-failure.png │ │ ├── aks-fleet-create-input.png │ │ ├── aks-fleet-create-right-click.png │ │ ├── aks-fleet-create-success.png │ │ ├── aks-gh-chat-create-cluster-1.png │ │ ├── aks-gh-chat-deploy-manifest-1.png │ │ ├── aks-gh-chat-kubectl-generation-1.png │ │ ├── aks-periscope-webview.png │ │ ├── aks-signin.png │ │ ├── aks-signinpopup.png │ │ ├── aks-signintoaccountorswitchaccount.png │ │ ├── aks-startstop-cluster.png │ │ ├── aks-tools.png │ │ ├── azure-service-operator-screenshot.png │ │ ├── create-cluster-webview-resource.png │ │ ├── create-cluster-webview.png │ │ ├── draft-command.png │ │ ├── draft-deployment.png │ │ ├── draft-dockerfile.png │ │ ├── draft-dockerfile1.png │ │ ├── draft-gh-workflow.png │ │ ├── inspector-gadget-1.png │ │ ├── inspector-gadget-2.png │ │ ├── inspector-gadget-3.png │ │ ├── kaito-deploy-page.png │ │ ├── kaito-deployment-example-in-progress.png │ │ ├── kaito-deployment-example-success.png │ │ ├── kaito-installation-page.png │ │ ├── kaito-installation-success.png │ │ ├── kaito-manage-page.png │ │ ├── kaito-test-page.png │ │ ├── kaito-test-response.png │ │ ├── kubectl-command-panel.png │ │ ├── retina-select-container.png │ │ ├── retina-select-nodes.png │ │ ├── retina-select-storage-account.png │ │ ├── retina-success-check-storage-account.png │ │ ├── retina-success-run-download.png │ │ ├── retina-success-run-upload.png │ │ ├── right-click-api-health-check-menu.png │ │ ├── right-click-compare-cluster.png │ │ ├── right-click-download-retina-capture.png │ │ ├── right-click-inspektor-gadget.png │ │ ├── right-click-menu-kubectl.png │ │ ├── right-click-menu-managedoperations.png │ │ ├── right-click-menu-workflow.png │ │ ├── right-click-menu.png │ │ ├── right-click-retina-capture.png │ │ ├── right-click-run-eraser-1.png │ │ ├── right-click-run-eraser.png │ │ ├── right-click-subscription.png │ │ ├── right-click-tcp-dump-collect.png │ │ ├── right-click-upload-retina-capture.png │ │ ├── show-properties-abort.png │ │ ├── show-properties-page-k8s-available-versions.png │ │ ├── show-properties-reconcile.png │ │ ├── tcp-dump-filter-interface.png │ │ ├── tcp-dump-filter-interface1.png │ │ ├── tcp-dump-linux-node.png │ │ ├── vscode-create-cluster-step-1.png │ │ ├── vscode-create-cluster-step-2.png │ │ ├── vscode-creating-notification.png │ │ └── vscode-creation-successful.png │ │ └── telemetery.md ├── maintenance │ └── README.md ├── package-scripts.md └── webview-development.md ├── eslint.config.mjs ├── l10n └── bundle.l10n.json ├── package-lock.json ├── package.json ├── package.nls.json ├── publish-book ├── install-mdbook-toc.sh └── install-mdbook.sh ├── resources ├── aks-tools.png ├── azure.svg ├── azureSubscription.svg ├── dark │ ├── filter.svg │ └── refresh.svg ├── draft │ ├── workflow-helm.yml │ └── workflow-manifests.yml ├── fleet-tree-icon.png ├── kaitollmconfig │ └── kaitollmconfig.json ├── light │ ├── filter.svg │ └── refresh.svg └── yaml │ ├── azureoperatorsettings.yaml │ └── kaitoworkspace.yaml ├── src ├── assets.ts ├── auth │ ├── README.md │ ├── azureAuth.ts │ ├── azureSessionProvider.ts │ └── types.ts ├── azure-api-utils.ts ├── commands │ ├── aksAccount │ │ └── aksAccount.ts │ ├── aksAttachAcrToCluster │ │ └── attachAcrToCluster.ts │ ├── aksClusterProperties │ │ └── aksClusterProperties.ts │ ├── aksCompareCluster │ │ └── aksCompareCluster.ts │ ├── aksCreateCluster │ │ ├── aksCreateCluster.ts │ │ └── aksCreateClusterFromCopilot.ts │ ├── aksCreateClusterNavToAzurePortal │ │ └── aksCreateClusterNavToAzurePortal.ts │ ├── aksDeleteCluster │ │ └── aksDeleteCluster.ts │ ├── aksDeployManifest │ │ └── aksDeployManifest.ts │ ├── aksEraserTool │ │ └── erasertool.ts │ ├── aksFleet │ │ └── aksFleetManager.ts │ ├── aksFleetProperties │ │ └── askFleetProperties.ts │ ├── aksInspektorGadget │ │ ├── aksInspektorGadget.ts │ │ ├── clusterOperations.ts │ │ ├── traceItems.ts │ │ └── traceWatcher.ts │ ├── aksKaito │ │ ├── aksKaito.ts │ │ ├── aksKaitoCreateCRD.ts │ │ ├── aksKaitoManage.ts │ │ ├── aksKaitoTest.ts │ │ └── akskaitoGenerateYaml.ts │ ├── aksKubectlCommands │ │ └── aksKubectlCommands.ts │ ├── aksNavToPortal │ │ └── aksNavToPortal.ts │ ├── aksOpenKubectlPanel │ │ └── aksOpenKubectlPanel.ts │ ├── aksReconcileCluster │ │ └── aksReconcileCluster.ts │ ├── aksRetinaCapture │ │ ├── aksDownloadRetinaCapture.ts │ │ ├── aksUploadRetinaCapture.ts │ │ └── utils.ts │ ├── aksRotateClusterCert │ │ └── aksRotateClusterCert.ts │ ├── aksTCPCollection │ │ └── tcpDumpCollection.ts │ ├── azureServiceOperators │ │ └── installAzureServiceOperator.ts │ ├── detectors │ │ └── detectors.ts │ ├── devhub │ │ └── aksAutomatedDeployments.ts │ ├── draft │ │ ├── README.md │ │ ├── baseWorkflowEditor.ts │ │ ├── draftCommands.ts │ │ ├── helmWorkflowEditor.ts │ │ ├── languages.ts │ │ ├── manifestsWorkflowEditor.ts │ │ ├── types.ts │ │ └── workflowUtils.ts │ ├── periscope │ │ ├── helpers │ │ │ └── periscopehelper.ts │ │ ├── models │ │ │ ├── RetinaDownloadConfig.ts │ │ │ ├── clusterFeatures.ts │ │ │ ├── config.ts │ │ │ └── storage.ts │ │ └── periscope.ts │ ├── refreshSubscriptions.ts │ └── utils │ │ ├── acrs.ts │ │ ├── arm.ts │ │ ├── azureResources.ts │ │ ├── azurestorage.ts │ │ ├── clusterfilter.ts │ │ ├── clusters.ts │ │ ├── commands.ts │ │ ├── config.ts │ │ ├── configureWorkflowHelper.ts │ │ ├── detectors.ts │ │ ├── dictionary.ts │ │ ├── download │ │ └── download.ts │ │ ├── draft.ts │ │ ├── env.ts │ │ ├── errorable.ts │ │ ├── featureRegistrations.ts │ │ ├── fleet.ts │ │ ├── ghCopilotHandlers.ts │ │ ├── git.ts │ │ ├── graph.ts │ │ ├── helper │ │ ├── binaryDownloadHelper.ts │ │ ├── draftBinaryDownload.ts │ │ ├── kubectlGadgetDownload.ts │ │ ├── kubeloginDownload.ts │ │ └── retinaBinaryDownload.ts │ │ ├── host.ts │ │ ├── json.ts │ │ ├── kaitoValidationHelpers.ts │ │ ├── kubectl.ts │ │ ├── managedServiceIdentity.ts │ │ ├── multiStepHelper.ts │ │ ├── octokitHelper.ts │ │ ├── reporter.ts │ │ ├── resourceGroups.ts │ │ ├── roleAssignments.ts │ │ ├── runtimeTypes.ts │ │ ├── shell.ts │ │ ├── sleep.ts │ │ ├── subscriptions.ts │ │ └── tempfile.ts ├── extension.ts ├── panels │ ├── AttachAcrToClusterPanel.ts │ ├── AzureServiceOperatorPanel.ts │ ├── BasePanel.ts │ ├── ClusterPropertiesPanel.ts │ ├── CreateClusterPanel.ts │ ├── CreateFleetPanel.ts │ ├── DetectorPanel.ts │ ├── DevHubAutoDeployPanel.ts │ ├── FleetPropertiesPanel.ts │ ├── InspektorGadgetPanel.ts │ ├── KaitoManagePanel.ts │ ├── KaitoModelsPanel.ts │ ├── KaitoPanel.ts │ ├── KaitoTestPanel.ts │ ├── KubectlPanel.ts │ ├── PeriscopePanel.ts │ ├── README.md │ ├── RetinaCapturePanel.ts │ ├── TcpDumpPanel.ts │ ├── draft │ │ ├── DraftDeploymentPanel.ts │ │ ├── DraftDockerfilePanel.ts │ │ ├── DraftValidatePanel.ts │ │ ├── DraftWorkflowPanel.ts │ │ └── commandUtils.ts │ ├── templates │ │ ├── AutomaticCreateCluster.json │ │ └── DevTestCreateCluster.json │ └── utilities │ │ ├── ClusterSpecCreationBuilder.ts │ │ ├── KaitoHelpers.ts │ │ ├── KubectlNetworkHelper.ts │ │ └── webview.ts ├── plugins │ ├── createAKS │ │ └── createAKSClusterPlugin.ts │ ├── deployManifest │ │ └── deployManifestToAKSPlugin.ts │ ├── getPlugins.ts │ ├── kubectlGeneration │ │ ├── aksDocsRag │ │ │ ├── client.ts │ │ │ └── constants.ts │ │ └── generateKubectlCommandPlugin.ts │ └── shared │ │ ├── clusterOptions │ │ ├── options │ │ │ ├── selectExistingClusterOption.ts │ │ │ ├── selectNewClusterOption.ts │ │ │ └── selectRecentClusterOption.ts │ │ ├── selectClusterOptions.ts │ │ └── state │ │ │ └── recentCluster.ts │ │ ├── pluginResponses.ts │ │ ├── telemetry │ │ └── logger.ts │ │ └── types.ts ├── tests │ ├── runTests.ts │ └── suite │ │ ├── TcpDumpPanel.test.ts │ │ ├── index.ts │ │ └── webview.test.ts ├── tree │ ├── aksClusterTreeItem.ts │ ├── azureAccountTreeItem.ts │ ├── azureResourceNodeContributor.ts │ ├── fleetTreeItem.ts │ └── subscriptionTreeItem.ts ├── types │ ├── aiazure │ │ ├── AiDriver.d.ts │ │ └── AzureAgent.d.ts │ └── git.d.ts ├── uriHandler.ts └── webview-contract │ ├── README.md │ ├── initialState.ts │ ├── messaging.ts │ ├── webviewDefinitions │ ├── attachAcrToCluster.ts │ ├── automatedDeployments.ts │ ├── azureServiceOperator.ts │ ├── clusterProperties.ts │ ├── createCluster.ts │ ├── createFleet.ts │ ├── detector.ts │ ├── draft │ │ ├── draftDeployment.ts │ │ ├── draftDockerfile.ts │ │ ├── draftValidate.ts │ │ ├── draftWorkflow.ts │ │ └── types.ts │ ├── fleetProperties.ts │ ├── inspektorGadget.ts │ ├── kaito.ts │ ├── kaitoManage.ts │ ├── kaitoModels.ts │ ├── kaitoTest.ts │ ├── kubectl.ts │ ├── periscope.ts │ ├── retinaCapture.ts │ ├── shared │ │ ├── fileSystemTypes.ts │ │ └── workspaceTypes.ts │ ├── tcpDump.ts │ └── testStyleViewer.ts │ └── webviewTypes.ts ├── tsconfig.json ├── webpack.config.js └── webview-ui ├── .eslintrc.cjs ├── .gitignore ├── eslint.config.mjs ├── index.html ├── package-lock.json ├── package.json ├── src ├── AttachAcrToCluster │ ├── AttachAcrToCluster.module.css │ ├── AttachAcrToCluster.tsx │ └── state │ │ ├── dataLoading.ts │ │ ├── state.ts │ │ ├── stateTypes.ts │ │ └── update │ │ ├── acrDataUpdate.ts │ │ ├── azureReferenceDataUpdate.ts │ │ └── subscriptionDataUpdate.ts ├── AutomatedDeployments │ ├── AutomatedDeployments.module.css │ ├── AutomatedDeployments.tsx │ └── state.ts ├── AzureServiceOperator │ ├── AzureServiceOperator.module.css │ ├── AzureServiceOperator.tsx │ ├── Inputs.tsx │ ├── Progress.tsx │ ├── ProgressStep.tsx │ └── helpers │ │ ├── inputs.ts │ │ └── state.ts ├── ClusterProperties │ ├── AgentPoolDisplay.tsx │ ├── ClusterDisplay.tsx │ ├── ClusterDisplayToolTip.tsx │ ├── ClusterProperties.module.css │ ├── ClusterProperties.tsx │ ├── ClusterUpgrade.tsx │ ├── ConfirmationDialog.tsx │ └── state.ts ├── CreateCluster │ ├── CreateCluster.module.css │ ├── CreateCluster.tsx │ ├── CreateClusterInput.tsx │ ├── CreateClusterPresetInput.tsx │ ├── CreateResourceGroup.tsx │ ├── Success.tsx │ └── helpers │ │ └── state.ts ├── CreateFleet │ ├── CreateFleet.module.css │ ├── CreateFleet.tsx │ ├── CreateFleetInput.tsx │ ├── CreateFleetModeInput.tsx │ └── helpers │ │ └── state.ts ├── Detector │ ├── Detector.module.css │ ├── Detector.tsx │ ├── SingleDetector.tsx │ ├── renderers │ │ ├── Error.tsx │ │ ├── InsightsRenderer.tsx │ │ ├── MarkdownRenderer.tsx │ │ ├── README.md │ │ └── common.tsx │ ├── state.ts │ └── utils.ts ├── Draft │ ├── Draft.module.css │ ├── DraftDeployment │ │ ├── DraftDeployment.tsx │ │ ├── dataLoading.ts │ │ └── state.ts │ ├── DraftDockerfile │ │ ├── DraftDockerfile.tsx │ │ └── state.ts │ ├── DraftValidate │ │ ├── DraftValidate.tsx │ │ └── state.ts │ ├── DraftWorkflow │ │ ├── DraftWorkflow.tsx │ │ ├── dataLoading.ts │ │ └── state.ts │ ├── index.ts │ └── state │ │ ├── stateTypes.ts │ │ └── update │ │ ├── acrDataUpdate.ts │ │ ├── acrRepoDataUpdate.ts │ │ ├── azureReferenceDataUpdate.ts │ │ ├── clusterDataUpdate.ts │ │ ├── gitHubReferenceDataUpdate.ts │ │ ├── gitHubRepoDataUpdate.ts │ │ └── subscriptionDataUpdate.ts ├── FleetProperties │ ├── FleetProperties.tsx │ └── state.ts ├── InspektorGadget │ ├── GadgetSelector.tsx │ ├── InspektorGadget.module.css │ ├── InspektorGadget.tsx │ ├── NewTraceDialog.tsx │ ├── Overview.tsx │ ├── ResourceSelector.tsx │ ├── TraceItemSortSelector.tsx │ ├── TraceOutput.tsx │ ├── Traces.tsx │ └── helpers │ │ ├── clusterResources.ts │ │ ├── gadgets.ts │ │ ├── gadgets │ │ ├── common.ts │ │ ├── profile.ts │ │ ├── snapshot.ts │ │ ├── top.ts │ │ ├── trace.ts │ │ └── types.ts │ │ └── state.ts ├── Kaito │ ├── Kaito.module.css │ ├── Kaito.tsx │ ├── kaito-image.png │ └── state.ts ├── KaitoManage │ ├── KaitoManage.module.css │ ├── KaitoManage.tsx │ └── state.ts ├── KaitoModels │ ├── KaitoModels.module.css │ ├── KaitoModels.tsx │ └── state.ts ├── KaitoTest │ ├── KaitoTest.module.css │ ├── KaitoTest.tsx │ └── state.ts ├── Kubectl │ ├── CommandInput.tsx │ ├── CommandList.tsx │ ├── CommandOutput.tsx │ ├── Kubectl.module.css │ ├── Kubectl.tsx │ ├── SaveCommandDialog.tsx │ └── helpers │ │ └── state.ts ├── Periscope │ ├── ErrorView.tsx │ ├── NoDiagnosticSettingView.tsx │ ├── NodeActions.tsx │ ├── NodeLogs.tsx │ ├── Periscope.module.css │ ├── Periscope.tsx │ ├── SuccessView.tsx │ └── state.ts ├── RetinaCapture │ ├── DeleteNodeExplorerDialog.tsx │ ├── RetinaCapture.module.css │ ├── RetinaCapture.tsx │ └── state.ts ├── TCPDump │ ├── CaptureFilters.tsx │ ├── TcpDump.module.css │ ├── TcpDump.tsx │ ├── filterScenarios │ │ ├── SpecificPodFilters.tsx │ │ └── TwoPodsFilters.tsx │ ├── protocols.ts │ ├── state.ts │ └── state │ │ ├── captureNodeUpdate.ts │ │ ├── dataLoading.ts │ │ ├── nodeReferenceDataUpdate.ts │ │ ├── referenceDataUpdate.ts │ │ └── scenarioFiltersUpdate.ts ├── TestStyleViewer │ ├── TestStyleViewer.tsx │ └── state.ts ├── components │ ├── CustomDropdown.module.css │ ├── CustomDropdown.tsx │ ├── CustomDropdownOption.tsx │ ├── Dialog.tsx │ ├── InlineAction.module.css │ ├── InlineAction.tsx │ ├── NodeSelector.tsx │ ├── ProgressRing.module.css │ ├── ProgressRing.tsx │ ├── ResourceSelector.tsx │ ├── TextWithDropdown.module.css │ └── TextWithDropdown.tsx ├── icons │ ├── ArrowIcon.tsx │ ├── AutomaticIcon.tsx │ ├── DevTestIcon.tsx │ └── svgProps.ts ├── main.css ├── main.tsx ├── manualTest │ ├── TestScenarioSelector │ │ ├── TestScenarioSelector.module.css │ │ └── TestScenarioSelector.tsx │ ├── asoTests.tsx │ ├── attachAcrToClusterTests.tsx │ ├── automatedDeploymentsTests.tsx │ ├── clusterPropertiesTests.tsx │ ├── components │ │ ├── FilePicker.module.css │ │ ├── FilePicker.tsx │ │ └── FilePickerWrapper.tsx │ ├── createClusterTests.tsx │ ├── createFleetTests.tsx │ ├── detectorData │ │ ├── Etcd.json │ │ ├── InvalidClientServicePrincipal.json │ │ ├── NodeDiskPressure.json │ │ ├── NodeMemoryPressure.json │ │ ├── ProvisioningStatusNodePool.json │ │ ├── aad-issues.json │ │ ├── advisor-autoscaling-clusters.json │ │ ├── advisor-b-series-vms.json │ │ ├── advisor-cluster-loadbalancer.json │ │ ├── advisor-pod-security-policy.json │ │ ├── advisor-shared-route-table.json │ │ ├── agentpool-image-upgrade-failures.json │ │ ├── agentpool-put-failures.json │ │ ├── aks-azure-cni-address-ranges.json │ │ ├── aks-cas-and-multiple-azs.json │ │ ├── aks-category-availability-perf.json │ │ ├── aks-category-connectivity.json │ │ ├── aks-category-crud.json │ │ ├── aks-category-identity-security.json │ │ ├── aks-category-node-health.json │ │ ├── aks-category-risk-assessment.json │ │ ├── aks-cluster-delete-failures.json │ │ ├── aks-cluster-put-failures.json │ │ ├── aks-deprecated-node-labels.json │ │ ├── aks-k8s-deprecations.json │ │ ├── aks-nodepool-delete-failures.json │ │ ├── aks-npm-breakingchange.json │ │ ├── aks-private-ips-and-ranges.json │ │ ├── aks-recommend-uptime-sla.json │ │ ├── aks-reserved-address-ranges.json │ │ ├── aks-restricted-vm-skus.json │ │ ├── aks-service-impacting-issues.json │ │ ├── aks-subnet-sharing.json │ │ ├── aks-windows2019-deprecation.json │ │ ├── aks-windowsdocker-deprecation.json │ │ ├── aksapiserverauthorizedranges2.json │ │ ├── cluster-autoscaler-failures.json │ │ ├── cluster-dns.json │ │ ├── cluster-min-version.json │ │ ├── cluster-stop-start-failures.json │ │ ├── cluster-subnet.json │ │ ├── clusterCertExpiredDetailed.json │ │ ├── containerd-masquerading-ips.json │ │ ├── customerthrottling.json │ │ ├── incorrect-os-disk-configuration.json │ │ ├── incorrect-pod-disruption-budgets.json │ │ ├── kms-issues.json │ │ ├── node-pid-pressure-alerts.json │ │ ├── nodeperfcpu.json │ │ ├── nodes-not-ready-alerts.json │ │ ├── oomkilled.json │ │ ├── pod-subnet-full.json │ │ ├── private-cluster-dns.json │ │ ├── reset-auth-profile.json │ │ ├── rotate-cluster-certificate.json │ │ ├── snat-usage.json │ │ ├── user-defined-routing.json │ │ └── windowsregresionk8sv124.json │ ├── detectorTests.tsx │ ├── draft │ │ ├── draftDeploymentTests.tsx │ │ ├── draftDockerfileTests.tsx │ │ ├── draftWorkflowTests.tsx │ │ ├── index.ts │ │ └── testData │ │ │ ├── azureData.ts │ │ │ ├── fileSystemData.ts │ │ │ └── gitHubData.ts │ ├── inspektorGadgetTests.tsx │ ├── kaitoManageTests.tsx │ ├── kaitoModelTests.tsx │ ├── kaitoTestTests.tsx │ ├── kaitoTests.tsx │ ├── kubectlTests.tsx │ ├── main.tsx │ ├── periscopeTests.tsx │ ├── retinaCaptureTests.tsx │ ├── tcpDumpTests.tsx │ ├── testStyleViewerTests.tsx │ ├── utilities │ │ ├── testDialogEvents.ts │ │ └── testFileSystemUtils.ts │ └── vars.css ├── utilities │ ├── array.ts │ ├── lazy.ts │ ├── manualTest.ts │ ├── maybe.ts │ ├── runtimeTypes.ts │ ├── state.ts │ ├── time.ts │ ├── validation.ts │ └── vscode.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es2020: true, 4 | node: true, 5 | mocha: true, 6 | }, 7 | ignorePatterns: [ 8 | ".github/", 9 | ".vscode-test/", 10 | "node_modules/", 11 | "dist/", 12 | "docs/", 13 | "out/", 14 | "publish-book/", 15 | "resources", 16 | "webview-ui/", 17 | "*.js", 18 | ], 19 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 20 | parser: "@typescript-eslint/parser", 21 | parserOptions: { 22 | ecmaVersion: 2020, 23 | project: "./tsconfig.json", 24 | sourceType: "module", 25 | }, 26 | plugins: ["@typescript-eslint"], 27 | root: true, 28 | rules: { 29 | "@typescript-eslint/naming-convention": [ 30 | "error", 31 | { 32 | selector: "variable", 33 | format: ["camelCase", "UPPER_CASE"], 34 | leadingUnderscore: "forbid", 35 | trailingUnderscore: "forbid", 36 | }, 37 | ], 38 | "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", 39 | "@typescript-eslint/no-unused-vars": ["error", { ignoreRestSiblings: true }], 40 | "@typescript-eslint/prefer-for-of": "error", 41 | curly: ["error", "multi-line"], 42 | eqeqeq: ["error", "always"], 43 | "id-denylist": [ 44 | "error", 45 | "any", 46 | "Number", 47 | "number", 48 | "String", 49 | "string", 50 | "Boolean", 51 | "boolean", 52 | "Undefined", 53 | "undefined", 54 | ], 55 | "no-underscore-dangle": "error", 56 | "no-var": "error", 57 | "prefer-const": "error", 58 | "prefer-template": "error", 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Mention the platform you are using** 14 | Mention what platform you are using (oss, vscode) and pick the corresponding label. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. iOS] 31 | - Browser [e.g. chrome, safari] 32 | - Version [e.g. 22] 33 | 34 | **Smartphone (please complete the following information):** 35 | - Device: [e.g. iPhone6] 36 | - OS: [e.g. iOS8.1] 37 | - Browser [e.g. stock browser, safari] 38 | - Version [e.g. 22] 39 | 40 | **Additional context** 41 | Add any other context about the problem here. 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Mention what platform you want to support the new feature** 14 | Mention what platform you would like the feature request to live in (oss, vscode) and select the proper label. 15 | 16 | **Describe the solution you'd like** 17 | A clear and concise description of what you want to happen. 18 | 19 | **Describe alternatives you've considered** 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | groups: 8 | typescript-eslint: 9 | patterns: 10 | - "@typescript-eslint/*" 11 | - package-ecosystem: "npm" 12 | directory: "/webview-ui" 13 | schedule: 14 | interval: "weekly" 15 | groups: 16 | typescript-eslint: 17 | patterns: 18 | - "@typescript-eslint/*" 19 | - package-ecosystem: "github-actions" 20 | directory: "/" 21 | schedule: 22 | interval: "weekly" 23 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | egress-policy: audit 23 | disable-sudo: true 24 | disable-telemetry: true 25 | 26 | - name: 'Checkout Repository' 27 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | - name: 'Dependency Review' 29 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 30 | -------------------------------------------------------------------------------- /.github/workflows/format-check.yml: -------------------------------------------------------------------------------- 1 | name: Prettier Check 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**/*.ts' 7 | - '**/*.tsx' 8 | push: 9 | branches: 10 | - main 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | prettier-check: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | actions: read 20 | contents: read 21 | deployments: read 22 | packages: none 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup Node.js 28 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 29 | with: 30 | node-version: '20' 31 | 32 | - name: Install dependencies 33 | run: | 34 | npm run install:all 35 | 36 | - name: Run Prettier check 37 | run: npx prettier --check "**/*.{ts,tsx,json,css,md}" 38 | -------------------------------------------------------------------------------- /.github/workflows/restrict-pr-size.yml: -------------------------------------------------------------------------------- 1 | name: 'PR Size Checker' 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | 7 | env: 8 | MAX_LINE_CHANGED: 1200 # Maximum number of lines changed allowed 9 | TARGET_BRANCH: main 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: read 19 | contents: read 20 | deployments: read 21 | packages: none 22 | steps: 23 | # checkout your code with your git history 24 | - name: Checkout code 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | with: 27 | fetch-depth: 0 28 | - id: get_total_lines_changed 29 | run: | 30 | size=$(git diff --stat origin/${{ env.TARGET_BRANCH }} \ 31 | | grep -v .lock \ 32 | | grep -v Bin \ 33 | | awk -F"|" '{ print $2 }' \ 34 | | awk '{ print $1 }' \ 35 | | sed '/^$/d' \ 36 | | paste -sd+ - \ 37 | | bc) 38 | 39 | echo "size=${size}" >> $GITHUB_ENV 40 | echo "" 41 | echo "Total lines changed (note: *.lock files are excluded from this count): " 42 | echo $size 43 | shell: bash 44 | - run: | 45 | COMMITMSG=$(git log --format=%B -n 1 ${{github.event.after}}) 46 | echo "${COMMITMSG}" 47 | if [[ $size -gt ${{ env.MAX_LINE_CHANGED }} && "${COMMITMSG}" != *"[skip pr-size]"* ]] 48 | then 49 | echo "Warning - total lines changed is greater than" ${{ env.MAX_LINE_CHANGED }}. 50 | echo "Please consider breaking this PR down." 51 | exit 1 52 | fi 53 | shell: bash 54 | -------------------------------------------------------------------------------- /.github/workflows/website.yaml: -------------------------------------------------------------------------------- 1 | name: generate github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - ".github/workflows/website.yaml" 9 | - "docs/**" 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | actions: read 19 | contents: write 20 | deployments: read 21 | packages: none 22 | steps: 23 | - name: Harden Runner 24 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 25 | with: 26 | egress-policy: audit 27 | disable-sudo: true 28 | disable-telemetry: true 29 | 30 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 31 | with: 32 | submodules: true 33 | fetch-depth: 0 34 | 35 | - name: Set TOOLS_BIN_DIR and add to PATH 36 | run: | 37 | TOOLS_BIN_DIR="${HOME}/.cargo/bin" 38 | echo "TOOLS_BIN_DIR=${TOOLS_BIN_DIR}" >> ${GITHUB_ENV} 39 | echo "${TOOLS_BIN_DIR}" >> ${GITHUB_PATH} 40 | 41 | - name: Build 42 | run: make -C docs/book build 43 | 44 | - name: Deploy 45 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 46 | if: ${{ github.ref == 'refs/heads/main' }} 47 | with: 48 | github_token: ${{ secrets.GITHUB_TOKEN }} 49 | publish_dir: ./docs/book/book 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | vsix 5 | .vscode-test/ 6 | *.vsix 7 | .DS_Store -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/jumanjihouse/pre-commit-hooks 7 | rev: 3.0.0 8 | hooks: 9 | - id: shellcheck 10 | - repo: https://github.com/pre-commit/mirrors-eslint 11 | rev: v8.38.0 12 | hooks: 13 | - id: eslint 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v4.4.0 16 | hooks: 17 | - id: end-of-file-fixer 18 | - id: trailing-whitespace 19 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | .vscode-test/ 3 | node_modules/ 4 | dist/ 5 | docs/ 6 | out/ 7 | publish-book/ 8 | resources/ 9 | 10 | **/*.json 11 | **/*.md -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 120, 4 | "bracketSpacing": true, 5 | "endOfLine": "lf", 6 | "arrowParens": "always", 7 | "singleQuote": false, 8 | "semi": true 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "amodio.tsl-problem-matcher" 8 | ] 9 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "env": { 17 | }, 18 | "outFiles": [ 19 | "${workspaceFolder}/dist/**/*.js" 20 | ], 21 | "preLaunchTask": "webpack-dev" 22 | }, 23 | { 24 | "name": "Extension Tests", 25 | "type": "extensionHost", 26 | "request": "launch", 27 | "runtimeExecutable": "${execPath}", 28 | "args": [ 29 | "--extensionDevelopmentPath=${workspaceFolder}", 30 | "--extensionTestsPath=${workspaceFolder}/out/src/tests/suite/index" 31 | ], 32 | "outFiles": [ 33 | "${workspaceFolder}/out/src/tests/**/*.js" 34 | ], 35 | "preLaunchTask": "test-compile" 36 | }, 37 | { 38 | "name": "Webview UI", 39 | "type": "msedge", 40 | "request": "launch", 41 | "url": "http://localhost:3000", 42 | "webRoot": "${workspaceFolder}/webview-ui", 43 | "outFiles": [ 44 | "${workspaceFolder}/webview-ui/dist/**/*.js" 45 | ], 46 | "preLaunchTask": "dev:webview" 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true, // set this to false to include "out" folder in search results 8 | "dist": true 9 | }, 10 | "eslint.workingDirectories": [".", "./webview-ui"], 11 | "editor.defaultFormatter": "esbenp.prettier-vscode", 12 | "editor.formatOnSave": true, 13 | "cloudcode.azureExplorerVisible": true 14 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "webpack-dev", 8 | "type": "npm", 9 | "script": "webpack-dev", 10 | "problemMatcher": "$ts-webpack-watch", 11 | "isBackground": true, 12 | }, 13 | { 14 | "label": "webpack", 15 | "type": "npm", 16 | "script": "webpack" 17 | }, 18 | { 19 | "label": "test-compile", 20 | "type": "npm", 21 | "script": "test-compile" 22 | }, 23 | { 24 | "label": "dev:webview", 25 | "type": "npm", 26 | "script": "dev:webview", 27 | "problemMatcher": { 28 | "base": "$tsc-watch", 29 | "fileLocation": "absolute", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": "^(?:.* page reload |\\[TypeScript\\]).*", 33 | "endsPattern": "^.*\\[TypeScript\\].*" 34 | } 35 | }, 36 | "isBackground": true, 37 | "presentation": { 38 | "reveal": "never" 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/** 4 | out/**/*.map 5 | src/** 6 | webview-ui/** 7 | !webview-ui/dist/assets 8 | .gitignore 9 | tsconfig.json 10 | vsc-extension-quickstart.md 11 | tslint.json 12 | node_modules/** 13 | !node_modules/vscode/** 14 | !node_modules/bufferutil/** 15 | !node_modules/cross-spawn/** 16 | !node_modules/utf-8-validate/** -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Contributing 4 | 5 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 6 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 7 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 8 | 9 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 10 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 11 | provided by the bot. You will only need to do this once across all repos using our CLA. 12 | 13 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 14 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 15 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code AKS Tools 2 | 3 | ## Maintenance 4 | 5 | * [How to release](maintenance/README.md) 6 | * [Webview Development](./webview-development.md) 7 | * [Package Scripts](./package-scripts.md) 8 | 9 | -------------------------------------------------------------------------------- /docs/book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /docs/book/Makefile: -------------------------------------------------------------------------------- 1 | TOOLS_BIN_DIR ?= $(PWD)/bin 2 | # include tools bin dir in path so that mdbook-toc can be run by mdbook 3 | PATH := ${PATH}:${TOOLS_BIN_DIR} 4 | MDBOOK_VERSION ?= v0.4.27 5 | # this version of mdbook-toc is built against mdbook 0.4.27 6 | MDBOOK_TOC_VERSION ?= 0.11.2 7 | MDBOOK_INSTALL := $(realpath ../../publish-book/install-mdbook.sh) 8 | MDBOOK_TOC_INSTALL := $(realpath ../../publish-book/install-mdbook-toc.sh) 9 | 10 | MDBOOK := $(TOOLS_BIN_DIR)/mdbook 11 | $(MDBOOK): 12 | $(MDBOOK_INSTALL) ${MDBOOK_VERSION} ${TOOLS_BIN_DIR} 13 | 14 | MDBOOK_TOC := $(TOOLS_BIN_DIR)/mdbook-toc 15 | $(MDBOOK_TOC): 16 | $(MDBOOK_TOC_INSTALL) ${MDBOOK_TOC_VERSION} ${TOOLS_BIN_DIR} 17 | 18 | DEPS := $(MDBOOK) $(MDBOOK_TOC) 19 | 20 | .PHONY: build 21 | build: $(DEPS) 22 | $(MDBOOK) build 23 | 24 | .PHONY: serve 25 | serve: $(DEPS) 26 | $(MDBOOK) serve 27 | -------------------------------------------------------------------------------- /docs/book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Tatsat Mishra"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Visual Studio Code AKS Tools" 7 | description = "Display Azure Kubernetes Services within VS Code" 8 | 9 | [preprocessor.toc] 10 | command = "mdbook-toc" 11 | 12 | [output.html] 13 | curly-quotes = true 14 | git-repository-url = "https://github.com/Azure/vscode-aks-tools" 15 | -------------------------------------------------------------------------------- /docs/book/src/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Contributing 4 | 5 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 6 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 7 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 8 | 9 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 10 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 11 | provided by the bot. You will only need to do this once across all repos using our CLA. 12 | 13 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 14 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 15 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 16 | -------------------------------------------------------------------------------- /docs/book/src/features/aks-compare-cluster.md: -------------------------------------------------------------------------------- 1 | # Compare 2 AKS Cluster within Same Subscription 2 | 3 | ### Compare AKS Clusters 4 | 5 | Right click on your AKS cluster and select **Compare AKS Cluster** to use vscode diff to compare json object of 2 AKS clusters. 6 | 7 | ![right click command](../resources/right-click-compare-cluster.png) 8 | 9 | ![Select AKS Cluster to Compare With](../resources/aks-compare-cluster-with.png) 10 | 11 | ![Select AKS Cluster to Compare From](../resources/aks-compare-cluster-from.png) 12 | 13 | ![AKS Cluster Diff Result](../resources/aks-compare-cluster-result.png) 14 | 15 | -------------------------------------------------------------------------------- /docs/book/src/features/aks-diagnostics.md: -------------------------------------------------------------------------------- 1 | # AKS Diagnostics 2 | 3 | ### AKS Diagnostics 4 | 5 | Right click on your AKS cluster and click on **Run AKS Diagnostics** to display diagnostics information based on your AKS cluster's backend telemetry for: 6 | 7 | - Best Practices 8 | - Create, Upgrade, Delete and Scale issues 9 | - Identity and Security 10 | - Known Issues, Availability and Performance 11 | - Network and Connectivity issues 12 | - Node Health 13 | 14 | To perform further checks on your AKS cluster to troubleshoot and get recommended solutions, click on the AKS Diagnostics link at the top of the page to open it for the selected cluster. For more information on AKS Diagnostics, visit [AKS Diagnostics Overview](https://docs.microsoft.com/azure/aks/concepts-diagnostics). 15 | 16 | ![AKS Diagnostics Webview](../resources/aks-diagnostics-webview.png) -------------------------------------------------------------------------------- /docs/book/src/features/aks-fleet-manager.md: -------------------------------------------------------------------------------- 1 | # AKS Fleet Manager 2 | 3 | The extension allows you to create AKS Fleet Manager resources and visualize them in the tree view. 4 | 5 | ### Create an AKS Fleet Manager 6 | - Right-click on the subscription where you want to create a Fleet. 7 | - Choose **Fleet Manager**, then select **Create Fleet**. 8 | 9 | ![Right-click Navigation](../resources/aks-fleet-create-right-click.png) 10 | 11 | A loading screen will appear while resource groups and locations are being retrieved. Once loaded, an input form will be displayed. 12 | 13 | Complete all required fields marked with an asterisk (*). If any input is invalid, an error message will indicate the issue and guide you on how to fix it. 14 | 15 | ![Input Form](../resources/aks-fleet-create-input.png) 16 | 17 | Once all required fields are filled with valid inputs, submit the form to create the Fleet resource. A loading screen will appear while the API processes the request. 18 | 19 | Upon successful creation, a confirmation page will be shown, including a link to view the newly created Fleet in the Azure portal. 20 | ![On Success](../resources/aks-fleet-create-success.png) 21 | 22 | If there is an error during creation, a failure page will be displayed with the error message from the API. 23 | ![On Failure](../resources/aks-fleet-create-failure.png) 24 | -------------------------------------------------------------------------------- /docs/book/src/features/create-gh-workflow.md: -------------------------------------------------------------------------------- 1 | # Create GitHub Workflow 2 | 3 | ## Deprecation note 4 | 5 | This command has been superseded by the [Draft Workflow command](./draft-integration.md). 6 | 7 | ### Create GitHub Workflow 8 | 9 | Right click on your AKS cluster and click on **Create GitHub Workflow** to easily open and create a workflow starter template. This helps in quick generation of the workflow templates with pre populates resource and clustername for: 10 | 11 | - [Starter Workflow](https://github.com/actions/starter-workflows/blob/main/deployments/azure-kubernetes-service.yml) 12 | - [Helm Workflow](https://github.com/actions/starter-workflows/blob/main/deployments/azure-kubernetes-service-helm.yml) 13 | - [Kompose Workflow](https://github.com/actions/starter-workflows/blob/main/deployments/azure-kubernetes-service-kompose.yml) 14 | - [Kustomize Workflow](https://github.com/actions/starter-workflows/blob/main/deployments/azure-kubernetes-service-kustomize.yml) -------------------------------------------------------------------------------- /docs/book/src/features/draft-integration.md: -------------------------------------------------------------------------------- 1 | # Automated Deployments: Draft Tool Integration 2 | 3 | The Automated Deployments commands integrate the Draft tool to provide: 4 | 5 | - Draft Dockerfile 6 | - Draft Deployment 7 | - Draft Workflow 8 | 9 | These can be launched from either: 10 | 11 | - The command palette. (To open: Hold `Ctrl` (`⌘ Cmd` on macOS) + `shift` + `p` ) 12 | 13 | This will allow user to have access to a complete power of Draft tool and allow users to take advantage for scaffolding geenration for their projects. 14 | 15 | ![Step 1: Command Palette](../resources/draft-command.png) 16 | 17 | ![Step 2: Draft Dockerfile](../resources/draft-dockerfile.png) 18 | 19 | ![Step 3: Draft Dockerfile first](../resources/draft-dockerfile1.png) 20 | 21 | ![Step 4: Draft Deployment](../resources/draft-deployment.png) 22 | 23 | ![Step 5: Draft GitHub Workflow](../resources/draft-gh-workflow.png) 24 | -------------------------------------------------------------------------------- /docs/book/src/features/features.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 3 | Once you successfully log in with your Azure Account, you can view all AKS clusters in your Azure subscriptions(s) under the section named **Azure**. You can right click on your AKS cluster and click a menu item to perform following actions. 4 | 5 | ![Cloud explorer extension menu](../resources/right-click-menu.png) 6 | 7 | ![Cloud explorer extension menu](../resources/right-click-menu-workflow.png) 8 | 9 | ![Cloud explorer extension menu](../resources/right-click-menu-kubectl.png) 10 | 11 | ![Cloud explorer extension menu](../resources/right-click-menu-managedoperations.png) 12 | 13 | ![Cloud explorer extension menu](../resources/right-click-inspektor-gadget.png) 14 | 15 | ![Cloud explorer extension menu](../resources/right-click-subscription.png) 16 | 17 | ![Cloud explorer health check panel](../resources/kubectl-command-panel.png) 18 | 19 | ![Cloud explorer new inspektor gadget panel](../resources/inspector-gadget-1.png) 20 | 21 | ![Cloud explorer create cluster webview](../resources/create-cluster-webview.png) 22 | 23 | ![Cloud explorer tcp menu](../resources/right-click-tcp-dump-collect.png) 24 | 25 | ![Cloud explorer tcp dump webview](../resources/tcp-dump-linux-node.png) 26 | 27 | ![Cloud explorer tcp dump filter interface](../resources/tcp-dump-filter-interface.png) 28 | 29 | ![Cloud explorer tcp dump filter interface enhanced](../resources/tcp-dump-filter-interface1.png) 30 | 31 | ![AKS Show Properties webview](../resources/show-properties-reconcile.png) 32 | 33 | ![Cloud Explorer Compare AKS Cluster](../resources/aks-compare-cluster-result.png) 34 | 35 | ![Cloud Explorer Run Retina Capture on AKS Cluster](../resources/retina-success-run-download.png) 36 | -------------------------------------------------------------------------------- /docs/book/src/features/image-cleaner-eraser-tool.md: -------------------------------------------------------------------------------- 1 | # Garbage collection Using Eraser Image Cleanup Tool 2 | 3 | ### Run Eraser Image Cleanup 4 | 5 | Right click on your AKS cluster and select **Run Eraser Image Cleanup** to deploy the [Eraser Tool](https://eraser-dev.github.io/eraser/docs/quick-start) to [auomatically clean images in a regular interval](https://eraser-dev.github.io/eraser/docs/quick-start#automatically-cleaning-images) for the selected AKS Cluster. 6 | 7 | ![Step 1: Menu](../resources/right-click-run-eraser.png) 8 | 9 | ![Step 2: pre run](../resources/right-click-run-eraser-1.png) 10 | -------------------------------------------------------------------------------- /docs/book/src/features/inspektor-gadget.md: -------------------------------------------------------------------------------- 1 | ### Inspektor Gadget 2 | 3 | ### Deploy and Undeploy InspektorGadget 4 | 5 | Right click on your AKS cluster and select **Troubleshoot Network Health** and then click on **Show Inspektor Gadget** to easily deploy gadget into your cluster. User can easily one-click deploy and undeploy gadget from this feature. 6 | 7 | ### Profile, Top, Trace and Snapshot Inspektor Gadget Commands 8 | 9 | Right-click on your AKS cluster and select **Show Inspektor Gadget** and choose **Gadget Commands** to easily use non-interactive Top, Trace, Profile or Snapshot commands for your cluster. 10 | 11 | ![Cloud explorer extension menu](../resources/inspector-gadget-1.png) 12 | 13 | ![Cloud explorer extension menu](../resources/inspector-gadget-2.png) 14 | 15 | ![Cloud explorer extension menu](../resources/inspector-gadget-3.png) -------------------------------------------------------------------------------- /docs/book/src/features/install-azureserviceoperator.md: -------------------------------------------------------------------------------- 1 | ### Install Azure Service Operator 2 | 3 | ### Install Azure Service Operator 4 | 5 | Right click on your AKS cluster and click on **Install Azure Service Operator** to easily deploy the latest version of Azure Service Operator (ASO) on your AKS cluster and provision and connect applications to Azure resources within Kubernetes. When you select this option, you'll be prompted for a service principal for ASO to use when performing Azure resource operations. This service principal must have appropriate permissions (typically Contributor at suitable scope). Fill out the service principal details and click **Submit** to kick off the installation of Azure Service Operator. 6 | 7 | > Install Azure Service Operator can only be performed on an AKS cluster that has never had ASO installed before. If you have already initiated the installation manually, follow the instructions on [Azure Service Operator](https://azure.github.io/azure-service-operator/#installation) to complete. 8 | 9 | For more information on Azure Service Operator, visit [Azure Service Operator (for Kubernetes)](https://github.com/Azure/azure-service-operator). If you are experiencing issues with Azure Service Operator, visit [Azure Service Operator (ASO) troubleshooting](https://github.com/Azure/azure-service-operator/blob/master/docs/troubleshooting.md). 10 | 11 | ![Azure Service Operator Webview](../resources/azure-service-operator-screenshot.png) -------------------------------------------------------------------------------- /docs/book/src/features/k8s-api-health-points.md: -------------------------------------------------------------------------------- 1 | ### Kubernetes API Health Endpoints 2 | 3 | ### Run Kubernetes API Health Endpoints 4 | 5 | Right click on your AKS cluster and click on **Run Kubectl Commands**. Select and run health check commands from the *Health* section as shown in the image below. Currently we provide: 6 | 7 | - Healthz 8 | - Livez 9 | - Readyz 10 | 11 | ![Kubectl health command panel](../resources/kubectl-command-panel.png) -------------------------------------------------------------------------------- /docs/book/src/features/kaito-manage-test.md: -------------------------------------------------------------------------------- 1 | # Manage and Test KAITO Deployments 2 | 3 | Actively monitor the the status of all KAITO deployments on the cluster, retrieve logs, test the inference servers, and delete/redeploy models. 4 | 5 | ### Manage KAITO Deployments 6 | 7 | Right click on your desired AKS cluster and select **Deploy an LLM with KAITO** and then click on **Manage KAITO Models**. 8 | 9 | ![Manage Page](../resources/kaito-manage-page.png) 10 | 11 | Once on this page, you will see all existing KAITO deployments on the cluster, alongside their status (ongoing, successful, or failed). 12 | 13 | For your selected deployment, click **Get Logs** to access the latest logs from the KAITO workspace pods. This action will generate a new text file containing the most recent 500 lines of logs. 14 | 15 | To delete a model, select **Delete Workspace** (or **Cancel** for ongoing deployments). For failed deployments, choose **Re-deploy Default CRD** to remove the current deployment and restart the model deployment process from scratch. 16 | 17 | ### Test a Model 18 | 19 | On your desired model, select **Test** to access the model testing page. 20 | 21 | ![Test Page](../resources/kaito-test-page.png) 22 | 23 | Once on the testing page, you can modify the parameters and enter a prompt for submsission. Click **Reset Params** to reset all configurable parameters to their default values. Click **Submit Prompt** to submit your query. 24 | 25 | ![Query Response](../resources/kaito-test-response.png) -------------------------------------------------------------------------------- /docs/book/src/features/manage-cluster-operations.md: -------------------------------------------------------------------------------- 1 | # Run Managed Cluster Operations 2 | 3 | ### Run Managed Cluster Operations from your AKS cluster 4 | 5 | Right click on your AKS cluster and click on **Managed Cluster Operations** to easily run few managed cluster operations on your cluster. Currently we have enable following operations: 6 | 7 | - Abort Last Operation 8 | - Delete Cluster 9 | - Reconcile Cluster 10 | - Rotate Cluster Certificate 11 | -------------------------------------------------------------------------------- /docs/book/src/features/merge-save-kubeconfig.md: -------------------------------------------------------------------------------- 1 | # Merge and Save Into Kubeconfig 2 | 3 | ### Merge into Kubeconfig 4 | 5 | Right click on your AKS cluster and click on **Merge into Kubeconfig** to add the selected AKS cluster to the kubeconfig file. 6 | 7 | ### Save Kubeconfig 8 | 9 | Right click on your AKS cluster and click on **Save Kubeconfig** to save the kubeconfig of the selected AKS cluster. 10 | 11 | -------------------------------------------------------------------------------- /docs/book/src/features/run-kubectl-command.md: -------------------------------------------------------------------------------- 1 | # Run Kubectl Commands 2 | 3 | ### Run Kubectl Commands from your AKS cluster 4 | 5 | Right click on your AKS cluster and click on **Run Kubectl Commands** to easily run few known kubectl commands on your cluster. Currently we have enable following kubectl commands for the AKS cluster: 6 | 7 | - Describe Services 8 | - Get All Pods 9 | - API Resources 10 | - Get Cluster Info 11 | - Get Node 12 | - Get All Events 13 | 14 | User can also run custom commands by typing or editing `kubectl` command parameters in the text field. Custom commands can optionally be saved for future use.. 15 | 16 | ![Kubectl commad](../resources/right-click-menu-kubectl.png) 17 | 18 | ![Kubectl command panel](../resources/kubectl-command-panel.png) -------------------------------------------------------------------------------- /docs/book/src/features/tcp-dumps.md: -------------------------------------------------------------------------------- 1 | # Collect TCP Dumps from AKS Cluster Linux Nodes 2 | 3 | ### Collect TCP Dumps 4 | 5 | Right click on your AKS cluster and select **Troubleshoot Network Health** and then select **Collect TCP Dumps** to capture TCP dumps for any Linux node and download them to your local machine with ease. 6 | 7 | Added filters to the TCP Dump functionality, so that you can target traffic capture to specific network interfaces, ports or protocols, to or from specific pods, or craft custom [pcap filter strings](https://www.tcpdump.org/manpages/pcap-filter.7.html). 8 | 9 | ![Step 1: Menu](../resources/right-click-tcp-dump-collect.png) 10 | 11 | ![Step 2: Select Page and Successful Run](../resources/tcp-dump-linux-node.png) 12 | 13 | ![Step 3: Advanced Filter for TCP DUMP](../resources/tcp-dump-filter-interface.png) 14 | 15 | ![Step 3: Advanced Filter for TCP DUMP more details](../resources/tcp-dump-filter-interface1.png) 16 | -------------------------------------------------------------------------------- /docs/book/src/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | 1. Download and install the [Azure Kubernetes Service extension](https://marketplace.visualstudio.com/items?itemName=ms-kubernetes-tools.vscode-aks-tools) for Visual Studio Code. 4 | 5 | 2. Wait for the extension to finish installing then reload Visual Studio Code when prompted. 6 | 7 | 3. Once the installation is complete, you'll see a section named **Azure** under **Clouds**. 8 | 9 | 4. Sign in to your Azure Account by clicking **Sign in to Azure…**, (Afternatively, user can use `ctrl + shift + p` or `cmd + shift + p` and choose `AKS: Sign in to Azure`) screenshot below could guide the switch for account as well. If your account has access to more than one Azure tenant, you will be prompted to pick one. To change the selected tenant later, you can run `AKS: Select tenant...` from the command palette `ctrl + shift + p` or `cmd + shift + p`. 10 | 11 | ![Sign in to your Azure Account](./resources/Sign-in.png) 12 | 13 | ![Sign in using Command Pallete](./resources/aks-signin.png) 14 | 15 | ![Sign in Pop-Up](./resources/aks-signinpopup.png) 16 | 17 | ![Sign Into or Switch Account](./resources/aks-signintoaccountorswitchaccount.png) 18 | -------------------------------------------------------------------------------- /docs/book/src/release/releasing.md: -------------------------------------------------------------------------------- 1 | ## How to Release 2 | 3 | To make a new release and publish it to the marketplace you have to follow the following steps. 4 | 5 | 1. Create a branch `publish-x.y.z` 6 | 2. Update `package.json` with the new version 7 | 3. Add a section to `CHANGELOG.md` with the header `## [x.y.z]` (N.B: make sure to write the new version in square brackets as the `changelog-reader` action only works if the `CHANGELOG.md` file follows the [Keep a Changelog standard](https://github.com/olivierlacan/keep-a-changelog)) 8 | 4. Create a new PR, get approval and merge 9 | 5. Run the `Build & Publish` workflow manually from the GH Actions tab 10 | 11 | ### Build & Publish 12 | 13 | The `Build & Publish` workflow allows to create a new release, package it in a VSIX file and publish to the VSCode marketplace with a single click. 14 | 15 | The only requirement needed to run the workflow is to have a secret named `VS_MARKETPLACE_TOKEN` containing the Personal Access Token of the publisher. You can find more infos about how to create a publisher/token in the [official documentation](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#create-a-publisher) 16 | 17 | Once everything is set up and you followed all first 4 steps in the previous section, you are ready to trigger the `Build & Publish` workflow. 18 | This is what it actually does: 19 | 20 | 1. Install all dependencies and build the project 21 | 2. Check if the `CHANGELOG.md` contains a section related to the new version 22 | 3. Create a new release 23 | 4. Create the VSIX file and publish it to the marketplace 24 | 5. Attach the VSIX file to the new release -------------------------------------------------------------------------------- /docs/book/src/resources/Sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/Sign-in.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-compare-cluster-from.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-compare-cluster-from.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-compare-cluster-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-compare-cluster-result.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-compare-cluster-with.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-compare-cluster-with.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-diagnostics-webview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-diagnostics-webview.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-fleet-create-failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-fleet-create-failure.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-fleet-create-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-fleet-create-input.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-fleet-create-right-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-fleet-create-right-click.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-fleet-create-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-fleet-create-success.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-gh-chat-create-cluster-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-gh-chat-create-cluster-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-gh-chat-deploy-manifest-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-gh-chat-deploy-manifest-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-gh-chat-kubectl-generation-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-gh-chat-kubectl-generation-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-periscope-webview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-periscope-webview.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-signin.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-signinpopup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-signinpopup.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-signintoaccountorswitchaccount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-signintoaccountorswitchaccount.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-startstop-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-startstop-cluster.png -------------------------------------------------------------------------------- /docs/book/src/resources/aks-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/aks-tools.png -------------------------------------------------------------------------------- /docs/book/src/resources/azure-service-operator-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/azure-service-operator-screenshot.png -------------------------------------------------------------------------------- /docs/book/src/resources/create-cluster-webview-resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/create-cluster-webview-resource.png -------------------------------------------------------------------------------- /docs/book/src/resources/create-cluster-webview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/create-cluster-webview.png -------------------------------------------------------------------------------- /docs/book/src/resources/draft-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/draft-command.png -------------------------------------------------------------------------------- /docs/book/src/resources/draft-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/draft-deployment.png -------------------------------------------------------------------------------- /docs/book/src/resources/draft-dockerfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/draft-dockerfile.png -------------------------------------------------------------------------------- /docs/book/src/resources/draft-dockerfile1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/draft-dockerfile1.png -------------------------------------------------------------------------------- /docs/book/src/resources/draft-gh-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/draft-gh-workflow.png -------------------------------------------------------------------------------- /docs/book/src/resources/inspector-gadget-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/inspector-gadget-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/inspector-gadget-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/inspector-gadget-2.png -------------------------------------------------------------------------------- /docs/book/src/resources/inspector-gadget-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/inspector-gadget-3.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-deploy-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-deploy-page.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-deployment-example-in-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-deployment-example-in-progress.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-deployment-example-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-deployment-example-success.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-installation-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-installation-page.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-installation-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-installation-success.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-manage-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-manage-page.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-test-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-test-page.png -------------------------------------------------------------------------------- /docs/book/src/resources/kaito-test-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kaito-test-response.png -------------------------------------------------------------------------------- /docs/book/src/resources/kubectl-command-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/kubectl-command-panel.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-select-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-select-container.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-select-nodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-select-nodes.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-select-storage-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-select-storage-account.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-success-check-storage-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-success-check-storage-account.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-success-run-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-success-run-download.png -------------------------------------------------------------------------------- /docs/book/src/resources/retina-success-run-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/retina-success-run-upload.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-api-health-check-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-api-health-check-menu.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-compare-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-compare-cluster.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-download-retina-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-download-retina-capture.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-inspektor-gadget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-inspektor-gadget.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-menu-kubectl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-menu-kubectl.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-menu-managedoperations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-menu-managedoperations.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-menu-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-menu-workflow.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-menu.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-retina-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-retina-capture.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-run-eraser-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-run-eraser-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-run-eraser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-run-eraser.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-subscription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-subscription.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-tcp-dump-collect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-tcp-dump-collect.png -------------------------------------------------------------------------------- /docs/book/src/resources/right-click-upload-retina-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/right-click-upload-retina-capture.png -------------------------------------------------------------------------------- /docs/book/src/resources/show-properties-abort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/show-properties-abort.png -------------------------------------------------------------------------------- /docs/book/src/resources/show-properties-page-k8s-available-versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/show-properties-page-k8s-available-versions.png -------------------------------------------------------------------------------- /docs/book/src/resources/show-properties-reconcile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/show-properties-reconcile.png -------------------------------------------------------------------------------- /docs/book/src/resources/tcp-dump-filter-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/tcp-dump-filter-interface.png -------------------------------------------------------------------------------- /docs/book/src/resources/tcp-dump-filter-interface1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/tcp-dump-filter-interface1.png -------------------------------------------------------------------------------- /docs/book/src/resources/tcp-dump-linux-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/tcp-dump-linux-node.png -------------------------------------------------------------------------------- /docs/book/src/resources/vscode-create-cluster-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/vscode-create-cluster-step-1.png -------------------------------------------------------------------------------- /docs/book/src/resources/vscode-create-cluster-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/vscode-create-cluster-step-2.png -------------------------------------------------------------------------------- /docs/book/src/resources/vscode-creating-notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/vscode-creating-notification.png -------------------------------------------------------------------------------- /docs/book/src/resources/vscode-creation-successful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/docs/book/src/resources/vscode-creation-successful.png -------------------------------------------------------------------------------- /docs/book/src/telemetery.md: -------------------------------------------------------------------------------- 1 | # Telemetry 2 | 3 | ## Telemetry 4 | 5 | This extension collects telemetry data to help us build a better experience for building applications with Azure Kubernetes Service and VS Code. We only collect the following data: 6 | 7 | * Which commands are executed. 8 | * Events pertaining to GitHub Copilot for Azure (@azure) handlers 9 | * Which VS Code command ID was used to enable handler 10 | * Whether or not if a subscription was selected 11 | * Whether or not a manifest file was selected 12 | * Whether or not a cluster was selected 13 | * Which cluster option was selected (see `SelectClusterOptions` type) 14 | * Whether or not a manifest deployment was cancelled 15 | * Whether or not a manifest deployment was successful 16 | * Whether or not the success manifest deployment link was clicked 17 | * Whether or not a cluster was successfully created 18 | 19 | We do not collect any information about image names, paths, etc. Read our [privacy statement](https://privacy.microsoft.com/privacystatement) to learn more. If you don’t wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). 20 | -------------------------------------------------------------------------------- /docs/maintenance/README.md: -------------------------------------------------------------------------------- 1 | ## How to Release 2 | 3 | To make a new release and publish it to the marketplace you have to follow the following steps. 4 | 5 | 1. Create a branch `publish-x.y.z` 6 | 2. Update `package.json` with the new version 7 | 3. Add a section to `CHANGELOG.md` with the header `## [x.y.z]` (N.B: make sure to write the new version in square brackets as the `changelog-reader` action only works if the `CHANGELOG.md` file follows the [Keep a Changelog standard](https://github.com/olivierlacan/keep-a-changelog)) 8 | 4. Create a new PR, get approval and merge 9 | 5. Run the `Build & Publish` workflow manually from the GH Actions tab 10 | 11 | ### Build & Publish 12 | 13 | The `Build & Publish` workflow allows to create a new release, package it in a VSIX file and publish to the VSCode marketplace with a single click. 14 | 15 | The only requirement needed to run the workflow is to have a secret named `VS_MARKETPLACE_TOKEN` containing the Personal Access Token of the publisher. You can find more infos about how to create a publisher/token in the [official documentation](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#create-a-publisher) 16 | 17 | Once everything is set up and you followed all first 4 steps in the previous section, you are ready to trigger the `Build & Publish` workflow. 18 | This is what it actually does: 19 | 20 | 1. Install all dependencies and build the project 21 | 2. Check if the `CHANGELOG.md` contains a section related to the new version 22 | 3. Create a new release 23 | 4. Create the VSIX file and publish it to the marketplace 24 | 5. Attach the VSIX file to the new release -------------------------------------------------------------------------------- /docs/package-scripts.md: -------------------------------------------------------------------------------- 1 | # Package Scripts 2 | 3 | This gives an overview of the `npm` scripts available for development and release of the extension. See the `scripts` block in [package.json](../package.json). 4 | 5 | These can all be run from the command line in the root of the repository (with `npm` installed), using `npm run {script-name}`. 6 | 7 | ## Environment Initialization 8 | 9 | - `install:all`: Installs `npm` dependencies for both the main extension project and the `webview-ui` sub-project. It's recommended to use this instead of `npm install`, which will only install dependencies for the main project. 10 | 11 | ## Development and Testing 12 | 13 | - [`dev-webview`](./webview-development.md#developing-the-ui): for concurrent development/debugging of webview UX. 14 | - `build-webview`: bundles and minifies the webview UX for consumption by the extension. 15 | - `webpack`: builds and packages the extension. 16 | - `test`: runs automated tests. 17 | 18 | ## Not for Running Directly 19 | 20 | Some scripts are invoked by other scripts or tools, so need not be run directly, or are otherwise not required for general development tasks: 21 | 22 | - `vscode:prepublish`: used by the `vsce` command for packaging the extension into a `vsix` file for distribution. 23 | - `webpack-dev`: bundles the extension code in development mode. Since we currently have no conditional logic that depends on whether the extension is running in development or production, this may be redundant. 24 | - `test-compile`: compiles the extension typescript (after building the `webview-ui` project) without webpacking it. This is a prerequisite to running automated tests. It _could_ be moved into `test`, but keeping it separate would allow it to be used in the future as a prelaunch task for debugging the extension without webpacking it. 25 | - `watch`: not currently used as part of any workflow I'm aware of, but could potentially be useful for editing while debugging. 26 | -------------------------------------------------------------------------------- /publish-book/install-mdbook-toc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | VERSION=${1} 8 | OUTPUT_PATH=${2} 9 | 10 | # Ensure the output folder exists 11 | mkdir -p "${OUTPUT_PATH}" 12 | 13 | RELEASE_NAME="" 14 | case "$OSTYPE" in 15 | darwin*) RELEASE_NAME="x86_64-apple-darwin.tar.gz" ;; 16 | linux*) RELEASE_NAME="x86_64-unknown-linux-gnu.tar.gz" ;; 17 | *) echo "No mdBook release available for: $OSTYPE" && exit 1;; 18 | esac 19 | 20 | # Download and extract the mdBook release 21 | curl -L "https://github.com/badboy/mdbook-toc/releases/download/${VERSION}/mdbook-toc-${VERSION}-${RELEASE_NAME}" | tar -xvz -C "${OUTPUT_PATH}" 22 | -------------------------------------------------------------------------------- /publish-book/install-mdbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | VERSION=${1} 8 | OUTPUT_PATH=${2} 9 | 10 | # Ensure the output folder exists 11 | mkdir -p "${OUTPUT_PATH}" 12 | 13 | RELEASE_NAME="" 14 | case "$OSTYPE" in 15 | darwin*) RELEASE_NAME="x86_64-apple-darwin.tar.gz" ;; 16 | linux*) RELEASE_NAME="x86_64-unknown-linux-gnu.tar.gz" ;; 17 | *) echo "No mdBook release available for: $OSTYPE" && exit 1;; 18 | esac 19 | 20 | # Download and extract the mdBook release 21 | curl -L "https://github.com/rust-lang/mdBook/releases/download/${VERSION}/mdbook-${VERSION}-${RELEASE_NAME}" | tar -xvz -C "${OUTPUT_PATH}" 22 | -------------------------------------------------------------------------------- /resources/aks-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/resources/aks-tools.png -------------------------------------------------------------------------------- /resources/azureSubscription.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Icon-general-2 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /resources/dark/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/fleet-tree-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/resources/fleet-tree-icon.png -------------------------------------------------------------------------------- /resources/light/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/yaml/azureoperatorsettings.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: aso-controller-settings 5 | namespace: azureserviceoperator-system 6 | stringData: 7 | AZURE_TENANT_ID: 8 | AZURE_SUBSCRIPTION_ID: 9 | AZURE_CLIENT_ID: 10 | AZURE_CLIENT_SECRET: 11 | AZURE_CLOUD_ENV: -------------------------------------------------------------------------------- /resources/yaml/kaitoworkspace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kaito.sh/v1alpha1 2 | kind: Workspace 3 | metadata: 4 | name: workspace- 5 | resource: 6 | instanceType: "" 7 | labelSelector: 8 | matchLabels: 9 | apps: 10 | inference: 11 | preset: 12 | name: "" -------------------------------------------------------------------------------- /src/assets.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { ExtensionContext, Uri } from "vscode"; 3 | import * as fs from "fs"; 4 | import { Errorable, getErrorMessage } from "./commands/utils/errorable"; 5 | 6 | let EXTENSION_CONTEXT: ExtensionContext | null = null; 7 | 8 | export function setAssetContext(context: ExtensionContext) { 9 | EXTENSION_CONTEXT = context; 10 | } 11 | 12 | export function getAssetContext(): ExtensionContext | null { 13 | return EXTENSION_CONTEXT; 14 | } 15 | 16 | export function assetPath(relativePath: string): string { 17 | if (EXTENSION_CONTEXT) { 18 | // which it always should be 19 | return EXTENSION_CONTEXT.asAbsolutePath(relativePath); 20 | } 21 | const absolutePath = path.join(__dirname, "..", relativePath); 22 | return absolutePath; 23 | } 24 | 25 | export function assetUri(relativePath: string): Uri { 26 | return Uri.file(assetPath(relativePath)); 27 | } 28 | 29 | export function getResourceFileContent(relativePath: string): Errorable { 30 | const fileUri = assetUri(relativePath); 31 | try { 32 | const content = fs.readFileSync(fileUri.fsPath); 33 | return { succeeded: true, result: content }; 34 | } catch (e) { 35 | return { succeeded: false, error: `Failed to read ${relativePath}: ${getErrorMessage(e)}` }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/auth/types.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationSession, Event } from "vscode"; 2 | import { Errorable } from "../commands/utils/errorable"; 3 | 4 | export type SignInStatus = "Initializing" | "SigningIn" | "SignedIn" | "SignedOut"; 5 | 6 | export type TokenInfo = { 7 | token: string; 8 | expiry: Date; 9 | }; 10 | 11 | export type AzureAuthenticationSession = AuthenticationSession & { 12 | tenantId: string; 13 | }; 14 | 15 | export type Tenant = { 16 | name: string; 17 | id: string; 18 | countryCode?: string; 19 | }; 20 | 21 | export type GetAuthSessionOptions = { 22 | applicationClientId?: string; 23 | scopes?: string[]; 24 | }; 25 | 26 | export type AzureSessionProvider = { 27 | signIn(): Promise; 28 | signInStatus: SignInStatus; 29 | availableTenants: Tenant[]; 30 | selectedTenant: Tenant | null; 31 | signInStatusChangeEvent: Event; 32 | getAuthSession(options?: GetAuthSessionOptions): Promise>; 33 | dispose(): void; 34 | }; 35 | 36 | export type ReadyAzureSessionProvider = AzureSessionProvider & { 37 | signInStatus: "SignedIn"; 38 | selectedTenant: Tenant; 39 | }; 40 | 41 | export function isReady(provider: AzureSessionProvider): provider is ReadyAzureSessionProvider { 42 | return provider.signInStatus === "SignedIn" && provider.selectedTenant !== null; 43 | } 44 | -------------------------------------------------------------------------------- /src/azure-api-utils.ts: -------------------------------------------------------------------------------- 1 | export function parseResource(armId: string): { 2 | parentResourceId: string | undefined; 3 | subscriptionId: string | undefined; 4 | resourceGroupName: string | undefined; 5 | name: string | undefined; 6 | } { 7 | // General armId Format: /subscriptions/{subid}/resourcegroups/{group}/providers/.../{name} 8 | // armId format of a member cluster, given parentResource is a Fleet {parentResourceId}/members/{name} 9 | const bits = armId.split("/").filter((bit) => bit.length > 0); 10 | const resourceGroupName = bits[3]; 11 | const subscriptionId = bits[1]; 12 | const name = bits[bits.length - 1]; 13 | const parentResourceId = `/${bits.slice(0, bits.length - 2).join("/")}`; // take all except for the last two bits 14 | return { parentResourceId, subscriptionId, resourceGroupName, name }; 15 | } 16 | 17 | export function parseSubId(armId: string): { subId: string } { 18 | // /subscriptions/{subid}/resourcegroups/{group}/providers/.../{name} 19 | const bits = armId.split("/").filter((bit) => bit.length > 0); 20 | const subId = bits[1]; 21 | return { subId }; 22 | } 23 | -------------------------------------------------------------------------------- /src/commands/aksClusterProperties/aksClusterProperties.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as k8s from "vscode-kubernetes-tools-api"; 3 | import { IActionContext } from "@microsoft/vscode-azext-utils"; 4 | import { getAksClusterTreeNode } from "../utils/clusters"; 5 | import { getExtension } from "../utils/host"; 6 | import { failed } from "../utils/errorable"; 7 | import { ClusterPropertiesDataProvider, ClusterPropertiesPanel } from "../../panels/ClusterPropertiesPanel"; 8 | import { getReadySessionProvider } from "../../auth/azureAuth"; 9 | 10 | export default async function aksClusterProperties(_context: IActionContext, target: unknown): Promise { 11 | const cloudExplorer = await k8s.extension.cloudExplorer.v1; 12 | const sessionProvider = await getReadySessionProvider(); 13 | if (failed(sessionProvider)) { 14 | vscode.window.showErrorMessage(sessionProvider.error); 15 | return; 16 | } 17 | 18 | const clusterNode = getAksClusterTreeNode(target, cloudExplorer); 19 | if (failed(clusterNode)) { 20 | vscode.window.showErrorMessage(clusterNode.error); 21 | return; 22 | } 23 | 24 | const extension = getExtension(); 25 | if (failed(extension)) { 26 | vscode.window.showErrorMessage(extension.error); 27 | return; 28 | } 29 | 30 | const dataProvider = new ClusterPropertiesDataProvider( 31 | sessionProvider.result, 32 | clusterNode.result.subscriptionId, 33 | clusterNode.result.resourceGroupName, 34 | clusterNode.result.name, 35 | target, 36 | ); 37 | const panel = new ClusterPropertiesPanel(extension.result.extensionUri); 38 | 39 | panel.show(dataProvider); 40 | } 41 | -------------------------------------------------------------------------------- /src/commands/aksCreateClusterNavToAzurePortal/aksCreateClusterNavToAzurePortal.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as k8s from "vscode-kubernetes-tools-api"; 3 | import { IActionContext } from "@microsoft/vscode-azext-utils"; 4 | import { getAksClusterSubscriptionNode } from "../utils/clusters"; 5 | import { failed } from "../utils/errorable"; 6 | 7 | export default async function aksCreateClusterNavToAzurePortal( 8 | _context: IActionContext, 9 | target: unknown, 10 | ): Promise { 11 | const cloudExplorer = await k8s.extension.cloudExplorer.v1; 12 | 13 | const subscriptionNode = getAksClusterSubscriptionNode(target, cloudExplorer); 14 | if (failed(subscriptionNode)) { 15 | vscode.window.showErrorMessage(subscriptionNode.error); 16 | return; 17 | } 18 | 19 | vscode.env.openExternal(vscode.Uri.parse(`https://portal.azure.com/#create/microsoft.aks`)); 20 | } 21 | -------------------------------------------------------------------------------- /src/commands/aksFleetProperties/askFleetProperties.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as k8s from "vscode-kubernetes-tools-api"; 3 | import { IActionContext } from "@microsoft/vscode-azext-utils"; 4 | import { getExtension } from "../utils/host"; 5 | import { failed } from "../utils/errorable"; 6 | import { getReadySessionProvider } from "../../auth/azureAuth"; 7 | import { getAksFleetTreeNode } from "../utils/fleet"; 8 | import { FleetPropertiesDataProvider, FleetPropertiesPanel } from "../../panels/FleetPropertiesPanel"; 9 | 10 | export default async function aksFleetProperties(_context: IActionContext, target: unknown): Promise { 11 | const cloudExplorer = await k8s.extension.cloudExplorer.v1; 12 | const sessionProvider = await getReadySessionProvider(); 13 | if (failed(sessionProvider)) { 14 | vscode.window.showErrorMessage(sessionProvider.error); 15 | return; 16 | } 17 | 18 | const fleetNode = getAksFleetTreeNode(target, cloudExplorer); 19 | if (failed(fleetNode)) { 20 | vscode.window.showErrorMessage(fleetNode.error); 21 | return; 22 | } 23 | 24 | const extension = getExtension(); 25 | if (failed(extension)) { 26 | vscode.window.showErrorMessage(extension.error); 27 | return; 28 | } 29 | 30 | const dataProvider = new FleetPropertiesDataProvider( 31 | sessionProvider.result, 32 | fleetNode.result.subscriptionId, 33 | fleetNode.result.resourceGroupName, 34 | fleetNode.result.name, 35 | ); 36 | 37 | const panel = new FleetPropertiesPanel(extension.result.extensionUri); 38 | panel.show(dataProvider); 39 | } 40 | -------------------------------------------------------------------------------- /src/commands/aksNavToPortal/aksNavToPortal.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as k8s from "vscode-kubernetes-tools-api"; 3 | import { IActionContext } from "@microsoft/vscode-azext-utils"; 4 | import { getAksClusterTreeNode } from "../utils/clusters"; 5 | import { getExtensionPath } from "../utils/host"; 6 | import { failed } from "../utils/errorable"; 7 | import { getPortalResourceUrl } from "../utils/env"; 8 | import { getEnvironment } from "../../auth/azureAuth"; 9 | 10 | export default async function aksNavToPortal(_context: IActionContext, target: unknown): Promise { 11 | const cloudExplorer = await k8s.extension.cloudExplorer.v1; 12 | 13 | const clusterNode = getAksClusterTreeNode(target, cloudExplorer); 14 | if (failed(clusterNode)) { 15 | vscode.window.showErrorMessage(clusterNode.error); 16 | return; 17 | } 18 | 19 | const extensionPath = getExtensionPath(); 20 | if (failed(extensionPath)) { 21 | vscode.window.showErrorMessage(extensionPath.error); 22 | return; 23 | } 24 | 25 | // armid is in the format: /subscriptions//resourceGroups//providers//managedClusters/ 26 | const resourceUrl = getPortalResourceUrl(getEnvironment(), clusterNode.result.armId); 27 | vscode.env.openExternal(vscode.Uri.parse(resourceUrl)); 28 | } 29 | -------------------------------------------------------------------------------- /src/commands/aksRetinaCapture/utils.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "vscode-kubernetes-tools-api"; 2 | import * as vscode from "vscode"; 3 | import { Errorable } from "../utils/errorable"; 4 | import { failed } from "../utils/errorable"; 5 | import { getLinuxNodes } from "../../panels/utilities/KubectlNetworkHelper"; 6 | 7 | export async function selectLinuxNodes( 8 | kubectl: k8s.APIAvailable, 9 | kubeConfigFilePath: string, 10 | title: string = "Select Nodes to Capture Traffic From", 11 | placeHolder: string = "Please select all the Nodes you want Retina to capture traffic from.", 12 | ): Promise> { 13 | const linuxNodesList = await getLinuxNodes(kubectl, kubeConfigFilePath); 14 | if (failed(linuxNodesList)) { 15 | return linuxNodesList; 16 | } 17 | 18 | const nodeNamesSelected = await vscode.window.showQuickPick(linuxNodesList.result, { 19 | canPickMany: true, 20 | placeHolder, 21 | title, 22 | }); 23 | 24 | if (!nodeNamesSelected || nodeNamesSelected.length === 0) { 25 | return { succeeded: false, error: "No nodes were selected." }; 26 | } 27 | 28 | const selectedNodes = nodeNamesSelected.join(","); 29 | 30 | return { succeeded: true, result: selectedNodes }; 31 | } 32 | -------------------------------------------------------------------------------- /src/commands/draft/types.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceFolder } from "vscode"; 2 | import { InitialSelection as DeploymentInitialSelection } from "../../webview-contract/webviewDefinitions/draft/draftDeployment"; 3 | import { InitialSelection as WorkflowInitialSelection } from "../../webview-contract/webviewDefinitions/draft/draftWorkflow"; 4 | 5 | export type DraftCommandParamsTypes = { 6 | "aks.draftDockerfile": { 7 | workspaceFolder?: WorkspaceFolder; 8 | initialLocation?: string; 9 | }; 10 | "aks.draftDeployment": { 11 | workspaceFolder?: WorkspaceFolder; 12 | initialLocation?: string; 13 | initialSelection?: DeploymentInitialSelection; 14 | }; 15 | "aks.draftWorkflow": { 16 | workspaceFolder?: WorkspaceFolder; 17 | initialSelection?: WorkflowInitialSelection; 18 | }; 19 | "aks.draftValidate": { 20 | workspaceFolder?: WorkspaceFolder; 21 | initialLocation?: string; 22 | }; 23 | }; 24 | 25 | export type DraftCommandName = keyof DraftCommandParamsTypes; 26 | 27 | export type DraftCommandParamsType = DraftCommandParamsTypes[T]; 28 | -------------------------------------------------------------------------------- /src/commands/periscope/models/RetinaDownloadConfig.ts: -------------------------------------------------------------------------------- 1 | export interface RetinaDownloadConfig { 2 | releaseTag: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/commands/periscope/models/clusterFeatures.ts: -------------------------------------------------------------------------------- 1 | export enum ClusterFeatures { 2 | None = 0, 3 | WindowsHpc = 1, 4 | } 5 | -------------------------------------------------------------------------------- /src/commands/periscope/models/config.ts: -------------------------------------------------------------------------------- 1 | export interface KustomizeConfig { 2 | repoOrg: string; 3 | containerRegistry: string; 4 | releaseTag: string; 5 | imageVersion: string; 6 | } 7 | 8 | export interface KubeloginConfig { 9 | releaseTag: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/commands/periscope/models/storage.ts: -------------------------------------------------------------------------------- 1 | export interface PeriscopeStorage { 2 | containerName: string; 3 | storageName: string; 4 | storageKey: string; 5 | blobEndpoint: string; 6 | storageDeploymentSas: string; 7 | sevenDaysSasKey: string; 8 | } 9 | 10 | export interface UploadStatus { 11 | nodeName: string; 12 | isUploaded: boolean; 13 | } 14 | 15 | export interface PodLogs { 16 | podName: string; 17 | logs: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/commands/refreshSubscriptions.ts: -------------------------------------------------------------------------------- 1 | import * as k8s from "vscode-kubernetes-tools-api"; 2 | import { IActionContext } from "@microsoft/vscode-azext-utils"; 3 | import { getAksClusterSubscriptionNode } from "./utils/clusters"; 4 | import { failed } from "./utils/errorable"; 5 | 6 | export default async function refreshSubscription(context: IActionContext, target: unknown): Promise { 7 | const cloudExplorer = await k8s.extension.cloudExplorer.v1; 8 | 9 | if (cloudExplorer.available) { 10 | const subscriptionNode = getAksClusterSubscriptionNode(target, cloudExplorer); 11 | if (failed(subscriptionNode)) { 12 | return; 13 | } 14 | 15 | subscriptionNode.result.treeDataProvider.refresh(context, subscriptionNode.result.treeItem); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/commands/utils/commands.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from "rxjs"; 2 | import { Disposable } from "vscode"; 3 | 4 | export class OutputStream extends Disposable { 5 | constructor( 6 | dispose: () => void, 7 | readonly lines: Observable, 8 | ) { 9 | super(dispose); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/commands/utils/configureWorkflowHelper.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import * as fs from "fs"; 3 | import { getExtensionPath } from "./host"; 4 | import * as vscode from "vscode"; 5 | import { Errorable, failed } from "./errorable"; 6 | 7 | export function getWorkflowYaml(workflowName: string): Errorable { 8 | const extensionPath = getExtensionPath(); 9 | if (failed(extensionPath)) { 10 | return extensionPath; 11 | } 12 | 13 | const yamlPathOnDisk = vscode.Uri.file( 14 | path.join(extensionPath.result, "resources", "yaml", `${workflowName}.yaml`), 15 | ); 16 | try { 17 | const content = fs.readFileSync(yamlPathOnDisk.fsPath, "utf8"); 18 | return { succeeded: true, result: content }; 19 | } catch (e) { 20 | return { succeeded: false, error: `Failed to read ${yamlPathOnDisk}: ${e}` }; 21 | } 22 | } 23 | 24 | export function substituteClusterInWorkflowYaml(workflowYaml: string, instanceType: string, appID: string): string { 25 | return workflowYaml.replace("", instanceType).replaceAll("", appID); 26 | } 27 | -------------------------------------------------------------------------------- /src/commands/utils/dictionary.ts: -------------------------------------------------------------------------------- 1 | export type Dictionary = { 2 | [key: string]: T; 3 | }; 4 | -------------------------------------------------------------------------------- /src/commands/utils/env.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from "@azure/ms-rest-azure-env"; 2 | import path from "path"; 3 | import meta from "../../../package.json"; 4 | 5 | export function ensureDirectoryInPath(directoryPath: string) { 6 | if (process.env.PATH === undefined) { 7 | process.env.PATH = directoryPath; 8 | } else if (process.env.PATH.indexOf(directoryPath) < 0) { 9 | process.env.PATH = directoryPath + path.delimiter + process.env.PATH; 10 | } 11 | } 12 | 13 | export function getPortalResourceUrl(environment: Environment, armId: string): string { 14 | const portalUrl = environment.portalUrl.replace(/\/$/, ""); 15 | return `${portalUrl}/#resource${armId}?referrer_source=vscode&referrer_context=${meta.name}`; 16 | } 17 | 18 | export function getDeploymentPortalUrl(environment: Environment, armId: string): string { 19 | const portalUrl = environment.portalUrl.replace(/\/$/, ""); 20 | const encodedArmId = encodeURIComponent(armId); 21 | const encodedReferrerContext = encodeURIComponent(meta.name); 22 | return `${portalUrl}/#view/HubsExtension/DeploymentDetailsBlade/~/overview/id/${encodedArmId}?api-version=2020-06-01&referrer_source=vscode&referrer_context=${encodedReferrerContext}`; 23 | } 24 | -------------------------------------------------------------------------------- /src/commands/utils/ghCopilotHandlers.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | export function checkExtension(ext: string): boolean { 4 | let extensions = vscode.extensions.all; 5 | extensions = extensions.filter((extension) => extension.id === ext); 6 | 7 | return extensions.length !== 0; 8 | } 9 | 10 | export function handleExtensionDoesNotExist(ext: string): void { 11 | vscode.window 12 | .showWarningMessage( 13 | `Extension with id : "${ext}" must be installed to use this feature.`, 14 | "Install from Marketplace", 15 | ) 16 | .then((selection) => { 17 | if (selection === "Install from Marketplace") { 18 | vscode.commands.executeCommand("extension.open", ext); 19 | } 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/commands/utils/git.ts: -------------------------------------------------------------------------------- 1 | import { extensions } from "vscode"; 2 | import { API, GitExtension } from "../../types/git"; 3 | import { Errorable } from "./errorable"; 4 | 5 | /** 6 | * Gets the Git extension API. This is a built-in extension which we can use to examine the local Git repository, 7 | * e.g. what branches or remotes exist, and the state of the workspace. 8 | */ 9 | export function getGitApi(): Errorable { 10 | const gitExtension = extensions.getExtension("vscode.git"); 11 | if (!gitExtension) { 12 | return { succeeded: false, error: "Git extension not found" }; 13 | } 14 | 15 | const git = gitExtension.exports.getAPI(1); 16 | return { succeeded: true, result: git }; 17 | } 18 | -------------------------------------------------------------------------------- /src/commands/utils/host.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { Errorable, map as errmap } from "./errorable"; 3 | import meta from "../../../package.json"; 4 | 5 | export async function longRunning(title: string, action: () => Promise): Promise { 6 | const options = { 7 | location: vscode.ProgressLocation.Notification, 8 | title: title, 9 | }; 10 | return await vscode.window.withProgress(options, () => action()); 11 | } 12 | 13 | export function getExtension(): Errorable> { 14 | const publisherName = `${meta.publisher}.${meta.name}`; 15 | const extension = vscode.extensions.getExtension(publisherName); 16 | return extension 17 | ? { succeeded: true, result: extension } 18 | : { succeeded: false, error: `Extension not found for ${publisherName}` }; 19 | } 20 | 21 | export function getExtensionPath(): Errorable { 22 | return errmap(getExtension(), (e) => e.extensionPath); 23 | } 24 | -------------------------------------------------------------------------------- /src/commands/utils/json.ts: -------------------------------------------------------------------------------- 1 | import { Errorable, getErrorMessage } from "./errorable"; 2 | 3 | export function parseJson(input: string): Errorable { 4 | try { 5 | const result = JSON.parse(input) as T; 6 | return { succeeded: true, result }; 7 | } catch (e) { 8 | return { succeeded: false, error: `Error parsing JSON: ${getErrorMessage(e)}\nInput: ${input}` }; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/commands/utils/managedServiceIdentity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FederatedIdentityCredential, 3 | ManagedServiceIdentityClient, 4 | UserAssignedIdentitiesGetResponse, 5 | } from "@azure/arm-msi"; 6 | import { Errorable, getErrorMessage } from "./errorable"; 7 | 8 | export async function getIdentity( 9 | client: ManagedServiceIdentityClient, 10 | resourceGroupName: string, 11 | resourceName: string, 12 | ): Promise> { 13 | try { 14 | const identity = await client.userAssignedIdentities.get(resourceGroupName, resourceName); 15 | if (!identity || !identity.principalId) { 16 | return { succeeded: false, error: "Identity does not have a principalId" }; 17 | } 18 | return { succeeded: true, result: identity }; 19 | } catch (e) { 20 | return { succeeded: false, error: getErrorMessage(e) }; 21 | } 22 | } 23 | 24 | export async function createFederatedCredential( 25 | client: ManagedServiceIdentityClient, 26 | resourceGroupName: string, 27 | resourceName: string, 28 | federatedCredentialName: string, 29 | issuer: string, 30 | subject: string, 31 | audience: string, 32 | ): Promise> { 33 | try { 34 | const federatedCredential: FederatedIdentityCredential = { 35 | issuer: issuer, 36 | subject: subject, 37 | audiences: [audience], 38 | }; 39 | await client.federatedIdentityCredentials.createOrUpdate( 40 | resourceGroupName, 41 | federatedCredentialName, 42 | resourceName, 43 | federatedCredential, 44 | ); 45 | return { succeeded: true, result: undefined }; 46 | } catch (e) { 47 | return { succeeded: false, error: getErrorMessage(e) }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/commands/utils/reporter.ts: -------------------------------------------------------------------------------- 1 | import TelemetryReporter from "@vscode/extension-telemetry"; 2 | import vscode from "vscode"; 3 | import meta from "../../../package.json"; 4 | 5 | export let reporter: TelemetryReporter; 6 | 7 | export class Reporter extends vscode.Disposable { 8 | constructor() { 9 | super(() => reporter.dispose()); 10 | reporter = new TelemetryReporter(meta.aiKey); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/commands/utils/resourceGroups.ts: -------------------------------------------------------------------------------- 1 | import { ResourceGroup } from "@azure/arm-resources"; 2 | import { getResourceManagementClient, listAll } from "./arm"; 3 | import { Errorable, map as errmap } from "./errorable"; 4 | import { ReadyAzureSessionProvider } from "../../auth/types"; 5 | 6 | /** 7 | * A resource group with the name and location properties guaranteed to be defined. 8 | */ 9 | export type DefinedResourceGroup = ResourceGroup & Required>; 10 | 11 | export async function getResourceGroups( 12 | sessionProvider: ReadyAzureSessionProvider, 13 | subscriptionId: string, 14 | ): Promise> { 15 | const client = getResourceManagementClient(sessionProvider, subscriptionId); 16 | const groupsResult = await listAll(client.resourceGroups.list()); 17 | return errmap(groupsResult, (groups) => groups.filter(asDefinedResourceGroup)); 18 | } 19 | 20 | function asDefinedResourceGroup(rg: ResourceGroup): rg is DefinedResourceGroup { 21 | return rg.name !== undefined && rg.location !== undefined; 22 | } 23 | -------------------------------------------------------------------------------- /src/commands/utils/runtimeTypes.ts: -------------------------------------------------------------------------------- 1 | export function isObject(value: unknown): value is object { 2 | return typeof value === "object" && value !== null && value.constructor.name === "Object"; 3 | } 4 | 5 | export function isArray(value: unknown): value is unknown[] { 6 | return typeof value === "object" && value !== null && value.constructor.name === "Array"; 7 | } 8 | -------------------------------------------------------------------------------- /src/commands/utils/sleep.ts: -------------------------------------------------------------------------------- 1 | export function sleep(ms: number): Promise { 2 | return new Promise((resolve) => { 3 | setTimeout(resolve, ms); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /src/commands/utils/tempfile.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as fs from "fs/promises"; 3 | import { fileSync } from "tmp"; 4 | 5 | export async function withOptionalTempFile( 6 | content: string, 7 | extension: string, 8 | fn: (filename: string) => Promise, 9 | ): Promise { 10 | const tempFile = await createTempFile(content, extension); 11 | 12 | try { 13 | return await fn(tempFile.filePath); 14 | } finally { 15 | tempFile.dispose(); 16 | } 17 | } 18 | 19 | export async function createTempFile(content: string, extension: string): Promise { 20 | // TODO: try/catch and return errorable? 21 | const tempFile = fileSync({ prefix: "aks-periscope-", postfix: `.${extension}` }); 22 | await fs.writeFile(tempFile.name, content); 23 | return new TempFile(tempFile); 24 | } 25 | 26 | export async function createTempFileWithPrefix(content: string, extension: string, prefix: string): Promise { 27 | // TODO: try/catch and return errorable? 28 | const tempFile = fileSync({ prefix: prefix || "aks-vscodetemfileprefix-", postfix: `.${extension}` }); 29 | await fs.writeFile(tempFile.name, content); 30 | return new TempFile(tempFile); 31 | } 32 | 33 | export class TempFile extends vscode.Disposable { 34 | public readonly filePath: string; 35 | 36 | constructor(reference: TempFileReference) { 37 | super(() => reference.removeCallback()); 38 | this.filePath = reference.name; 39 | } 40 | } 41 | 42 | interface TempFileReference { 43 | name: string; 44 | removeCallback(): void; 45 | } 46 | -------------------------------------------------------------------------------- /src/panels/README.md: -------------------------------------------------------------------------------- 1 | # `panels` Directory 2 | 3 | This is adapted from the [Webview UI Toolkit](https://github.com/microsoft/vscode-webview-ui-toolkit) [guide](https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/docs/getting-started.md) for developing Webviews. 4 | 5 | See also: 6 | - [Samples](https://github.com/microsoft/vscode-webview-ui-toolkit-samples) 7 | - The [React sample](https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-vite) 8 | 9 | This directory contains all of the webview-related code that will be executed within the extension context. It can be thought of as the place where all of the "backend" code of a webview panel is contained. 10 | 11 | A `Panel` is a TypeScript class which manages the state and behaviour of a Webview panel, and handles: 12 | - Creating and rendering the webview panel 13 | - Properly cleaning up and disposing of webview resources when the panel is closed 14 | - Setting message listeners so data can be passed between the webview and extension 15 | - Setting the HTML (and by proxy CSS/JavaScript) content of the webview panel 16 | 17 | Each Webview (corresponding to its own extension command) extends the `BasePanel` class to configure its own initial state and message passing. 18 | -------------------------------------------------------------------------------- /src/panels/draft/commandUtils.ts: -------------------------------------------------------------------------------- 1 | import { commands } from "vscode"; 2 | import { DraftCommandName, DraftCommandParamsType } from "../../commands/draft/types"; 3 | 4 | export function launchDraftCommand( 5 | command: TCommand, 6 | params: DraftCommandParamsType, 7 | ): void { 8 | commands.executeCommand(command, params); 9 | } 10 | -------------------------------------------------------------------------------- /src/panels/utilities/KubectlNetworkHelper.ts: -------------------------------------------------------------------------------- 1 | import { platform } from "os"; 2 | import { Uri, workspace } from "vscode"; 3 | import { relative } from "path"; 4 | import { invokeKubectlCommand } from "../../commands/utils/kubectl"; 5 | import { Errorable, map as errmap } from "../../commands/utils/errorable"; 6 | import * as k8s from "vscode-kubernetes-tools-api"; 7 | 8 | export function getLocalKubectlCpPath(fileUri: Uri): string { 9 | if (platform().toLowerCase() !== "win32") { 10 | return fileUri.fsPath; 11 | } 12 | 13 | // Use a relative path to work around Windows path issues: 14 | // - https://github.com/kubernetes/kubernetes/issues/77310 15 | // - https://github.com/kubernetes/kubernetes/issues/110120 16 | // To use a relative path we need to know the current working directory. 17 | // This should be `process.cwd()` but it actually seems to be that of the first workspace folder, if any exist. 18 | // TODO: Investigate why, and look at alternative ways of getting the working directory, or working around 19 | // the need to to this altogether by allowing absolute paths. 20 | const workingDirectory = 21 | workspace.workspaceFolders && workspace.workspaceFolders?.length > 0 22 | ? workspace.workspaceFolders[0].uri.fsPath 23 | : process.cwd(); 24 | 25 | return relative(workingDirectory, fileUri.fsPath); 26 | } 27 | 28 | export async function getLinuxNodes( 29 | kubectl: k8s.APIAvailable, 30 | kubeConfigFile: string, 31 | ): Promise> { 32 | const command = `get node -l kubernetes.io/os=linux --no-headers -o custom-columns=":metadata.name"`; 33 | const commandResult = await invokeKubectlCommand(kubectl, kubeConfigFile, command); 34 | return errmap(commandResult, (sr) => sr.stdout.trim().split("\n")); 35 | } 36 | -------------------------------------------------------------------------------- /src/panels/utilities/webview.ts: -------------------------------------------------------------------------------- 1 | import { Uri, Webview } from "vscode"; 2 | 3 | /** 4 | * A helper function which will get the webview URI of a given file or resource. 5 | * 6 | * @remarks This URI can be used within a webview's HTML as a link to the 7 | * given file/resource. 8 | * 9 | * @param webview A reference to the extension webview 10 | * @param extensionUri The URI of the directory containing the extension 11 | * @param pathList An array of strings representing the path to a file/resource 12 | * @returns A URI pointing to the file/resource 13 | */ 14 | export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) { 15 | // Find the path under the 'dist' folder in the 'webview-ui' project. 16 | pathList = ["webview-ui", "dist", ...pathList]; 17 | return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)); 18 | } 19 | 20 | /** 21 | * A helper function that returns a unique alphanumeric identifier called a nonce. 22 | * 23 | * @remarks This function is primarily used to help enforce content security 24 | * policies for resources/scripts being executed in a webview context. 25 | * 26 | * @returns A nonce 27 | */ 28 | export function getNonce() { 29 | const allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 30 | const length = 32; 31 | return [...Array(length)] 32 | .map(() => Math.floor(Math.random() * allowedChars.length)) 33 | .map((i) => allowedChars[i]) 34 | .join(""); 35 | } 36 | -------------------------------------------------------------------------------- /src/plugins/getPlugins.ts: -------------------------------------------------------------------------------- 1 | import { createAKSClusterPlugin } from "./createAKS/createAKSClusterPlugin"; 2 | import { GetPluginsCommandResult } from "../types/aiazure/AzureAgent"; 3 | import { deployManifestPluginToAKSPlugin } from "./deployManifest/deployManifestToAKSPlugin"; 4 | import { generateKubectlCommandPlugin } from "./kubectlGeneration/generateKubectlCommandPlugin"; 5 | 6 | export async function getPlugins(): Promise { 7 | return { 8 | plugins: [createAKSClusterPlugin, deployManifestPluginToAKSPlugin, generateKubectlCommandPlugin], 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/plugins/kubectlGeneration/aksDocsRag/constants.ts: -------------------------------------------------------------------------------- 1 | export const aksDocsRAGScopes = ["https://management.core.windows.net/.default"]; 2 | 3 | export const aksDocsRAGEndpoint = "https://pcnxcopilot-g8adfneyayg0d9em.b02.azurefd.net/aks-docs-rag-mid"; 4 | 5 | export const aksDocsRAGScenario = "Azure Kubernetes Service"; 6 | 7 | export const aksDocsRAGIntent = "RCH"; 8 | 9 | // https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/communication-services/concepts/european-union-data-boundary.md 10 | export const EU_COUNTRIES = [ 11 | "AT", 12 | "BE", 13 | "BG", 14 | "CH", 15 | "CY", 16 | "CZ", 17 | "DE", 18 | "DK", 19 | "EE", 20 | "ES", 21 | "FI", 22 | "FR", 23 | "GR", 24 | "HR", 25 | "HU", 26 | "IE", 27 | "IS", 28 | "IT", 29 | "LI", 30 | "LT", 31 | "LU", 32 | "LV", 33 | "MT", 34 | "NL", 35 | "NO", 36 | "PL", 37 | "PT", 38 | "RO", 39 | "SE", 40 | "SI", 41 | "SK", 42 | ]; 43 | -------------------------------------------------------------------------------- /src/plugins/shared/clusterOptions/options/selectNewClusterOption.ts: -------------------------------------------------------------------------------- 1 | import { commands } from "vscode"; 2 | import { Errorable } from "../../../../commands/utils/errorable"; 3 | 4 | export async function selectNewClusterOption(): Promise> { 5 | await commands.executeCommand("aks.aksCreateClusterFromCopilot"); 6 | return { succeeded: true, result: true }; 7 | } 8 | -------------------------------------------------------------------------------- /src/plugins/shared/clusterOptions/options/selectRecentClusterOption.ts: -------------------------------------------------------------------------------- 1 | import { ClusterPreference } from "../../types"; 2 | import { Errorable, failed } from "../../../../commands/utils/errorable"; 3 | import { RecentCluster } from "../state/recentCluster"; 4 | 5 | export async function selectRecentClusterOption(): Promise> { 6 | const currentCluster = await RecentCluster.getRecentCluster(); 7 | 8 | return failed(currentCluster) 9 | ? { succeeded: false, error: currentCluster.error } 10 | : { succeeded: true, result: currentCluster.result }; 11 | } 12 | -------------------------------------------------------------------------------- /src/plugins/shared/telemetry/logger.ts: -------------------------------------------------------------------------------- 1 | import { reporter } from "../../../commands/utils/reporter"; 2 | import { SelectClusterOptions } from "../clusterOptions/selectClusterOptions"; 3 | import { CommandIdForPluginResponse } from "../types"; 4 | 5 | interface GHCopilotEventProperties { 6 | // command that invoked the telemetry event, log only the VS Code command Id 7 | commandId?: CommandIdForPluginResponse; 8 | 9 | // determine if subscription was successfully selected or not during flow 10 | subscriptionSelected?: "true" | "false"; 11 | 12 | // determine if manifest was successfully selected or not during flow 13 | manifestSelected?: "true" | "false"; 14 | 15 | // determine if cluster was successfully selected or not during flow 16 | clusterSelected?: "true" | "false"; 17 | 18 | // determine if manifest deployment was cancelled or not during flow 19 | manifestDeploymentCancelled?: "true" | "false"; 20 | 21 | // determine if manifest deployment was successful or not during flow 22 | manifestDeploymentSuccess?: "true" | "false"; 23 | 24 | // cluster option selected by user during flow 25 | clusterOptionSelected?: SelectClusterOptions; 26 | 27 | // determine if successful manifest deployment link was clicked or not 28 | manifestDeploymentLinkClicked?: "true" | "false"; 29 | } 30 | const TELEMETRY_EVENT_NAME = "aks.ghcp"; 31 | 32 | export function logGitHubCopilotPluginEvent(properties?: GHCopilotEventProperties): void { 33 | reporter.sendTelemetryEvent(TELEMETRY_EVENT_NAME, { ...properties }); 34 | } 35 | -------------------------------------------------------------------------------- /src/plugins/shared/types.ts: -------------------------------------------------------------------------------- 1 | import { ManagedClusterSKU } from "@azure/arm-containerservice"; 2 | 3 | export type ClusterPreference = { 4 | subscriptionId: string; 5 | clusterName: string; 6 | clusterId: string; 7 | resourceGroup: string; 8 | kubeConfigYAML: string; 9 | sku?: ManagedClusterSKU; 10 | }; 11 | 12 | export type MessageForLanguageModel = { 13 | description?: string; 14 | descriptionInstructions?: string; 15 | steps?: string[]; 16 | stepsInstructions?: string; 17 | chatResponseInstructions?: string; 18 | }; 19 | 20 | export type GitHubCopilotForAzureChatPluginResponse = { 21 | messageForLanguageModel: T; 22 | buttonLabel: string; 23 | commandID: CommandIdForPluginResponse; 24 | }; 25 | 26 | export type GitHubCopilotForAzureChatPluginResponseExtended = GitHubCopilotForAzureChatPluginResponse & { 27 | arguments: T[] | undefined; 28 | }; 29 | 30 | export type GitHubCopilotForAzureChatPluginErrorResponse = { 31 | messageForLanguageModel: T; 32 | }; 33 | 34 | export type CommandIdForPluginResponse = 35 | | "aks.aksDeployManifest" 36 | | "aks.aksOpenKubectlPanel" 37 | | "aks.aksCreateClusterFromCopilot"; 38 | -------------------------------------------------------------------------------- /src/tests/runTests.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { runTests } from "@vscode/test-electron"; 3 | 4 | async function main() { 5 | try { 6 | // The folder containing the Extension Manifest package.json 7 | // Passed to `--extensionDevelopmentPath` 8 | const extensionDevelopmentPath = path.resolve(__dirname, "../../.."); 9 | 10 | // The path to test runner 11 | // Passed to --extensionTestsPath 12 | const extensionTestsPath = path.resolve(__dirname, "./suite/index"); 13 | 14 | // Download VS Code, unzip it and run the integration test 15 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 16 | } catch (err) { 17 | console.error(`Failed to run tests:\n${err}`); 18 | if (err instanceof Error) { 19 | console.log(`message: ${err.message}\nname: ${err.name}\nstack: ${err.stack}`); 20 | } 21 | process.exit(1); 22 | } 23 | } 24 | 25 | main(); 26 | -------------------------------------------------------------------------------- /src/tests/suite/TcpDumpPanel.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | import { escapeRegExp } from "../../panels/TcpDumpPanel"; 3 | 4 | describe("testEscapeRegExp", () => { 5 | it("should escape special regex characters", () => { 6 | const input = "a.b*c?d+e^f$g|h(i)j{k}l[m]n\\o"; 7 | const expected = "a\\.b\\*c\\?d\\+e\\^f\\$g\\|h\\(i\\)j\\{k\\}l\\[m\\]n\\\\o"; 8 | const escapedInput = escapeRegExp(input); 9 | assert.equal(escapedInput, expected); 10 | }); 11 | 12 | it("should not double escape already escaped characters", () => { 13 | const input = "a\\.b\\*c\\?d\\+e\\^f\\$g\\|h\\(i\\)j\\{k\\}l\\[m\\]n\\\\o"; 14 | const expected = "a\\.b\\*c\\?d\\+e\\^f\\$g\\|h\\(i\\)j\\{k\\}l\\[m\\]n\\\\o"; 15 | const escapedInput = escapeRegExp(input); 16 | assert.equal(escapedInput, expected); 17 | }); 18 | 19 | it("should return an empty string if input is empty", () => { 20 | const input = ""; 21 | const expected = ""; 22 | const escapedInput = escapeRegExp(input); 23 | assert.equal(escapedInput, expected); 24 | }); 25 | 26 | it("should return the same string if no special characters", () => { 27 | const input = "abcdefg"; 28 | const expected = "abcdefg"; 29 | const escapedInput = escapeRegExp(input); 30 | assert.equal(escapedInput, expected); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/tests/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import Mocha from "mocha"; 3 | import { glob } from "glob"; 4 | 5 | export async function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: "bdd", 9 | }); 10 | 11 | const testsRoot = path.resolve(__dirname); 12 | const files = await glob("**/**.test.js", { cwd: testsRoot }); 13 | 14 | // Add files to the test suite 15 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 16 | 17 | try { 18 | return new Promise((c, e) => { 19 | // Run the mocha test 20 | mocha.run((failures) => { 21 | if (failures > 0) { 22 | e(new Error(`${failures} tests failed.`)); 23 | } else { 24 | c(); 25 | } 26 | }); 27 | }); 28 | } catch (err) { 29 | console.error(err); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/uriHandler.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | //Defines a type that represents a function that returns void or null, initalized to null 4 | let resolveCallback: (() => void) | null = null; 5 | 6 | // Create a Promise with a timeout 7 | export const onCallbackHandled = new Promise((resolve, reject) => { 8 | resolveCallback = resolve; 9 | 10 | const timeout = setTimeout(() => { 11 | reject(new Error("Timeout: Callback was not handled within the expected time.")); 12 | resolveCallback = null; // Clear the callback 13 | }, 180000); //3 Minute Timeout 14 | 15 | // Ensure the timeout is cleared when the Promise resolves 16 | resolveCallback = () => { 17 | clearTimeout(timeout); 18 | resolve(); 19 | resolveCallback = null; // Ensure the Promise resolves only once 20 | }; 21 | }); 22 | 23 | //Gets called once upon extension registration in the extension.ts file 24 | export function registerUriHandler(context: vscode.ExtensionContext) { 25 | const handleUri = (uri: vscode.Uri) => { 26 | vscode.window.showInformationMessage("inside handleUri"); 27 | 28 | if (uri.path === "/callback") { 29 | vscode.window.showInformationMessage("AKS extension: Handling callback"); 30 | console.log("AKS extension: Handling callback"); 31 | 32 | // Resolve the Promise 33 | // Checks to make sure resolveCallback is not null 34 | if (resolveCallback) { 35 | resolveCallback(); // Calls the resolve function 36 | } 37 | } else { 38 | console.log(`Unexpected URI path: ${uri.path}`); 39 | } 40 | }; 41 | 42 | // Register the URI handler 43 | context.subscriptions.push(vscode.window.registerUriHandler({ handleUri })); 44 | } 45 | -------------------------------------------------------------------------------- /src/webview-contract/README.md: -------------------------------------------------------------------------------- 1 | # Webview Contract Code 2 | 3 | This folder contains code that's shared between the VS Code extension and the `webview-ui` project. 4 | 5 | The intent is to provide a single source of truth for the data types that both parties need to communicate. 6 | 7 | This includes: 8 | - Unique Webview identifiers (content IDs) 9 | - Initial state 10 | - Command message types 11 | - Other types that make up components of Webviews 12 | - Message subscription logic 13 | 14 | Types that are specific to individual Webviews are in the `webviewDefinitions` directory. Each of these 15 | exports a `WebviewDefinition` type that combines the common types for each Webview. 16 | 17 | Each Webview's types are associated with a `ContentId` key in `AllWebviewDefinitions`, spefified in `webviewTypes.ts`. -------------------------------------------------------------------------------- /src/webview-contract/initialState.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Serializes and encodes the initial state so that it's safe to be placed in an HTML attribute. 3 | * If no state is defined this will return the empty string. 4 | */ 5 | export function encodeState(initialState?: T): string { 6 | const initialStateJson = initialState ? JSON.stringify(initialState) : ""; 7 | return initialStateJson.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); 8 | } 9 | 10 | /** 11 | * Decodes and deserializes initial state from a value in an HTML attribute. If no value is supplied 12 | * this will return an empty object. 13 | */ 14 | export function decodeState(encodedState?: string): T { 15 | const initialStateJson = (encodedState || "{}") 16 | .replace(/"/g, '"') 17 | .replace(/>/g, ">") 18 | .replace(/</g, "<") 19 | .replace(/&/g, "&"); 20 | 21 | return JSON.parse(initialStateJson); 22 | } 23 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/clusterProperties.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | clusterName: string; 5 | } 6 | 7 | export type ClusterInfo = { 8 | provisioningState: string; 9 | fqdn: string; 10 | kubernetesVersion: string; 11 | powerStateCode: string; 12 | agentPoolProfiles: AgentPoolProfileInfo[]; 13 | supportedVersions: KubernetesVersionInfo[]; 14 | availableUpgradeVersions: string[]; 15 | }; 16 | 17 | export type AgentPoolProfileInfo = { 18 | name: string; 19 | nodeImageVersion: string; 20 | powerStateCode: string; 21 | osDiskSizeGB: number; 22 | provisioningState: string; 23 | vmSize: string; 24 | count: number; 25 | osType: string; 26 | }; 27 | 28 | export type KubernetesVersionInfo = { 29 | version: string; 30 | patchVersions: string[]; 31 | supportPlan: string[]; 32 | isPreview: boolean; 33 | }; 34 | 35 | export type ToVsCodeMsgDef = { 36 | getPropertiesRequest: void; 37 | stopClusterRequest: void; 38 | startClusterRequest: void; 39 | abortAgentPoolOperation: string; 40 | abortClusterOperation: void; 41 | reconcileClusterRequest: void; 42 | refreshRequest: void; 43 | upgradeClusterVersionRequest: string; 44 | detectorCRUDRequest: void; 45 | }; 46 | 47 | export type ToWebViewMsgDef = { 48 | getPropertiesResponse: ClusterInfo; 49 | errorNotification: string; 50 | upgradeClusterVersionResponse: boolean; 51 | }; 52 | 53 | export type ClusterPropertiesDefinition = WebviewDefinition; 54 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/createCluster.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | subscriptionId: string; 5 | subscriptionName: string; 6 | } 7 | 8 | export interface ResourceGroup { 9 | name: string; 10 | location: string; 11 | } 12 | 13 | export enum ProgressEventType { 14 | InProgress, 15 | Cancelled, 16 | Failed, 17 | Success, 18 | } 19 | 20 | export type CreatedCluster = { 21 | portalUrl: string; 22 | }; 23 | 24 | export interface CreateClusterParams { 25 | isNewResourceGroup: boolean; 26 | resourceGroupName: string; 27 | location: string; 28 | name: string; 29 | preset: PresetType; 30 | } 31 | 32 | export enum PresetType { 33 | Dev, 34 | Automatic, 35 | } 36 | 37 | export type ToVsCodeMsgDef = { 38 | getLocationsRequest: void; 39 | getResourceGroupsRequest: void; 40 | createClusterRequest: CreateClusterParams; 41 | }; 42 | 43 | export type ToWebViewMsgDef = { 44 | getLocationsResponse: { 45 | locations: string[]; 46 | }; 47 | getResourceGroupsResponse: { 48 | groups: ResourceGroup[]; 49 | }; 50 | progressUpdate: { 51 | operationDescription: string; 52 | event: ProgressEventType; 53 | errorMessage: string | null; 54 | deploymentPortalUrl: string | null; 55 | createdCluster: CreatedCluster | null; 56 | }; 57 | }; 58 | 59 | export type CreateClusterDefinition = WebviewDefinition; 60 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/createFleet.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | subscriptionId: string; 5 | subscriptionName: string; 6 | } 7 | 8 | export interface ResourceGroup { 9 | name: string; 10 | location: string; 11 | } 12 | 13 | export enum ProgressEventType { 14 | InProgress, 15 | Cancelled, 16 | Failed, 17 | Success, 18 | } 19 | 20 | export type CreatedFleet = { 21 | portalUrl: string; 22 | }; 23 | 24 | export interface CreateFleetParams { 25 | resourceGroupName: string; 26 | location: string; 27 | name: string; 28 | hubMode: HubMode; 29 | dnsPrefix: string | null; // required, if hubMode is "With" 30 | } 31 | 32 | // Fleet resource can be created with or without a hub cluster. 33 | // A hub cluster is a managed cluster that acts as a hub to store and propagate Kubernetes resources. 34 | // More Info: https://learn.microsoft.com/en-us/azure/kubernetes-fleet/concepts-choosing-fleet 35 | export enum HubMode { 36 | Without, 37 | With, 38 | } 39 | 40 | export type ToVsCodeMsgDef = { 41 | getLocationsRequest: void; 42 | getResourceGroupsRequest: void; 43 | createFleetRequest: CreateFleetParams; 44 | }; 45 | 46 | export type ToWebViewMsgDef = { 47 | getLocationsResponse: { 48 | locations: string[]; 49 | }; 50 | getResourceGroupsResponse: { 51 | groups: ResourceGroup[]; 52 | }; 53 | progressUpdate: { 54 | operationDescription: string; 55 | event: ProgressEventType; 56 | errorMessage: string | null; 57 | deploymentPortalUrl: string | null; 58 | createdFleet: CreatedFleet | null; 59 | }; 60 | }; 61 | 62 | export type CreateFleetDefinition = WebviewDefinition; 63 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/draft/draftDockerfile.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../../webviewTypes"; 2 | import { OpenFileOptions } from "../shared/fileSystemTypes"; 3 | import { WorkspaceFolderConfig } from "../shared/workspaceTypes"; 4 | import { LanguageInfo, LanguageVersionInfo } from "./types"; 5 | 6 | export interface InitialState { 7 | workspaceConfig: WorkspaceFolderConfig; 8 | location: string; 9 | supportedLanguages: LanguageInfo[]; 10 | existingFiles: ExistingFiles; 11 | } 12 | 13 | export type ExistingFiles = string[]; 14 | 15 | export type CreateParams = { 16 | language: string; 17 | builderImageTag: string | null; 18 | runtimeImageTag: string; 19 | port: number; 20 | location: string; 21 | }; 22 | 23 | export type ToVsCodeMsgDef = { 24 | pickLocationRequest: OpenFileOptions; 25 | getLanguageVersionInfoRequest: { 26 | language: string; 27 | version: string; 28 | }; 29 | createDockerfileRequest: CreateParams; 30 | openFileRequest: string; 31 | launchDraftDeployment: { 32 | initialTargetPort: number | null; 33 | initialLocation: string; 34 | }; 35 | launchDraftWorkflow: { 36 | initialDockerfileLocation: string; 37 | }; 38 | }; 39 | 40 | export type ToWebViewMsgDef = { 41 | pickLocationResponse: { 42 | location: string; 43 | existingFiles: ExistingFiles; 44 | }; 45 | getLanguageVersionInfoResponse: { 46 | language: string; 47 | versionInfo: LanguageVersionInfo; 48 | }; 49 | createDockerfileResponse: ExistingFiles; 50 | }; 51 | 52 | export type DraftDockerfileDefinition = WebviewDefinition; 53 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/draft/draftValidate.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../../webviewTypes"; 2 | 3 | export interface InitialState { 4 | validationResults: string; 5 | } 6 | 7 | export type ExistingFiles = string[]; 8 | 9 | export type ToVsCodeMsgDef = { 10 | createDraftValidateRequest: string; 11 | }; 12 | 13 | export type ToWebViewMsgDef = { 14 | validationResult: { result: string }; 15 | }; 16 | 17 | export type DraftValidateDefinition = WebviewDefinition; 18 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/fleetProperties.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | import { HubMode } from "./createFleet"; 3 | 4 | export interface InitialState { 5 | fleetName: string; 6 | } 7 | 8 | export type FleetInfo = { 9 | resourceGroup: string; 10 | provisioningState: string; 11 | location: string; 12 | hubClusterMode: HubMode; 13 | fqdn: undefined | string; 14 | }; 15 | 16 | export type ToVsCodeMsgDef = { 17 | getPropertiesRequest: void; 18 | refreshRequest: void; 19 | }; 20 | 21 | export type ToWebViewMsgDef = { 22 | getPropertiesResponse: FleetInfo; 23 | errorNotification: string; 24 | }; 25 | 26 | export type FleetProperties = WebviewDefinition; 27 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/kaito.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | clusterName: string; 5 | subscriptionId: string; 6 | resourceGroupName: string; 7 | } 8 | 9 | export interface Workspace { 10 | name: string; 11 | instanceType: string; 12 | } 13 | 14 | export interface WorkspaceCRD { 15 | workspace: string; // workspace CRD yaml 16 | } 17 | 18 | export type ToVsCodeMsgDef = { 19 | installKaitoRequest: void; // from webview when install kaito button is clicked 20 | generateWorkspaceRequest: void; // from webview when generate workspace button is clicked 21 | }; 22 | 23 | export enum ProgressEventType { 24 | NotStarted, 25 | InProgress, 26 | Cancelled, 27 | Failed, 28 | Success, 29 | } 30 | 31 | export type ToWebViewMsgDef = { 32 | kaitoInstallProgressUpdate: { 33 | operationDescription: string; 34 | event: ProgressEventType; 35 | errorMessage: string | undefined; 36 | }; // to webview during kaito installation 37 | getWorkspaceResponse: { 38 | workspace: WorkspaceCRD; 39 | }; // to webview after workspace CRD is generated 40 | }; 41 | 42 | export type ModelDetails = { 43 | family: string; 44 | modelName: string; 45 | minimumGpu: number; 46 | kaitoVersion: string; 47 | modelSource: string; 48 | }; 49 | 50 | export type KaitoDefinition = WebviewDefinition; 51 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/kaitoManage.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | clusterName: string; 5 | models: ModelState[]; 6 | } 7 | 8 | export type ModelState = { 9 | name: string; 10 | instance: string; 11 | resourceReady: boolean | null; 12 | inferenceReady: boolean | null; 13 | workspaceReady: boolean | null; 14 | age: number; 15 | namespace: string; 16 | }; 17 | 18 | export type ToVsCodeMsgDef = { 19 | monitorUpdateRequest: {}; 20 | deleteWorkspaceRequest: { model: string; namespace: string }; 21 | redeployWorkspaceRequest: { modelName: string; modelYaml: string | undefined; namespace: string }; 22 | getLogsRequest: {}; 23 | testWorkspaceRequest: { modelName: string; namespace: string }; 24 | }; 25 | 26 | export type ToWebViewMsgDef = { 27 | monitorUpdate: { 28 | clusterName: string; 29 | models: ModelState[]; 30 | }; 31 | }; 32 | 33 | export type KaitoManageDefinition = WebviewDefinition; 34 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/kaitoModels.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | clusterName: string; 5 | modelName: string; 6 | workspaceExists: boolean; 7 | resourceReady: boolean | null; 8 | inferenceReady: boolean | null; 9 | workspaceReady: boolean | null; 10 | age: number; 11 | } 12 | 13 | export type ToVsCodeMsgDef = { 14 | generateCRDRequest: { model: string | undefined }; 15 | deployKaitoRequest: { model: string; yaml: string | undefined; gpu: string }; 16 | resetStateRequest: {}; 17 | cancelRequest: { model: string }; 18 | kaitoManageRedirectRequest: {}; 19 | }; 20 | 21 | export type ToWebViewMsgDef = { 22 | deploymentProgressUpdate: { 23 | clusterName: string; 24 | modelName: string; 25 | workspaceExists: boolean; 26 | resourceReady: boolean | null; 27 | inferenceReady: boolean | null; 28 | workspaceReady: boolean | null; 29 | age: number; 30 | }; 31 | }; 32 | 33 | export type ModelDetails = { 34 | family: string; 35 | modelName: string; 36 | minimumGpu: number; 37 | kaitoVersion: string; 38 | modelSource: string; 39 | }; 40 | 41 | export type KaitoModelsDefinition = WebviewDefinition; 42 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/kaitoTest.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | clusterName: string; 5 | modelName: string; 6 | output: string; 7 | } 8 | 9 | export type ToVsCodeMsgDef = { 10 | queryRequest: { 11 | prompt: string; 12 | temperature: number; 13 | topP: number; 14 | topK: number; 15 | repetitionPenalty: number; 16 | maxLength: number; 17 | }; 18 | }; 19 | 20 | export type ToWebViewMsgDef = { 21 | testUpdate: { 22 | clusterName: string; 23 | modelName: string; 24 | output: string; 25 | }; 26 | }; 27 | 28 | export type KaitoTestDefinition = WebviewDefinition; 29 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/periscope.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface NodeUploadStatus { 4 | nodeName: string; 5 | isUploaded: boolean; 6 | } 7 | 8 | export interface PodLogs { 9 | podName: string; 10 | logs: string; 11 | } 12 | 13 | export type DeploymentState = "error" | "noDiagnosticsConfigured" | "success"; 14 | 15 | export interface KustomizeConfig { 16 | repoOrg: string; 17 | containerRegistry: string; 18 | imageVersion: string; 19 | releaseTag: string; 20 | } 21 | 22 | export interface InitialState { 23 | clusterName: string; 24 | runId: string; 25 | state: DeploymentState; 26 | message: string; 27 | nodes: string[]; 28 | kustomizeConfig: KustomizeConfig | null; 29 | blobContainerUrl: string; 30 | shareableSas: string; 31 | } 32 | 33 | export type ToVsCodeMsgDef = { 34 | uploadStatusRequest: void; 35 | nodeLogsRequest: { 36 | nodeName: string; 37 | }; 38 | }; 39 | 40 | export type ToWebViewMsgDef = { 41 | uploadStatusResponse: { 42 | uploadStatuses: NodeUploadStatus[]; 43 | }; 44 | nodeLogsResponse: { 45 | nodeName: string; 46 | logs: PodLogs[]; 47 | }; 48 | }; 49 | 50 | export type PeriscopeDefinition = WebviewDefinition; 51 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/retinaCapture.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | selectedNode: string; 5 | clusterName: string; 6 | retinaOutput: string[]; 7 | allNodes: string[]; 8 | captureFolderName: string; 9 | isNodeExplorerPodExists: boolean; 10 | isDownloadRetinaCapture: boolean; 11 | } 12 | 13 | export type ToVsCodeMsgDef = { 14 | deleteRetinaNodeExplorer: string; 15 | handleCaptureFileDownload: string; 16 | }; 17 | 18 | export type ToWebViewMsgDef = Record; 19 | 20 | export type RetinaCaptureDefinition = WebviewDefinition; 21 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/shared/fileSystemTypes.ts: -------------------------------------------------------------------------------- 1 | export type FileSystemType = "file" | "directory"; 2 | 3 | export type FileFilters = { [name: string]: string[] }; 4 | 5 | export interface SaveFileOptions { 6 | defaultPath?: string; 7 | buttonLabel?: string; 8 | filters?: FileFilters; 9 | title?: string; 10 | } 11 | 12 | export interface OpenFileOptions { 13 | defaultPath?: string; 14 | buttonLabel?: string; 15 | type: FileSystemType; 16 | canSelectMany?: boolean; 17 | filters?: FileFilters; 18 | title?: string; 19 | } 20 | 21 | export type SaveFileResult = { 22 | path: string; 23 | exists: boolean; 24 | }; 25 | 26 | export type OpenFileResult = { 27 | paths: [string, ...string[]]; 28 | }; 29 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/shared/workspaceTypes.ts: -------------------------------------------------------------------------------- 1 | export type WorkspaceFolderConfig = { 2 | name: string; 3 | fullPath: string; 4 | pathSeparator: string; 5 | }; 6 | -------------------------------------------------------------------------------- /src/webview-contract/webviewDefinitions/testStyleViewer.ts: -------------------------------------------------------------------------------- 1 | import { WebviewDefinition } from "../webviewTypes"; 2 | 3 | export interface InitialState { 4 | isVSCode: boolean; 5 | } 6 | 7 | export interface CssRule { 8 | selector: string; 9 | text: string; 10 | } 11 | 12 | export type ToVsCodeMsgDef = { 13 | reportCssVars: { 14 | cssVars: string[]; 15 | }; 16 | reportCssRules: { 17 | rules: CssRule[]; 18 | }; 19 | }; 20 | 21 | export type ToWebViewMsgDef = Record; 22 | 23 | export type TestStyleViewerDefinition = WebviewDefinition; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES2022", 5 | "outDir": "out", 6 | "lib": ["ES2022"], 7 | "sourceMap": true, 8 | "rootDir": ".", /* This is not the `src` directory because some TS files import `package.json` */ 9 | "strict": true, /* enable all strict type-checking options */ 10 | "noUnusedLocals": true, /* Report errors on unused locals. */ 11 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 12 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 13 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 14 | "resolveJsonModule": true, /* Needed to resolve JSON file imports, e.g. package.json */ 15 | "esModuleInterop": true, /* Needed to resolve JSON file imports, e.g. package.json */ 16 | "skipLibCheck": true, 17 | }, 18 | "include": ["src"] 19 | } -------------------------------------------------------------------------------- /webview-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /webview-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | Webview Test Page 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webview-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webview-ui", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "lint": "eslint -c .eslintrc.cjs ./src", 9 | "lint-fix": "eslint -c .eslintrc.cjs ./src --fix", 10 | "build": "tsc && vite build", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@fortawesome/fontawesome-svg-core": "^6.7.2", 15 | "@fortawesome/free-regular-svg-icons": "^6.7.2", 16 | "@fortawesome/free-solid-svg-icons": "^6.7.2", 17 | "@fortawesome/react-fontawesome": "^0.2.2", 18 | "@vscode/codicons": "^0.0.36", 19 | "react": "^19.0.0", 20 | "react-dom": "^19.1.0", 21 | "react-markdown": "^10.1.0", 22 | "rehype-raw": "^7.0.0" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^19.1.6", 26 | "@types/react-dom": "^19.1.5", 27 | "@types/vscode-webview": "^1.57.5", 28 | "@typescript-eslint/eslint-plugin": "^8.33.1", 29 | "@typescript-eslint/parser": "^8.32.1", 30 | "@vitejs/plugin-react": "^4.5.0", 31 | "eslint": "^9.28.0", 32 | "eslint-plugin-react": "^7.37.5", 33 | "eslint-plugin-react-hooks": "^5.2.0", 34 | "typescript": "^5.8.3", 35 | "vite": "^6.3.5", 36 | "vite-plugin-checker": "^0.9.3", 37 | "vite-plugin-eslint": "^1.8.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /webview-ui/src/AttachAcrToCluster/AttachAcrToCluster.module.css: -------------------------------------------------------------------------------- 1 | .inputContainer { 2 | display: grid; 3 | grid-template-columns: 12rem auto; 4 | column-gap: 1rem; 5 | row-gap: 0.5rem; 6 | } 7 | 8 | .inputContainer p { 9 | margin-top: 0; 10 | margin-bottom: 0.5rem; 11 | } 12 | 13 | .inputContainer .label { 14 | grid-column: 1 / 2; 15 | margin: 0.2rem 0; 16 | } 17 | 18 | .inputContainer .control { 19 | grid-column: 2 / 3; 20 | margin: 0.2rem 0; 21 | width: 25rem; 22 | } 23 | 24 | .inputContainer .controlSupplement { 25 | grid-column: 2 / 3; 26 | margin-top: -0.7rem; /* grid row gap + control border */ 27 | margin-bottom: 0.2rem; 28 | } 29 | 30 | .inputContainer .fullWidth { 31 | grid-column: 1 / 3; 32 | } 33 | 34 | .actionItemList { 35 | display: flex; 36 | flex-direction: column; 37 | align-items: stretch; 38 | gap: 0.2rem; 39 | } 40 | -------------------------------------------------------------------------------- /webview-ui/src/AttachAcrToCluster/state/stateTypes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Acr, 3 | Cluster, 4 | ClusterKey, 5 | Subscription, 6 | } from "../../../../src/webview-contract/webviewDefinitions/attachAcrToCluster"; 7 | import { Lazy } from "../../utilities/lazy"; 8 | 9 | export type AzureReferenceData = { 10 | subscriptions: Lazy; 11 | }; 12 | 13 | export type SubscriptionReferenceData = { 14 | subscription: Subscription; 15 | acrs: Lazy; 16 | clusters: Lazy; 17 | }; 18 | 19 | export type AcrReferenceData = { 20 | acr: Acr; 21 | assignedRoleDefinitions: { 22 | [clusterId: string]: Lazy; 23 | }; 24 | }; 25 | 26 | export type AcrRoleState = { 27 | hasAcrPull: boolean; 28 | }; 29 | 30 | export function createClusterId(clusterKey: ClusterKey): string { 31 | return `${clusterKey.resourceGroup}/${clusterKey.clusterName}`; 32 | } 33 | -------------------------------------------------------------------------------- /webview-ui/src/AttachAcrToCluster/state/update/acrDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { ClusterKey } from "../../../../../src/webview-contract/webviewDefinitions/attachAcrToCluster"; 2 | import { newLoaded, newLoading } from "../../../utilities/lazy"; 3 | import { AcrReferenceData, createClusterId } from "../stateTypes"; 4 | 5 | export function setRoleAssignmentLoading(data: AcrReferenceData, clusterKey: ClusterKey): AcrReferenceData { 6 | return { 7 | ...data, 8 | assignedRoleDefinitions: { 9 | ...data.assignedRoleDefinitions, 10 | [createClusterId(clusterKey)]: newLoading(), 11 | }, 12 | }; 13 | } 14 | 15 | export function updateRoleAssignments( 16 | data: AcrReferenceData, 17 | clusterKey: ClusterKey, 18 | hasAcrPull: boolean, 19 | ): AcrReferenceData { 20 | return { 21 | ...data, 22 | assignedRoleDefinitions: { 23 | ...data.assignedRoleDefinitions, 24 | [createClusterId(clusterKey)]: newLoaded({ hasAcrPull }), 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /webview-ui/src/AutomatedDeployments/AutomatedDeployments.module.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | width: 32rem; 3 | } 4 | 5 | .inputContainer { 6 | display: grid; 7 | grid-template-columns: 14rem auto; 8 | column-gap: 1rem; 9 | row-gap: 0.5rem; 10 | } 11 | 12 | .inputContainer .label { 13 | grid-column: 1 / 2; 14 | margin: 0.2rem 0; 15 | } 16 | 17 | .inputContainer .control { 18 | grid-column: 2 / 3; 19 | margin: 0.2rem 0; 20 | width: 20rem; 21 | } 22 | 23 | .inputContainer .controlSupplement { 24 | grid-column: 2 / 3; 25 | margin-top: -0.7rem; /* grid row gap + control border */ 26 | margin-bottom: 0.2rem; 27 | } 28 | 29 | .inputContainer .fullWidth { 30 | grid-column: 1 / 3; 31 | } 32 | 33 | .inputContainer .validationMessage { 34 | grid-column: 2 / 3; 35 | margin-top: -0.5rem; 36 | margin-bottom: 1rem; 37 | } 38 | 39 | .iconButton { 40 | text-align: left; 41 | width: auto; 42 | color: var(--vscode-textLink-foreground); 43 | } 44 | 45 | .buttonContainer { 46 | margin-top: 1rem; 47 | margin-bottom: 1rem; 48 | display: flex; 49 | flex-direction: row; 50 | column-gap: 0.5rem; 51 | } 52 | 53 | .inlineIcon { 54 | vertical-align: middle; 55 | margin-left: 0.3rem; 56 | margin-right: 0.3rem; 57 | } 58 | 59 | .sideControl { 60 | grid-column: 3 / 4; 61 | } 62 | 63 | .label { 64 | grid-column: 1 / 2; 65 | } 66 | 67 | .midControl { 68 | grid-column: 2 / 3; 69 | } 70 | -------------------------------------------------------------------------------- /webview-ui/src/AzureServiceOperator/Progress.tsx: -------------------------------------------------------------------------------- 1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 2 | import { faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; 3 | import styles from "./AzureServiceOperator.module.css"; 4 | import { ProgressStep } from "./ProgressStep"; 5 | import { InstallStep, InstallStepStatus } from "./helpers/state"; 6 | 7 | export type StepWithDescription = { 8 | step: InstallStep; 9 | description: string; 10 | }; 11 | 12 | export interface ProgressProps { 13 | steps: StepWithDescription[]; 14 | } 15 | 16 | export function Progress(props: ProgressProps) { 17 | const succeeded = props.steps[props.steps.length - 1].step.status === InstallStepStatus.Succeeded; 18 | const failed = props.steps.some((s) => s.step.status === InstallStepStatus.Failed); 19 | const heading = succeeded ? "Successfully Installed ASO" : failed ? "Failed to Install ASO" : "Progress"; 20 | 21 | return ( 22 |
23 |

24 | {succeeded && } 25 | {failed && } 26 | {heading} 27 |

28 | {props.steps.map((s) => ( 29 | 30 | ))} 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /webview-ui/src/AzureServiceOperator/helpers/inputs.ts: -------------------------------------------------------------------------------- 1 | import { InstallSettingsParams } from "../../../../src/webview-contract/webviewDefinitions/azureServiceOperator"; 2 | import { ASOState } from "./state"; 3 | 4 | export function getRequiredInputs(state: ASOState): InstallSettingsParams | null { 5 | const { appId, appSecret, cloudName, selectedSubscription, tenantId } = state; 6 | if (!appId) return null; 7 | if (!appSecret) return null; 8 | if (!cloudName) return null; 9 | if (!selectedSubscription) return null; 10 | if (!tenantId) return null; 11 | return { appId, appSecret, cloudName, subscriptionId: selectedSubscription.id, tenantId }; 12 | } 13 | -------------------------------------------------------------------------------- /webview-ui/src/ClusterProperties/ConfirmationDialog.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./ClusterProperties.module.css"; 3 | 4 | export interface ConfirmationDialogProps { 5 | title: string; 6 | message: React.ReactNode; 7 | confirmLabel: string; 8 | cancelLabel: string; 9 | isOpen: boolean; 10 | onConfirm: () => void; 11 | onCancel: () => void; 12 | } 13 | 14 | export function ConfirmationDialog(props: ConfirmationDialogProps) { 15 | if (!props.isOpen) { 16 | return null; 17 | } 18 | 19 | return ( 20 |
21 |
22 |

{props.title}

23 |
{props.message}
24 |
25 | 28 | 31 |
32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /webview-ui/src/CreateCluster/Success.tsx: -------------------------------------------------------------------------------- 1 | interface SuccessProps { 2 | portalClusterUrl: string; 3 | name: string; 4 | } 5 | 6 | export function Success(props: SuccessProps) { 7 | return ( 8 | <> 9 |

Cluster {props.name} was created successfully

10 |

11 | Click here to view your cluster in the Azure Portal. 12 |

13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/Detector.tsx: -------------------------------------------------------------------------------- 1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/detector"; 2 | import { SingleDetector } from "./SingleDetector"; 3 | import { useStateManagement } from "../utilities/state"; 4 | import { stateUpdater, vscode } from "./state"; 5 | 6 | export function Detector(initialState: InitialState) { 7 | const { state } = useStateManagement(stateUpdater, initialState, vscode); 8 | 9 | return ( 10 | <> 11 |

{state.name}

12 | {state.description && state.description !== "test" &&

{state.description}

} 13 | To perform more checks on your cluster, visit AKS Diagnostics. 14 |
15 | {state.detectors.map((detector) => ( 16 | 17 | ))} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/SingleDetector.tsx: -------------------------------------------------------------------------------- 1 | import { SingleDataset, SingleDetectorARMResponse } from "../../../src/webview-contract/webviewDefinitions/detector"; 2 | import { InsightsRenderer } from "./renderers/InsightsRenderer"; 3 | import { MarkdownRenderer } from "./renderers/MarkdownRenderer"; 4 | import { Status, getOverallStatus } from "./utils"; 5 | import styles from "./Detector.module.css"; 6 | 7 | export function SingleDetector(detector: SingleDetectorARMResponse) { 8 | const status = getOverallStatus(detector); 9 | const panelClassNames = getPanelClassNames(status); 10 | 11 | return ( 12 |
13 |
14 |

{detector.properties.metadata.name}

15 |
16 |
17 | {detector.properties.dataset.map(getRenderer).filter((r) => r !== null)} 18 |
19 |
20 | ); 21 | } 22 | 23 | function getRenderer(dataset: SingleDataset, index: number) { 24 | switch (dataset.renderingProperties.type) { 25 | case 7: 26 | return ; 27 | case 9: 28 | return ; 29 | default: 30 | return null; 31 | } 32 | } 33 | 34 | function getPanelClassNames(status: Status) { 35 | switch (status) { 36 | case Status.Success: 37 | return `${styles.detectorPanel} ${styles.success}`; 38 | case Status.Warning: 39 | return `${styles.detectorPanel} ${styles.warning}`; 40 | default: 41 | return `${styles.detectorPanel} ${styles.error}`; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/renderers/Error.tsx: -------------------------------------------------------------------------------- 1 | export interface ErrorProps { 2 | message: string; 3 | data: unknown; 4 | } 5 | 6 | export function Error(props: ErrorProps) { 7 | return ( 8 | <> 9 |

Rendering Error

10 |

{props.message}

11 |

Data

12 |
{JSON.stringify(props.data, null, 2)}
13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/renderers/MarkdownRenderer.tsx: -------------------------------------------------------------------------------- 1 | import ReactMarkdown from "react-markdown"; 2 | import rehypeRaw from "rehype-raw"; 3 | import { SingleDataset } from "../../../../src/webview-contract/webviewDefinitions/detector"; 4 | import { getMarkdownComponents } from "./common"; 5 | 6 | const markdownComponents = getMarkdownComponents(); 7 | 8 | export function MarkdownRenderer(props: SingleDataset) { 9 | return ( 10 | <> 11 |

{props.renderingProperties.title}

12 | {props.table.rows.map((r, i) => ( 13 | 14 | {String(r[0])} 15 | 16 | ))} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/renderers/README.md: -------------------------------------------------------------------------------- 1 | # Detector Renderer Components 2 | 3 | This folder contains renderers for each `type` of `dataset`. 4 | 5 | The output of a detector is structured like: 6 | 7 | ```json 8 | { 9 | "properties": { 10 | "dataset": [ 11 | { 12 | "renderingProperties": { 13 | "type": 7 14 | }, 15 | "table": { 16 | "columns": [...], 17 | "rows": [...] 18 | } 19 | }, 20 | // ... 21 | ], 22 | // ... 23 | }, 24 | // ... 25 | } 26 | ``` 27 | 28 | I.e. it contains a collection of `dataset`s, each of which contain metadata about how they are rendered. Specifically, they contain a `renderingProperties.type` value, 29 | which informs how the data in the `table` structure should be rendered. 30 | 31 | Some common types are: 32 | - 7 ("insights"): A status value and message, with zero or more name/value pairs of extra data. 33 | - 9 ("markdown"): One or more blobs of markdown. 34 | - 2 ("time series"): Numeric data over time, to be rendered as a chart. 35 | - 10 ("detector"): Used in 'category' detectors - contains no data, just references to other detectors. 36 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/renderers/common.tsx: -------------------------------------------------------------------------------- 1 | import { Components } from "react-markdown"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { 4 | faCheckCircle, 5 | faExclamationCircle, 6 | faExclamationTriangle, 7 | faInfoCircle, 8 | } from "@fortawesome/free-solid-svg-icons"; 9 | import styles from "../Detector.module.css"; 10 | 11 | /** 12 | * Gets a mapping that converts rendered markdown components to custom React elements, 13 | * so that we can apply our own styling or icons to the markdown that comes from the detectors. 14 | */ 15 | export function getMarkdownComponents(): Components { 16 | return { 17 | table: ({ node, ...props }) => , 18 | span: ({ node, ...props }) => { 19 | return ( 20 | <> 21 | {getIcons((node?.properties?.className as string[]) || [])} 22 | 23 | 24 | ); 25 | }, 26 | }; 27 | } 28 | 29 | function getIcons(classNames: string[]) { 30 | return classNames.map(getIcon).filter((i) => i !== null); 31 | } 32 | 33 | function getIcon(className: string) { 34 | switch (className) { 35 | case "fa-exclamation-triangle": 36 | return ; 37 | case "fa-exclamation-circle": 38 | return ; 39 | case "fa-check-circle": 40 | return ; 41 | case "fa-info-circle": 42 | return ; 43 | default: 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /webview-ui/src/Detector/state.ts: -------------------------------------------------------------------------------- 1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/detector"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | import { getWebviewMessageContext } from "../utilities/vscode"; 4 | 5 | export type EventDef = Record; 6 | 7 | export type DetectorState = InitialState; 8 | 9 | export const stateUpdater: WebviewStateUpdater<"detector", EventDef, DetectorState> = { 10 | createState: (initialState) => ({ ...initialState }), 11 | vscodeMessageHandler: {}, 12 | eventHandler: {}, 13 | }; 14 | 15 | export const vscode = getWebviewMessageContext<"detector">({}); 16 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/DraftValidate/DraftValidate.tsx: -------------------------------------------------------------------------------- 1 | import { InitialState } from "../../../../src/webview-contract/webviewDefinitions/draft/draftValidate"; 2 | import { useStateManagement } from "../../utilities/state"; 3 | import { stateUpdater, vscode } from "./state"; 4 | import { useEffect } from "react"; 5 | 6 | export function DraftValidate(initialState: InitialState) { 7 | const { state } = useStateManagement(stateUpdater, initialState, vscode); 8 | 9 | //Request the validation results from the backend once when the component is mounted. 10 | useEffect(() => { 11 | vscode.postCreateDraftValidateRequest(""); 12 | }, []); 13 | 14 | return ( 15 | <> 16 |

Draft Validate

17 |
{state.validationResults}
18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/DraftValidate/state.ts: -------------------------------------------------------------------------------- 1 | import { WebviewStateUpdater } from "../../utilities/state"; 2 | import { getWebviewMessageContext } from "../../utilities/vscode"; 3 | 4 | export type EventDef = { 5 | //Defines the events that can originate from the webview and be sent to the backend (ToVsCodeMsgDef). 6 | draftValidateRequest: string; 7 | }; 8 | 9 | export type DraftValidateState = { 10 | validationResults: string; 11 | }; 12 | 13 | export const stateUpdater: WebviewStateUpdater<"draftValidate", EventDef, DraftValidateState> = { 14 | createState: (initialState) => ({ 15 | validationResults: initialState.validationResults, 16 | }), 17 | vscodeMessageHandler: { 18 | // This handler updates the state when a message from the extension 19 | validationResult: (state, response) => ({ 20 | ...state, 21 | validationResults: response.result, 22 | }), 23 | }, 24 | eventHandler: { 25 | draftValidateRequest: (state) => ({ 26 | ...state, 27 | }), 28 | }, 29 | }; 30 | 31 | export const vscode = getWebviewMessageContext<"draftValidate">({ 32 | createDraftValidateRequest: null, 33 | }); 34 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/index.ts: -------------------------------------------------------------------------------- 1 | import { DraftDockerfile } from "./DraftDockerfile/DraftDockerfile"; 2 | import { DraftDeployment } from "./DraftDeployment/DraftDeployment"; 3 | import { DraftWorkflow } from "./DraftWorkflow/DraftWorkflow"; 4 | import { DraftValidate } from "./DraftValidate/DraftValidate"; 5 | 6 | export { DraftDockerfile, DraftDeployment, DraftWorkflow, DraftValidate }; 7 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/state/stateTypes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AcrKey, 3 | ClusterKey, 4 | GitHubRepo, 5 | RepositoryKey, 6 | Subscription, 7 | } from "../../../../src/webview-contract/webviewDefinitions/draft/types"; 8 | import { Lazy } from "../../utilities/lazy"; 9 | 10 | export type AzureReferenceData = { 11 | subscriptions: Lazy; 12 | }; 13 | 14 | export type SubscriptionReferenceData = { 15 | subscription: Subscription; 16 | acrs: Lazy; 17 | clusters: Lazy; 18 | }; 19 | 20 | export type AcrReferenceData = { 21 | key: AcrKey; 22 | repositories: Lazy; 23 | }; 24 | 25 | export type RepositoryReferenceData = { 26 | key: RepositoryKey; 27 | tags: Lazy; 28 | }; 29 | 30 | export type ClusterReferenceData = { 31 | key: ClusterKey; 32 | namespaces: Lazy; 33 | }; 34 | 35 | export type GitHubReferenceData = { 36 | repositories: GitHubRepositoryReferenceData[]; 37 | }; 38 | 39 | export type GitHubRepositoryReferenceData = { 40 | repository: GitHubRepo; 41 | branches: Lazy; 42 | }; 43 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/state/update/acrRepoDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { newLoaded, newLoading } from "../../../utilities/lazy"; 2 | import { RepositoryReferenceData } from "../stateTypes"; 3 | 4 | export function setTagsLoading(data: RepositoryReferenceData): RepositoryReferenceData { 5 | return { ...data, tags: newLoading() }; 6 | } 7 | 8 | export function updateTags(data: RepositoryReferenceData, tags: string[]): RepositoryReferenceData { 9 | return { 10 | ...data, 11 | tags: newLoaded(tags), 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/state/update/clusterDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { newLoaded, newLoading } from "../../../utilities/lazy"; 2 | import { ClusterReferenceData } from "../stateTypes"; 3 | 4 | export function setNamespacesLoading(data: ClusterReferenceData): ClusterReferenceData { 5 | return { 6 | ...data, 7 | namespaces: newLoading(), 8 | }; 9 | } 10 | 11 | export function updateNamespaces(data: ClusterReferenceData, namespaces: string[]): ClusterReferenceData { 12 | return { 13 | ...data, 14 | namespaces: newLoaded(namespaces), 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/state/update/gitHubReferenceDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { GitHubRepoKey } from "../../../../../src/webview-contract/webviewDefinitions/draft/types"; 2 | import { replaceItem } from "../../../utilities/array"; 3 | import { GitHubReferenceData, GitHubRepositoryReferenceData } from "../stateTypes"; 4 | import * as RepoDataUpdate from "./gitHubRepoDataUpdate"; 5 | 6 | export function setBranchesLoading(data: GitHubReferenceData, repoKey: GitHubRepoKey): GitHubReferenceData { 7 | return updateRepository(data, repoKey, (repoData) => RepoDataUpdate.setBranchesLoading(repoData)); 8 | } 9 | 10 | export function updateBranches( 11 | data: GitHubReferenceData, 12 | repoKey: GitHubRepoKey, 13 | branches: string[], 14 | ): GitHubReferenceData { 15 | return updateRepository(data, repoKey, (repoData) => RepoDataUpdate.updateBranches(repoData, branches)); 16 | } 17 | 18 | function updateRepository( 19 | data: GitHubReferenceData, 20 | repoKey: GitHubRepoKey, 21 | updater: (data: GitHubRepositoryReferenceData) => GitHubRepositoryReferenceData, 22 | ): GitHubReferenceData { 23 | return { 24 | ...data, 25 | repositories: replaceItem( 26 | data.repositories, 27 | (data) => 28 | data.repository.gitHubRepoOwner === repoKey.gitHubRepoOwner && 29 | data.repository.gitHubRepoName === repoKey.gitHubRepoName, 30 | updater, 31 | ), 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /webview-ui/src/Draft/state/update/gitHubRepoDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { newLoaded, newLoading } from "../../../utilities/lazy"; 2 | import { GitHubRepositoryReferenceData } from "../stateTypes"; 3 | 4 | export function setBranchesLoading(data: GitHubRepositoryReferenceData): GitHubRepositoryReferenceData { 5 | return { 6 | ...data, 7 | branches: newLoading(), 8 | }; 9 | } 10 | 11 | export function updateBranches(data: GitHubRepositoryReferenceData, branches: string[]): GitHubRepositoryReferenceData { 12 | return { 13 | ...data, 14 | branches: newLoaded(branches), 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /webview-ui/src/FleetProperties/state.ts: -------------------------------------------------------------------------------- 1 | import { FleetInfo, InitialState } from "../../../src/webview-contract/webviewDefinitions/fleetProperties"; 2 | import { Lazy, newLoaded, newLoading, newNotLoaded } from "../utilities/lazy"; 3 | import { WebviewStateUpdater } from "../utilities/state"; 4 | import { getWebviewMessageContext } from "../utilities/vscode"; 5 | 6 | export type CreateFleetState = InitialState & { 7 | fleetInfo: Lazy; 8 | fleetOperationRequested: boolean; 9 | errorMessage: string | null; 10 | }; 11 | 12 | export type EventDef = { 13 | setPropertiesLoading: void; 14 | setFleetOperationRequested: void; 15 | }; 16 | 17 | export const stateUpdater: WebviewStateUpdater<"fleetProperties", EventDef, CreateFleetState> = { 18 | createState: (initialState) => ({ 19 | ...initialState, 20 | fleetInfo: newNotLoaded(), 21 | fleetOperationRequested: false, 22 | errorMessage: null, 23 | }), 24 | vscodeMessageHandler: { 25 | getPropertiesResponse: (state, fleetInfo) => ({ 26 | ...state, 27 | fleetInfo: newLoaded(fleetInfo), 28 | fleetOperationRequested: false, 29 | }), 30 | errorNotification: (state, err) => ({ ...state, errorMessage: err }), 31 | }, 32 | eventHandler: { 33 | setPropertiesLoading: (state) => ({ ...state, fleetInfo: newLoading() }), 34 | setFleetOperationRequested: (state) => ({ ...state, fleetOperationRequested: true }), 35 | }, 36 | }; 37 | 38 | export const vscode = getWebviewMessageContext<"fleetProperties">({ 39 | getPropertiesRequest: null, 40 | refreshRequest: null, 41 | }); 42 | -------------------------------------------------------------------------------- /webview-ui/src/InspektorGadget/GadgetSelector.tsx: -------------------------------------------------------------------------------- 1 | import { GadgetCategory } from "./helpers/gadgets/types"; 2 | import { configuredGadgetResources } from "./helpers/gadgets"; 3 | import { CustomDropdown } from "../components/CustomDropdown"; 4 | import { CustomDropdownOption } from "../components/CustomDropdownOption"; 5 | import { useState } from "react"; 6 | export interface GadgetSelectorProps { 7 | category: GadgetCategory; 8 | id: string; 9 | className?: string; 10 | required?: boolean; 11 | onResourceChanged: (resource: string | null) => void; 12 | } 13 | 14 | export function GadgetSelector(props: GadgetSelectorProps) { 15 | function handleResourceChange(value: string) { 16 | const resource = value ? value : null; 17 | setSelectedNode(value); 18 | props.onResourceChanged(resource); 19 | } 20 | 21 | const configuredResources = configuredGadgetResources[props.category]; 22 | const [selectedNode, setSelectedNode] = useState(""); 23 | 24 | return ( 25 | 26 | 27 | {Object.keys(configuredResources).map((resource) => ( 28 | 29 | ))} 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /webview-ui/src/InspektorGadget/TraceItemSortSelector.tsx: -------------------------------------------------------------------------------- 1 | import { FormEvent } from "react"; 2 | import { ItemProperty, SortSpecifier, fromSortString, toSortString } from "./helpers/gadgets/types"; 3 | 4 | type ChangeEvent = Event | FormEvent; 5 | 6 | export interface TraceItemSortSelectorProps { 7 | id: string; 8 | className?: string; 9 | required?: boolean; 10 | sortSpecifiers: SortSpecifier[]; 11 | allProperties: ItemProperty[]; 12 | onSortSpecifiersChange: (sortSpecifiers: SortSpecifier[]) => void; 13 | } 14 | 15 | export function TraceItemSortSelector(props: TraceItemSortSelectorProps) { 16 | function handleSortStringChange(e: ChangeEvent) { 17 | const input = e.currentTarget as HTMLInputElement; 18 | const sortSpecifiers = fromSortString(input.value, props.allProperties); 19 | props.onSortSpecifiersChange(sortSpecifiers); 20 | } 21 | 22 | const sortString = toSortString(props.sortSpecifiers); 23 | const allowedIdentifiers = props.allProperties.map((p) => p.identifier).sort(); 24 | const title = `Allowed properties:\n${allowedIdentifiers.join("\n")}`; 25 | return ( 26 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /webview-ui/src/Kaito/kaito-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/webview-ui/src/Kaito/kaito-image.png -------------------------------------------------------------------------------- /webview-ui/src/Kaito/state.ts: -------------------------------------------------------------------------------- 1 | import { InitialState, ProgressEventType } from "../../../src/webview-contract/webviewDefinitions/kaito"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | import { getWebviewMessageContext } from "../utilities/vscode"; 4 | 5 | export type EventDef = Record; 6 | 7 | export type KaitoState = InitialState & { 8 | operationDescription: string; 9 | kaitoInstallStatus: ProgressEventType; 10 | errors: string | undefined; 11 | }; 12 | 13 | export const stateUpdater: WebviewStateUpdater<"kaito", EventDef, KaitoState> = { 14 | createState: (initialState) => ({ 15 | ...initialState, 16 | operationDescription: "", 17 | kaitoInstallStatus: ProgressEventType.NotStarted, 18 | errors: undefined, 19 | }), 20 | vscodeMessageHandler: { 21 | kaitoInstallProgressUpdate: (state, args) => ({ 22 | ...state, 23 | operationDescription: args.operationDescription, 24 | kaitoInstallStatus: args.event, 25 | errors: args.errorMessage, 26 | }), 27 | getWorkspaceResponse: (state, args) => ({ 28 | ...state, 29 | workspace: args.workspace, 30 | }), 31 | }, 32 | eventHandler: {}, 33 | }; 34 | 35 | export const vscode = getWebviewMessageContext<"kaito">({ 36 | installKaitoRequest: null, 37 | generateWorkspaceRequest: null, 38 | }); 39 | -------------------------------------------------------------------------------- /webview-ui/src/KaitoManage/state.ts: -------------------------------------------------------------------------------- 1 | import { WebviewStateUpdater } from "../utilities/state"; 2 | import { getWebviewMessageContext } from "../utilities/vscode"; 3 | import { ModelState } from "../../../src/webview-contract/webviewDefinitions/kaitoManage"; 4 | 5 | export type EventDef = Record; 6 | 7 | export type MonitorState = { 8 | clusterName: string; 9 | models: ModelState[]; 10 | }; 11 | 12 | export const vscode = getWebviewMessageContext<"kaitoManage">({ 13 | monitorUpdateRequest: null, 14 | deleteWorkspaceRequest: null, 15 | redeployWorkspaceRequest: null, 16 | getLogsRequest: null, 17 | testWorkspaceRequest: null, 18 | }); 19 | 20 | export const stateUpdater: WebviewStateUpdater<"kaitoManage", EventDef, MonitorState> = { 21 | createState: (initialState) => ({ 22 | ...initialState, 23 | }), 24 | vscodeMessageHandler: { 25 | monitorUpdate: (state, args) => ({ 26 | ...state, 27 | models: args.models, 28 | }), 29 | }, 30 | eventHandler: {}, 31 | }; 32 | -------------------------------------------------------------------------------- /webview-ui/src/KaitoModels/state.ts: -------------------------------------------------------------------------------- 1 | import { WebviewStateUpdater } from "../utilities/state"; 2 | import { getWebviewMessageContext } from "../utilities/vscode"; 3 | 4 | export type EventDef = Record; 5 | 6 | export type DeploymentState = { 7 | clusterName: string; 8 | modelName: string; 9 | workspaceExists: boolean; 10 | resourceReady: boolean | null; 11 | inferenceReady: boolean | null; 12 | workspaceReady: boolean | null; 13 | age: number; 14 | }; 15 | 16 | export const vscode = getWebviewMessageContext<"kaitoModels">({ 17 | generateCRDRequest: null, 18 | deployKaitoRequest: null, 19 | resetStateRequest: null, 20 | cancelRequest: null, 21 | kaitoManageRedirectRequest: null, 22 | }); 23 | 24 | export const stateUpdater: WebviewStateUpdater<"kaitoModels", EventDef, DeploymentState> = { 25 | createState: (initialState) => ({ 26 | ...initialState, 27 | modelName: "", 28 | workspaceExists: false, 29 | resourceReady: null, 30 | inferenceReady: null, 31 | workspaceReady: null, 32 | age: 0, 33 | }), 34 | vscodeMessageHandler: { 35 | deploymentProgressUpdate: (state, args) => ({ 36 | ...state, 37 | modelName: args.modelName, 38 | workspaceExists: args.workspaceExists, 39 | resourceReady: args.resourceReady, 40 | inferenceReady: args.inferenceReady, 41 | workspaceReady: args.workspaceReady, 42 | age: args.age, 43 | }), 44 | }, 45 | eventHandler: {}, 46 | }; 47 | -------------------------------------------------------------------------------- /webview-ui/src/KaitoTest/state.ts: -------------------------------------------------------------------------------- 1 | import { getWebviewMessageContext } from "../utilities/vscode"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | 4 | export const vscode = getWebviewMessageContext<"kaitoTest">({ 5 | queryRequest: null, 6 | }); 7 | 8 | export type EventDef = Record; 9 | 10 | export type TestState = { 11 | clusterName: string; 12 | modelName: string; 13 | output: string; 14 | }; 15 | 16 | export const stateUpdater: WebviewStateUpdater<"kaitoTest", EventDef, TestState> = { 17 | createState: (initialState) => ({ 18 | ...initialState, 19 | }), 20 | vscodeMessageHandler: { 21 | testUpdate: (state, args) => ({ 22 | ...state, 23 | modelName: args.modelName, 24 | output: args.output, 25 | }), 26 | }, 27 | eventHandler: {}, 28 | }; 29 | -------------------------------------------------------------------------------- /webview-ui/src/Kubectl/CommandInput.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./Kubectl.module.css"; 2 | import { FormEvent } from "react"; 3 | 4 | type ChangeEvent = Event | FormEvent; 5 | 6 | export interface CommandInputProps { 7 | command: string; 8 | matchesExisting: boolean; 9 | onCommandUpdate: (command: string) => void; 10 | onRunCommand: (command: string) => void; 11 | onSaveRequest: () => void; 12 | } 13 | 14 | export function CommandInput(props: CommandInputProps) { 15 | function handleCommandChange(e: ChangeEvent) { 16 | const input = e.currentTarget as HTMLInputElement; 17 | props.onCommandUpdate(input.value); 18 | } 19 | 20 | function onKeyPress(e: React.KeyboardEvent) { 21 | if (e.nativeEvent instanceof KeyboardEvent) { 22 | if (e.code === "Enter") { 23 | props.onRunCommand(props.command); 24 | } 25 | } 26 | } 27 | 28 | const canRun = props.command.trim().length > 0; 29 | return ( 30 |
31 | 34 | 42 |
43 | 46 | {!props.matchesExisting && } 47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /webview-ui/src/Kubectl/CommandOutput.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./Kubectl.module.css"; 2 | import { EventHandlers } from "../utilities/state"; 3 | import { EventDef } from "./helpers/state"; 4 | import { ProgressRing } from "../components/ProgressRing"; 5 | 6 | export interface CommandOutputProps { 7 | isCommandRunning: boolean; 8 | output: string | null; 9 | errorMessage: string | null; 10 | eventHandlers: EventHandlers; 11 | } 12 | 13 | export function CommandOutput(props: CommandOutputProps) { 14 | const hasOutput = props.output !== undefined; 15 | const hasError = props.errorMessage !== undefined; 16 | 17 | return ( 18 | <> 19 | {props.isCommandRunning && } 20 | {hasOutput &&
{props.output}
} 21 | {hasError &&
{props.errorMessage}
} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/ErrorView.tsx: -------------------------------------------------------------------------------- 1 | import { KustomizeConfig } from "../../../src/webview-contract/webviewDefinitions/periscope"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faTimesCircle } from "@fortawesome/free-solid-svg-icons"; 4 | import styles from "./Periscope.module.css"; 5 | 6 | export interface ErrorViewProps { 7 | clusterName: string; 8 | message: string; 9 | config: KustomizeConfig; 10 | } 11 | 12 | export function ErrorView(props: ErrorViewProps) { 13 | return ( 14 | <> 15 | 16 | AKS Periscope failed to run on '{props.clusterName}'. Please see the error below for more details. 17 |
18 |

Periscope settings (from VS Code Configuration)

19 |
20 |
GitHub organisation (containing aks-periscope repo with Kustomize base)
21 |
{props.config.repoOrg}
22 | 23 |
Container registry (containing Periscope image to deploy)
24 |
{props.config.containerRegistry}
25 | 26 |
Image version (tag for {props.config.containerRegistry}/aks/periscope image)
27 |
{props.config.imageVersion}
28 | 29 |
Release tag (for {props.config.repoOrg}/aks-periscope GitHub repo)
30 |
{props.config.releaseTag}
31 |
32 |
33 |
{props.message}
34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/NoDiagnosticSettingView.tsx: -------------------------------------------------------------------------------- 1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 2 | import { faTimesCircle } from "@fortawesome/free-solid-svg-icons"; 3 | import styles from "./Periscope.module.css"; 4 | 5 | export interface NoDiagnosticSettingViewProps { 6 | clusterName: string; 7 | } 8 | 9 | export function NoDiagnosticSettingView(props: NoDiagnosticSettingViewProps) { 10 | return ( 11 | <> 12 |
13 | 14 | We didn’t find any storage account associated with `{props.clusterName}`. Please use the Diagnostics 15 | settings in the Azure Portal to configure a storage account for your cluster and try again. 16 |
17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/NodeActions.tsx: -------------------------------------------------------------------------------- 1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 2 | import { faClone, faDownload } from "@fortawesome/free-solid-svg-icons"; 3 | 4 | export interface NodeActionsProps { 5 | runId: string; 6 | nodeName: string; 7 | containerUrl: string; 8 | shareableSas: string; 9 | isUploaded: boolean; 10 | } 11 | 12 | export function NodeActions(props: NodeActionsProps) { 13 | const shareableLink = `${props.containerUrl}/${props.runId}/${props.nodeName}/${props.nodeName}.zip${props.shareableSas}`; 14 | 15 | function copyShareLink(e: React.MouseEvent) { 16 | e.stopPropagation(); 17 | navigator.clipboard.writeText(shareableLink); 18 | } 19 | 20 | return ( 21 | <> 22 | 26 |   27 | {props.isUploaded && ( 28 | 29 | 30 |  Download Zip 31 | 32 | )} 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/NodeLogs.tsx: -------------------------------------------------------------------------------- 1 | import { PodLogs } from "../../../src/webview-contract/webviewDefinitions/periscope"; 2 | 3 | export interface NodeLogsProps { 4 | node: string; 5 | podLogs: PodLogs[]; 6 | } 7 | 8 | export function NodeLogs(props: NodeLogsProps) { 9 | return ( 10 | <> 11 |

{props.node} Node Logs

12 | {(props.podLogs || []).map((podLog) => ( 13 |
14 |

{podLog.podName}

15 |
{podLog.logs}
16 |
17 | ))} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/Periscope.module.css: -------------------------------------------------------------------------------- 1 | table.nodelist { 2 | border-collapse: collapse; 3 | width: 100%; 4 | } 5 | 6 | table.nodelist th { 7 | height: 50px; 8 | text-align: left; 9 | } 10 | 11 | table.nodelist td { 12 | padding: 5px; 13 | } 14 | 15 | table.nodelist tbody tr.selected { 16 | background-color: var(--vscode-list-activeSelectionBackground); 17 | color: var(--vscode-list-activeSelectionForeground); 18 | } 19 | 20 | table.nodelist tbody tr:hover { 21 | background-color: var(--vscode-list-hoverBackground); 22 | color: var(--vscode-list-hoverForeground); 23 | cursor: pointer; 24 | } 25 | 26 | .actionsContainer { 27 | display: flex; 28 | margin: auto; 29 | align-items: center; 30 | justify-content: left; 31 | } 32 | 33 | .errorIndicator { 34 | color: var(--vscode-testing-iconFailed); 35 | padding-right: 5px; 36 | } 37 | 38 | .successIndicator { 39 | color: var(--vscode-testing-iconPassed); 40 | padding-right: 5px; 41 | } 42 | 43 | dl.settinglist { 44 | display: flex; 45 | flex-flow: row wrap; 46 | } 47 | 48 | dl.settinglist dt { 49 | flex-basis: 50%; 50 | padding: 2px 4px; 51 | font-weight: bold; 52 | } 53 | 54 | dl.settinglist dd { 55 | padding: 2px 4px; 56 | } 57 | -------------------------------------------------------------------------------- /webview-ui/src/Periscope/state.ts: -------------------------------------------------------------------------------- 1 | import { InitialState, NodeUploadStatus, PodLogs } from "../../../src/webview-contract/webviewDefinitions/periscope"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | import { getWebviewMessageContext } from "../utilities/vscode"; 4 | 5 | export type PeriscopeState = InitialState & { 6 | nodeUploadStatuses: NodeUploadStatus[]; 7 | selectedNode: string; 8 | nodePodLogs: PodLogs[] | null; 9 | }; 10 | 11 | export type EventDef = { 12 | setSelectedNode: string; 13 | }; 14 | 15 | export const stateUpdater: WebviewStateUpdater<"periscope", EventDef, PeriscopeState> = { 16 | createState: (initialState) => ({ 17 | ...initialState, 18 | nodeUploadStatuses: initialState.nodes.map((n) => ({ nodeName: n, isUploaded: false })), 19 | selectedNode: "", 20 | nodePodLogs: null, 21 | }), 22 | vscodeMessageHandler: { 23 | nodeLogsResponse: (state, args) => ({ ...state, nodePodLogs: args.logs }), 24 | uploadStatusResponse: (state, args) => ({ ...state, nodeUploadStatuses: args.uploadStatuses }), 25 | }, 26 | eventHandler: { 27 | setSelectedNode: (state, node) => ({ ...state, selectedNode: node, nodePodLogs: null }), 28 | }, 29 | }; 30 | 31 | export const vscode = getWebviewMessageContext<"periscope">({ 32 | nodeLogsRequest: null, 33 | uploadStatusRequest: null, 34 | }); 35 | -------------------------------------------------------------------------------- /webview-ui/src/RetinaCapture/DeleteNodeExplorerDialog.tsx: -------------------------------------------------------------------------------- 1 | import { Dialog } from "../components/Dialog"; 2 | import styles from "./RetinaCapture.module.css"; 3 | 4 | export interface DeleteNodeExplorerDialogProps { 5 | isShown: boolean; 6 | nodes: string[]; 7 | onCancel: () => void; 8 | onAccept: (nodeName: string) => void; 9 | } 10 | 11 | export function DeleteNodeExplorerDialog(props: DeleteNodeExplorerDialogProps) { 12 | function handleYes() { 13 | props.onAccept(props.nodes.join(",")); 14 | } 15 | 16 | function handleNo() { 17 | props.onCancel(); 18 | } 19 | 20 | return ( 21 | props.onCancel()}> 22 |

Delete Node Explorer

23 | 24 |
25 |
26 | Are you sure you want to delete the Node Explorer? Deleting the Node Explorer will introduce delay 27 | for kubectl copy next time. 28 |
29 | 30 |
31 | 32 | 33 |
34 | 35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /webview-ui/src/RetinaCapture/RetinaCapture.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | display: grid; 3 | grid-template-columns: 6rem 50rem; 4 | grid-gap: 1rem; 5 | align-items: center; 6 | } 7 | 8 | .inputContainer { 9 | display: grid; 10 | grid-template-columns: 8rem 20rem 8rem; 11 | grid-gap: 1rem; 12 | } 13 | 14 | .buttonContainer { 15 | margin-top: 1rem; 16 | display: flex; 17 | flex-direction: row; 18 | column-gap: 0.5rem; 19 | } 20 | 21 | .checkboxLabel { 22 | position: relative; 23 | top: -0.25rem; 24 | left: 0.2rem; 25 | } 26 | 27 | .checkboxLabel:hover { 28 | cursor: pointer; 29 | } 30 | 31 | .buttonDiv { 32 | margin-top: 1rem; 33 | display: flex; 34 | } 35 | -------------------------------------------------------------------------------- /webview-ui/src/RetinaCapture/state.ts: -------------------------------------------------------------------------------- 1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/retinaCapture"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | import { getWebviewMessageContext } from "../utilities/vscode"; 4 | 5 | export type EventDef = Record; 6 | 7 | export type RetinaState = InitialState; 8 | 9 | export const stateUpdater: WebviewStateUpdater<"retinaCapture", EventDef, RetinaState> = { 10 | createState: (initialState) => ({ 11 | ...initialState, 12 | }), 13 | vscodeMessageHandler: {}, 14 | eventHandler: {}, 15 | }; 16 | 17 | export const vscode = getWebviewMessageContext<"retinaCapture">({ 18 | deleteRetinaNodeExplorer: undefined, 19 | handleCaptureFileDownload: undefined, 20 | }); 21 | -------------------------------------------------------------------------------- /webview-ui/src/TCPDump/TcpDump.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | display: grid; 3 | grid-template-columns: 6rem 50rem; 4 | grid-gap: 1rem; 5 | align-items: center; 6 | } 7 | 8 | .filterContent { 9 | margin-left: 1rem; 10 | display: grid; 11 | grid-template-columns: 8rem 47rem; 12 | grid-gap: 1rem; 13 | align-items: center; 14 | } 15 | 16 | .content > .label, 17 | .filterContent > .label { 18 | grid-column: 1 / 2; 19 | } 20 | 21 | .content > .control, 22 | .filterContent > .control { 23 | grid-column: 2 / 3; 24 | } 25 | 26 | .content > .numberControl, 27 | .filterContent > .numberControl { 28 | grid-column: 2 / 3; 29 | max-width: 4rem; 30 | } 31 | 32 | .content > .controlDropdown { 33 | grid-column: 2 / 3; 34 | max-width: 30rem; 35 | } 36 | 37 | .filterContent > .controlDropdown { 38 | grid-column: 2 / 3; 39 | max-width: 27rem; 40 | } 41 | 42 | .content > .controlButton, 43 | .filterContent > .controlButton { 44 | grid-column: 2 / 3; 45 | max-width: 8rem; 46 | } 47 | 48 | .content > .fullWidth, 49 | .filterContent > .fullWidth { 50 | grid-column: 1 / 3; 51 | } 52 | 53 | table.capturelist { 54 | border-collapse: collapse; 55 | width: 100%; 56 | } 57 | 58 | table.capturelist th { 59 | height: 50px; 60 | text-align: left; 61 | } 62 | 63 | table.capturelist td { 64 | padding: 5px; 65 | text-align: left; 66 | } 67 | 68 | button span { 69 | margin-right: 0.5rem; 70 | } 71 | 72 | .pcapFilterInput { 73 | width: 27rem; 74 | } 75 | -------------------------------------------------------------------------------- /webview-ui/src/TCPDump/protocols.ts: -------------------------------------------------------------------------------- 1 | export const transportLayerProtocols = ["TCP", "UDP", "ICMP"]; 2 | export type TransportLayerProtocol = (typeof transportLayerProtocols)[number]; 3 | 4 | export const applicationLayerProtocols = ["HTTP", "HTTPS", "DNS"] as const; 5 | export type ApplicationLayerProtocol = (typeof applicationLayerProtocols)[number]; 6 | 7 | export type ProtocolMapping = Record< 8 | ApplicationLayerProtocol, 9 | { 10 | port: number; 11 | protocol: TransportLayerProtocol | null; 12 | } 13 | >; 14 | 15 | export const protocolMapping: ProtocolMapping = { 16 | HTTP: { port: 80, protocol: "TCP" }, 17 | HTTPS: { port: 443, protocol: "TCP" }, 18 | DNS: { port: 53, protocol: null }, // TODO: Do we do like NSGs and use "DNS (TCP)" and "DNS (UDP)"? 19 | }; 20 | -------------------------------------------------------------------------------- /webview-ui/src/TCPDump/state/dataLoading.ts: -------------------------------------------------------------------------------- 1 | import { NodeName } from "../../../../src/webview-contract/webviewDefinitions/tcpDump"; 2 | import { isNotLoaded } from "../../utilities/lazy"; 3 | import { EventHandlers } from "../../utilities/state"; 4 | import { EventDef, NodeReferenceData, NodeState, ReferenceData, vscode } from "../state"; 5 | 6 | export type EventHandlerFunc = (eventHandlers: EventHandlers) => void; 7 | 8 | export function loadAllNodes(referenceData: ReferenceData, updates: EventHandlerFunc[]): void { 9 | if (isNotLoaded(referenceData.nodes)) { 10 | vscode.postGetAllNodes(); 11 | updates.push((e) => e.onSetLoadingNodes()); 12 | } 13 | } 14 | 15 | export function loadFilterPods(nodeReferenceData: NodeReferenceData, updates: EventHandlerFunc[]): void { 16 | if (isNotLoaded(nodeReferenceData.filterPods)) { 17 | vscode.postGetFilterPodsForNode({ node: nodeReferenceData.node }); 18 | updates.push((e) => e.onSetLoadingFilterPods({ node: nodeReferenceData.node })); 19 | } 20 | } 21 | 22 | export function loadCaptureInterfaces(nodeState: NodeState, node: NodeName, updates: EventHandlerFunc[]): void { 23 | if (isNotLoaded(nodeState.captureInterfaces)) { 24 | vscode.postGetInterfaces({ node }); 25 | updates.push((e) => e.onSetLoadingInterfaces({ node })); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webview-ui/src/TCPDump/state/nodeReferenceDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { FilterPod } from "../../../../src/webview-contract/webviewDefinitions/tcpDump"; 2 | import { newLoaded, newLoading } from "../../utilities/lazy"; 3 | import { NodeReferenceData } from "../state"; 4 | 5 | export function setFilterPodsLoading(data: NodeReferenceData): NodeReferenceData { 6 | return { ...data, filterPods: newLoading() }; 7 | } 8 | 9 | export function updateFilterPods(data: NodeReferenceData, pods: FilterPod[]): NodeReferenceData { 10 | return { 11 | ...data, 12 | filterPods: newLoaded(pods), 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /webview-ui/src/TCPDump/state/referenceDataUpdate.ts: -------------------------------------------------------------------------------- 1 | import { FilterPod, NodeName } from "../../../../src/webview-contract/webviewDefinitions/tcpDump"; 2 | import { replaceItem, updateValues } from "../../utilities/array"; 3 | import { map as lazyMap, newLoaded, newLoading, newNotLoaded, orDefault } from "../../utilities/lazy"; 4 | import { ReferenceData, NodeReferenceData } from "../state"; 5 | import * as NodeReferenceDataUpdate from "./nodeReferenceDataUpdate"; 6 | 7 | export function setNodesLoading(data: ReferenceData): ReferenceData { 8 | return { ...data, nodes: newLoading() }; 9 | } 10 | 11 | export function updateNodes(data: ReferenceData, nodes: NodeName[]): ReferenceData { 12 | const existingNodes = orDefault(data.nodes, []); 13 | const updatedNodes = updateValues( 14 | existingNodes, 15 | nodes, 16 | (node, item) => node === item.node, 17 | (node) => ({ 18 | node, 19 | filterPods: newNotLoaded(), 20 | }), 21 | ); 22 | 23 | return { 24 | ...data, 25 | nodes: newLoaded(updatedNodes), 26 | }; 27 | } 28 | 29 | export function setFilterPodsLoading(data: ReferenceData, node: NodeName): ReferenceData { 30 | return updateNode(data, node, (data) => NodeReferenceDataUpdate.setFilterPodsLoading(data)); 31 | } 32 | 33 | export function updateFilterPods(data: ReferenceData, node: NodeName, pods: FilterPod[]): ReferenceData { 34 | return updateNode(data, node, (data) => NodeReferenceDataUpdate.updateFilterPods(data, pods)); 35 | } 36 | 37 | function updateNode( 38 | data: ReferenceData, 39 | node: NodeName, 40 | updater: (data: NodeReferenceData) => NodeReferenceData, 41 | ): ReferenceData { 42 | return { 43 | ...data, 44 | nodes: lazyMap(data.nodes, (nodes) => replaceItem(nodes, (data) => data.node === node, updater)), 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /webview-ui/src/TestStyleViewer/state.ts: -------------------------------------------------------------------------------- 1 | import { CssRule, InitialState } from "../../../src/webview-contract/webviewDefinitions/testStyleViewer"; 2 | import { WebviewStateUpdater } from "../utilities/state"; 3 | import { getWebviewMessageContext } from "../utilities/vscode"; 4 | 5 | export type State = InitialState & { 6 | cssVars: string[]; 7 | cssRules: CssRule[]; 8 | }; 9 | 10 | export type EventDef = { 11 | cssVarsUpdate: string[]; 12 | cssRulesUpdate: CssRule[]; 13 | }; 14 | 15 | export const stateUpdater: WebviewStateUpdater<"style", EventDef, State> = { 16 | createState: (initialState) => ({ 17 | ...initialState, 18 | cssVars: [], 19 | cssRules: [], 20 | }), 21 | vscodeMessageHandler: {}, 22 | eventHandler: { 23 | cssVarsUpdate: (state, cssVars) => ({ ...state, cssVars }), 24 | cssRulesUpdate: (state, cssRules) => ({ ...state, cssRules }), 25 | }, 26 | }; 27 | 28 | export const vscode = getWebviewMessageContext<"style">({ 29 | reportCssRules: null, 30 | reportCssVars: null, 31 | }); 32 | -------------------------------------------------------------------------------- /webview-ui/src/components/CustomDropdownOption.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./CustomDropdown.module.css"; 3 | 4 | export interface CustomDropdownOptionProps extends Omit, "onClick"> { 5 | id?: string; 6 | value: string; 7 | label: string; 8 | className?: string; 9 | onClick?: (value: string) => void; 10 | } 11 | 12 | // eslint-disable-next-line @typescript-eslint/naming-convention 13 | export const CustomDropdownOption: React.FC = ({ id, value, label, className, onClick }) => { 14 | const handleClick = () => { 15 | if (onClick) { 16 | onClick(value); 17 | } 18 | }; 19 | 20 | return ( 21 |
  • 22 | {label} 23 |
  • 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /webview-ui/src/components/Dialog.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | interface DialogProps { 4 | isShown: boolean; 5 | onCancel: () => void; 6 | } 7 | 8 | export function Dialog(props: React.PropsWithChildren) { 9 | const dialogRef = useRef(null); 10 | 11 | useEffect(() => { 12 | document.body.addEventListener("click", handleDocumentClick); 13 | return () => document.removeEventListener("click", handleDocumentClick); 14 | }); 15 | 16 | useEffect(() => { 17 | const elem = dialogRef.current; 18 | elem?.addEventListener("close", handleClose); 19 | return () => elem?.removeEventListener("close", handleClose); 20 | }); 21 | 22 | useEffect(() => { 23 | if (props.isShown && !dialogRef.current!.hasAttribute("open")) { 24 | dialogRef.current!.showModal(); 25 | } else if (!props.isShown && dialogRef.current!.hasAttribute("open")) { 26 | dialogRef.current!.close(); 27 | } 28 | }, [props.isShown, dialogRef]); 29 | 30 | function handleClose() { 31 | if (props.isShown) { 32 | props.onCancel(); 33 | } 34 | } 35 | 36 | function handleDocumentClick(e: MouseEvent) { 37 | if (e.target === dialogRef.current) { 38 | e.preventDefault(); 39 | e.stopPropagation(); 40 | dialogRef.current!.close(); 41 | } 42 | } 43 | 44 | return {props.children}; 45 | } 46 | -------------------------------------------------------------------------------- /webview-ui/src/components/InlineAction.module.css: -------------------------------------------------------------------------------- 1 | .actionItem { 2 | display: grid; 3 | grid-template-columns: auto auto; 4 | justify-content: stretch; 5 | } 6 | 7 | .actionItem .actionDescription { 8 | grid-column: 1 / 2; 9 | } 10 | 11 | .actionItem .actionButtons { 12 | grid-column: 2 / 3; 13 | display: flex; 14 | flex-direction: row; 15 | justify-content: flex-end; 16 | align-items: center; 17 | gap: 0.5rem; 18 | } 19 | 20 | .successIndicator { 21 | color: var(--vscode-testing-iconPassed); 22 | } 23 | 24 | .inlineIcon { 25 | vertical-align: middle; 26 | margin-left: 0.3rem; 27 | margin-right: 0.3rem; 28 | } 29 | -------------------------------------------------------------------------------- /webview-ui/src/components/NodeSelector.tsx: -------------------------------------------------------------------------------- 1 | import { CustomDropdown } from "./CustomDropdown"; 2 | import { CustomDropdownOption } from "./CustomDropdownOption"; 3 | import { useState } from "react"; 4 | 5 | export interface NodeSelectorProps { 6 | nodes: string[]; 7 | id: string; 8 | className?: string; 9 | required?: boolean; 10 | onNodeChanged: (node: string | null) => void; 11 | } 12 | 13 | export function NodeSelector(props: NodeSelectorProps) { 14 | const [selectedNode, setSelectedNode] = useState(""); 15 | 16 | function handleNodeChange(node: string) { 17 | setSelectedNode(node); 18 | props.onNodeChanged(node); 19 | } 20 | 21 | return ( 22 | 23 | 24 | {props.nodes.map((node) => ( 25 | 26 | ))} 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /webview-ui/src/components/ProgressRing.module.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | border: 3px solid var(--vscode-progressBar-background); 3 | border-top: 3px solid transparent; 4 | border-left: 3px solid transparent; 5 | border-radius: 50%; 6 | animation: spin 0.6s linear infinite; 7 | } 8 | 9 | @keyframes spin { 10 | to { 11 | transform: rotate(360deg); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /webview-ui/src/components/ProgressRing.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./ProgressRing.module.css"; 3 | 4 | interface ProgressRingProps { 5 | size?: number; 6 | className?: string; 7 | } 8 | // eslint-disable-next-line @typescript-eslint/naming-convention 9 | export const ProgressRing: React.FC = ({ size = "1rem", className }) => { 10 | return
    ; 11 | }; 12 | -------------------------------------------------------------------------------- /webview-ui/src/components/TextWithDropdown.module.css: -------------------------------------------------------------------------------- 1 | .indicator { 2 | cursor: pointer; 3 | margin-right: -2px; 4 | } 5 | 6 | .listbox { 7 | position: absolute; 8 | width: 100%; 9 | margin: 0; 10 | background: var(--vscode-dropdown-background); 11 | border: 1px solid var(--vscode-focusBorder); 12 | border-radius: 2px; 13 | box-sizing: border-box; 14 | display: inline-flex; 15 | flex-direction: column; 16 | left: 0px; 17 | max-height: 200px; 18 | padding: 0px; 19 | overflow-y: auto; 20 | z-index: 1; 21 | } 22 | 23 | .listbox.hidden { 24 | display: none; 25 | } 26 | 27 | .listboxItem { 28 | flex-shrink: 0; 29 | cursor: pointer; 30 | padding: 2px; 31 | } 32 | 33 | .listboxItem:hover { 34 | background: var(--vscode-list-activeSelectionBackground); 35 | } 36 | 37 | .listboxItem.selected { 38 | background: var(--vscode-list-activeSelectionBackground); 39 | } 40 | 41 | .inputField { 42 | position: relative; 43 | width: 100%; 44 | } 45 | 46 | .selectedValue { 47 | width: 100%; 48 | padding-right: 1.8rem !important; 49 | box-sizing: border-box; 50 | } 51 | .selectedValue:hover { 52 | cursor: pointer; 53 | } 54 | 55 | .dropDownButton { 56 | position: absolute; 57 | right: 8px; 58 | top: 50%; 59 | transform: translateY(-50%); 60 | pointer-events: none; 61 | } 62 | -------------------------------------------------------------------------------- /webview-ui/src/icons/ArrowIcon.tsx: -------------------------------------------------------------------------------- 1 | import { SvgProps } from "./svgProps"; 2 | 3 | export function ArrowIcon(props: SvgProps) { 4 | return ( 5 | 6 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /webview-ui/src/icons/svgProps.ts: -------------------------------------------------------------------------------- 1 | export type SvgProps = { 2 | style?: React.CSSProperties; 3 | className?: string; 4 | }; 5 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/TestScenarioSelector/TestScenarioSelector.module.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | height: 100%; 3 | width: 15rem; 4 | position: fixed; 5 | padding: 1rem 0 0 0; 6 | margin: 0; 7 | top: 0; 8 | left: 0; 9 | overflow-x: hidden; 10 | box-sizing: border-box; 11 | } 12 | 13 | .contentLink { 14 | padding: 0.2rem 1rem; 15 | text-decoration: none; 16 | display: block; 17 | } 18 | 19 | .contentLink:hover { 20 | background-color: var(--vscode-list-hoverBackground); 21 | color: var(--vscode-list-hoverForeground); 22 | } 23 | 24 | .contentLink.selected { 25 | background-color: var(--vscode-list-activeSelectionBackground); 26 | color: var(--vscode-list-activeSelectionForeground); 27 | } 28 | 29 | .main { 30 | margin-left: 15rem; 31 | } 32 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/components/FilePicker.module.css: -------------------------------------------------------------------------------- 1 | ul.nodeList { 2 | list-style-type: none; 3 | padding: 0; 4 | padding-left: 0; 5 | margin: 0; 6 | } 7 | 8 | ul.nodeList ul.nodeList { 9 | padding-left: 1rem; 10 | border-left: 1px solid var(--vscode-menu-border); 11 | } 12 | 13 | ul.nodeList > li { 14 | margin: 0.2rem; 15 | } 16 | 17 | ul.nodeList > li > .item { 18 | padding: 0.2rem; 19 | } 20 | 21 | ul.nodeList > li > .item.selected { 22 | background-color: var(--vscode-list-activeSelectionBackground); 23 | color: var(--vscode-list-activeSelectionForeground); 24 | } 25 | 26 | ul.nodeList > li > .item:hover { 27 | cursor: pointer; 28 | background-color: var(--vscode-list-hoverBackground); 29 | color: var(--vscode-list-hoverForeground); 30 | } 31 | 32 | .expander { 33 | cursor: pointer; 34 | padding-right: 0.3rem; 35 | } 36 | 37 | .inputContainer { 38 | display: grid; 39 | grid-template-columns: auto auto; 40 | grid-auto-rows: min-content; 41 | grid-gap: 1rem; 42 | padding-bottom: 0.5rem; 43 | padding-top: 0.5rem; 44 | } 45 | 46 | .inputContainer .label { 47 | grid-column: 1 / 2; 48 | } 49 | 50 | .inputContainer .control { 51 | grid-column: 2 / 3; 52 | } 53 | 54 | .buttonContainer { 55 | margin-top: 1rem; 56 | display: flex; 57 | flex-direction: row; 58 | column-gap: 0.5rem; 59 | } 60 | 61 | .title { 62 | margin: 0rem 0 0.5rem 0; 63 | } 64 | 65 | .itemSpan { 66 | position: relative; 67 | top: -0.1rem; 68 | } 69 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/draft/index.ts: -------------------------------------------------------------------------------- 1 | import { getDraftDeploymentScenarios } from "./draftDeploymentTests"; 2 | import { getDraftDockerfileScenarios } from "./draftDockerfileTests"; 3 | import { getDraftWorkflowScenarios } from "./draftWorkflowTests"; 4 | 5 | export { getDraftDeploymentScenarios, getDraftDockerfileScenarios, getDraftWorkflowScenarios }; 6 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/draft/testData/gitHubData.ts: -------------------------------------------------------------------------------- 1 | export type GitHubRepoData = { 2 | forkName: string; 3 | ownerName: string; 4 | repoName: string; 5 | isFork: boolean; 6 | defaultBranch: string; 7 | branches: string[]; 8 | }; 9 | 10 | export function getGitHubRepoData(): GitHubRepoData[] { 11 | return [ 12 | { 13 | forkName: "upstream", 14 | ownerName: "Contoso", 15 | repoName: "aks-store-demo", 16 | isFork: false, 17 | defaultBranch: "main", 18 | branches: ["main", "feature1", "feature2"], 19 | }, 20 | { 21 | forkName: "origin", 22 | ownerName: "developer", 23 | repoName: "aks-store-demo", 24 | isFork: true, 25 | defaultBranch: "main", 26 | branches: ["main", "feature1", "feature2"], 27 | }, 28 | { 29 | forkName: "other-remote", 30 | ownerName: "otherdev", 31 | repoName: "aks-store-demo", 32 | isFork: true, 33 | defaultBranch: "main", 34 | branches: ["main", "feature1", "feature2"], 35 | }, 36 | ]; 37 | } 38 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/kaitoTestTests.tsx: -------------------------------------------------------------------------------- 1 | import { MessageHandler, MessageSink } from "../../../src/webview-contract/messaging"; 2 | import { 3 | InitialState, 4 | ToVsCodeMsgDef, 5 | ToWebViewMsgDef, 6 | } from "../../../src/webview-contract/webviewDefinitions/kaitoTest"; 7 | import { KaitoTest } from "../KaitoTest/KaitoTest"; 8 | import { stateUpdater } from "../KaitoTest/state"; 9 | import { Scenario } from "../utilities/manualTest"; 10 | 11 | export function getKaitoTestScenarios() { 12 | const initialState: InitialState = { 13 | clusterName: "ai-service", 14 | modelName: "falcon-7b-instruct", 15 | output: "", 16 | }; 17 | 18 | function getMessageHandler(webview: MessageSink): MessageHandler { 19 | return { 20 | queryRequest: (params) => { 21 | console.log("queryRequest", params); 22 | webview.postTestUpdate({ 23 | clusterName: initialState.clusterName, 24 | modelName: initialState.modelName, 25 | output: "What is the meaning of life?\n\nThe meaning of life is to be happy. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim i", 26 | }); 27 | }, 28 | }; 29 | } 30 | 31 | return [ 32 | Scenario.create( 33 | "kaitoTest", 34 | "Test Page", 35 | () => , 36 | getMessageHandler, 37 | stateUpdater.vscodeMessageHandler, 38 | ), 39 | ]; 40 | } 41 | -------------------------------------------------------------------------------- /webview-ui/src/manualTest/testStyleViewerTests.tsx: -------------------------------------------------------------------------------- 1 | import { MessageHandler } from "../../../src/webview-contract/messaging"; 2 | import { 3 | CssRule, 4 | InitialState, 5 | ToVsCodeMsgDef, 6 | } from "../../../src/webview-contract/webviewDefinitions/testStyleViewer"; 7 | import { Scenario } from "./../utilities/manualTest"; 8 | import { TestStyleViewer } from "./../TestStyleViewer/TestStyleViewer"; 9 | import { stateUpdater } from "../TestStyleViewer/state"; 10 | 11 | export function getTestStyleViewerScenarios() { 12 | const messageHandler: MessageHandler = { 13 | reportCssRules: (args) => handleReportCssRules(args.rules), 14 | reportCssVars: (args) => handleReportCssVars(args.cssVars), 15 | }; 16 | 17 | function handleReportCssVars(cssVars: string[]) { 18 | console.log(cssVars.join("\n")); 19 | } 20 | 21 | function handleReportCssRules(rules: CssRule[]) { 22 | console.log(rules.map((r) => r.text).join("\n")); 23 | } 24 | 25 | const initialState: InitialState = { 26 | isVSCode: false, 27 | }; 28 | 29 | return [ 30 | Scenario.create( 31 | "style", 32 | "", 33 | () => , 34 | () => messageHandler, 35 | stateUpdater.vscodeMessageHandler, 36 | ), 37 | ]; 38 | } 39 | -------------------------------------------------------------------------------- /webview-ui/src/utilities/lazy.ts: -------------------------------------------------------------------------------- 1 | enum LoadingState { 2 | NotLoaded, 3 | Loading, 4 | Loaded, 5 | } 6 | 7 | export type NotLoaded = { 8 | loadingState: LoadingState.NotLoaded; 9 | }; 10 | 11 | export type Loading = { 12 | loadingState: LoadingState.Loading; 13 | }; 14 | 15 | export type Loaded = { 16 | loadingState: LoadingState.Loaded; 17 | value: T; 18 | }; 19 | 20 | export type Lazy = NotLoaded | Loading | Loaded; 21 | 22 | export function isNotLoaded(l: Lazy): l is NotLoaded { 23 | return l.loadingState === LoadingState.NotLoaded; 24 | } 25 | 26 | export function isLoading(l: Lazy): l is Loading { 27 | return l.loadingState === LoadingState.Loading; 28 | } 29 | 30 | export function isLoaded(l: Lazy): l is Loaded { 31 | return l.loadingState === LoadingState.Loaded; 32 | } 33 | 34 | export function newNotLoaded(): NotLoaded { 35 | return { loadingState: LoadingState.NotLoaded }; 36 | } 37 | 38 | export function newLoading(): Loading { 39 | return { loadingState: LoadingState.Loading }; 40 | } 41 | 42 | export function newLoaded(value: T): Loaded { 43 | return { loadingState: LoadingState.Loaded, value }; 44 | } 45 | 46 | export function isLazy(value: T | Lazy): value is Lazy { 47 | return value instanceof Object && "loadingState" in value; 48 | } 49 | 50 | export function asLazy(item: Lazy | T): Lazy { 51 | if (isLazy(item)) { 52 | return item; 53 | } 54 | 55 | return newLoaded(item); 56 | } 57 | 58 | export function orDefault(lazy: Lazy, fallback: T): T { 59 | return isLoaded(lazy) ? lazy.value : fallback; 60 | } 61 | 62 | export function map(l: Lazy, fn: (f: T1) => T2): Lazy { 63 | if (!isLoaded(l)) { 64 | return l; 65 | } 66 | 67 | return newLoaded(fn(l.value)); 68 | } 69 | -------------------------------------------------------------------------------- /webview-ui/src/utilities/manualTest.ts: -------------------------------------------------------------------------------- 1 | import { JSX } from "react"; 2 | import { CommandKeys, MessageHandler, MessageSink } from "../../../src/webview-contract/messaging"; 3 | import { ContentId, ToVsCodeMsgDef, ToWebviewMsgDef } from "../../../src/webview-contract/webviewTypes"; 4 | import { getTestVscodeMessageContext } from "./vscode"; 5 | 6 | /** 7 | * Represents scenarios for manual testing webviews in a browser. 8 | * 9 | * The same Webview can be set up with different initial data or message handlers. 10 | */ 11 | export class Scenario { 12 | private constructor( 13 | readonly name: string, 14 | readonly factory: () => JSX.Element, 15 | ) {} 16 | 17 | static create( 18 | contentId: T, 19 | description: string, 20 | factory: () => JSX.Element, 21 | getHandler: (webview: MessageSink>) => MessageHandler>, 22 | cmdKeys: CommandKeys>, 23 | ): Scenario { 24 | const name = description ? `${contentId} (${description})` : contentId; 25 | return new Scenario(name, () => { 26 | const context = getTestVscodeMessageContext(cmdKeys); 27 | // Set up the subscription before creating the element 28 | context.subscribeToMessages(getHandler(context)); 29 | return factory(); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /webview-ui/src/utilities/maybe.ts: -------------------------------------------------------------------------------- 1 | export type Just = { 2 | hasValue: true; 3 | value: T; 4 | }; 5 | 6 | export type Nothing = { 7 | hasValue: false; 8 | }; 9 | 10 | export type Maybe = Just | Nothing; 11 | 12 | export function just(value: T): Just { 13 | return { 14 | hasValue: true, 15 | value, 16 | }; 17 | } 18 | 19 | export function nothing(): Nothing { 20 | return { hasValue: false }; 21 | } 22 | 23 | export function hasValue(m: Maybe): m is Just { 24 | return m.hasValue; 25 | } 26 | 27 | export function isNothing(m: Maybe): m is Nothing { 28 | return !m.hasValue; 29 | } 30 | 31 | export function map(m: Maybe, fn: (value: T) => U): Maybe { 32 | return hasValue(m) ? just(fn(m.value)) : nothing(); 33 | } 34 | 35 | export function orDefault(m: Maybe, fallback: T): T { 36 | return hasValue(m) ? m.value : fallback; 37 | } 38 | 39 | export function asNullable(m: Maybe): T | null { 40 | return m.hasValue ? m.value : null; 41 | } 42 | -------------------------------------------------------------------------------- /webview-ui/src/utilities/runtimeTypes.ts: -------------------------------------------------------------------------------- 1 | export function isObject(value: unknown): value is object { 2 | return typeof value === "object" && value !== null && value.constructor.name === "Object"; 3 | } 4 | 5 | export function isArray(value: unknown): value is unknown[] { 6 | return typeof value === "object" && value !== null && value.constructor.name === "Array"; 7 | } 8 | -------------------------------------------------------------------------------- /webview-ui/src/utilities/time.ts: -------------------------------------------------------------------------------- 1 | export async function delay(ms: number): Promise { 2 | return new Promise((resolve) => setTimeout(resolve, ms)); 3 | } 4 | -------------------------------------------------------------------------------- /webview-ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /webview-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ES2022", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /webview-ui/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /webview-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import checker from "vite-plugin-checker"; 4 | import eslintPlugin from "vite-plugin-eslint"; 5 | import { resolve } from "path"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | react(), 11 | eslintPlugin({ 12 | cache: false, 13 | include: ["./src/**/*.ts", "./src/**/*.tsx"], 14 | exclude: [], 15 | }), 16 | // By default, vite doesn't output typescript errors. 17 | // This plugin causes errors to be printed to stdout/stderr, meaning they 18 | // can be picked up by the problem matcher defined in .vscode/tasks.json. 19 | // https://github.com/vitejs/vite/issues/4393#issuecomment-890996317 20 | checker({ typescript: true }), 21 | ], 22 | base: "./", 23 | server: { 24 | port: 3000, 25 | }, 26 | build: { 27 | rollupOptions: { 28 | input: { 29 | main: resolve(__dirname, "src/main.tsx"), 30 | }, 31 | output: { 32 | entryFileNames: `assets/[name].js`, 33 | chunkFileNames: `assets/[name].js`, 34 | assetFileNames: `assets/[name].[ext]`, 35 | }, 36 | }, 37 | }, 38 | }); 39 | --------------------------------------------------------------------------------