mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-26 11:01:37 -05:00
Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a45ba7cf2 | ||
|
|
3b23809846 | ||
|
|
0ff54a11df | ||
|
|
ce547fa4b6 | ||
|
|
d47b5f1afb | ||
|
|
66ef175501 | ||
|
|
f92db4ae76 | ||
|
|
c520c009a5 | ||
|
|
fd148e557b | ||
|
|
b0d3d06b5d | ||
|
|
2fbfb2ad61 | ||
|
|
4b12216c07 | ||
|
|
98dc71a08b | ||
|
|
05f8bb94f3 | ||
|
|
de027df955 | ||
|
|
4a4888891b | ||
|
|
0b2d353cd2 | ||
|
|
2dd4232a40 | ||
|
|
dc0ccba767 | ||
|
|
a4bccb6e6c | ||
|
|
f1b9931116 | ||
|
|
0f229b3444 | ||
|
|
1a97516cb6 | ||
|
|
9ee941eb12 | ||
|
|
512c2d3ddc | ||
|
|
57f5e04590 | ||
|
|
fddfb72e10 | ||
|
|
90ac21ccfb | ||
|
|
310d651df8 | ||
|
|
63c461dca8 | ||
|
|
6c40f52f35 | ||
|
|
013ce71166 | ||
|
|
3a4caa64d2 | ||
|
|
aeabb325f6 | ||
|
|
b8da94f9ef | ||
|
|
e6f356accc | ||
|
|
991b9af198 | ||
|
|
09a4dfa5b0 | ||
|
|
8ed463b4d7 | ||
|
|
3b0cf9db50 | ||
|
|
9f761c44c4 | ||
|
|
c6308b77df | ||
|
|
33baaa475d | ||
|
|
35983659b1 | ||
|
|
2b103a79c3 | ||
|
|
99b5c5ce8c | ||
|
|
84d85ba43e | ||
|
|
7b709b37cd | ||
|
|
3d7edd2d6a | ||
|
|
cf97ced7f1 | ||
|
|
0060b0e27b | ||
|
|
47b249a7b1 | ||
|
|
c4ef48dcd8 | ||
|
|
366fe7c162 | ||
|
|
d189805bcc | ||
|
|
aef4474a08 | ||
|
|
5e34982fd9 | ||
|
|
43158a60e3 | ||
|
|
e3d672cea1 | ||
|
|
0567141bc4 | ||
|
|
6985d95300 | ||
|
|
e2dd257fa9 | ||
|
|
da7585eb44 | ||
|
|
2f8d00af56 | ||
|
|
7cdb21cca5 | ||
|
|
b35e78a07f | ||
|
|
052cb54199 | ||
|
|
42b1a10fec | ||
|
|
d28d77dbfc | ||
|
|
aadf2ae081 | ||
|
|
760cf01022 | ||
|
|
97978cbe81 | ||
|
|
466193adbe | ||
|
|
56ad631478 | ||
|
|
919cc732b7 | ||
|
|
244e27c2de | ||
|
|
0a181a1ba8 | ||
|
|
045dc3e558 | ||
|
|
30393a1f1b | ||
|
|
748bb53173 | ||
|
|
373a519f25 | ||
|
|
3af2b4a13d | ||
|
|
91676afd0d | ||
|
|
914fe8fc29 | ||
|
|
65cc61fdbd | ||
|
|
83af84774a | ||
|
|
b3e9428898 | ||
|
|
2427cbe3c6 | ||
|
|
179678b495 | ||
|
|
a7c1bcaf93 | ||
|
|
3362462142 | ||
|
|
87cc568493 | ||
|
|
8a67f87090 | ||
|
|
47151435e7 | ||
|
|
30dffdf696 | ||
|
|
68a22421f7 | ||
|
|
d6fd64c5eb | ||
|
|
245ae5b9ee | ||
|
|
788c84a1ee | ||
|
|
aafe0876bb | ||
|
|
e3c7e06983 | ||
|
|
251d250523 | ||
|
|
f4d0bdc784 | ||
|
|
0bbbb91adf | ||
|
|
39f65b1881 | ||
|
|
0e9d956ee5 | ||
|
|
01671b118d | ||
|
|
ef3d2e7d99 | ||
|
|
1b073c6748 | ||
|
|
daa897936b | ||
|
|
eaf9757565 | ||
|
|
d1d858090c | ||
|
|
c178b6327a | ||
|
|
c32c09e1a7 | ||
|
|
25e237fa35 | ||
|
|
118d03c151 | ||
|
|
27c86e3c45 | ||
|
|
07ad50670e | ||
|
|
8afd420971 | ||
|
|
5ff102d531 | ||
|
|
63a65f5821 | ||
|
|
2d8e0d648a | ||
|
|
7a35d4aeeb | ||
|
|
35207a1e04 | ||
|
|
7c14ec2b6d | ||
|
|
f8da3cc32a | ||
|
|
f01e9e2fc0 | ||
|
|
b2c203eaef | ||
|
|
24349885d3 | ||
|
|
2e9eff7ffc | ||
|
|
6a08af4d9a | ||
|
|
a0f56890b5 | ||
|
|
70fc6bd43d | ||
|
|
c60bcc0d0d | ||
|
|
c3c6d8ee8c | ||
|
|
9a71846e22 | ||
|
|
e4db31b334 | ||
|
|
63dc94009e | ||
|
|
77d397ce18 | ||
|
|
6aeea8f1df | ||
|
|
fdb426cda5 | ||
|
|
f72a252fb0 | ||
|
|
6fcfa93329 | ||
|
|
c4ce3bef8d | ||
|
|
9fda448303 | ||
|
|
01aefe7b9f | ||
|
|
cb8c4b80d0 | ||
|
|
668ab43865 | ||
|
|
be0edf9606 | ||
|
|
151522013f | ||
|
|
66c62fcce3 | ||
|
|
699648ff6d | ||
|
|
7968d51172 | ||
|
|
2e8d9c50d4 | ||
|
|
880cfc3b59 | ||
|
|
c2d45fa01f | ||
|
|
c0dd781d77 | ||
|
|
df5ed2c889 | ||
|
|
107023c7d0 | ||
|
|
ce4fa98691 | ||
|
|
b2a9074a25 | ||
|
|
c082b572d5 | ||
|
|
0509f8f0c3 | ||
|
|
88d28b7d51 | ||
|
|
df177ec779 | ||
|
|
64d432c8e2 | ||
|
|
380457122c | ||
|
|
7d6d8dbe96 | ||
|
|
f2dcfacc8c | ||
|
|
fd954ddcb2 | ||
|
|
c5c7ca019d | ||
|
|
6d4608dd8b | ||
|
|
f390c4cbc2 | ||
|
|
d031211693 | ||
|
|
c1f4c50177 | ||
|
|
87633faaa4 | ||
|
|
dd4e87ed41 | ||
|
|
b472539646 | ||
|
|
b2a2a48ed6 | ||
|
|
a3a06b92e8 | ||
|
|
3b0fff63d4 | ||
|
|
2bc6a881bd | ||
|
|
efa82650f8 | ||
|
|
d1892b514f | ||
|
|
32da4219a9 | ||
|
|
fb16924f93 | ||
|
|
94b99c7862 | ||
|
|
78d905a217 | ||
|
|
be3d966cf0 | ||
|
|
ba6359e1ff | ||
|
|
43cf19e316 | ||
|
|
1eb03404ad | ||
|
|
a322c5be9d | ||
|
|
d663ec6129 | ||
|
|
88fd0cae3b | ||
|
|
3e2bf7b9fa | ||
|
|
3ce6f9e78c | ||
|
|
efaf39f96a | ||
|
|
7c82d0291e | ||
|
|
86c2547b22 | ||
|
|
4a51cb2020 | ||
|
|
260fdac944 | ||
|
|
85082dee75 | ||
|
|
a9b338b5c4 | ||
|
|
f92aa1ead8 | ||
|
|
d4e367e4f9 | ||
|
|
57df7e706f | ||
|
|
b58c19684f | ||
|
|
d0c7028d97 | ||
|
|
dc471faa7a | ||
|
|
4aaa7eae29 | ||
|
|
1000e97091 | ||
|
|
9b31e7beac | ||
|
|
f0b158edda | ||
|
|
9f29efba85 | ||
|
|
de0719d91a | ||
|
|
b9041b0afe | ||
|
|
adb0ec3cab | ||
|
|
3eefc70cbe | ||
|
|
a7c6a98ad9 | ||
|
|
87f5f4edfc | ||
|
|
08d6b71929 | ||
|
|
aba576dd2f | ||
|
|
2c2e2bb984 | ||
|
|
07567d2514 | ||
|
|
9cbcf9e2c6 | ||
|
|
e0cad0231d | ||
|
|
379c60dd27 | ||
|
|
eaba5679d4 | ||
|
|
fdaf29ccb4 | ||
|
|
fa21781df2 | ||
|
|
f978331e7b | ||
|
|
8aedeab9d4 | ||
|
|
669987ccf3 | ||
|
|
2984244377 | ||
|
|
fbb17c047c | ||
|
|
36e1aeb43d | ||
|
|
62d86163c9 | ||
|
|
5171c37b6a | ||
|
|
05f1f84872 | ||
|
|
3d8fb454c0 | ||
|
|
430e2c9916 | ||
|
|
e026b743ab | ||
|
|
c170fdfc68 | ||
|
|
d0ecb292f7 | ||
|
|
7d3fa81d3a | ||
|
|
c1e365bdc8 | ||
|
|
c6f8f6ffd0 | ||
|
|
43e9f6da8c | ||
|
|
5fb7f9e452 | ||
|
|
64ea6c569a | ||
|
|
efdf38dbc4 | ||
|
|
1fe6939f92 | ||
|
|
8332fc6b1d | ||
|
|
7669ad0180 | ||
|
|
e6fd98caa8 | ||
|
|
814f5bea3a | ||
|
|
2e81300f9e | ||
|
|
eed495ef64 | ||
|
|
105636dda6 | ||
|
|
f4ef8d1374 | ||
|
|
01a594ea83 | ||
|
|
f0f6aee89b | ||
|
|
36286f7df6 | ||
|
|
ed5a2ee90d | ||
|
|
0483c5c8b2 | ||
|
|
7f614b0fde | ||
|
|
7c5de73fde | ||
|
|
e499fd9e80 | ||
|
|
a8e4d01ff0 | ||
|
|
ba0261eb98 | ||
|
|
dbf15745d0 | ||
|
|
6e4c7a1075 | ||
|
|
c85ad7b7d5 | ||
|
|
b705e1ca61 | ||
|
|
bc1bf60a98 | ||
|
|
6837b4b801 | ||
|
|
f127b7d94a | ||
|
|
d1d9795965 | ||
|
|
83300acb38 | ||
|
|
24b45a9baf | ||
|
|
4e67aa86d8 | ||
|
|
0dbde9e9b1 | ||
|
|
712633fadd | ||
|
|
7a419426ad | ||
|
|
f17689319c | ||
|
|
ee0896ea5d | ||
|
|
acea03ea61 | ||
|
|
d88c49702a | ||
|
|
5e29c936d7 | ||
|
|
0474a5ca9e | ||
|
|
d09ba43d15 | ||
|
|
164b100421 | ||
|
|
2d2c568609 | ||
|
|
ff6e377477 | ||
|
|
63e97caa94 | ||
|
|
eed792f3db | ||
|
|
3847271e67 | ||
|
|
f53a06a403 | ||
|
|
78a144b5ca | ||
|
|
3f047ae15a | ||
|
|
6df69f525c | ||
|
|
669623a228 | ||
|
|
96efba004d | ||
|
|
dc0eb133f9 | ||
|
|
ac2198c7d3 | ||
|
|
49519232ba | ||
|
|
8c9cc03c89 | ||
|
|
c4830d9efb | ||
|
|
5923793f0c | ||
|
|
126a0383ce | ||
|
|
c70d5b957e | ||
|
|
245156a66c | ||
|
|
ebb02de1c4 | ||
|
|
f662d480e7 | ||
|
|
49dbce5171 | ||
|
|
b3d8e522f7 | ||
|
|
4c421e0a38 | ||
|
|
4275fe89a7 | ||
|
|
9c3d88b64a | ||
|
|
0bf574c227 | ||
|
|
5059c94adc | ||
|
|
2a74ad4190 | ||
|
|
b45f3e7218 | ||
|
|
0ff8786885 | ||
|
|
1e81b6f054 | ||
|
|
785839baac | ||
|
|
d03fbbc066 | ||
|
|
06da33bb3b | ||
|
|
202036ca47 | ||
|
|
8530bf214e | ||
|
|
8faf115329 | ||
|
|
1bd1c64b08 | ||
|
|
68e0f86120 | ||
|
|
43183c90a1 | ||
|
|
b497063482 | ||
|
|
63d4cc0e80 | ||
|
|
351a55121d | ||
|
|
f8583f53c5 | ||
|
|
39aefa7e29 | ||
|
|
a4ffa64918 | ||
|
|
181cad5b75 | ||
|
|
4fd890e651 | ||
|
|
b5411f0f6f | ||
|
|
5496e9ac33 | ||
|
|
5a428b83ae | ||
|
|
1308878650 | ||
|
|
c85e164836 | ||
|
|
c72a194bc3 | ||
|
|
c131c252cd | ||
|
|
72c0d30517 | ||
|
|
ba87c8e245 | ||
|
|
11f5ddf062 | ||
|
|
8faa0cf0e2 | ||
|
|
f7bf914bcb | ||
|
|
d5f52ba53d | ||
|
|
f059612bd0 | ||
|
|
52c21a7885 | ||
|
|
743fd9f97c | ||
|
|
e7f614ebf7 | ||
|
|
bd1633c04c | ||
|
|
5524a3659c | ||
|
|
2785538afb | ||
|
|
0438e9cd41 | ||
|
|
601a4aaed1 | ||
|
|
a0f46fec65 | ||
|
|
a7311764be | ||
|
|
7fc69cc4d5 | ||
|
|
32a5ec3cd0 | ||
|
|
b4ab73a636 | ||
|
|
625d4bc4bf | ||
|
|
45d664fea2 | ||
|
|
cf05dc0016 | ||
|
|
5c17529e40 | ||
|
|
f5e38482c3 | ||
|
|
e3dc417df4 | ||
|
|
affe3a838b | ||
|
|
9fc2cff654 | ||
|
|
32ba55b7ed | ||
|
|
8b383294f7 | ||
|
|
2b8ae507aa | ||
|
|
f7fc145b0c | ||
|
|
6baf2ee982 | ||
|
|
ba20cdb885 | ||
|
|
d8433ddbf4 | ||
|
|
1c1da18f45 |
@@ -12,6 +12,8 @@
|
|||||||
**/vscode-api-tests/testWorkspace2/**
|
**/vscode-api-tests/testWorkspace2/**
|
||||||
**/extensions/**/out/**
|
**/extensions/**/out/**
|
||||||
**/extensions/**/build/**
|
**/extensions/**/build/**
|
||||||
|
**/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts
|
||||||
|
**/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts
|
||||||
**/extensions/markdown-language-features/media/**
|
**/extensions/markdown-language-features/media/**
|
||||||
**/extensions/markdown-language-features/notebook-out/**
|
**/extensions/markdown-language-features/notebook-out/**
|
||||||
**/extensions/typescript-basics/test/colorize-fixtures/**
|
**/extensions/typescript-basics/test/colorize-fixtures/**
|
||||||
|
|||||||
@@ -581,7 +581,9 @@
|
|||||||
"iconv-lite-umd",
|
"iconv-lite-umd",
|
||||||
"jschardet",
|
"jschardet",
|
||||||
"@angular/*",
|
"@angular/*",
|
||||||
"rxjs/**"
|
"rxjs/**",
|
||||||
|
"sanitize-html",
|
||||||
|
"ansi_up"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -738,12 +740,12 @@
|
|||||||
"rxjs/**",
|
"rxjs/**",
|
||||||
"ng2-charts",
|
"ng2-charts",
|
||||||
"chart.js",
|
"chart.js",
|
||||||
"plotly.js-dist-min",
|
"plotly.js",
|
||||||
"angular2-grid",
|
"angular2-grid",
|
||||||
|
"html-to-image",
|
||||||
"html-query-plan",
|
"html-query-plan",
|
||||||
"turndown",
|
"turndown",
|
||||||
"gridstack",
|
"gridstack",
|
||||||
"gridstack/**",
|
|
||||||
"mark.js",
|
"mark.js",
|
||||||
"vscode-textmate",
|
"vscode-textmate",
|
||||||
"vscode-oniguruma",
|
"vscode-oniguruma",
|
||||||
|
|||||||
9
.github/CODEOWNERS
vendored
9
.github/CODEOWNERS
vendored
@@ -3,10 +3,12 @@
|
|||||||
# Syntax can be found here: https://docs.github.com/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
|
# Syntax can be found here: https://docs.github.com/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
|
||||||
|
|
||||||
/extensions/admin-tool-ext-win @Charles-Gagnon
|
/extensions/admin-tool-ext-win @Charles-Gagnon
|
||||||
/extensions/arc/ @Charles-Gagnon
|
/extensions/arc/ @Charles-Gagnon @swells @candiceye
|
||||||
/extensions/azdata/ @Charles-Gagnon
|
/extensions/azcli/ @Charles-Gagnon @swells @candiceye
|
||||||
|
/extensions/azdata/ @Charles-Gagnon @swells @candiceye
|
||||||
/extensions/big-data-cluster/ @Charles-Gagnon
|
/extensions/big-data-cluster/ @Charles-Gagnon
|
||||||
/extensions/dacpac/ @kisantia
|
/extensions/dacpac/ @kisantia
|
||||||
|
/extensions/notebook @azure-data-studio-notebook-devs
|
||||||
/extensions/query-history/ @Charles-Gagnon
|
/extensions/query-history/ @Charles-Gagnon
|
||||||
/extensions/resource-deployment/ @Charles-Gagnon
|
/extensions/resource-deployment/ @Charles-Gagnon
|
||||||
/extensions/schema-compare/ @kisantia
|
/extensions/schema-compare/ @kisantia
|
||||||
@@ -14,3 +16,6 @@
|
|||||||
/extensions/mssql/config.json @Charles-Gagnon @alanrenmsft @kburtram
|
/extensions/mssql/config.json @Charles-Gagnon @alanrenmsft @kburtram
|
||||||
|
|
||||||
/src/sql/*.d.ts @alanrenmsft @Charles-Gagnon
|
/src/sql/*.d.ts @alanrenmsft @Charles-Gagnon
|
||||||
|
/src/sql/workbench/browser/modelComponents @Charles-Gagnon @alanrenmsft
|
||||||
|
/src/sql/workbench/api @Charles-Gagnon @alanrenmsft
|
||||||
|
/src/sql/**/notebook @azure-data-studio-notebook-devs
|
||||||
|
|||||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -204,6 +204,14 @@ jobs:
|
|||||||
- name: Compile and Download
|
- name: Compile and Download
|
||||||
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions
|
||||||
|
|
||||||
|
# This is required for keytar unittests, otherwise we hit
|
||||||
|
# https://github.com/atom/node-keytar/issues/76
|
||||||
|
- name: Create temporary keychain
|
||||||
|
run: |
|
||||||
|
security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain
|
||||||
|
security default-keychain -s $RUNNER_TEMP/buildagent.keychain
|
||||||
|
security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain
|
||||||
|
|
||||||
- name: Run Unit Tests (Electron)
|
- name: Run Unit Tests (Electron)
|
||||||
run: DISPLAY=:10 ./scripts/test.sh
|
run: DISPLAY=:10 ./scripts/test.sh
|
||||||
|
|
||||||
|
|||||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,11 +1,29 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## Version 1.31.1
|
||||||
|
* Release date: July 29, 2021
|
||||||
|
* Release status: General Availability
|
||||||
|
## Hotfix Release
|
||||||
|
- Fix for [#16436 Database Connection Toolbar Missing](https://github.com/microsoft/azuredatastudio/issues/16436)
|
||||||
|
|
||||||
|
## Version 1.31.0
|
||||||
|
* Release date: July 21, 2021
|
||||||
|
* Release status: General Availability
|
||||||
|
* New Notebook Features:
|
||||||
|
* WYSIWYG link improvements
|
||||||
|
* Extension Updates:
|
||||||
|
* Import
|
||||||
|
* SandDance
|
||||||
|
* SQL Database Projects
|
||||||
|
* Bug Fixes
|
||||||
|
* Accessibility bug fixes
|
||||||
|
|
||||||
## Version 1.30.0
|
## Version 1.30.0
|
||||||
* Release date: June 17, 2021
|
* Release date: June 17, 2021
|
||||||
* Release status: General Availability
|
* Release status: General Availability
|
||||||
* New Notebook Features:
|
* New Notebook Features:
|
||||||
* Show book's notebook TOC title in pinned notebooks view
|
* Show book's notebook TOC title in pinned notebooks view
|
||||||
* Add new book icon
|
* Add new book icon
|
||||||
* Update Python to 3.8.10
|
* Update Python to 3.8.10
|
||||||
* Query Editor Features:
|
* Query Editor Features:
|
||||||
* Added filtering/sorting feature for query result grid in query editor and notebook, the feature can be invoked from the column headers. Note that this feature is only available when you enable the preview features
|
* Added filtering/sorting feature for query result grid in query editor and notebook, the feature can be invoked from the column headers. Note that this feature is only available when you enable the preview features
|
||||||
@@ -14,7 +32,7 @@
|
|||||||
* SQL Database Projects
|
* SQL Database Projects
|
||||||
* Machine Learning
|
* Machine Learning
|
||||||
* Bug Fixes
|
* Bug Fixes
|
||||||
* Fix WYSIWYG Table cell adding new line in table cell
|
* Fix WYSIWYG Table cell adding new line in table cell
|
||||||
|
|
||||||
## Version 1.29.0
|
## Version 1.29.0
|
||||||
* Release date: May 19, 2021
|
* Release date: May 19, 2021
|
||||||
|
|||||||
14
README.md
14
README.md
@@ -131,10 +131,10 @@ Copyright (c) Microsoft Corporation. All rights reserved.
|
|||||||
|
|
||||||
Licensed under the [Source EULA](LICENSE.txt).
|
Licensed under the [Source EULA](LICENSE.txt).
|
||||||
|
|
||||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2165736
|
[win-user]: https://go.microsoft.com/fwlink/?linkid=2168181
|
||||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2165737
|
[win-system]: https://go.microsoft.com/fwlink/?linkid=2168180
|
||||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2165838
|
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2168436
|
||||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2165942
|
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2168435
|
||||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2165841
|
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2168338
|
||||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2165842
|
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2168271
|
||||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2165738
|
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2168339
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ expressly granted herein, whether by implication, estoppel or otherwise.
|
|||||||
getmac: https://github.com/bevry/getmac
|
getmac: https://github.com/bevry/getmac
|
||||||
graceful-fs: https://github.com/isaacs/node-graceful-fs
|
graceful-fs: https://github.com/isaacs/node-graceful-fs
|
||||||
gridstack: https://github.com/gridstack/gridstack.js
|
gridstack: https://github.com/gridstack/gridstack.js
|
||||||
|
html-to-image: https://github.com/bubkoo/html-to-image
|
||||||
html-query-plan: https://github.com/JustinPealing/html-query-plan
|
html-query-plan: https://github.com/JustinPealing/html-query-plan
|
||||||
http-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent
|
http-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent
|
||||||
https-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent
|
https-proxy-agent: https://github.com/TooTallNate/node-https-proxy-agent
|
||||||
@@ -520,6 +521,32 @@ SOFTWARE.
|
|||||||
=========================================
|
=========================================
|
||||||
END OF gridstack NOTICES AND INFORMATION
|
END OF gridstack NOTICES AND INFORMATION
|
||||||
|
|
||||||
|
%% html-to-image NOTICES AND INFORMATION BEGIN HERE
|
||||||
|
=========================================
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 W.Y.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
=========================================
|
||||||
|
END OF html-to-image NOTICES AND INFORMATION
|
||||||
|
|
||||||
%% html-query-plan NOTICES AND INFORMATION BEGIN HERE
|
%% html-query-plan NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ steps:
|
|||||||
done
|
done
|
||||||
displayName: Archive Logs
|
displayName: Archive Logs
|
||||||
continueOnError: true
|
continueOnError: true
|
||||||
condition: succeededOrFailed()
|
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
|
|||||||
@@ -11,25 +11,25 @@ steps:
|
|||||||
inputs:
|
inputs:
|
||||||
versionSpec: "14.x"
|
versionSpec: "14.x"
|
||||||
|
|
||||||
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
|
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: "1.x"
|
versionSpec: "1.x"
|
||||||
|
|
||||||
- bash: |
|
# - bash: |
|
||||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
# TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
CHANNEL="G1C14HJ2F"
|
# CHANNEL="G1C14HJ2F"
|
||||||
|
|
||||||
if [ "$TAG_VERSION" == "1.999.0" ]; then
|
# if [ "$TAG_VERSION" == "1.999.0" ]; then
|
||||||
MESSAGE="<!here>. Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local."
|
# MESSAGE="<!here>. Someone pushed 1.999.0 tag. Please delete it ASAP from remote and local."
|
||||||
|
|
||||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
# curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||||
-H 'Content-type: application/json; charset=utf-8' \
|
# -H 'Content-type: application/json; charset=utf-8' \
|
||||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
# --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
||||||
https://slack.com/api/chat.postMessage
|
# https://slack.com/api/chat.postMessage
|
||||||
|
|
||||||
exit 1
|
# exit 1
|
||||||
fi
|
# fi
|
||||||
displayName: Check 1.999.0 tag
|
# displayName: Check 1.999.0 tag
|
||||||
|
|
||||||
- bash: |
|
- bash: |
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
@@ -37,47 +37,54 @@ steps:
|
|||||||
node build/azure-pipelines/publish-types/check-version.js
|
node build/azure-pipelines/publish-types/check-version.js
|
||||||
displayName: Check version
|
displayName: Check version
|
||||||
|
|
||||||
|
# {{SQL CARBON EDIT}} Modify to fit our own scenario - specifically currently we need to use a fork of the repo since we don't
|
||||||
|
# have an account with push access to DT
|
||||||
- bash: |
|
- bash: |
|
||||||
git config --global user.email "vscode@microsoft.com"
|
git config --global user.email "azuredatastudio@microsoft.com"
|
||||||
git config --global user.name "VSCode"
|
git config --global user.name "Azure Data Studio"
|
||||||
|
|
||||||
git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1
|
git clone https://$(GITHUB_TOKEN)@$(REPO) --depth=1
|
||||||
node build/azure-pipelines/publish-types/update-types.js
|
node build/azure-pipelines/publish-types/update-types.js
|
||||||
|
|
||||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
|
|
||||||
cd DefinitelyTyped
|
cd DefinitelyTyped
|
||||||
|
|
||||||
|
# Sync up to latest from the DT repo
|
||||||
|
git remote add upstream https://github.com/DefinitelyTyped/DefinitelyTyped.git
|
||||||
|
git merge upstream/master
|
||||||
|
git push origin
|
||||||
|
|
||||||
git diff --color | cat
|
git diff --color | cat
|
||||||
git add -A
|
git add -A
|
||||||
git status
|
git status
|
||||||
git checkout -b "vscode-types-$TAG_VERSION"
|
git checkout -b "azdata-types-$TAG_VERSION"
|
||||||
git commit -m "VS Code $TAG_VERSION Extension API"
|
git commit -m "Azure Data Studio $TAG_VERSION Extension API"
|
||||||
git push origin "vscode-types-$TAG_VERSION"
|
git push origin "azdata-types-$TAG_VERSION"
|
||||||
|
|
||||||
displayName: Push update to DefinitelyTyped
|
displayName: Push update to DefinitelyTyped
|
||||||
|
|
||||||
- bash: |
|
# - bash: |
|
||||||
TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
# TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||||
CHANNEL="G1C14HJ2F"
|
# CHANNEL="G1C14HJ2F"
|
||||||
|
|
||||||
MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame champion, please open this link, examine changes and create a PR:"
|
# MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame champion, please open this link, examine changes and create a PR:"
|
||||||
LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details."
|
# LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details."
|
||||||
MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode."
|
# MESSAGE2="[@eamodio, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode."
|
||||||
|
|
||||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
# curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||||
-H 'Content-type: application/json; charset=utf-8' \
|
# -H 'Content-type: application/json; charset=utf-8' \
|
||||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
# --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \
|
||||||
https://slack.com/api/chat.postMessage
|
# https://slack.com/api/chat.postMessage
|
||||||
|
|
||||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
# curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||||
-H 'Content-type: application/json; charset=utf-8' \
|
# -H 'Content-type: application/json; charset=utf-8' \
|
||||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \
|
# --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \
|
||||||
https://slack.com/api/chat.postMessage
|
# https://slack.com/api/chat.postMessage
|
||||||
|
|
||||||
curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
# curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \
|
||||||
-H 'Content-type: application/json; charset=utf-8' \
|
# -H 'Content-type: application/json; charset=utf-8' \
|
||||||
--data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \
|
# --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \
|
||||||
https://slack.com/api/chat.postMessage
|
# https://slack.com/api/chat.postMessage
|
||||||
|
|
||||||
displayName: Send message on Slack
|
# displayName: Send message on Slack
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ try {
|
|||||||
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
.execSync('git describe --tags `git rev-list --tags --max-count=1`')
|
||||||
.toString()
|
.toString()
|
||||||
.trim();
|
.trim();
|
||||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
const dtsUri = `https://raw.githubusercontent.com/microsoft/azuredatastudio/${tag}/src/sql/azdata.d.ts`; // {{SQL CARBON EDIT}} Use our typings
|
||||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/azdata/index.d.ts'); // {{SQL CARBON EDIT}} Use our typings
|
||||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||||
updateDTSFile(outPath, tag);
|
updateDTSFile(outPath, tag);
|
||||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
console.log(`Done updating azdata.d.ts at ${outPath}`); // {{SQL CARBON EDIT}} Use our typings
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -51,21 +51,25 @@ function getNewFileContent(content, tag) {
|
|||||||
function getNewFileHeader(tag) {
|
function getNewFileHeader(tag) {
|
||||||
const [major, minor] = tag.split('.');
|
const [major, minor] = tag.split('.');
|
||||||
const shorttag = `${major}.${minor}`;
|
const shorttag = `${major}.${minor}`;
|
||||||
|
// {{SQL CARBON EDIT}} Use our own header
|
||||||
const header = [
|
const header = [
|
||||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
`// Type definitions for Azure Data Studio ${shorttag}`,
|
||||||
`// Project: https://github.com/microsoft/vscode`,
|
`// Project: https://github.com/microsoft/azuredatastudio`,
|
||||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
`// Definitions by: Charles Gagnon <https://github.com/Charles-Gagnon>`,
|
||||||
|
`// Alan Ren: <https://github.com/alanrenmsft>`,
|
||||||
|
`// Karl Burtram: <https://github.com/kburtram>`,
|
||||||
|
`// Ken Van Hyning: <https://github.com/kenvanhyning>`,
|
||||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||||
``,
|
``,
|
||||||
`/*---------------------------------------------------------------------------------------------`,
|
`/*---------------------------------------------------------------------------------------------`,
|
||||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||||
` * Licensed under the Source EULA.`,
|
` * Licensed under the MIT License.`,
|
||||||
` * See https://github.com/Microsoft/vscode/blob/main/LICENSE.txt for license information.`,
|
` * See https://github.com/Microsoft/azuredatastudio/blob/main/LICENSE.txt for license information.`,
|
||||||
` *--------------------------------------------------------------------------------------------*/`,
|
` *--------------------------------------------------------------------------------------------*/`,
|
||||||
``,
|
``,
|
||||||
`/**`,
|
`/**`,
|
||||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
` * Type Definition for Azure Data Studio ${shorttag} Extension API`,
|
||||||
` * See https://code.visualstudio.com/api for more information`,
|
` * See https://docs.microsoft.com/sql/azure-data-studio/extensibility-apis for more information`,
|
||||||
` */`
|
` */`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
return header;
|
return header;
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ try {
|
|||||||
.toString()
|
.toString()
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`;
|
const dtsUri = `https://raw.githubusercontent.com/microsoft/azuredatastudio/${tag}/src/sql/azdata.d.ts`; // {{SQL CARBON EDIT}} Use our typings
|
||||||
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts');
|
const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/azdata/index.d.ts'); // {{SQL CARBON EDIT}} Use our typings
|
||||||
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
cp.execSync(`curl ${dtsUri} --output ${outPath}`);
|
||||||
|
|
||||||
updateDTSFile(outPath, tag);
|
updateDTSFile(outPath, tag);
|
||||||
|
|
||||||
console.log(`Done updating vscode.d.ts at ${outPath}`);
|
console.log(`Done updating azdata.d.ts at ${outPath}`); // {{SQL CARBON EDIT}} Use our typings
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
console.error('Failed to update types');
|
console.error('Failed to update types');
|
||||||
@@ -63,21 +63,25 @@ function getNewFileHeader(tag: string) {
|
|||||||
const [major, minor] = tag.split('.');
|
const [major, minor] = tag.split('.');
|
||||||
const shorttag = `${major}.${minor}`;
|
const shorttag = `${major}.${minor}`;
|
||||||
|
|
||||||
|
// {{SQL CARBON EDIT}} Use our own header
|
||||||
const header = [
|
const header = [
|
||||||
`// Type definitions for Visual Studio Code ${shorttag}`,
|
`// Type definitions for Azure Data Studio ${shorttag}`,
|
||||||
`// Project: https://github.com/microsoft/vscode`,
|
`// Project: https://github.com/microsoft/azuredatastudio`,
|
||||||
`// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft>`,
|
`// Definitions by: Charles Gagnon <https://github.com/Charles-Gagnon>`,
|
||||||
|
`// Alan Ren: <https://github.com/alanrenmsft>`,
|
||||||
|
`// Karl Burtram: <https://github.com/kburtram>`,
|
||||||
|
`// Ken Van Hyning: <https://github.com/kenvanhyning>`,
|
||||||
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
`// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`,
|
||||||
``,
|
``,
|
||||||
`/*---------------------------------------------------------------------------------------------`,
|
`/*---------------------------------------------------------------------------------------------`,
|
||||||
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
|
||||||
` * Licensed under the Source EULA.`,
|
` * Licensed under the MIT License.`,
|
||||||
` * See https://github.com/Microsoft/vscode/blob/main/LICENSE.txt for license information.`,
|
` * See https://github.com/Microsoft/azuredatastudio/blob/main/LICENSE.txt for license information.`,
|
||||||
` *--------------------------------------------------------------------------------------------*/`,
|
` *--------------------------------------------------------------------------------------------*/`,
|
||||||
``,
|
``,
|
||||||
`/**`,
|
`/**`,
|
||||||
` * Type Definition for Visual Studio Code ${shorttag} Extension API`,
|
` * Type Definition for Azure Data Studio ${shorttag} Extension API`,
|
||||||
` * See https://code.visualstudio.com/api for more information`,
|
` * See https://docs.microsoft.com/sql/azure-data-studio/extensibility-apis for more information`,
|
||||||
` */`
|
` */`
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
|
|||||||
@@ -79,19 +79,8 @@ steps:
|
|||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
yarn sqllint
|
yarn npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check sqllint strict-vscode
|
||||||
yarn gulp hygiene
|
displayName: Compile & Hygiene
|
||||||
yarn strict-vscode
|
|
||||||
yarn valid-layers-check
|
|
||||||
displayName: Run hygiene, eslint
|
|
||||||
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
|
|
||||||
|
|
||||||
- script: |
|
|
||||||
set -e
|
|
||||||
yarn gulp compile-build
|
|
||||||
yarn gulp compile-extensions-build
|
|
||||||
yarn gulp minify-vscode
|
|
||||||
displayName: Compile
|
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
set -e
|
set -e
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const task = require('./lib/task');
|
|||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const vsce = require('vsce');
|
const vsce = require('vsce');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
|
const rename = require('gulp-rename');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
gulp.task('fmt', () => formatStagedFiles());
|
gulp.task('fmt', () => formatStagedFiles());
|
||||||
const formatFiles = (some) => {
|
const formatFiles = (some) => {
|
||||||
@@ -94,12 +96,14 @@ const root = path.dirname(__dirname);
|
|||||||
|
|
||||||
gulp.task('package-external-extensions', task.series(
|
gulp.task('package-external-extensions', task.series(
|
||||||
task.define('bundle-external-extensions-build', () => ext.packageExternalExtensionsStream().pipe(gulp.dest('.build/external'))),
|
task.define('bundle-external-extensions-build', () => ext.packageExternalExtensionsStream().pipe(gulp.dest('.build/external'))),
|
||||||
task.define('create-external-extension-vsix-build', () => {
|
task.define('create-external-extension-vsix-build', async () => {
|
||||||
const vsixes = glob.sync('.build/external/extensions/*/package.json').map(manifestPath => {
|
const vsixes = glob.sync('.build/external/extensions/*/package.json').map(manifestPath => {
|
||||||
const extensionPath = path.dirname(path.join(root, manifestPath));
|
const extensionPath = path.dirname(path.join(root, manifestPath));
|
||||||
const extensionName = path.basename(extensionPath);
|
const extensionName = path.basename(extensionPath);
|
||||||
return { name: extensionName, path: extensionPath };
|
return { name: extensionName, path: extensionPath };
|
||||||
}).map(element => {
|
})
|
||||||
|
.filter(element => ext.vscodeExternalExtensions.indexOf(element.name) === -1) // VS Code external extensions are bundled into ADS so no need to create a normal VSIX for them
|
||||||
|
.map(element => {
|
||||||
const pkgJson = require(path.join(element.path, 'package.json'));
|
const pkgJson = require(path.join(element.path, 'package.json'));
|
||||||
const vsixDirectory = path.join(root, '.build', 'extensions');
|
const vsixDirectory = path.join(root, '.build', 'extensions');
|
||||||
mkdirp.sync(vsixDirectory);
|
mkdirp.sync(vsixDirectory);
|
||||||
@@ -111,8 +115,46 @@ gulp.task('package-external-extensions', task.series(
|
|||||||
useYarn: true
|
useYarn: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// Wait for all the initial VSIXes to be completed before making the VS Code ones since we'll be overwriting
|
||||||
|
// values in the package.json for those.
|
||||||
|
await Promise.all(vsixes);
|
||||||
|
|
||||||
return Promise.all(vsixes);
|
// Go through and find the extensions which build separate versions of themselves for VS Code.
|
||||||
|
// This is currently a pretty simplistic process, essentially just replacing certain values in
|
||||||
|
// the package.json. It doesn't handle more complex tasks such as replacing localized strings.
|
||||||
|
const vscodeVsixes = glob.sync('.build/external/extensions/*/package.vscode.json')
|
||||||
|
.map(async vscodeManifestRelativePath => {
|
||||||
|
const vscodeManifestFullPath = path.join(root, vscodeManifestRelativePath);
|
||||||
|
const packageDir = path.dirname(vscodeManifestFullPath);
|
||||||
|
const packageManifestPath = path.join(packageDir, 'package.json');
|
||||||
|
const json = require('gulp-json-editor');
|
||||||
|
const packageJsonStream = gulp.src(packageManifestPath) // Create stream for the original package.json
|
||||||
|
.pipe(json(data => { // And now use gulp-json-editor to modify the contents
|
||||||
|
const updateData = JSON.parse(fs.readFileSync(vscodeManifestFullPath)); // Read in the set of values to replace from package.vscode.json
|
||||||
|
Object.keys(updateData).forEach(key => {
|
||||||
|
data[key] = updateData[key];
|
||||||
|
});
|
||||||
|
// Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts
|
||||||
|
// More can be added to the list as needed.
|
||||||
|
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
|
||||||
|
delete data.contributes.menus[menu];
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}, { beautify: false }))
|
||||||
|
.pipe(gulp.dest(packageDir));
|
||||||
|
await new Promise(resolve => packageJsonStream.on('finish', resolve)); // Wait for the files to finish being updated before packaging
|
||||||
|
const pkgJson = JSON.parse(fs.readFileSync(packageManifestPath));
|
||||||
|
const vsixDirectory = path.join(root, '.build', 'extensions');
|
||||||
|
const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
|
||||||
|
console.info('Creating vsix for ' + packageDir + ' result:' + packagePath);
|
||||||
|
return vsce.createVSIX({
|
||||||
|
cwd: packageDir,
|
||||||
|
packagePath: packagePath,
|
||||||
|
useYarn: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(vscodeVsixes);
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -112,33 +112,33 @@ gulp.task(optimizeVSCodeTask);
|
|||||||
|
|
||||||
// List of ADS extension XLF files that we want to put into the English resource folder.
|
// List of ADS extension XLF files that we want to put into the English resource folder.
|
||||||
const extensionsFilter = filter([
|
const extensionsFilter = filter([
|
||||||
"**/admin-tool-ext-win.xlf",
|
'**/admin-tool-ext-win.xlf',
|
||||||
"**/agent.xlf",
|
'**/agent.xlf',
|
||||||
"**/arc.xlf",
|
'**/arc.xlf',
|
||||||
"**/asde-deployment.xlf",
|
'**/asde-deployment.xlf',
|
||||||
"**/azdata.xlf",
|
'**/azdata.xlf',
|
||||||
"**/azurecore.xlf",
|
'**/azurecore.xlf',
|
||||||
"**/azurehybridtoolkit.xlf",
|
'**/azurehybridtoolkit.xlf',
|
||||||
"**/big-data-cluster.xlf",
|
'**/big-data-cluster.xlf',
|
||||||
"**/cms.xlf",
|
'**/cms.xlf',
|
||||||
"**/dacpac.xlf",
|
'**/dacpac.xlf',
|
||||||
"**/data-workspace.xlf",
|
'**/data-workspace.xlf',
|
||||||
"**/import.xlf",
|
'**/import.xlf',
|
||||||
"**/kusto.xlf",
|
'**/kusto.xlf',
|
||||||
"**/machine-learning.xlf",
|
'**/machine-learning.xlf',
|
||||||
"**/Microsoft.sqlservernotebook.xlf",
|
'**/Microsoft.sqlservernotebook.xlf',
|
||||||
"**/mssql.xlf",
|
'**/mssql.xlf',
|
||||||
"**/notebook.xlf",
|
'**/notebook.xlf',
|
||||||
"**/profiler.xlf",
|
'**/profiler.xlf',
|
||||||
"**/query-history.xlf",
|
'**/query-history.xlf',
|
||||||
"**/resource-deployment.xlf",
|
'**/resource-deployment.xlf',
|
||||||
"**/schema-compare.xlf",
|
'**/schema-compare.xlf',
|
||||||
"**/server-report.xlf",
|
'**/server-report.xlf',
|
||||||
"**/sql-assessment.xlf",
|
'**/sql-assessment.xlf',
|
||||||
"**/sql-database-projects.xlf",
|
'**/sql-database-projects.xlf',
|
||||||
"**/sql-migration.xlf",
|
'**/sql-migration.xlf',
|
||||||
"**/xml-language-features.xlf"
|
'**/xml-language-features.xlf'
|
||||||
])
|
]);
|
||||||
|
|
||||||
// Copy ADS extension XLFs into English resource folder.
|
// Copy ADS extension XLFs into English resource folder.
|
||||||
const importExtensionsTask = task.define('import-extensions-xlfs', function () {
|
const importExtensionsTask = task.define('import-extensions-xlfs', function () {
|
||||||
@@ -149,7 +149,7 @@ const importExtensionsTask = task.define('import-extensions-xlfs', function () {
|
|||||||
)
|
)
|
||||||
.pipe(vfs.dest(`./resources/xlf/en`));
|
.pipe(vfs.dest(`./resources/xlf/en`));
|
||||||
});
|
});
|
||||||
gulp.task(importExtensionsTask)
|
gulp.task(importExtensionsTask);
|
||||||
// {{SQL CARBON EDIT}} end
|
// {{SQL CARBON EDIT}} end
|
||||||
|
|
||||||
const sourceMappingURLBase = `https://sqlopsbuilds.blob.core.windows.net/sourcemaps/${commit}`;
|
const sourceMappingURLBase = `https://sqlopsbuilds.blob.core.windows.net/sourcemaps/${commit}`;
|
||||||
@@ -486,7 +486,7 @@ const vscodeTranslationsExport = task.define(
|
|||||||
'vscode-translations-export',
|
'vscode-translations-export',
|
||||||
task.series(
|
task.series(
|
||||||
compileBuildTask,
|
compileBuildTask,
|
||||||
compileLocalizationExtensionsBuildTask, // {{SQL CARBON EDIT}} now include all extensions in ADS, not just a subset. (replaces "compileExtensionsBuildTask" here).
|
compileLocalizationExtensionsBuildTask, // {{SQL CARBON EDIT}} now include all extensions in ADS, not just a subset. (replaces 'compileExtensionsBuildTask' here).
|
||||||
optimizeVSCodeTask,
|
optimizeVSCodeTask,
|
||||||
function () {
|
function () {
|
||||||
const pathToMetadata = './out-vscode/nls.metadata.json';
|
const pathToMetadata = './out-vscode/nls.metadata.json';
|
||||||
@@ -501,7 +501,7 @@ const vscodeTranslationsExport = task.define(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
gulp.task(vscodeTranslationsExport)
|
gulp.task(vscodeTranslationsExport);
|
||||||
|
|
||||||
// {{SQL CARBON EDIT}} Localization gulp task, runs vscodeTranslationsExport and imports a subset of the generated XLFs into the folder.
|
// {{SQL CARBON EDIT}} Localization gulp task, runs vscodeTranslationsExport and imports a subset of the generated XLFs into the folder.
|
||||||
gulp.task(task.define(
|
gulp.task(task.define(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.translatePackageJSON = exports.packageRebuildExtensionsStream = exports.cleanRebuildExtensions = exports.packageExternalExtensionsStream = exports.scanBuiltinExtensions = exports.packageMarketplaceExtensionsStream = exports.packageLocalExtensionsStream = exports.fromMarketplace = exports.fromLocalNormal = exports.fromLocal = void 0;
|
exports.translatePackageJSON = exports.packageRebuildExtensionsStream = exports.cleanRebuildExtensions = exports.packageExternalExtensionsStream = exports.scanBuiltinExtensions = exports.packageMarketplaceExtensionsStream = exports.packageLocalExtensionsStream = exports.vscodeExternalExtensions = exports.fromMarketplace = exports.fromLocalNormal = exports.fromLocal = void 0;
|
||||||
const es = require("event-stream");
|
const es = require("event-stream");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const glob = require("glob");
|
const glob = require("glob");
|
||||||
@@ -232,6 +232,12 @@ const externalExtensions = [
|
|||||||
'sql-database-projects',
|
'sql-database-projects',
|
||||||
'sql-migration'
|
'sql-migration'
|
||||||
];
|
];
|
||||||
|
/**
|
||||||
|
* Extensions that are built into ADS but should be packaged externally as well for VS Code.
|
||||||
|
*/
|
||||||
|
exports.vscodeExternalExtensions = [
|
||||||
|
'data-workspace'
|
||||||
|
];
|
||||||
// extensions that require a rebuild since they have native parts
|
// extensions that require a rebuild since they have native parts
|
||||||
const rebuildExtensions = [
|
const rebuildExtensions = [
|
||||||
'big-data-cluster',
|
'big-data-cluster',
|
||||||
@@ -348,7 +354,7 @@ function packageExternalExtensionsStream() {
|
|||||||
const extensionName = path.basename(extensionPath);
|
const extensionName = path.basename(extensionPath);
|
||||||
return { name: extensionName, path: extensionPath };
|
return { name: extensionName, path: extensionPath };
|
||||||
})
|
})
|
||||||
.filter(({ name }) => externalExtensions.indexOf(name) >= 0);
|
.filter(({ name }) => externalExtensions.indexOf(name) >= 0 || exports.vscodeExternalExtensions.indexOf(name) >= 0);
|
||||||
const builtExtensions = extenalExtensionDescriptions.map(extension => {
|
const builtExtensions = extenalExtensionDescriptions.map(extension => {
|
||||||
return fromLocal(extension.path, false)
|
return fromLocal(extension.path, false)
|
||||||
.pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`));
|
.pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`));
|
||||||
|
|||||||
@@ -268,6 +268,13 @@ const externalExtensions = [
|
|||||||
'sql-migration'
|
'sql-migration'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions that are built into ADS but should be packaged externally as well for VS Code.
|
||||||
|
*/
|
||||||
|
export const vscodeExternalExtensions = [
|
||||||
|
'data-workspace'
|
||||||
|
];
|
||||||
|
|
||||||
// extensions that require a rebuild since they have native parts
|
// extensions that require a rebuild since they have native parts
|
||||||
const rebuildExtensions = [
|
const rebuildExtensions = [
|
||||||
'big-data-cluster',
|
'big-data-cluster',
|
||||||
@@ -425,7 +432,7 @@ export function packageExternalExtensionsStream(): NodeJS.ReadWriteStream {
|
|||||||
const extensionName = path.basename(extensionPath);
|
const extensionName = path.basename(extensionPath);
|
||||||
return { name: extensionName, path: extensionPath };
|
return { name: extensionName, path: extensionPath };
|
||||||
})
|
})
|
||||||
.filter(({ name }) => externalExtensions.indexOf(name) >= 0);
|
.filter(({ name }) => externalExtensions.indexOf(name) >= 0 || vscodeExternalExtensions.indexOf(name) >= 0);
|
||||||
|
|
||||||
const builtExtensions = extenalExtensionDescriptions.map(extension => {
|
const builtExtensions = extenalExtensionDescriptions.map(extension => {
|
||||||
return fromLocal(extension.path, false)
|
return fromLocal(extension.path, false)
|
||||||
|
|||||||
@@ -527,7 +527,11 @@ function createXlfFilesForCoreBundle() {
|
|||||||
if (file.isBuffer()) {
|
if (file.isBuffer()) {
|
||||||
const xlfs = Object.create(null);
|
const xlfs = Object.create(null);
|
||||||
const json = JSON.parse(file.contents.toString('utf8'));
|
const json = JSON.parse(file.contents.toString('utf8'));
|
||||||
for (let coreModule in json.keys) {
|
// {{SQL CARBON EDIT}} - Must sort the keys for easier translation.
|
||||||
|
let sortedKeys = Object.keys(json.keys).sort();
|
||||||
|
for (let i = 0; i < sortedKeys.length; i++) {
|
||||||
|
let coreModule = sortedKeys[i];
|
||||||
|
// {{SQL CARBON EDIT}} - End
|
||||||
const projectResource = getResource(coreModule);
|
const projectResource = getResource(coreModule);
|
||||||
const resource = projectResource.name;
|
const resource = projectResource.name;
|
||||||
const project = projectResource.project;
|
const project = projectResource.project;
|
||||||
|
|||||||
@@ -652,7 +652,11 @@ export function createXlfFilesForCoreBundle(): ThroughStream {
|
|||||||
if (file.isBuffer()) {
|
if (file.isBuffer()) {
|
||||||
const xlfs: Map<XLF> = Object.create(null);
|
const xlfs: Map<XLF> = Object.create(null);
|
||||||
const json: BundledFormat = JSON.parse((file.contents as Buffer).toString('utf8'));
|
const json: BundledFormat = JSON.parse((file.contents as Buffer).toString('utf8'));
|
||||||
for (let coreModule in json.keys) {
|
// {{SQL CARBON EDIT}} - Must sort the keys for easier translation.
|
||||||
|
let sortedKeys = Object.keys(json.keys).sort();
|
||||||
|
for (let i = 0; i < sortedKeys.length; i++) {
|
||||||
|
let coreModule = sortedKeys[i];
|
||||||
|
// {{SQL CARBON EDIT}} - End
|
||||||
const projectResource = getResource(coreModule);
|
const projectResource = getResource(coreModule);
|
||||||
const resource = projectResource.name;
|
const resource = projectResource.name;
|
||||||
const project = projectResource.project;
|
const project = projectResource.project;
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ function refreshLangpacks() {
|
|||||||
}
|
}
|
||||||
let packageJSON = JSON.parse(fs.readFileSync(path.join(locExtFolder, 'package.json')).toString());
|
let packageJSON = JSON.parse(fs.readFileSync(path.join(locExtFolder, 'package.json')).toString());
|
||||||
//processing extension fields, version and folder name must be changed manually.
|
//processing extension fields, version and folder name must be changed manually.
|
||||||
packageJSON['name'] = packageJSON['name'].replace('vscode', textFields.nameText);
|
packageJSON['name'] = packageJSON['name'].replace('vscode', textFields.nameText).toLowerCase();
|
||||||
packageJSON['displayName'] = packageJSON['displayName'].replace('Visual Studio Code', textFields.displayNameText);
|
packageJSON['displayName'] = packageJSON['displayName'].replace('Visual Studio Code', textFields.displayNameText);
|
||||||
packageJSON['publisher'] = textFields.publisherText;
|
packageJSON['publisher'] = textFields.publisherText;
|
||||||
packageJSON['license'] = textFields.licenseText;
|
packageJSON['license'] = textFields.licenseText;
|
||||||
@@ -266,18 +266,6 @@ function refreshLangpacks() {
|
|||||||
if (languageId === "zh-tw") {
|
if (languageId === "zh-tw") {
|
||||||
languageId = "zh-hant";
|
languageId = "zh-hant";
|
||||||
}
|
}
|
||||||
//remove extensions not part of ADS.
|
|
||||||
if (fs.existsSync(translationDataFolder)) {
|
|
||||||
let totalExtensions = fs.readdirSync(path.join(translationDataFolder, 'extensions'));
|
|
||||||
for (let extensionTag in totalExtensions) {
|
|
||||||
let extensionFileName = totalExtensions[extensionTag];
|
|
||||||
let xlfPath = path.join(location, `${languageId}`, extensionFileName.replace('.i18n.json', '.xlf'));
|
|
||||||
if (!(fs.existsSync(xlfPath) || VSCODEExtensions.indexOf(extensionFileName.replace('.i18n.json', '')) !== -1)) {
|
|
||||||
let filePath = path.join(translationDataFolder, 'extensions', extensionFileName);
|
|
||||||
rimraf.sync(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(`Importing translations for ${languageId} from '${location}' to '${translationDataFolder}' ...`);
|
console.log(`Importing translations for ${languageId} from '${location}' to '${translationDataFolder}' ...`);
|
||||||
let translationPaths = [];
|
let translationPaths = [];
|
||||||
gulp.src(path.join(location, languageId, '**', '*.xlf'))
|
gulp.src(path.join(location, languageId, '**', '*.xlf'))
|
||||||
@@ -356,6 +344,8 @@ function renameVscodeLangpacks() {
|
|||||||
}
|
}
|
||||||
let locADSFolder = path.join('.', 'i18n', `ads-language-pack-${langId}`);
|
let locADSFolder = path.join('.', 'i18n', `ads-language-pack-${langId}`);
|
||||||
let locVSCODEFolder = path.join('.', 'i18n', `vscode-language-pack-${langId}`);
|
let locVSCODEFolder = path.join('.', 'i18n', `vscode-language-pack-${langId}`);
|
||||||
|
let translationDataFolder = path.join(locVSCODEFolder, 'translations');
|
||||||
|
let xlfFolder = path.join('.', 'resources', 'xlf');
|
||||||
try {
|
try {
|
||||||
fs.statSync(locVSCODEFolder);
|
fs.statSync(locVSCODEFolder);
|
||||||
}
|
}
|
||||||
@@ -363,12 +353,31 @@ function renameVscodeLangpacks() {
|
|||||||
console.log('vscode pack is not in ADS yet: ' + langId);
|
console.log('vscode pack is not in ADS yet: ' + langId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gulp.src(`i18n/ads-language-pack-${langId}/*.md`)
|
//Delete any erroneous zip files found in vscode folder.
|
||||||
.pipe(vfs.dest(locVSCODEFolder, { overwrite: true }))
|
let globZipArray = glob.sync(path.join(locVSCODEFolder, '*.zip'));
|
||||||
.end(function () {
|
globZipArray.forEach(element => {
|
||||||
rimraf.sync(locADSFolder);
|
fs.unlinkSync(element);
|
||||||
fs.renameSync(locVSCODEFolder, locADSFolder);
|
|
||||||
});
|
});
|
||||||
|
// Delete extension files in vscode language pack that are not in ADS.
|
||||||
|
if (fs.existsSync(translationDataFolder)) {
|
||||||
|
let totalExtensions = fs.readdirSync(path.join(translationDataFolder, 'extensions'));
|
||||||
|
for (let extensionTag in totalExtensions) {
|
||||||
|
let extensionFileName = totalExtensions[extensionTag];
|
||||||
|
let xlfPath = path.join(xlfFolder, `${langId}`, extensionFileName.replace('.i18n.json', '.xlf'));
|
||||||
|
if (!(fs.existsSync(xlfPath) || VSCODEExtensions.indexOf(extensionFileName.replace('.i18n.json', '')) !== -1)) {
|
||||||
|
let filePath = path.join(translationDataFolder, 'extensions', extensionFileName);
|
||||||
|
rimraf.sync(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Get list of md files in ADS langpack, to copy to vscode langpack prior to renaming.
|
||||||
|
let globMDArray = glob.sync(path.join(locADSFolder, '*.md'));
|
||||||
|
//Copy files to vscode langpack, then remove the ADS langpack, and finally rename the vscode langpack to match the ADS one.
|
||||||
|
globMDArray.forEach(element => {
|
||||||
|
fs.copyFileSync(element, path.join(locVSCODEFolder, path.parse(element).base));
|
||||||
|
});
|
||||||
|
rimraf.sync(locADSFolder);
|
||||||
|
fs.renameSync(locVSCODEFolder, locADSFolder);
|
||||||
}
|
}
|
||||||
console.log("Langpack Rename Completed.");
|
console.log("Langpack Rename Completed.");
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export function refreshLangpacks(): Promise<void> {
|
|||||||
}
|
}
|
||||||
let packageJSON = JSON.parse(fs.readFileSync(path.join(locExtFolder, 'package.json')).toString());
|
let packageJSON = JSON.parse(fs.readFileSync(path.join(locExtFolder, 'package.json')).toString());
|
||||||
//processing extension fields, version and folder name must be changed manually.
|
//processing extension fields, version and folder name must be changed manually.
|
||||||
packageJSON['name'] = packageJSON['name'].replace('vscode', textFields.nameText);
|
packageJSON['name'] = packageJSON['name'].replace('vscode', textFields.nameText).toLowerCase();
|
||||||
packageJSON['displayName'] = packageJSON['displayName'].replace('Visual Studio Code', textFields.displayNameText);
|
packageJSON['displayName'] = packageJSON['displayName'].replace('Visual Studio Code', textFields.displayNameText);
|
||||||
packageJSON['publisher'] = textFields.publisherText;
|
packageJSON['publisher'] = textFields.publisherText;
|
||||||
packageJSON['license'] = textFields.licenseText;
|
packageJSON['license'] = textFields.licenseText;
|
||||||
@@ -287,20 +287,6 @@ export function refreshLangpacks(): Promise<void> {
|
|||||||
languageId = "zh-hant";
|
languageId = "zh-hant";
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove extensions not part of ADS.
|
|
||||||
if (fs.existsSync(translationDataFolder)) {
|
|
||||||
let totalExtensions = fs.readdirSync(path.join(translationDataFolder, 'extensions'));
|
|
||||||
for (let extensionTag in totalExtensions) {
|
|
||||||
let extensionFileName = totalExtensions[extensionTag];
|
|
||||||
let xlfPath = path.join(location, `${languageId}`, extensionFileName.replace('.i18n.json', '.xlf'))
|
|
||||||
if (!(fs.existsSync(xlfPath) || VSCODEExtensions.indexOf(extensionFileName.replace('.i18n.json', '')) !== -1)) {
|
|
||||||
let filePath = path.join(translationDataFolder, 'extensions', extensionFileName);
|
|
||||||
rimraf.sync(filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
console.log(`Importing translations for ${languageId} from '${location}' to '${translationDataFolder}' ...`);
|
console.log(`Importing translations for ${languageId} from '${location}' to '${translationDataFolder}' ...`);
|
||||||
let translationPaths: any = [];
|
let translationPaths: any = [];
|
||||||
gulp.src(path.join(location, languageId, '**', '*.xlf'))
|
gulp.src(path.join(location, languageId, '**', '*.xlf'))
|
||||||
@@ -380,6 +366,8 @@ export function renameVscodeLangpacks(): Promise<void> {
|
|||||||
}
|
}
|
||||||
let locADSFolder = path.join('.', 'i18n', `ads-language-pack-${langId}`);
|
let locADSFolder = path.join('.', 'i18n', `ads-language-pack-${langId}`);
|
||||||
let locVSCODEFolder = path.join('.', 'i18n', `vscode-language-pack-${langId}`);
|
let locVSCODEFolder = path.join('.', 'i18n', `vscode-language-pack-${langId}`);
|
||||||
|
let translationDataFolder = path.join(locVSCODEFolder, 'translations');
|
||||||
|
let xlfFolder = path.join('.', 'resources', 'xlf');
|
||||||
try {
|
try {
|
||||||
fs.statSync(locVSCODEFolder);
|
fs.statSync(locVSCODEFolder);
|
||||||
}
|
}
|
||||||
@@ -387,12 +375,35 @@ export function renameVscodeLangpacks(): Promise<void> {
|
|||||||
console.log('vscode pack is not in ADS yet: ' + langId);
|
console.log('vscode pack is not in ADS yet: ' + langId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gulp.src(`i18n/ads-language-pack-${langId}/*.md`)
|
|
||||||
.pipe(vfs.dest(locVSCODEFolder, {overwrite: true}))
|
//Delete any erroneous zip files found in vscode folder.
|
||||||
.end(function () {
|
let globZipArray = glob.sync(path.join(locVSCODEFolder, '*.zip'));
|
||||||
rimraf.sync(locADSFolder);
|
globZipArray.forEach(element => {
|
||||||
fs.renameSync(locVSCODEFolder, locADSFolder);
|
fs.unlinkSync(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Delete extension files in vscode language pack that are not in ADS.
|
||||||
|
if (fs.existsSync(translationDataFolder)) {
|
||||||
|
let totalExtensions = fs.readdirSync(path.join(translationDataFolder, 'extensions'));
|
||||||
|
for (let extensionTag in totalExtensions) {
|
||||||
|
let extensionFileName = totalExtensions[extensionTag];
|
||||||
|
let xlfPath = path.join(xlfFolder, `${langId}`, extensionFileName.replace('.i18n.json', '.xlf'))
|
||||||
|
if (!(fs.existsSync(xlfPath) || VSCODEExtensions.indexOf(extensionFileName.replace('.i18n.json', '')) !== -1)) {
|
||||||
|
let filePath = path.join(translationDataFolder, 'extensions', extensionFileName);
|
||||||
|
rimraf.sync(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get list of md files in ADS langpack, to copy to vscode langpack prior to renaming.
|
||||||
|
let globMDArray = glob.sync(path.join(locADSFolder, '*.md'));
|
||||||
|
|
||||||
|
//Copy files to vscode langpack, then remove the ADS langpack, and finally rename the vscode langpack to match the ADS one.
|
||||||
|
globMDArray.forEach(element => {
|
||||||
|
fs.copyFileSync(element, path.join(locVSCODEFolder,path.parse(element).base));
|
||||||
|
});
|
||||||
|
rimraf.sync(locADSFolder);
|
||||||
|
fs.renameSync(locVSCODEFolder, locADSFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Langpack Rename Completed.");
|
console.log("Langpack Rename Completed.");
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const serviceDownloader = require('service-downloader').ServiceDownloadProvider;
|
const serviceDownloader = require('@microsoft/ads-service-downloader').ServiceDownloadProvider;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@microsoft/ads-extension-telemetry": "^1.1.3",
|
"@microsoft/ads-extension-telemetry": "^1.1.3",
|
||||||
"service-downloader": "0.2.1",
|
"@microsoft/ads-service-downloader": "0.2.2",
|
||||||
"vscode-nls": "^4.1.2"
|
"vscode-nls": "^4.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -189,6 +189,20 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
vscode-extension-telemetry "^0.1.6"
|
vscode-extension-telemetry "^0.1.6"
|
||||||
|
|
||||||
|
"@microsoft/ads-service-downloader@0.2.2":
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@microsoft/ads-service-downloader/-/ads-service-downloader-0.2.2.tgz#1a32c62eadb77fdab23eed257aaa5b590ccf2ce4"
|
||||||
|
integrity sha512-sEvp1dCQu8ZnUyrjX9qUo/S9IIvXkPCkCSuJ4IhuGEmGm2/++evixOtUgRKWYSRRvL1QskIUjZ0I59m6bKJ5yQ==
|
||||||
|
dependencies:
|
||||||
|
async-retry "^1.2.3"
|
||||||
|
eventemitter2 "^5.0.1"
|
||||||
|
http-proxy-agent "^2.1.0"
|
||||||
|
https-proxy-agent "^2.2.3"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
tar "^6.1.6"
|
||||||
|
tmp "^0.0.33"
|
||||||
|
yauzl "^2.10.0"
|
||||||
|
|
||||||
"@types/mocha@^5.2.5":
|
"@types/mocha@^5.2.5":
|
||||||
version "5.2.7"
|
version "5.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
|
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
|
||||||
@@ -299,10 +313,10 @@ charenc@~0.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||||
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||||
|
|
||||||
chownr@^1.1.3:
|
chownr@^2.0.0:
|
||||||
version "1.1.4"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||||
|
|
||||||
cls-hooked@^4.2.2:
|
cls-hooked@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
@@ -695,10 +709,10 @@ minipass@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
minizlib@^2.1.0:
|
minizlib@^2.1.1:
|
||||||
version "2.1.0"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3"
|
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||||
integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==
|
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
|
||||||
dependencies:
|
dependencies:
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
@@ -839,20 +853,6 @@ semver@^6.0.0, semver@^6.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||||
|
|
||||||
service-downloader@0.2.1:
|
|
||||||
version "0.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/service-downloader/-/service-downloader-0.2.1.tgz#8bd756bc4bc0cbfdf04fe71d4337f19ce6196203"
|
|
||||||
integrity sha512-5IEy2nyMJj/f41pI65b8RMeJyCecGNrMmNCpUW8hckZ9cBMyX+VCp8GjYoM6Mz/X0XSaGVz7V5gtCWjfeJI7gA==
|
|
||||||
dependencies:
|
|
||||||
async-retry "^1.2.3"
|
|
||||||
eventemitter2 "^5.0.1"
|
|
||||||
http-proxy-agent "^2.1.0"
|
|
||||||
https-proxy-agent "^2.2.3"
|
|
||||||
mkdirp "^0.5.1"
|
|
||||||
tar "^6.0.1"
|
|
||||||
tmp "^0.0.33"
|
|
||||||
yauzl "^2.10.0"
|
|
||||||
|
|
||||||
shimmer@^1.1.0, shimmer@^1.2.0:
|
shimmer@^1.1.0, shimmer@^1.2.0:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
|
resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337"
|
||||||
@@ -950,15 +950,15 @@ supports-color@^7.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
tar@^6.0.1:
|
tar@^6.1.6:
|
||||||
version "6.0.1"
|
version "6.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.1.tgz#7b3bd6c313cb6e0153770108f8d70ac298607efa"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.6.tgz#c23d797b0a1efe5d479b1490805c5443f3560c5d"
|
||||||
integrity sha512-bKhKrrz2FJJj5s7wynxy/fyxpE0CmCjmOQ1KV4KkgXFWOgoIT/NbTMnB1n+LFNrNk0SSBVGGxcK5AGsyC+pW5Q==
|
integrity sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr "^1.1.3"
|
chownr "^2.0.0"
|
||||||
fs-minipass "^2.0.0"
|
fs-minipass "^2.0.0"
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
minizlib "^2.1.0"
|
minizlib "^2.1.1"
|
||||||
mkdirp "^1.0.3"
|
mkdirp "^1.0.3"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
Welcome to Microsoft Azure Arc Extension for Azure Data Studio!
|
Welcome to Microsoft Azure Arc Extension for Azure Data Studio!
|
||||||
|
|
||||||
**This extension is only applicable to customers in the Azure Arc data services public preview.**
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This extension adds the following features to Azure Data Studio.
|
This extension adds the following features to Azure Data Studio.
|
||||||
|
|||||||
@@ -47,7 +47,8 @@
|
|||||||
"|Tools|Description|Installation|\n",
|
"|Tools|Description|Installation|\n",
|
||||||
"|---|---|---|\n",
|
"|---|---|---|\n",
|
||||||
"|kubectl | Command-line tool for monitoring the underlying Kubernetes cluster | [Installation](https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-binary-using-native-package-management) |\n",
|
"|kubectl | Command-line tool for monitoring the underlying Kubernetes cluster | [Installation](https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-binary-using-native-package-management) |\n",
|
||||||
"|Azure Data CLI (azdata) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/sql/azdata/install/deploy-install-azdata) |"
|
"|Azure CLI (az) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/cli/azure/install-azure-cli-windows?tabs=azure-cli) |\n",
|
||||||
|
"|Azure CLI arcdata extension | Commands for using Azure Arc for Azure data services. | [Installation](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension)"
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "714582b9-10ee-409e-ab12-15a4825c9471"
|
"azdata_cell_guid": "714582b9-10ee-409e-ab12-15a4825c9471"
|
||||||
@@ -64,8 +65,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"import sys,os,json,html,getpass,time, tempfile\n",
|
"import sys,os,getpass\n",
|
||||||
"def run_command(command):\n",
|
"def run_command(command):\n",
|
||||||
" print(\"Executing: \" + command)\n",
|
" print(\"Executing: \" + command)\n",
|
||||||
" !{command}\n",
|
" !{command}\n",
|
||||||
@@ -73,12 +75,11 @@
|
|||||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||||
" print(f'Successfully executed: {command}')"
|
" print(f'Successfully executed: {command}')"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "d973d5b4-7f0a-4a9d-b204-a16480f3940d",
|
"azdata_cell_guid": "d973d5b4-7f0a-4a9d-b204-a16480f3940d",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -101,15 +102,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"run_command('azdata --version')"
|
"run_command('az --version')"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "691671d7-3f05-406c-a183-4cff7d17f83d",
|
"azdata_cell_guid": "691671d7-3f05-406c-a183-4cff7d17f83d",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -122,6 +123,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"if \"AZDATA_NB_VAR_ARC_ADMIN_PASSWORD\" in os.environ:\n",
|
"if \"AZDATA_NB_VAR_ARC_ADMIN_PASSWORD\" in os.environ:\n",
|
||||||
" arc_admin_password = os.environ[\"AZDATA_NB_VAR_ARC_ADMIN_PASSWORD\"]\n",
|
" arc_admin_password = os.environ[\"AZDATA_NB_VAR_ARC_ADMIN_PASSWORD\"]\n",
|
||||||
@@ -134,12 +136,11 @@
|
|||||||
" if arc_admin_password != confirm_password:\n",
|
" if arc_admin_password != confirm_password:\n",
|
||||||
" sys.exit(f'Passwords do not match.')"
|
" sys.exit(f'Passwords do not match.')"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "e7e10828-6cae-45af-8c2f-1484b6d4f9ac",
|
"azdata_cell_guid": "e7e10828-6cae-45af-8c2f-1484b6d4f9ac",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -152,17 +153,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"os.environ[\"KUBECONFIG\"] = arc_config_file\n",
|
"os.environ[\"KUBECONFIG\"] = arc_config_file\n",
|
||||||
"run_command(f'kubectl config use-context {arc_cluster_context}')\n",
|
"run_command(f'kubectl config use-context {arc_cluster_context}')\n",
|
||||||
"run_command('kubectl config current-context')"
|
"run_command('kubectl config current-context')"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "7d1a03d4-1df8-48eb-bff0-0042603b95b1",
|
"azdata_cell_guid": "7d1a03d4-1df8-48eb-bff0-0042603b95b1",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -175,22 +176,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"print (f'Creating Azure Arc Data Controller: {arc_data_controller_name} using configuration {arc_cluster_context}')\n",
|
"print (f'Creating Azure Arc Data Controller: {arc_data_controller_name} using configuration {arc_cluster_context}')\n",
|
||||||
"os.environ[\"ACCEPT_EULA\"] = 'yes'\n",
|
|
||||||
"os.environ[\"AZDATA_USERNAME\"] = arc_admin_username\n",
|
"os.environ[\"AZDATA_USERNAME\"] = arc_admin_username\n",
|
||||||
"os.environ[\"AZDATA_PASSWORD\"] = arc_admin_password\n",
|
"os.environ[\"AZDATA_PASSWORD\"] = arc_admin_password\n",
|
||||||
|
"\n",
|
||||||
"if os.name == 'nt':\n",
|
"if os.name == 'nt':\n",
|
||||||
" print(f'If you don\\'t see output produced by azdata, you can run the following command in a terminal window to check the deployment status:\\n\\t {os.environ[\"AZDATA_NB_VAR_KUBECTL\"]} get pods -n {arc_data_controller_namespace}')\n",
|
" print(f'If you don\\'t see output produced by az, you can run the following command in a terminal window to check the deployment status:\\n\\t {os.environ[\"AZDATA_NB_VAR_KUBECTL\"]} get pods -n {arc_data_controller_namespace}')\n",
|
||||||
"run_command(f'azdata arc dc create --connectivity-mode Indirect -n {arc_data_controller_name} -ns {arc_data_controller_namespace} -s {arc_subscription} -g {arc_resource_group} -l {arc_data_controller_location} -sc {arc_data_controller_storage_class} --profile-name {arc_profile}')\n",
|
"run_command(f'az arcdata dc create --connectivity-mode indirect --name {arc_data_controller_name} --k8s-namespace {arc_data_controller_namespace} --subscription {arc_subscription} --resource-group {arc_resource_group} --location {arc_data_controller_location} --storage-class {arc_data_controller_storage_class} --profile-name {arc_profile} --infrastructure {arc_infrastructure} --use-k8s')\n",
|
||||||
"print(f'Azure Arc Data Controller: {arc_data_controller_name} created.') "
|
"print(f'Azure Arc Data Controller: {arc_data_controller_name} created.') "
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "373947a1-90b9-49ee-86f4-17a4c7d4ca76",
|
"azdata_cell_guid": "373947a1-90b9-49ee-86f4-17a4c7d4ca76",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -203,38 +204,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"# Setting context to Data Controller.\n",
|
"# Setting context to Data Controller.\n",
|
||||||
"#\n",
|
"#\n",
|
||||||
"run_command(f'kubectl config set-context --current --namespace {arc_data_controller_namespace}')"
|
"run_command(f'kubectl config set-context --current --namespace {arc_data_controller_namespace}')"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "c974561f-13d0-4e7a-b74b-d781c2e06d68"
|
"azdata_cell_guid": "c974561f-13d0-4e7a-b74b-d781c2e06d68"
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"source": [
|
|
||||||
"### **Login to the Data Controller.**\n"
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"azdata_cell_guid": "9376b2ab-0edf-478f-9e3c-5ff46ae3501a"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"# Login to the Data Controller.\n",
|
|
||||||
"#\n",
|
|
||||||
"run_command(f'azdata login --namespace {arc_data_controller_namespace}')"
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"azdata_cell_guid": "9aed0c5a-2c8a-4ad7-becb-60281923a196"
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,8 @@
|
|||||||
" \n",
|
" \n",
|
||||||
"|Tools|Description|Installation|\n",
|
"|Tools|Description|Installation|\n",
|
||||||
"|---|---|---|\n",
|
"|---|---|---|\n",
|
||||||
"|Azure Data CLI (azdata) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/sql/azdata/install/deploy-install-azdata) |"
|
"|Azure CLI (az) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/cli/azure/install-azure-cli-windows?tabs=azure-cli) |\n",
|
||||||
|
"|Azure CLI arcdata extension | Commands for using Azure Arc for Azure data services. | [Installation](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension)"
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "20fe3985-a01e-461c-bce0-235f7606cc3c"
|
"azdata_cell_guid": "20fe3985-a01e-461c-bce0-235f7606cc3c"
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"import sys,os,json,subprocess\n",
|
"import sys,os,json,subprocess\n",
|
||||||
"def run_command():\n",
|
"def run_command():\n",
|
||||||
@@ -76,16 +78,14 @@
|
|||||||
" print(f'Successfully executed: {cmd}')\n",
|
" print(f'Successfully executed: {cmd}')\n",
|
||||||
" print(f'\\t>>>Output: {output.stdout.decode(\"utf-8\")}\\n')\n",
|
" print(f'\\t>>>Output: {output.stdout.decode(\"utf-8\")}\\n')\n",
|
||||||
" return output.stdout.decode(\"utf-8\")\n",
|
" return output.stdout.decode(\"utf-8\")\n",
|
||||||
"cmd = 'azdata --version'\n",
|
"cmd = 'az --version'\n",
|
||||||
"out = run_command()\n",
|
"out = run_command()\n"
|
||||||
""
|
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "749d8dba-3da8-46e9-ae48-2b38056ab7a2",
|
"azdata_cell_guid": "749d8dba-3da8-46e9-ae48-2b38056ab7a2",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -111,48 +111,50 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"source": [
|
"execution_count": null,
|
||||||
"# Login to the data controller.\n",
|
|
||||||
"#\n",
|
|
||||||
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_CONTROLLER_PASSWORD\"]\n",
|
|
||||||
"os.environ[\"KUBECONFIG\"] = controller_kubeconfig\n",
|
|
||||||
"os.environ[\"KUBECTL_CONTEXT\"] = controller_kubectl_context\n",
|
|
||||||
"endpoint_option = f' -e {controller_endpoint}' if controller_endpoint else \"\"\n",
|
|
||||||
"cmd = f'azdata login --namespace {arc_data_controller_namespace} -u {controller_username}{endpoint_option}'\n",
|
|
||||||
"out=run_command()"
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"azdata_cell_guid": "71366399-5963-4e24-b2f2-6bb5bffba4ec"
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
"source": [
|
||||||
"print (f'Creating the PostgreSQL Hyperscale - Azure Arc instance')\n",
|
"print (f'Creating the PostgreSQL Hyperscale - Azure Arc instance')\n",
|
||||||
"\n",
|
"\n",
|
||||||
"workers_option = f' -w {postgres_server_group_workers}' if postgres_server_group_workers else \"\"\n",
|
"workers = f' --workers {postgres_server_group_workers}' if postgres_server_group_workers else \"\"\n",
|
||||||
"port_option = f' --port \"{postgres_server_group_port}\"' if postgres_server_group_port else \"\"\n",
|
"port = f' --port \"{postgres_server_group_port}\"' if postgres_server_group_port else \"\"\n",
|
||||||
"engine_version_option = f' -ev {postgres_server_group_engine_version}' if postgres_server_group_engine_version else \"\"\n",
|
"engine_version = f' --engine-version {postgres_server_group_engine_version}' if postgres_server_group_engine_version else \"\"\n",
|
||||||
"extensions_option = f' --extensions \"{postgres_server_group_extensions}\"' if postgres_server_group_extensions else \"\"\n",
|
"extensions = f' --extensions \"{postgres_server_group_extensions}\"' if postgres_server_group_extensions else \"\"\n",
|
||||||
"volume_size_data_option = f' -vsd {postgres_server_group_volume_size_data}Gi' if postgres_server_group_volume_size_data else \"\"\n",
|
"volume_size_data = f' --volume-size-data {postgres_server_group_volume_size_data}Gi' if postgres_server_group_volume_size_data else \"\"\n",
|
||||||
"volume_size_logs_option = f' -vsl {postgres_server_group_volume_size_logs}Gi' if postgres_server_group_volume_size_logs else \"\"\n",
|
"volume_size_logs = f' --volume-size-logs {postgres_server_group_volume_size_logs}Gi' if postgres_server_group_volume_size_logs else \"\"\n",
|
||||||
"volume_size_backups_option = f' -vsb {postgres_server_group_volume_size_backups}Gi' if postgres_server_group_volume_size_backups else \"\"\n",
|
"volume_size_backups = f' --volume-size-backups {postgres_server_group_volume_size_backups}Gi' if postgres_server_group_volume_size_backups else \"\"\n",
|
||||||
"cores_request_option = f' -cr \"c={postgres_server_group_coordinator_cores_request},w={postgres_server_group_workers_cores_request}\"' if postgres_server_group_coordinator_cores_request and postgres_server_group_workers_cores_request else f' -cr \"c={postgres_server_group_coordinator_cores_request}\"' if postgres_server_group_coordinator_cores_request else f' -cr \"w={postgres_server_group_workers_cores_request}\"' if postgres_server_group_workers_cores_request else \"\"\n",
|
"\n",
|
||||||
"cores_limit_option = f' -cl \"c={postgres_server_group_coordinator_cores_limit},w={postgres_server_group_workers_cores_limit}\"' if postgres_server_group_coordinator_cores_limit and postgres_server_group_workers_cores_limit else f' -cl \"c={postgres_server_group_coordinator_cores_limit}\"' if postgres_server_group_coordinator_cores_limit else f' -cl \"w={postgres_server_group_workers_cores_limit}\"' if postgres_server_group_workers_cores_limit else \"\"\n",
|
"def get_per_role_argument(argument, roles, unit=''):\n",
|
||||||
"memory_request_option = f' -mr \"c={postgres_server_group_coordinator_memory_request}Gi,w={postgres_server_group_workers_memory_request}Gi\"' if postgres_server_group_coordinator_memory_request and postgres_server_group_workers_memory_request else f' -mr \"c={postgres_server_group_coordinator_memory_request}Gi\"' if postgres_server_group_coordinator_memory_request else f' -mr \"w={postgres_server_group_workers_memory_request}Gi\"' if postgres_server_group_workers_memory_request else \"\"\n",
|
" value = ','.join(f'{role}={value}{unit}' for role,value in roles.items() if value not in (None, ''))\n",
|
||||||
"memory_limit_option = f' -ml \"c={postgres_server_group_coordinator_memory_limit}Gi,w={postgres_server_group_workers_memory_limit}Gi\"' if postgres_server_group_coordinator_memory_limit and postgres_server_group_workers_memory_limit else f' -ml \"c={postgres_server_group_coordinator_memory_limit}Gi\"' if postgres_server_group_coordinator_memory_limit else f' -ml \"w={postgres_server_group_workers_memory_limit}Gi\"' if postgres_server_group_workers_memory_limit else \"\"\n",
|
" return f' {argument} {value}' if value else ''\n",
|
||||||
|
"\n",
|
||||||
|
"cores_request = get_per_role_argument('--cores-request', {\n",
|
||||||
|
" 'c': postgres_server_group_coordinator_cores_request,\n",
|
||||||
|
" 'w': postgres_server_group_workers_cores_request\n",
|
||||||
|
"})\n",
|
||||||
|
"\n",
|
||||||
|
"cores_limit = get_per_role_argument('--cores-limit', {\n",
|
||||||
|
" 'c': postgres_server_group_coordinator_cores_limit,\n",
|
||||||
|
" 'w': postgres_server_group_workers_cores_limit\n",
|
||||||
|
"})\n",
|
||||||
|
"\n",
|
||||||
|
"memory_request = get_per_role_argument('--memory-request', {\n",
|
||||||
|
" 'c': postgres_server_group_coordinator_memory_request,\n",
|
||||||
|
" 'w': postgres_server_group_workers_memory_request\n",
|
||||||
|
"}, 'Gi')\n",
|
||||||
|
"\n",
|
||||||
|
"memory_limit = get_per_role_argument('--memory-limit', {\n",
|
||||||
|
" 'c': postgres_server_group_coordinator_memory_limit,\n",
|
||||||
|
" 'w': postgres_server_group_workers_memory_limit\n",
|
||||||
|
"}, 'Gi')\n",
|
||||||
"\n",
|
"\n",
|
||||||
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_POSTGRES_SERVER_GROUP_PASSWORD\"]\n",
|
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_POSTGRES_SERVER_GROUP_PASSWORD\"]\n",
|
||||||
"cmd = f'azdata arc postgres server create -n {postgres_server_group_name} -scd {postgres_storage_class_data} -scl {postgres_storage_class_logs} -scb {postgres_storage_class_backups}{workers_option}{port_option}{engine_version_option}{extensions_option}{volume_size_data_option}{volume_size_logs_option}{volume_size_backups_option}{cores_request_option}{cores_limit_option}{memory_request_option}{memory_limit_option}'\n",
|
"cmd = f'az postgres arc-server create --name {postgres_server_group_name} --k8s-namespace {arc_data_controller_namespace} --use-k8s --storage-class-data {postgres_storage_class_data} --storage-class-logs {postgres_storage_class_logs} --storage-class-backups {postgres_storage_class_backups}{workers}{port}{engine_version}{extensions}{volume_size_data}{volume_size_logs}{volume_size_backups}{cores_request}{cores_limit}{memory_request}{memory_limit}'\n",
|
||||||
"out=run_command()"
|
"out=run_command()"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "4fbaf071-55a1-40bc-be7e-7b9b5547b886"
|
"azdata_cell_guid": "4fbaf071-55a1-40bc-be7e-7b9b5547b886"
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,8 @@
|
|||||||
" \n",
|
" \n",
|
||||||
"|Tools|Description|Installation|\n",
|
"|Tools|Description|Installation|\n",
|
||||||
"|---|---|---|\n",
|
"|---|---|---|\n",
|
||||||
"|Azure Data CLI (azdata) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/sql/azdata/install/deploy-install-azdata) |"
|
"|Azure CLI (az) | Command-line tool for installing and managing resources in an Azure Arc cluster |[Installation](https://docs.microsoft.com/cli/azure/install-azure-cli-windows?tabs=azure-cli) |\n",
|
||||||
|
"|Azure CLI arcdata extension | Commands for using Azure Arc for Azure data services. | [Installation](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension)"
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "d1c8258e-9efd-4380-a48c-cd675423ed2f"
|
"azdata_cell_guid": "d1c8258e-9efd-4380-a48c-cd675423ed2f"
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"import sys,os,json,subprocess\n",
|
"import sys,os,json,subprocess\n",
|
||||||
"def run_command():\n",
|
"def run_command():\n",
|
||||||
@@ -76,16 +78,14 @@
|
|||||||
" print(f'Successfully executed: {cmd}')\n",
|
" print(f'Successfully executed: {cmd}')\n",
|
||||||
" print(f'\\t>>>Output: {output.stdout.decode(\"utf-8\")}\\n')\n",
|
" print(f'\\t>>>Output: {output.stdout.decode(\"utf-8\")}\\n')\n",
|
||||||
" return output.stdout.decode(\"utf-8\")\n",
|
" return output.stdout.decode(\"utf-8\")\n",
|
||||||
"cmd = 'azdata --version'\n",
|
"cmd = 'az --version'\n",
|
||||||
"out = run_command()\n",
|
"out = run_command()\n"
|
||||||
""
|
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "749d8dba-3da8-46e9-ae48-2b38056ab7a2",
|
"azdata_cell_guid": "749d8dba-3da8-46e9-ae48-2b38056ab7a2",
|
||||||
"tags": []
|
"tags": []
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
@@ -111,41 +111,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"source": [
|
"execution_count": null,
|
||||||
"# Login to the data controller.\n",
|
|
||||||
"#\n",
|
|
||||||
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_CONTROLLER_PASSWORD\"]\n",
|
|
||||||
"os.environ[\"KUBECONFIG\"] = controller_kubeconfig\n",
|
|
||||||
"os.environ[\"KUBECTL_CONTEXT\"] = controller_kubectl_context\n",
|
|
||||||
"cmd = f'azdata login -e {controller_endpoint} -u {controller_username}'\n",
|
|
||||||
"out=run_command()"
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"azdata_cell_guid": "1437c536-17e8-4a7f-80c1-aa43ad02686c"
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
"source": [
|
||||||
"print (f'Creating the SQL managed instance - Azure Arc instance')\n",
|
"print (f'Creating the SQL managed instance - Azure Arc instance')\n",
|
||||||
"\n",
|
"\n",
|
||||||
"cores_request_option = f' -cr \"{sql_cores_request}\"' if sql_cores_request else \"\"\n",
|
"cores_request_option = f' --cores-request \"{sql_cores_request}\"' if sql_cores_request else \"\"\n",
|
||||||
"cores_limit_option = f' -cl \"{sql_cores_limit}\"' if sql_cores_limit else \"\"\n",
|
"cores_limit_option = f' --cores-limit \"{sql_cores_limit}\"' if sql_cores_limit else \"\"\n",
|
||||||
"memory_request_option = f' -mr \"{sql_memory_request}Gi\"' if sql_memory_request else \"\"\n",
|
"memory_request_option = f' --memory-request \"{sql_memory_request}Gi\"' if sql_memory_request else \"\"\n",
|
||||||
"memory_limit_option = f' -ml \"{sql_memory_limit}Gi\"' if sql_memory_limit else \"\"\n",
|
"memory_limit_option = f' --memory-limit \"{sql_memory_limit}Gi\"' if sql_memory_limit else \"\"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"os.environ[\"AZDATA_USERNAME\"] = sql_username\n",
|
"os.environ[\"AZDATA_USERNAME\"] = sql_username\n",
|
||||||
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_SQL_PASSWORD\"]\n",
|
"os.environ[\"AZDATA_PASSWORD\"] = os.environ[\"AZDATA_NB_VAR_SQL_PASSWORD\"]\n",
|
||||||
"cmd = f'azdata arc sql mi create -n {sql_instance_name} -scd {sql_storage_class_data} -scl {sql_storage_class_logs} --replicas {sql_replicas}{cores_request_option}{cores_limit_option}{memory_request_option}{memory_limit_option}'\n",
|
"cmd = f'az sql mi-arc create --name {sql_instance_name} --k8s-namespace {arc_data_controller_namespace} --replicas {sql_replicas}{cores_request_option}{cores_limit_option}{memory_request_option}{memory_limit_option} --use-k8s'\n",
|
||||||
"out=run_command()"
|
"out=run_command()"
|
||||||
],
|
],
|
||||||
|
"outputs": [],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"azdata_cell_guid": "4fbaf071-55a1-40bc-be7e-7b9b5547b886"
|
"azdata_cell_guid": "4fbaf071-55a1-40bc-be7e-7b9b5547b886"
|
||||||
},
|
}
|
||||||
"outputs": [],
|
|
||||||
"execution_count": null
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"icon": "images/extension.png",
|
"icon": "images/extension.png",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "*",
|
"vscode": "*",
|
||||||
"azdata": ">=1.28.0"
|
"azdata": ">=1.32.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onCommand:arc.connectToController",
|
"onCommand:arc.connectToController",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"onView:azureArc"
|
"onView:azureArc"
|
||||||
],
|
],
|
||||||
"extensionDependencies": [
|
"extensionDependencies": [
|
||||||
"Microsoft.azdata",
|
"Microsoft.azcli",
|
||||||
"Microsoft.resource-deployment"
|
"Microsoft.resource-deployment"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
"editable": false,
|
"editable": false,
|
||||||
"options": {
|
"options": {
|
||||||
"source": {
|
"source": {
|
||||||
"providerId": "arc.controller.config.profiles",
|
"providerId": "azcli.arc.controller.config.profiles",
|
||||||
"loadingText": "%arc.data.controller.cluster.config.profile.loading%",
|
"loadingText": "%arc.data.controller.cluster.config.profile.loading%",
|
||||||
"loadingCompletedText": "%arc.data.controller.cluster.config.profile.loadingcompleted%"
|
"loadingCompletedText": "%arc.data.controller.cluster.config.profile.loadingcompleted%"
|
||||||
},
|
},
|
||||||
@@ -289,6 +289,21 @@
|
|||||||
"variableName": "AZDATA_NB_VAR_ARC_DATA_CONTROLLER_STORAGE_CLASS",
|
"variableName": "AZDATA_NB_VAR_ARC_DATA_CONTROLLER_STORAGE_CLASS",
|
||||||
"type": "kube_storage_class",
|
"type": "kube_storage_class",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "options",
|
||||||
|
"label": "%arc.data.controller.infrastructure%",
|
||||||
|
"defaultValue": "azure",
|
||||||
|
"required": true,
|
||||||
|
"variableName": "AZDATA_NB_VAR_ARC_INFRASTRUCTURE",
|
||||||
|
"options": [
|
||||||
|
"azure",
|
||||||
|
"gcp",
|
||||||
|
"aws",
|
||||||
|
"alibaba",
|
||||||
|
"onpremises",
|
||||||
|
"other"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -507,6 +522,12 @@
|
|||||||
"type": "readonly_text",
|
"type": "readonly_text",
|
||||||
"isEvaluated": true,
|
"isEvaluated": true,
|
||||||
"defaultValue": "$(AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAME)"
|
"defaultValue": "$(AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAME)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "%arc.data.controller.summary.data.controller.infrastructure%",
|
||||||
|
"type": "readonly_text",
|
||||||
|
"isEvaluated": true,
|
||||||
|
"defaultValue": "$(AZDATA_NB_VAR_ARC_INFRASTRUCTURE)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -519,8 +540,7 @@
|
|||||||
"name": "kubectl"
|
"name": "kubectl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "azdata",
|
"name": "azure-cli"
|
||||||
"version": "20.3.4"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"when": true
|
"when": true
|
||||||
@@ -573,11 +593,8 @@
|
|||||||
"providerId": "arc.controllers",
|
"providerId": "arc.controllers",
|
||||||
"variableNames": {
|
"variableNames": {
|
||||||
"namespace": "AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAMESPACE",
|
"namespace": "AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAMESPACE",
|
||||||
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
|
||||||
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
|
||||||
"kubeConfig": "AZDATA_NB_VAR_CONTROLLER_KUBECONFIG",
|
"kubeConfig": "AZDATA_NB_VAR_CONTROLLER_KUBECONFIG",
|
||||||
"clusterContext": "AZDATA_NB_VAR_CONTROLLER_KUBECTL_CONTEXT",
|
"clusterContext": "AZDATA_NB_VAR_CONTROLLER_KUBECTL_CONTEXT"
|
||||||
"password": "AZDATA_NB_VAR_CONTROLLER_PASSWORD"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optionsType": "dropdown"
|
"optionsType": "dropdown"
|
||||||
@@ -838,8 +855,7 @@
|
|||||||
"name": "kubectl"
|
"name": "kubectl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "azdata",
|
"name": "azure-cli"
|
||||||
"version": "20.3.4"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"when": "true"
|
"when": "true"
|
||||||
@@ -915,11 +931,9 @@
|
|||||||
"source": {
|
"source": {
|
||||||
"providerId": "arc.controllers",
|
"providerId": "arc.controllers",
|
||||||
"variableNames": {
|
"variableNames": {
|
||||||
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
"namespace": "AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAMESPACE",
|
||||||
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
|
||||||
"kubeConfig": "AZDATA_NB_VAR_CONTROLLER_KUBECONFIG",
|
"kubeConfig": "AZDATA_NB_VAR_CONTROLLER_KUBECONFIG",
|
||||||
"clusterContext": "AZDATA_NB_VAR_CONTROLLER_KUBECTL_CONTEXT",
|
"clusterContext": "AZDATA_NB_VAR_CONTROLLER_KUBECTL_CONTEXT"
|
||||||
"password": "AZDATA_NB_VAR_CONTROLLER_PASSWORD"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optionsType": "dropdown"
|
"optionsType": "dropdown"
|
||||||
@@ -1067,8 +1081,7 @@
|
|||||||
"name": "kubectl"
|
"name": "kubectl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "azdata",
|
"name": "azure-cli"
|
||||||
"version": "20.3.4"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"when": "mi-type=arc-mi"
|
"when": "mi-type=arc-mi"
|
||||||
@@ -1092,6 +1105,9 @@
|
|||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"text": "%arc.agreement.sql.help.text.learn.more%",
|
"text": "%arc.agreement.sql.help.text.learn.more%",
|
||||||
|
"accessibilityInformation": {
|
||||||
|
"label": "%arc.agreement.sql.help.text.learn.more.ariaLabel%"
|
||||||
|
},
|
||||||
"url": "https://go.microsoft.com/fwlink/?linkid=2141849"
|
"url": "https://go.microsoft.com/fwlink/?linkid=2141849"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"arc.data.controller.name": "Data controller name",
|
"arc.data.controller.name": "Data controller name",
|
||||||
"arc.data.controller.name.validation.description": "Name must consist of lower case alphanumeric characters, '-' or '.', start/end with an alphanumeric character and be 253 characters or less in length.",
|
"arc.data.controller.name.validation.description": "Name must consist of lower case alphanumeric characters, '-' or '.', start/end with an alphanumeric character and be 253 characters or less in length.",
|
||||||
"arc.data.controller.location": "Location",
|
"arc.data.controller.location": "Location",
|
||||||
|
"arc.data.controller.infrastructure": "Infrastructure",
|
||||||
"arc.data.controller.admin.account.title": "Administrator account",
|
"arc.data.controller.admin.account.title": "Administrator account",
|
||||||
"arc.data.controller.admin.account.name": "Data controller login",
|
"arc.data.controller.admin.account.name": "Data controller login",
|
||||||
"arc.data.controller.admin.account.password": "Password",
|
"arc.data.controller.admin.account.password": "Password",
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
"arc.data.controller.summary.resource.group": "Resource group",
|
"arc.data.controller.summary.resource.group": "Resource group",
|
||||||
"arc.data.controller.summary.data.controller.name": "Data controller name",
|
"arc.data.controller.summary.data.controller.name": "Data controller name",
|
||||||
"arc.data.controller.summary.data.controller.namespace": "Data controller namespace",
|
"arc.data.controller.summary.data.controller.namespace": "Data controller namespace",
|
||||||
|
"arc.data.controller.summary.data.controller.infrastructure": "Data controller infrastructure",
|
||||||
"arc.data.controller.summary.controller": "Controller",
|
"arc.data.controller.summary.controller": "Controller",
|
||||||
"arc.data.controller.summary.location": "Location",
|
"arc.data.controller.summary.location": "Location",
|
||||||
"arc.data.controller.agreement": "I accept {0} and {1}.",
|
"arc.data.controller.agreement": "I accept {0} and {1}.",
|
||||||
@@ -153,6 +155,7 @@
|
|||||||
"cores.limit.greater.than.or.equal.to.requested.cores": "Cores limit must be greater than or equal to requested cores",
|
"cores.limit.greater.than.or.equal.to.requested.cores": "Cores limit must be greater than or equal to requested cores",
|
||||||
"requested.memory.less.than.or.equal.to.memory.limit": "Requested memory must be less than or equal to memory limit",
|
"requested.memory.less.than.or.equal.to.memory.limit": "Requested memory must be less than or equal to memory limit",
|
||||||
"memory.limit.greater.than.or.equal.to.requested.memory": "Memory limit must be greater than or equal to requested memory",
|
"memory.limit.greater.than.or.equal.to.requested.memory": "Memory limit must be greater than or equal to requested memory",
|
||||||
"arc.agreement.sql.help.text": "Azure Arc enabled Managed Instance provides SQL Server access and feature compatibility that can be deployed on the infrastructure of your choice. While this service is in preview, it has some feature limitations compared to SQL Managed Instance on Azure. {0}",
|
"arc.agreement.sql.help.text": "Azure Arc enabled Managed Instance provides SQL Server access and feature compatibility that can be deployed on the infrastructure of your choice. {0}",
|
||||||
"arc.agreement.sql.help.text.learn.more": "Learn more"
|
"arc.agreement.sql.help.text.learn.more": "Learn more",
|
||||||
|
"arc.agreement.sql.help.text.learn.more.ariaLabel": "Learn more about Azure Arc enabled Managed Instance"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
import * as arc from 'arc';
|
import * as arc from 'arc';
|
||||||
import * as rd from 'resource-deployment';
|
import * as rd from 'resource-deployment';
|
||||||
import * as loc from '../localizedConstants';
|
|
||||||
import { PasswordToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
import { ControllerTreeNode } from '../ui/tree/controllerTreeNode';
|
import { ControllerTreeNode } from '../ui/tree/controllerTreeNode';
|
||||||
|
|
||||||
@@ -17,26 +15,10 @@ export class UserCancelledError extends Error implements rd.ErrorWithType {
|
|||||||
}
|
}
|
||||||
export function arcApi(treeDataProvider: AzureArcTreeDataProvider): arc.IExtension {
|
export function arcApi(treeDataProvider: AzureArcTreeDataProvider): arc.IExtension {
|
||||||
return {
|
return {
|
||||||
getRegisteredDataControllers: () => getRegisteredDataControllers(treeDataProvider),
|
getRegisteredDataControllers: () => getRegisteredDataControllers(treeDataProvider)
|
||||||
getControllerPassword: (controllerInfo: arc.ControllerInfo) => getControllerPassword(treeDataProvider, controllerInfo),
|
|
||||||
reacquireControllerPassword: (controllerInfo: arc.ControllerInfo) => reacquireControllerPassword(treeDataProvider, controllerInfo)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function reacquireControllerPassword(treeDataProvider: AzureArcTreeDataProvider, controllerInfo: arc.ControllerInfo): Promise<string> {
|
|
||||||
const dialog = new PasswordToControllerDialog(treeDataProvider);
|
|
||||||
dialog.showDialog(controllerInfo);
|
|
||||||
const model = await dialog.waitForClose();
|
|
||||||
if (!model) {
|
|
||||||
throw new UserCancelledError(loc.userCancelledError);
|
|
||||||
}
|
|
||||||
return model.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getControllerPassword(treeDataProvider: AzureArcTreeDataProvider, controllerInfo: arc.ControllerInfo): Promise<string> {
|
|
||||||
return await treeDataProvider.getPassword(controllerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getRegisteredDataControllers(treeDataProvider: AzureArcTreeDataProvider): Promise<arc.DataController[]> {
|
export async function getRegisteredDataControllers(treeDataProvider: AzureArcTreeDataProvider): Promise<arc.DataController[]> {
|
||||||
return (await treeDataProvider.getChildren())
|
return (await treeDataProvider.getChildren())
|
||||||
.filter(node => node instanceof ControllerTreeNode)
|
.filter(node => node instanceof ControllerTreeNode)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<arc.IE
|
|||||||
dialog.showDialog();
|
dialog.showDialog();
|
||||||
const model = await dialog.waitForClose();
|
const model = await dialog.waitForClose();
|
||||||
if (model) {
|
if (model) {
|
||||||
await treeDataProvider.addOrUpdateController(model.controllerModel, model.password);
|
await treeDataProvider.addOrUpdateController(model.controllerModel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -50,10 +50,10 @@ export async function activate(context: vscode.ExtensionContext): Promise<arc.IE
|
|||||||
|
|
||||||
vscode.commands.registerCommand('arc.editConnection', async (treeNode: ControllerTreeNode) => {
|
vscode.commands.registerCommand('arc.editConnection', async (treeNode: ControllerTreeNode) => {
|
||||||
const dialog = new ConnectToControllerDialog(treeDataProvider);
|
const dialog = new ConnectToControllerDialog(treeDataProvider);
|
||||||
dialog.showDialog(treeNode.model.info, await treeDataProvider.getPassword(treeNode.model.info));
|
dialog.showDialog(treeNode.model.info);
|
||||||
const model = await dialog.waitForClose();
|
const model = await dialog.waitForClose();
|
||||||
if (model) {
|
if (model) {
|
||||||
await treeDataProvider.addOrUpdateController(model.controllerModel, model.password, true);
|
await treeDataProvider.addOrUpdateController(model.controllerModel, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,9 @@ export const dropText = localize('arc.drop', "Drop");
|
|||||||
export const saveText = localize('arc.save', "Save");
|
export const saveText = localize('arc.save', "Save");
|
||||||
export const discardText = localize('arc.discard', "Discard");
|
export const discardText = localize('arc.discard', "Discard");
|
||||||
export const resetPassword = localize('arc.resetPassword', "Reset Password");
|
export const resetPassword = localize('arc.resetPassword', "Reset Password");
|
||||||
export const addExtensions = localize('arc.addExtensions', "Add extensions");
|
export const loadExtensions = localize('arc.loadExtensions', "Load extensions");
|
||||||
export const dropExtension = localize('arc.dropExtension', "Drop extension");
|
export const unloadExtensions = localize('arc.unloadExtensions', "Unload extensions");
|
||||||
|
export const noExtensions = localize('arc.noExtensions', "No extensions listed in configuration.");
|
||||||
export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal");
|
export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal");
|
||||||
export const resourceGroup = localize('arc.resourceGroup', "Resource Group");
|
export const resourceGroup = localize('arc.resourceGroup', "Resource Group");
|
||||||
export const region = localize('arc.region', "Region");
|
export const region = localize('arc.region', "Region");
|
||||||
@@ -60,6 +61,8 @@ export const controllerEndpoint = localize('arc.controllerEndpoint', "Controller
|
|||||||
export const extensionName = localize('arc.extensionName', "Extension name");
|
export const extensionName = localize('arc.extensionName', "Extension name");
|
||||||
export const extensionsDescription = localize('arc.extensionsDescription', "PostgreSQL provides the ability to extend the functionality of your database by using extensions. Extensions allow for bundling multiple related SQL objects together in a single package that can be loaded or removed from your database with a single command. After being loaded in the database, extensions can function like built-in features.");
|
export const extensionsDescription = localize('arc.extensionsDescription', "PostgreSQL provides the ability to extend the functionality of your database by using extensions. Extensions allow for bundling multiple related SQL objects together in a single package that can be loaded or removed from your database with a single command. After being loaded in the database, extensions can function like built-in features.");
|
||||||
export const extensionsFunction = localize('arc.extensionsFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. These preloaded extensions can be viewed and edited below.");
|
export const extensionsFunction = localize('arc.extensionsFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. These preloaded extensions can be viewed and edited below.");
|
||||||
|
export function extensionsAddFunction(extensions: string): string { return localize('arc.extensionsAddFunction', "Some extensions must be loaded into PostgreSQL at startup time before they can be used. To edit, type in comma separated list of valid extensions: ({0}).", extensions); }
|
||||||
|
export function extensionsAddErrorrMessage(extensions: string): string { return localize('arc.extensionsAddErrorrMessage', "Value should be either of the following: ({0}).", extensions); }
|
||||||
export const extensionsLearnMore = localize('arc.extensionsLearnMore', "Learn more about PostgreSQL extensions.");
|
export const extensionsLearnMore = localize('arc.extensionsLearnMore', "Learn more about PostgreSQL extensions.");
|
||||||
export const extensionsTableLoading = localize('arc.extensionsTableLoading', "Table of preloaded extensions are loading.");
|
export const extensionsTableLoading = localize('arc.extensionsTableLoading', "Table of preloaded extensions are loading.");
|
||||||
export const extensionsTableLabel = localize('arc.extensionsTableLabel', "Table of preloaded extensions.");
|
export const extensionsTableLabel = localize('arc.extensionsTableLabel', "Table of preloaded extensions.");
|
||||||
@@ -72,6 +75,7 @@ export const grafanaDashboard = localize('arc.grafanaDashboard', "Grafana Dashbo
|
|||||||
export const kibanaDashboardDescription = localize('arc.kibanaDashboardDescription', "Dashboard for viewing logs");
|
export const kibanaDashboardDescription = localize('arc.kibanaDashboardDescription', "Dashboard for viewing logs");
|
||||||
export const grafanaDashboardDescription = localize('arc.grafanaDashboardDescription', "Dashboard for viewing metrics");
|
export const grafanaDashboardDescription = localize('arc.grafanaDashboardDescription', "Dashboard for viewing metrics");
|
||||||
export const serviceEndpoints = localize('arc.serviceEndpoints', "Service endpoints");
|
export const serviceEndpoints = localize('arc.serviceEndpoints', "Service endpoints");
|
||||||
|
export const serviceEndpointsTable = localize('arc.serviceEndpointsTable', "Service endpoints table");
|
||||||
export const databases = localize('arc.databases', "Databases");
|
export const databases = localize('arc.databases', "Databases");
|
||||||
export const endpoint = localize('arc.endpoint', "Endpoint");
|
export const endpoint = localize('arc.endpoint', "Endpoint");
|
||||||
export const description = localize('arc.description', "Description");
|
export const description = localize('arc.description', "Description");
|
||||||
@@ -206,6 +210,8 @@ export const noWorkerPods = localize('arc.noWorkerPods', "No worker pods in this
|
|||||||
export const podsReady = localize('arc.podsReady', "pods ready");
|
export const podsReady = localize('arc.podsReady', "pods ready");
|
||||||
export const podsPresent = localize('arc.podsPresent', "Pods Present");
|
export const podsPresent = localize('arc.podsPresent', "Pods Present");
|
||||||
export const podsUsedDescription = localize('arc.podsUsedDescription', "Select a pod in the dropdown below for detailed health information.");
|
export const podsUsedDescription = localize('arc.podsUsedDescription', "Select a pod in the dropdown below for detailed health information.");
|
||||||
|
export const podsUsedDescriptionAria = localize('arc.podsUsedDescriptionAria', "Select a pod in the dropdown below for detailed health information");
|
||||||
|
export const podConditionsTable = localize('arc.podConditionsTable', "Pod conditions table");
|
||||||
export const connectToPostgresDescription = localize('arc.connectToPostgresDescription', "A connection to the server is required to show and set database engine settings, which will require the PostgreSQL Extension to be installed.");
|
export const connectToPostgresDescription = localize('arc.connectToPostgresDescription', "A connection to the server is required to show and set database engine settings, which will require the PostgreSQL Extension to be installed.");
|
||||||
export const postgresExtension = localize('arc.postgresExtension', "microsoft.azuredatastudio-postgresql");
|
export const postgresExtension = localize('arc.postgresExtension', "microsoft.azuredatastudio-postgresql");
|
||||||
export const podInitialized = localize('arc.podInitialized', "Pod is initialized.");
|
export const podInitialized = localize('arc.podInitialized', "Pod is initialized.");
|
||||||
@@ -223,7 +229,7 @@ export function extensionInstalled(name: string): string { return localize('arc.
|
|||||||
export function updatingInstance(name: string): string { return localize('arc.updatingInstance', "Updating instance '{0}'...", name); }
|
export function updatingInstance(name: string): string { return localize('arc.updatingInstance', "Updating instance '{0}'...", name); }
|
||||||
export function instanceDeleted(name: string): string { return localize('arc.instanceDeleted', "Instance '{0}' deleted", name); }
|
export function instanceDeleted(name: string): string { return localize('arc.instanceDeleted', "Instance '{0}' deleted", name); }
|
||||||
export function instanceUpdated(name: string): string { return localize('arc.instanceUpdated', "Instance '{0}' updated", name); }
|
export function instanceUpdated(name: string): string { return localize('arc.instanceUpdated', "Instance '{0}' updated", name); }
|
||||||
export function extensionDropped(name: string): string { return localize('arc.extensionDropped', "Extension '{0}' deleted", name); }
|
export function extensionsDropped(name: string): string { return localize('arc.extensionsDropped', "Extensions '{0}' dropped", name); }
|
||||||
export function extensionsAdded(name: string): string { return localize('arc.extensionsAdded', "Extensions '{0}' added", name); }
|
export function extensionsAdded(name: string): string { return localize('arc.extensionsAdded', "Extensions '{0}' added", name); }
|
||||||
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
||||||
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
||||||
@@ -242,6 +248,7 @@ export function numVCores(vCores: string | undefined): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function updated(when: string): string { return localize('arc.updated', "Updated {0}", when); }
|
export function updated(when: string): string { return localize('arc.updated', "Updated {0}", when); }
|
||||||
|
export function connectionString(type: string): string { return localize({ key: 'arc.connectionString', comment: ['{0} is the name of the type of connection string (e.g. Java)'] }, "Connection string for {0}", type); }
|
||||||
export function copyConnectionStringToClipboard(type: string): string { return localize({ key: 'arc.copyConnectionStringToClipboard', comment: ['{0} is the name of the type of connection string (e.g. Java)'] }, "Copy {0} Connection String to clipboard", type); }
|
export function copyConnectionStringToClipboard(type: string): string { return localize({ key: 'arc.copyConnectionStringToClipboard', comment: ['{0} is the name of the type of connection string (e.g. Java)'] }, "Copy {0} Connection String to clipboard", type); }
|
||||||
export function copyValueToClipboard(valueName: string): string { return localize({ key: 'arc.copyValueToClipboard', comment: ['{0} is the name of the type of value being copied (e.g. Coordinator endpoint)'] }, "Copy {0} to clipboard", valueName); }
|
export function copyValueToClipboard(valueName: string): string { return localize({ key: 'arc.copyValueToClipboard', comment: ['{0} is the name of the type of value being copied (e.g. Coordinator endpoint)'] }, "Copy {0} to clipboard", valueName); }
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,9 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo, ResourceType } from 'arc';
|
import { ControllerInfo, ResourceType } from 'arc';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { UserCancelledError } from '../common/api';
|
|
||||||
import { getCurrentClusterContext, getKubeConfigClusterContexts } from '../common/kubeUtils';
|
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
|
|
||||||
export type Registration = {
|
export type Registration = {
|
||||||
@@ -19,13 +16,13 @@ export type Registration = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class ControllerModel {
|
export class ControllerModel {
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
private _endpoints: azdataExt.DcEndpointListResult[] = [];
|
private _endpoints: azExt.DcEndpointListResult[] = [];
|
||||||
private _registrations: Registration[] = [];
|
private _registrations: Registration[] = [];
|
||||||
private _controllerConfig: azdataExt.DcConfigShowResult | undefined = undefined;
|
private _controllerConfig: azExt.DcConfigShowResult | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.DcConfigShowResult | undefined>();
|
private readonly _onConfigUpdated = new vscode.EventEmitter<azExt.DcConfigShowResult | undefined>();
|
||||||
private readonly _onEndpointsUpdated = new vscode.EventEmitter<azdataExt.DcEndpointListResult[]>();
|
private readonly _onEndpointsUpdated = new vscode.EventEmitter<azExt.DcEndpointListResult[]>();
|
||||||
private readonly _onRegistrationsUpdated = new vscode.EventEmitter<Registration[]>();
|
private readonly _onRegistrationsUpdated = new vscode.EventEmitter<Registration[]>();
|
||||||
private readonly _onInfoUpdated = new vscode.EventEmitter<ControllerInfo>();
|
private readonly _onInfoUpdated = new vscode.EventEmitter<ControllerInfo>();
|
||||||
|
|
||||||
@@ -38,85 +35,26 @@ export class ControllerModel {
|
|||||||
public endpointsLastUpdated?: Date;
|
public endpointsLastUpdated?: Date;
|
||||||
public registrationsLastUpdated?: Date;
|
public registrationsLastUpdated?: Date;
|
||||||
|
|
||||||
constructor(public treeDataProvider: AzureArcTreeDataProvider, private _info: ControllerInfo, private _password?: string) {
|
constructor(public treeDataProvider: AzureArcTreeDataProvider, private _info: ControllerInfo) {
|
||||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = <azExt.IExtension>vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get info(): ControllerInfo {
|
public get info(): ControllerInfo {
|
||||||
return this._info;
|
return this._info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the controller context to use when executing azdata commands. This is in one of two forms :
|
|
||||||
*
|
|
||||||
* If no URL is specified for this controller then just the namespace is used (e.g. test-namespace)
|
|
||||||
* If a URL is specified then a 3-part name is used, combining the namespace, username and URL separated by
|
|
||||||
* / (e.g. test-namespace/admin/https://10.91.86.13:30080)
|
|
||||||
*/
|
|
||||||
public get controllerContext(): string {
|
|
||||||
if (this._info.endpoint) {
|
|
||||||
return `${this._info.namespace}/${this._info.username}/${this._info.endpoint}`;
|
|
||||||
}
|
|
||||||
return this._info.namespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set info(value: ControllerInfo) {
|
public set info(value: ControllerInfo) {
|
||||||
this._info = value;
|
this._info = value;
|
||||||
this._onInfoUpdated.fire(this._info);
|
this._onInfoUpdated.fire(this._info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get azdataAdditionalEnvVars(): azdataExt.AdditionalEnvVars {
|
public get azAdditionalEnvVars(): azExt.AdditionalEnvVars {
|
||||||
return {
|
return {
|
||||||
'KUBECONFIG': this.info.kubeConfigFilePath,
|
'KUBECONFIG': this.info.kubeConfigFilePath,
|
||||||
'KUBECTL_CONTEXT': this.info.kubeClusterContext
|
'KUBECTL_CONTEXT': this.info.kubeClusterContext
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls azdata login to set the context to this controller and acquires a login session to prevent other
|
|
||||||
* calls from changing the context while commands for this session are being executed.
|
|
||||||
* @param promptReconnect
|
|
||||||
*/
|
|
||||||
public async login(promptReconnect: boolean = false): Promise<void> {
|
|
||||||
let promptForValidClusterContext: boolean = false;
|
|
||||||
try {
|
|
||||||
const contexts = getKubeConfigClusterContexts(this.info.kubeConfigFilePath);
|
|
||||||
getCurrentClusterContext(contexts, this.info.kubeClusterContext, true); // this throws if this.info.kubeClusterContext is not found in 'contexts'
|
|
||||||
} catch (error) {
|
|
||||||
const response = await vscode.window.showErrorMessage(loc.clusterContextConfigNoLongerValid(this.info.kubeConfigFilePath, this.info.kubeClusterContext, error), loc.yes, loc.no);
|
|
||||||
if (response === loc.yes) {
|
|
||||||
promptForValidClusterContext = true;
|
|
||||||
} else {
|
|
||||||
if (!promptReconnect) { //throw unless we are required to prompt for reconnect anyways
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We haven't gotten our password yet or we want to prompt for a reconnect or we want to prompt to reacquire valid cluster context or any and all of these.
|
|
||||||
if (!this._password || promptReconnect || promptForValidClusterContext) {
|
|
||||||
this._password = '';
|
|
||||||
if (this.info.rememberPassword) {
|
|
||||||
// It should be in the credentials store, get it from there
|
|
||||||
this._password = await this.treeDataProvider.getPassword(this.info);
|
|
||||||
}
|
|
||||||
if (promptReconnect || !this._password || promptForValidClusterContext) {
|
|
||||||
// No password yet or we want to re-prompt for credentials so prompt for it from the user
|
|
||||||
const dialog = new ConnectToControllerDialog(this.treeDataProvider);
|
|
||||||
dialog.showDialog(this.info, this._password);
|
|
||||||
const model = await dialog.waitForClose();
|
|
||||||
if (model) {
|
|
||||||
await this.treeDataProvider.addOrUpdateController(model.controllerModel, model.password, false);
|
|
||||||
this._password = model.password;
|
|
||||||
this._info = model.controllerModel.info;
|
|
||||||
} else {
|
|
||||||
throw new UserCancelledError(loc.userCancelledError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this._azdataApi.azdata.login({ endpoint: this.info.endpoint, namespace: this.info.namespace }, this.info.username, this._password, this.azdataAdditionalEnvVars);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the Tree Node for this model. This will also result in the model being refreshed.
|
* Refreshes the Tree Node for this model. This will also result in the model being refreshed.
|
||||||
*/
|
*/
|
||||||
@@ -125,16 +63,14 @@ export class ControllerModel {
|
|||||||
if (node) {
|
if (node) {
|
||||||
this.treeDataProvider.refreshNode(node);
|
this.treeDataProvider.refreshNode(node);
|
||||||
} else {
|
} else {
|
||||||
await this.refresh(false);
|
await this.refresh(false, this.info.namespace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async refresh(showErrors: boolean = true): Promise<void> {
|
public async refresh(showErrors: boolean = true, namespace: string): Promise<void> {
|
||||||
// First need to log in to ensure that we're able to authenticate with the controller
|
|
||||||
await this.login(false);
|
|
||||||
const newRegistrations: Registration[] = [];
|
const newRegistrations: Registration[] = [];
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._azdataApi.azdata.arc.dc.config.show(this.azdataAdditionalEnvVars, this.controllerContext).then(result => {
|
this._azApi.az.arcdata.dc.config.show(namespace, this.azAdditionalEnvVars).then(result => {
|
||||||
this._controllerConfig = result.result;
|
this._controllerConfig = result.stdout;
|
||||||
this.configLastUpdated = new Date();
|
this.configLastUpdated = new Date();
|
||||||
this._onConfigUpdated.fire(this._controllerConfig);
|
this._onConfigUpdated.fire(this._controllerConfig);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -147,8 +83,8 @@ export class ControllerModel {
|
|||||||
this._onConfigUpdated.fire(this._controllerConfig);
|
this._onConfigUpdated.fire(this._controllerConfig);
|
||||||
throw err;
|
throw err;
|
||||||
}),
|
}),
|
||||||
this._azdataApi.azdata.arc.dc.endpoint.list(this.azdataAdditionalEnvVars, this.controllerContext).then(result => {
|
this._azApi.az.arcdata.dc.endpoint.list(namespace, this.azAdditionalEnvVars).then(result => {
|
||||||
this._endpoints = result.result;
|
this._endpoints = result.stdout;
|
||||||
this.endpointsLastUpdated = new Date();
|
this.endpointsLastUpdated = new Date();
|
||||||
this._onEndpointsUpdated.fire(this._endpoints);
|
this._onEndpointsUpdated.fire(this._endpoints);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -162,8 +98,8 @@ export class ControllerModel {
|
|||||||
throw err;
|
throw err;
|
||||||
}),
|
}),
|
||||||
Promise.all([
|
Promise.all([
|
||||||
this._azdataApi.azdata.arc.postgres.server.list(this.azdataAdditionalEnvVars, this.controllerContext).then(result => {
|
this._azApi.az.postgres.arcserver.list(namespace, this.azAdditionalEnvVars).then(result => {
|
||||||
newRegistrations.push(...result.result.map(r => {
|
newRegistrations.push(...result.stdout.map(r => {
|
||||||
return {
|
return {
|
||||||
instanceName: r.name,
|
instanceName: r.name,
|
||||||
state: r.state,
|
state: r.state,
|
||||||
@@ -171,14 +107,15 @@ export class ControllerModel {
|
|||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
this._azdataApi.azdata.arc.sql.mi.list(this.azdataAdditionalEnvVars, this.controllerContext).then(result => {
|
this._azApi.az.sql.miarc.list(namespace, this.azAdditionalEnvVars).then(result => {
|
||||||
newRegistrations.push(...result.result.map(r => {
|
newRegistrations.push(...result.stdout.map(r => {
|
||||||
return {
|
return {
|
||||||
instanceName: r.name,
|
instanceName: r.name,
|
||||||
state: r.state,
|
state: r.state,
|
||||||
instanceType: ResourceType.sqlManagedInstances
|
instanceType: ResourceType.sqlManagedInstances
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
})
|
})
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
this._registrations = newRegistrations;
|
this._registrations = newRegistrations;
|
||||||
@@ -188,11 +125,11 @@ export class ControllerModel {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get endpoints(): azdataExt.DcEndpointListResult[] {
|
public get endpoints(): azExt.DcEndpointListResult[] {
|
||||||
return this._endpoints;
|
return this._endpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEndpoint(name: string): azdataExt.DcEndpointListResult | undefined {
|
public getEndpoint(name: string): azExt.DcEndpointListResult | undefined {
|
||||||
return this._endpoints.find(e => e.name === name);
|
return this._endpoints.find(e => e.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +137,7 @@ export class ControllerModel {
|
|||||||
return this._registrations;
|
return this._registrations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get controllerConfig(): azdataExt.DcConfigShowResult | undefined {
|
public get controllerConfig(): azExt.DcConfigShowResult | undefined {
|
||||||
return this._controllerConfig;
|
return this._controllerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +151,6 @@ export class ControllerModel {
|
|||||||
* property to for use a display label for this controller
|
* property to for use a display label for this controller
|
||||||
*/
|
*/
|
||||||
public get label(): string {
|
public get label(): string {
|
||||||
return `${this.info.name} (${this.controllerContext})`;
|
return `${this.info.name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { MiaaResourceInfo } from 'arc';
|
import { MiaaResourceInfo } from 'arc';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { UserCancelledError } from '../common/api';
|
import { UserCancelledError } from '../common/api';
|
||||||
import { Deferred } from '../common/promise';
|
import { Deferred } from '../common/promise';
|
||||||
@@ -20,12 +20,12 @@ export type DatabaseModel = { name: string, status: string };
|
|||||||
|
|
||||||
export class MiaaModel extends ResourceModel {
|
export class MiaaModel extends ResourceModel {
|
||||||
|
|
||||||
private _config: azdataExt.SqlMiShowResult | undefined;
|
private _config: azExt.SqlMiShowResult | undefined;
|
||||||
private _databases: DatabaseModel[] = [];
|
private _databases: DatabaseModel[] = [];
|
||||||
|
|
||||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.SqlMiShowResult | undefined>();
|
private readonly _onConfigUpdated = new vscode.EventEmitter<azExt.SqlMiShowResult | undefined>();
|
||||||
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
private readonly _onDatabasesUpdated = new vscode.EventEmitter<DatabaseModel[]>();
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
public onConfigUpdated = this._onConfigUpdated.event;
|
public onConfigUpdated = this._onConfigUpdated.event;
|
||||||
public onDatabasesUpdated = this._onDatabasesUpdated.event;
|
public onDatabasesUpdated = this._onDatabasesUpdated.event;
|
||||||
public configLastUpdated: Date | undefined;
|
public configLastUpdated: Date | undefined;
|
||||||
@@ -35,7 +35,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
|
|
||||||
constructor(_controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
constructor(_controllerModel: ControllerModel, private _miaaInfo: MiaaResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(_controllerModel, _miaaInfo, registration);
|
super(_controllerModel, _miaaInfo, registration);
|
||||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = <azExt.IExtension>vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +48,7 @@ export class MiaaModel extends ResourceModel {
|
|||||||
/**
|
/**
|
||||||
* The status of this instance
|
* The status of this instance
|
||||||
*/
|
*/
|
||||||
public get config(): azdataExt.SqlMiShowResult | undefined {
|
public get config(): azExt.SqlMiShowResult | undefined {
|
||||||
return this._config;
|
return this._config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ export class MiaaModel extends ResourceModel {
|
|||||||
this._refreshPromise = new Deferred();
|
this._refreshPromise = new Deferred();
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
const result = await this._azdataApi.azdata.arc.sql.mi.show(this.info.name, this.controllerModel.azdataAdditionalEnvVars, this.controllerModel.controllerContext);
|
const result = await this._azApi.az.sql.miarc.show(this.info.name, this.controllerModel.info.namespace, this.controllerModel.azAdditionalEnvVars);
|
||||||
this._config = result.result;
|
this._config = result.stdout;
|
||||||
this.configLastUpdated = new Date();
|
this.configLastUpdated = new Date();
|
||||||
this._onConfigUpdated.fire(this._config);
|
this._onConfigUpdated.fire(this._config);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { PGResourceInfo } from 'arc';
|
import { PGResourceInfo } from 'arc';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { ConnectToPGSqlDialog } from '../ui/dialogs/connectPGDialog';
|
import { ConnectToPGSqlDialog } from '../ui/dialogs/connectPGDialog';
|
||||||
@@ -27,12 +27,12 @@ export type EngineSettingsModel = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class PostgresModel extends ResourceModel {
|
export class PostgresModel extends ResourceModel {
|
||||||
private _config?: azdataExt.PostgresServerShowResult;
|
private _config?: azExt.PostgresServerShowResult;
|
||||||
public workerNodesEngineSettings: EngineSettingsModel[] = [];
|
public workerNodesEngineSettings: EngineSettingsModel[] = [];
|
||||||
public coordinatorNodeEngineSettings: EngineSettingsModel[] = [];
|
public coordinatorNodeEngineSettings: EngineSettingsModel[] = [];
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.PostgresServerShowResult>();
|
private readonly _onConfigUpdated = new vscode.EventEmitter<azExt.PostgresServerShowResult>();
|
||||||
public onConfigUpdated = this._onConfigUpdated.event;
|
public onConfigUpdated = this._onConfigUpdated.event;
|
||||||
public configLastUpdated?: Date;
|
public configLastUpdated?: Date;
|
||||||
public engineSettingsLastUpdated?: Date;
|
public engineSettingsLastUpdated?: Date;
|
||||||
@@ -42,11 +42,11 @@ export class PostgresModel extends ResourceModel {
|
|||||||
|
|
||||||
constructor(_controllerModel: ControllerModel, private _pgInfo: PGResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
constructor(_controllerModel: ControllerModel, private _pgInfo: PGResourceInfo, registration: Registration, private _treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(_controllerModel, _pgInfo, registration);
|
super(_controllerModel, _pgInfo, registration);
|
||||||
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = <azExt.IExtension>vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the configuration of Postgres */
|
/** Returns the configuration of Postgres */
|
||||||
public get config(): azdataExt.PostgresServerShowResult | undefined {
|
public get config(): azExt.PostgresServerShowResult | undefined {
|
||||||
return this._config;
|
return this._config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ export class PostgresModel extends ResourceModel {
|
|||||||
}
|
}
|
||||||
this._refreshPromise = new Deferred();
|
this._refreshPromise = new Deferred();
|
||||||
try {
|
try {
|
||||||
this._config = (await this._azdataApi.azdata.arc.postgres.server.show(this.info.name, this.controllerModel.azdataAdditionalEnvVars, this.controllerModel.controllerContext)).result;
|
this._config = (await this._azApi.az.postgres.arcserver.show(this.info.name, this.controllerModel.info.namespace, this.controllerModel.azAdditionalEnvVars)).stdout;
|
||||||
this.configLastUpdated = new Date();
|
this.configLastUpdated = new Date();
|
||||||
this._onConfigUpdated.fire(this._config);
|
this._onConfigUpdated.fire(this._config);
|
||||||
this._refreshPromise.resolve();
|
this._refreshPromise.resolve();
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as arc from 'arc';
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as rd from 'resource-deployment';
|
import * as rd from 'resource-deployment';
|
||||||
import { getControllerPassword, getRegisteredDataControllers, reacquireControllerPassword } from '../common/api';
|
import { getRegisteredDataControllers } from '../common/api';
|
||||||
import { throwUnless } from '../common/utils';
|
import { throwUnless } from '../common/utils';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
@@ -31,32 +30,17 @@ export class ArcControllersOptionsSourceProvider implements rd.IOptionsSourcePro
|
|||||||
throwUnless(controller !== undefined, loc.noControllerInfoFound(controllerLabel));
|
throwUnless(controller !== undefined, loc.noControllerInfoFound(controllerLabel));
|
||||||
switch (variableName) {
|
switch (variableName) {
|
||||||
case 'namespace': return controller.info.namespace || '';
|
case 'namespace': return controller.info.namespace || '';
|
||||||
case 'endpoint': return controller.info.endpoint || '';
|
|
||||||
case 'username': return controller.info.username;
|
|
||||||
case 'kubeConfig': return controller.info.kubeConfigFilePath;
|
case 'kubeConfig': return controller.info.kubeConfigFilePath;
|
||||||
case 'clusterContext': return controller.info.kubeClusterContext;
|
case 'clusterContext': return controller.info.kubeClusterContext;
|
||||||
case 'password': return this.getPassword(controller);
|
|
||||||
default: throw new Error(loc.variableValueFetchForUnsupportedVariable(variableName));
|
default: throw new Error(loc.variableValueFetchForUnsupportedVariable(variableName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPassword(controller: arc.DataController): Promise<string> {
|
|
||||||
let password = await getControllerPassword(this._treeProvider, controller.info);
|
|
||||||
if (!password) {
|
|
||||||
password = await reacquireControllerPassword(this._treeProvider, controller.info);
|
|
||||||
}
|
|
||||||
throwUnless(password !== undefined, loc.noPasswordFound(controller.label));
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getIsPassword(variableName: string): boolean {
|
public getIsPassword(variableName: string): boolean {
|
||||||
switch (variableName) {
|
switch (variableName) {
|
||||||
case 'namespace': return false;
|
case 'namespace': return false;
|
||||||
case 'endpoint': return false;
|
|
||||||
case 'username': return false;
|
|
||||||
case 'kubeConfig': return false;
|
case 'kubeConfig': return false;
|
||||||
case 'clusterContext': return false;
|
case 'clusterContext': return false;
|
||||||
case 'password': return true;
|
|
||||||
default: throw new Error(loc.isPasswordFetchForUnsupportedVariable(variableName));
|
default: throw new Error(loc.isPasswordFetchForUnsupportedVariable(variableName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple fake Azdata Api used to mock the API during tests
|
|
||||||
*/
|
|
||||||
export class FakeAzdataApi implements azdataExt.IAzdataApi {
|
|
||||||
|
|
||||||
private _arcApi = {
|
|
||||||
dc: {
|
|
||||||
create(_namespace: string, _name: string, _connectivityMode: string, _resourceGroup: string, _location: string, _subscription: string, _profileName?: string, _storageClass?: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
|
||||||
endpoint: {
|
|
||||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.DcEndpointListResult[]>> { return <any>{ result: [] }; }
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
list(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigListResult[]>> { throw new Error('Method not implemented.'); },
|
|
||||||
async show(): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigShowResult>> { return <any>{ result: undefined! }; }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postgres: {
|
|
||||||
server: {
|
|
||||||
postgresInstances: <azdataExt.PostgresServerListResult[]>[],
|
|
||||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
|
||||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerListResult[]>> { return { result: this.postgresInstances, logs: [], stdout: [], stderr: [] }; },
|
|
||||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerShowResult>> { throw new Error('Method not implemented.'); },
|
|
||||||
edit(
|
|
||||||
_name: string,
|
|
||||||
_args: {
|
|
||||||
adminPassword?: boolean,
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
coordinatorEngineSettings?: string,
|
|
||||||
engineSettings?: string,
|
|
||||||
extensions?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
port?: number,
|
|
||||||
replaceEngineSettings?: boolean,
|
|
||||||
workerEngineSettings?: string,
|
|
||||||
workers?: number
|
|
||||||
},
|
|
||||||
_additionalEnvVars?: azdataExt.AdditionalEnvVars
|
|
||||||
): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sql: {
|
|
||||||
mi: {
|
|
||||||
miaaInstances: <azdataExt.SqlMiListResult[]>[],
|
|
||||||
delete(_name: string): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); },
|
|
||||||
async list(): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiListResult[]>> { return { logs: [], stdout: [], stderr: [], result: this.miaaInstances }; },
|
|
||||||
show(_name: string): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiShowResult>> { throw new Error('Method not implemented.'); },
|
|
||||||
edit(
|
|
||||||
_name: string,
|
|
||||||
_args: {
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean
|
|
||||||
}): Promise<azdataExt.AzdataOutput<void>> { throw new Error('Method not implemented.'); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public set postgresInstances(instances: azdataExt.PostgresServerListResult[]) {
|
|
||||||
this._arcApi.postgres.server.postgresInstances = instances;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set miaaInstances(instances: azdataExt.SqlMiListResult[]) {
|
|
||||||
this._arcApi.sql.mi.miaaInstances = instances;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// API Implementation
|
|
||||||
//
|
|
||||||
public get arc() {
|
|
||||||
return this._arcApi;
|
|
||||||
}
|
|
||||||
getPath(): Promise<string> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
login(_endpointOrNamespace: azdataExt.EndpointOrNamespace, _username: string, _password: string, _additionalEnvVars: azdataExt.AdditionalEnvVars = {}, _azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> {
|
|
||||||
return <any>undefined;
|
|
||||||
}
|
|
||||||
version(): Promise<azdataExt.AzdataOutput<string>> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
getSemVersion(): any {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,9 @@ import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider
|
|||||||
|
|
||||||
export class FakeControllerModel extends ControllerModel {
|
export class FakeControllerModel extends ControllerModel {
|
||||||
|
|
||||||
constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>, password?: string) {
|
constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>) {
|
||||||
const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [] }, info);
|
const _info: ControllerInfo = Object.assign({ id: uuid(), endpoint: '', kubeConfigFilePath: '', kubeClusterContext: '', name: '', namespace: '', username: '', rememberPassword: false, resources: [] }, info);
|
||||||
super(treeDataProvider!, _info, password);
|
super(treeDataProvider!, _info);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,221 +1,221 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo } from 'arc';
|
// import { ControllerInfo } from 'arc';
|
||||||
import * as azdata from 'azdata';
|
// import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
// import * as azExt from 'azdata-ext';
|
||||||
import * as should from 'should';
|
// import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import * as TypeMoq from 'typemoq';
|
// import * as TypeMoq from 'typemoq';
|
||||||
import { v4 as uuid } from 'uuid';
|
// import { v4 as uuid } from 'uuid';
|
||||||
import * as vscode from 'vscode';
|
// import * as vscode from 'vscode';
|
||||||
import * as loc from '../../localizedConstants';
|
// import * as loc from '../../localizedConstants';
|
||||||
import * as kubeUtils from '../../common/kubeUtils';
|
// import * as kubeUtils from '../../common/kubeUtils';
|
||||||
import { UserCancelledError } from '../../common/api';
|
// import { UserCancelledError } from '../../common/api';
|
||||||
import { ControllerModel } from '../../models/controllerModel';
|
// import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { ConnectToControllerDialog } from '../../ui/dialogs/connectControllerDialog';
|
// import { ConnectToControllerDialog } from '../../ui/dialogs/connectControllerDialog';
|
||||||
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
|
// import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
|
||||||
|
|
||||||
interface ExtensionGlobalMemento extends vscode.Memento {
|
// interface ExtensionGlobalMemento extends vscode.Memento {
|
||||||
setKeysForSync(keys: string[]): void;
|
// setKeysForSync(keys: string[]): void;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getDefaultControllerInfo(): ControllerInfo {
|
// function getDefaultControllerInfo(): ControllerInfo {
|
||||||
return {
|
// return {
|
||||||
id: uuid(),
|
// id: uuid(),
|
||||||
endpoint: '127.0.0.1',
|
// endpoint: '127.0.0.1',
|
||||||
kubeConfigFilePath: '/path/to/.kube/config',
|
// kubeConfigFilePath: '/path/to/.kube/config',
|
||||||
kubeClusterContext: 'currentCluster',
|
// kubeClusterContext: 'currentCluster',
|
||||||
username: 'admin',
|
// username: 'admin',
|
||||||
name: 'arc',
|
// name: 'arc',
|
||||||
namespace: 'arc-ns',
|
// namespace: 'arc-ns',
|
||||||
rememberPassword: true,
|
// rememberPassword: true,
|
||||||
resources: []
|
// resources: []
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
describe('ControllerModel', function (): void {
|
// describe('ControllerModel', function (): void {
|
||||||
afterEach(function (): void {
|
// afterEach(function (): void {
|
||||||
sinon.restore();
|
// sinon.restore();
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('azdataLogin', function (): void {
|
// describe('azdataLogin', function (): void {
|
||||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
// let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||||
let mockGlobalState: TypeMoq.IMock<ExtensionGlobalMemento>;
|
// let mockGlobalState: TypeMoq.IMock<ExtensionGlobalMemento>;
|
||||||
|
|
||||||
before(function (): void {
|
// before(function (): void {
|
||||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
// mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
mockGlobalState = TypeMoq.Mock.ofType<ExtensionGlobalMemento>();
|
// mockGlobalState = TypeMoq.Mock.ofType<ExtensionGlobalMemento>();
|
||||||
mockExtensionContext.setup(x => x.globalState).returns(() => mockGlobalState.object);
|
// mockExtensionContext.setup(x => x.globalState).returns(() => mockGlobalState.object);
|
||||||
});
|
// });
|
||||||
|
|
||||||
beforeEach(function (): void {
|
// beforeEach(function (): void {
|
||||||
sinon.stub(ConnectToControllerDialog.prototype, 'showDialog');
|
// sinon.stub(ConnectToControllerDialog.prototype, 'showDialog');
|
||||||
sinon.stub(kubeUtils, 'getKubeConfigClusterContexts').returns([{ name: 'currentCluster', isCurrentContext: true }]);
|
// sinon.stub(kubeUtils, 'getKubeConfigClusterContexts').returns([{ name: 'currentCluster', isCurrentContext: true }]);
|
||||||
sinon.stub(vscode.window, 'showErrorMessage').resolves(<any>loc.yes);
|
// sinon.stub(vscode.window, 'showErrorMessage').resolves(<any>loc.yes);
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Rejected with expected error when user cancels', async function (): Promise<void> {
|
// it('Rejected with expected error when user cancels', async function (): Promise<void> {
|
||||||
// Returning an undefined model here indicates that the dialog closed without clicking "Ok" - usually through the user clicking "Cancel"
|
// // Returning an undefined model here indicates that the dialog closed without clicking "Ok" - usually through the user clicking "Cancel"
|
||||||
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(undefined));
|
// sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(undefined));
|
||||||
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
// const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
||||||
await should(model.login()).be.rejectedWith(new UserCancelledError(loc.userCancelledError));
|
// await should(model.login()).be.rejectedWith(new UserCancelledError(loc.userCancelledError));
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Reads password from cred store', async function (): Promise<void> {
|
// it('Reads password from cred store', async function (): Promise<void> {
|
||||||
const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Test password, not actually used")]
|
// const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Test password, not actually used")]
|
||||||
|
|
||||||
// Set up cred store to return our password
|
// // Set up cred store to return our password
|
||||||
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
// const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
||||||
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: password }));
|
// credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: password }));
|
||||||
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
// // Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
// credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
||||||
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
// sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
||||||
|
|
||||||
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExtApiMock = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
|
// const azdataMock = TypeMoq.Mock.ofType<azExt.IAzdataApi>();
|
||||||
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
// azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
||||||
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
// azExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExtApiMock.object });
|
||||||
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
// const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
||||||
|
|
||||||
await model.login();
|
// await model.login();
|
||||||
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
// azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Prompt for password when not in cred store', async function (): Promise<void> {
|
// it('Prompt for password when not in cred store', async function (): Promise<void> {
|
||||||
const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
// const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
||||||
|
|
||||||
// Set up cred store to return empty password
|
// // Set up cred store to return empty password
|
||||||
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
// const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
||||||
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: '' }));
|
// credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: '' }));
|
||||||
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
// // Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
// credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
||||||
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
// sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
||||||
|
|
||||||
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExtApiMock = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
|
// const azdataMock = TypeMoq.Mock.ofType<azExt.IAzdataApi>();
|
||||||
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
// azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
||||||
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
// azExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExtApiMock.object });
|
||||||
|
|
||||||
// Set up dialog to return new model with our password
|
// // Set up dialog to return new model with our password
|
||||||
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
// const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
||||||
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
// sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
||||||
|
|
||||||
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
// const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
||||||
|
|
||||||
await model.login();
|
// await model.login();
|
||||||
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
// azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Prompt for password when rememberPassword is true but prompt reconnect is true', async function (): Promise<void> {
|
// it('Prompt for password when rememberPassword is true but prompt reconnect is true', async function (): Promise<void> {
|
||||||
const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
// const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
||||||
// Set up cred store to return a password to start with
|
// // Set up cred store to return a password to start with
|
||||||
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
// const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
||||||
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
// credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
||||||
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
// // Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
// credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
||||||
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
// sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
||||||
|
|
||||||
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExtApiMock = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
|
// const azdataMock = TypeMoq.Mock.ofType<azExt.IAzdataApi>();
|
||||||
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
// azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
||||||
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
// azExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExtApiMock.object });
|
||||||
|
|
||||||
// Set up dialog to return new model with our new password from the reprompt
|
// // Set up dialog to return new model with our new password from the reprompt
|
||||||
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
// const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
||||||
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
// const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
||||||
|
|
||||||
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
// const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo());
|
||||||
|
|
||||||
await model.login(true);
|
// await model.login(true);
|
||||||
should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
// should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
||||||
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
// azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Prompt for password when we already have a password but prompt reconnect is true', async function (): Promise<void> {
|
// it('Prompt for password when we already have a password but prompt reconnect is true', async function (): Promise<void> {
|
||||||
const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
// const password = 'password123'; // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Stub value for testing")]
|
||||||
// Set up cred store to return a password to start with
|
// // Set up cred store to return a password to start with
|
||||||
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
// const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
||||||
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
// credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
||||||
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
// // Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
// credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
||||||
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
// sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
||||||
|
|
||||||
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExtApiMock = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
|
// const azdataMock = TypeMoq.Mock.ofType<azExt.IAzdataApi>();
|
||||||
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
// azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
||||||
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
// azExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExtApiMock.object });
|
||||||
|
|
||||||
// Set up dialog to return new model with our new password from the reprompt
|
// // Set up dialog to return new model with our new password from the reprompt
|
||||||
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
// const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), password);
|
||||||
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
// const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
|
||||||
|
|
||||||
// Set up original model with a password
|
// // Set up original model with a password
|
||||||
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), 'originalPassword');
|
// const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), getDefaultControllerInfo(), 'originalPassword');
|
||||||
|
|
||||||
await model.login(true);
|
// await model.login(true);
|
||||||
should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
// should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
||||||
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
// azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Model values are updated correctly when modified during reconnect', async function (): Promise<void> {
|
// it('Model values are updated correctly when modified during reconnect', async function (): Promise<void> {
|
||||||
const treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
|
// const treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
|
||||||
|
|
||||||
// Set up cred store to return a password to start with
|
// // Set up cred store to return a password to start with
|
||||||
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
// const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
|
||||||
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
// credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
|
||||||
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
// // Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
|
||||||
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
// credProviderMock.setup((x: any) => x.then).returns(() => undefined);
|
||||||
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
// sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
|
||||||
|
|
||||||
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExtApiMock = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
|
// const azdataMock = TypeMoq.Mock.ofType<azExt.IAzdataApi>();
|
||||||
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
// azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
|
||||||
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
// azExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExtApiMock.object });
|
||||||
|
|
||||||
// Add existing model to provider
|
// // Add existing model to provider
|
||||||
const originalPassword = 'originalPassword';
|
// const originalPassword = 'originalPassword';
|
||||||
const model = new ControllerModel(
|
// const model = new ControllerModel(
|
||||||
treeDataProvider,
|
// treeDataProvider,
|
||||||
getDefaultControllerInfo(),
|
// getDefaultControllerInfo(),
|
||||||
originalPassword
|
// originalPassword
|
||||||
);
|
// );
|
||||||
await treeDataProvider.addOrUpdateController(model, originalPassword);
|
// await treeDataProvider.addOrUpdateController(model, originalPassword);
|
||||||
|
|
||||||
const newInfo: ControllerInfo = {
|
// const newInfo: ControllerInfo = {
|
||||||
id: model.info.id, // The ID stays the same since we're just re-entering information for the same model
|
// id: model.info.id, // The ID stays the same since we're just re-entering information for the same model
|
||||||
endpoint: 'newUrl',
|
// endpoint: 'newUrl',
|
||||||
kubeConfigFilePath: '/path/to/.kube/config',
|
// kubeConfigFilePath: '/path/to/.kube/config',
|
||||||
kubeClusterContext: 'currentCluster',
|
// kubeClusterContext: 'currentCluster',
|
||||||
username: 'newUser',
|
// username: 'newUser',
|
||||||
name: 'newName',
|
// name: 'newName',
|
||||||
namespace: 'newNamespace',
|
// namespace: 'newNamespace',
|
||||||
rememberPassword: true,
|
// rememberPassword: true,
|
||||||
resources: []
|
// resources: []
|
||||||
};
|
// };
|
||||||
const newPassword = 'newPassword';
|
// const newPassword = 'newPassword';
|
||||||
// Set up dialog to return new model with our new password from the reprompt
|
// // Set up dialog to return new model with our new password from the reprompt
|
||||||
const newModel = new ControllerModel(
|
// const newModel = new ControllerModel(
|
||||||
treeDataProvider,
|
// treeDataProvider,
|
||||||
newInfo,
|
// newInfo,
|
||||||
newPassword);
|
// newPassword);
|
||||||
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(
|
// const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(
|
||||||
{ controllerModel: newModel, password: newPassword }));
|
// { controllerModel: newModel, password: newPassword }));
|
||||||
|
|
||||||
await model.login(true);
|
// await model.login(true);
|
||||||
should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
// should(waitForCloseStub.called).be.true('waitForClose should have been called');
|
||||||
should((await treeDataProvider.getChildren()).length).equal(1, 'Tree Data provider should still only have 1 node');
|
// should((await treeDataProvider.getChildren()).length).equal(1, 'Tree Data provider should still only have 1 node');
|
||||||
should(model.info).deepEqual(newInfo, 'Model info should have been updated');
|
// should(model.info).deepEqual(newInfo, 'Model info should have been updated');
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +1,77 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { PGResourceInfo, ResourceType } from 'arc';
|
// import { PGResourceInfo, ResourceType } from 'arc';
|
||||||
import * as azdataExt from 'azdata-ext';
|
// import * as azExt from 'azdata-ext';
|
||||||
import * as should from 'should';
|
// import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import * as TypeMoq from 'typemoq';
|
// import * as TypeMoq from 'typemoq';
|
||||||
import * as vscode from 'vscode';
|
// import * as vscode from 'vscode';
|
||||||
import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/modelView/modelViewMock';
|
// import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/modelView/modelViewMock';
|
||||||
import { ControllerModel, Registration } from '../../../models/controllerModel';
|
// import { ControllerModel, Registration } from '../../../models/controllerModel';
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
// import { PostgresModel } from '../../../models/postgresModel';
|
||||||
import { PostgresConnectionStringsPage } from '../../../ui/dashboards/postgres/postgresConnectionStringsPage';
|
// import { PostgresConnectionStringsPage } from '../../../ui/dashboards/postgres/postgresConnectionStringsPage';
|
||||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
// import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||||
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
// import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
||||||
import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
// import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
||||||
import { FakePostgresServerShowOutput } from '../../models/postgresModel.test';
|
// import { FakePostgresServerShowOutput } from '../../models/postgresModel.test';
|
||||||
|
|
||||||
describe('postgresConnectionStringsPage', function (): void {
|
// describe('postgresConnectionStringsPage', function (): void {
|
||||||
let controllerModel: ControllerModel;
|
// let controllerModel: ControllerModel;
|
||||||
let postgresModel: PostgresModel;
|
// let postgresModel: PostgresModel;
|
||||||
let azdataApi: azdataExt.IAzdataApi;
|
// let azdataApi: azExt.IAzdataApi;
|
||||||
let postgresConnectionStrings: PostgresConnectionStringsPage;
|
// let postgresConnectionStrings: PostgresConnectionStringsPage;
|
||||||
|
|
||||||
afterEach(function (): void {
|
// afterEach(function (): void {
|
||||||
sinon.restore();
|
// sinon.restore();
|
||||||
});
|
// });
|
||||||
|
|
||||||
beforeEach(async () => {
|
// beforeEach(async () => {
|
||||||
// Stub the azdata CLI API
|
// // Stub the azdata CLI API
|
||||||
azdataApi = new FakeAzdataApi();
|
// azdataApi = new FakeAzdataApi();
|
||||||
const azdataExt = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExt = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
azdataExt.setup(x => x.azdata).returns(() => azdataApi);
|
// azExt.setup(x => x.azdata).returns(() => azdataApi);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExt.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExt.object });
|
||||||
|
|
||||||
// Setup Controller Model
|
// // Setup Controller Model
|
||||||
controllerModel = new FakeControllerModel();
|
// controllerModel = new FakeControllerModel();
|
||||||
|
|
||||||
//Stub calling azdata login and acquiring session
|
// // Setup PostgresModel
|
||||||
sinon.stub(controllerModel, 'login').returns(Promise.resolve());
|
// const postgresResource: PGResourceInfo = { name: 'pgt', resourceType: '' };
|
||||||
|
// const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
||||||
|
// postgresModel = new PostgresModel(controllerModel, postgresResource, registration, new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object));
|
||||||
|
|
||||||
// Setup PostgresModel
|
// // Setup stub of show call
|
||||||
const postgresResource: PGResourceInfo = { name: 'pgt', resourceType: '' };
|
// const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
||||||
const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
// sinon.stub(azdataApi, 'arc').get(() => {
|
||||||
postgresModel = new PostgresModel(controllerModel, postgresResource, registration, new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object));
|
// return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
||||||
|
// });
|
||||||
|
|
||||||
// Setup stub of show call
|
// // Setup the PostgresConnectionsStringsPage
|
||||||
const postgresShow = sinon.stub().returns(FakePostgresServerShowOutput);
|
// let { modelViewMock } = createModelViewMock();
|
||||||
sinon.stub(azdataApi, 'arc').get(() => {
|
// postgresConnectionStrings = new PostgresConnectionStringsPage(modelViewMock.object, undefined!, postgresModel);
|
||||||
return { postgres: { server: { show(name: string) { return postgresShow(name); } } } };
|
// });
|
||||||
});
|
|
||||||
|
|
||||||
// Setup the PostgresConnectionsStringsPage
|
// describe('getConnectionStrings', function (): void {
|
||||||
let { modelViewMock } = createModelViewMock();
|
|
||||||
postgresConnectionStrings = new PostgresConnectionStringsPage(modelViewMock.object, undefined!, postgresModel);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getConnectionStrings', function (): void {
|
// it('Strings container should be empty since postgres model has not been refreshed', async function (): Promise<void> {
|
||||||
|
// should(postgresConnectionStrings['getConnectionStrings']()).be.empty();
|
||||||
|
// });
|
||||||
|
|
||||||
it('Strings container should be empty since postgres model has not been refreshed', async function (): Promise<void> {
|
// it('String contain correct ip and port', async function (): Promise<void> {
|
||||||
should(postgresConnectionStrings['getConnectionStrings']()).be.empty();
|
// // Call to provide external endpoint
|
||||||
});
|
// await postgresModel.refresh();
|
||||||
|
|
||||||
it('String contain correct ip and port', async function (): Promise<void> {
|
// let endpoint = FakePostgresServerShowOutput.result.status.primaryEndpoint.split(':');
|
||||||
// Call to provide external endpoint
|
|
||||||
await postgresModel.refresh();
|
|
||||||
|
|
||||||
let endpoint = FakePostgresServerShowOutput.result.status.primaryEndpoint.split(':');
|
// postgresConnectionStrings['getConnectionStrings']().forEach(k => {
|
||||||
|
// should(k.value.includes(endpoint[0])).be.True();
|
||||||
|
// should(k.value.includes(endpoint[1])).be.True();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
postgresConnectionStrings['getConnectionStrings']().forEach(k => {
|
// });
|
||||||
should(k.value.includes(endpoint[0])).be.True();
|
|
||||||
should(k.value.includes(endpoint[1])).be.True();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,110 +1,109 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
// import * as vscode from 'vscode';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import * as TypeMoq from 'typemoq';
|
// import * as TypeMoq from 'typemoq';
|
||||||
import * as azdataExt from 'azdata-ext';
|
// import * as azExt from 'azdata-ext';
|
||||||
import * as utils from '../../../common/utils';
|
// import * as utils from '../../../common/utils';
|
||||||
import * as loc from '../../../localizedConstants';
|
// import * as loc from '../../../localizedConstants';
|
||||||
import { Deferred } from '../../../common/promise';
|
// import { Deferred } from '../../../common/promise';
|
||||||
import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/modelView/modelViewMock';
|
// import { createModelViewMock } from '@microsoft/azdata-test/out/mocks/modelView/modelViewMock';
|
||||||
import { StubButton } from '@microsoft/azdata-test/out/stubs/modelView/stubButton';
|
// import { StubButton } from '@microsoft/azdata-test/out/stubs/modelView/stubButton';
|
||||||
import { PGResourceInfo, ResourceType } from 'arc';
|
// import { PGResourceInfo, ResourceType } from 'arc';
|
||||||
import { PostgresOverviewPage } from '../../../ui/dashboards/postgres/postgresOverviewPage';
|
// import { PostgresOverviewPage } from '../../../ui/dashboards/postgres/postgresOverviewPage';
|
||||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
// import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||||
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
// import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
||||||
import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
// import { FakeAzApi } from '../../mocks/fakeAzdataApi';
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
// import { PostgresModel } from '../../../models/postgresModel';
|
||||||
import { ControllerModel, Registration } from '../../../models/controllerModel';
|
// import { ControllerModel, Registration } from '../../../models/controllerModel';
|
||||||
|
|
||||||
describe('postgresOverviewPage', () => {
|
// describe('postgresOverviewPage', () => {
|
||||||
let postgresOverview: PostgresOverviewPage;
|
// let postgresOverview: PostgresOverviewPage;
|
||||||
let azdataApi: azdataExt.IAzdataApi;
|
// let azdataApi: azExt.IAzdataApi;
|
||||||
let controllerModel: ControllerModel;
|
// let controllerModel: ControllerModel;
|
||||||
let postgresModel: PostgresModel;
|
// let postgresModel: PostgresModel;
|
||||||
|
|
||||||
let showInformationMessage: sinon.SinonStub;
|
// let showInformationMessage: sinon.SinonStub;
|
||||||
let showErrorMessage: sinon.SinonStub;
|
// let showErrorMessage: sinon.SinonStub;
|
||||||
|
|
||||||
let informationMessageShown: Deferred;
|
// let informationMessageShown: Deferred;
|
||||||
let errorMessageShown: Deferred;
|
// let errorMessageShown: Deferred;
|
||||||
|
|
||||||
beforeEach(async () => {
|
// beforeEach(async () => {
|
||||||
// Stub the azdata CLI API
|
// // Stub the azdata CLI API
|
||||||
azdataApi = new FakeAzdataApi();
|
// azdataApi = new FakeAzdataApi();
|
||||||
const azdataExt = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const azExt = TypeMoq.Mock.ofType<azExt.IExtension>();
|
||||||
azdataExt.setup(x => x.azdata).returns(() => azdataApi);
|
// azExt.setup(x => x.azdata).returns(() => azdataApi);
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExt.object });
|
// sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azExt.object });
|
||||||
|
|
||||||
// Stub the window UI
|
// // Stub the window UI
|
||||||
informationMessageShown = new Deferred();
|
// informationMessageShown = new Deferred();
|
||||||
showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage').callsFake(
|
// showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage').callsFake(
|
||||||
(_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
// (_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
||||||
informationMessageShown.resolve();
|
// informationMessageShown.resolve();
|
||||||
return Promise.resolve(undefined);
|
// return Promise.resolve(undefined);
|
||||||
});
|
// });
|
||||||
|
|
||||||
errorMessageShown = new Deferred();
|
// errorMessageShown = new Deferred();
|
||||||
showErrorMessage = sinon.stub(vscode.window, 'showErrorMessage').callsFake(
|
// showErrorMessage = sinon.stub(vscode.window, 'showErrorMessage').callsFake(
|
||||||
(_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
// (_: string, __: vscode.MessageOptions, ...___: vscode.MessageItem[]) => {
|
||||||
errorMessageShown.resolve();
|
// errorMessageShown.resolve();
|
||||||
return Promise.resolve(undefined);
|
// return Promise.resolve(undefined);
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Setup the PostgresModel
|
// // Setup the PostgresModel
|
||||||
controllerModel = new FakeControllerModel();
|
// controllerModel = new FakeControllerModel();
|
||||||
const postgresResource: PGResourceInfo = { name: 'my-pg', resourceType: '' };
|
// const postgresResource: PGResourceInfo = { name: 'my-pg', resourceType: '' };
|
||||||
const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
// const registration: Registration = { instanceName: '', state: '', instanceType: ResourceType.postgresInstances };
|
||||||
const treeDataProvider = new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object);
|
// const treeDataProvider = new AzureArcTreeDataProvider(TypeMoq.Mock.ofType<vscode.ExtensionContext>().object);
|
||||||
postgresModel = new PostgresModel(controllerModel, postgresResource, registration, treeDataProvider);
|
// postgresModel = new PostgresModel(controllerModel, postgresResource, registration, treeDataProvider);
|
||||||
|
|
||||||
// Setup the PostgresOverviewPage
|
// // Setup the PostgresOverviewPage
|
||||||
const { modelViewMock } = createModelViewMock();
|
// const { modelViewMock } = createModelViewMock();
|
||||||
postgresOverview = new PostgresOverviewPage(modelViewMock.object, undefined!, controllerModel, postgresModel);
|
// postgresOverview = new PostgresOverviewPage(modelViewMock.object, undefined!, controllerModel, postgresModel);
|
||||||
// Call the getter to initialize toolbar, but we don't need to use it for anything
|
// // Call the getter to initialize toolbar, but we don't need to use it for anything
|
||||||
// eslint-disable-next-line code-no-unused-expressions
|
// // eslint-disable-next-line code-no-unused-expressions
|
||||||
postgresOverview['toolbarContainer'];
|
// postgresOverview['toolbarContainer'];
|
||||||
});
|
// });
|
||||||
|
|
||||||
afterEach(() => {
|
// afterEach(() => {
|
||||||
sinon.restore();
|
// sinon.restore();
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('delete button', () => {
|
// describe('delete button', () => {
|
||||||
let refreshTreeNode: sinon.SinonStub;
|
// let refreshTreeNode: sinon.SinonStub;
|
||||||
|
|
||||||
beforeEach(() => {
|
// beforeEach(() => {
|
||||||
sinon.stub(utils, 'promptForInstanceDeletion').returns(Promise.resolve(true));
|
// sinon.stub(utils, 'promptForInstanceDeletion').returns(Promise.resolve(true));
|
||||||
sinon.stub(controllerModel, 'login').returns(Promise.resolve());
|
// refreshTreeNode = sinon.stub(controllerModel, 'refreshTreeNode');
|
||||||
refreshTreeNode = sinon.stub(controllerModel, 'refreshTreeNode');
|
// });
|
||||||
});
|
|
||||||
|
|
||||||
it('deletes Postgres on success', async () => {
|
// it('deletes Postgres on success', async () => {
|
||||||
// Stub 'azdata arc postgres server delete' to return success
|
// // Stub 'azdata arc postgres server delete' to return success
|
||||||
const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete');
|
// const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete');
|
||||||
|
|
||||||
(postgresOverview['deleteButton'] as StubButton).click();
|
// (postgresOverview['deleteButton'] as StubButton).click();
|
||||||
await informationMessageShown;
|
// await informationMessageShown;
|
||||||
sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
// sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||||
sinon.assert.calledOnceWithExactly(showInformationMessage, loc.instanceDeleted(postgresModel.info.name));
|
// sinon.assert.calledOnceWithExactly(showInformationMessage, loc.instanceDeleted(postgresModel.info.name));
|
||||||
sinon.assert.notCalled(showErrorMessage);
|
// sinon.assert.notCalled(showErrorMessage);
|
||||||
sinon.assert.calledOnce(refreshTreeNode);
|
// sinon.assert.calledOnce(refreshTreeNode);
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('shows an error message on failure', async () => {
|
// it('shows an error message on failure', async () => {
|
||||||
// Stub 'azdata arc postgres server delete' to throw an exception
|
// // Stub 'azdata arc postgres server delete' to throw an exception
|
||||||
const error = new Error('something bad happened');
|
// const error = new Error('something bad happened');
|
||||||
const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete').throws(error);
|
// const postgresDeleteStub = sinon.stub(azdataApi.arc.postgres.server, 'delete').throws(error);
|
||||||
|
|
||||||
(postgresOverview['deleteButton'] as StubButton).click();
|
// (postgresOverview['deleteButton'] as StubButton).click();
|
||||||
await errorMessageShown;
|
// await errorMessageShown;
|
||||||
sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
// sinon.assert.calledOnceWithExactly(postgresDeleteStub, postgresModel.info.name, sinon.match.any, sinon.match.any);
|
||||||
sinon.assert.notCalled(showInformationMessage);
|
// sinon.assert.notCalled(showInformationMessage);
|
||||||
sinon.assert.calledOnceWithExactly(showErrorMessage, loc.instanceDeletionFailed(postgresModel.info.name, error.message));
|
// sinon.assert.calledOnceWithExactly(showErrorMessage, loc.instanceDeletionFailed(postgresModel.info.name, error.message));
|
||||||
sinon.assert.notCalled(refreshTreeNode);
|
// sinon.assert.notCalled(refreshTreeNode);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|||||||
@@ -1,97 +1,97 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo } from 'arc';
|
// import { ControllerInfo } from 'arc';
|
||||||
import * as should from 'should';
|
// import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import { v4 as uuid } from 'uuid';
|
// import { v4 as uuid } from 'uuid';
|
||||||
import * as loc from '../../../localizedConstants';
|
// import * as loc from '../../../localizedConstants';
|
||||||
import { ControllerModel } from '../../../models/controllerModel';
|
// import { ControllerModel } from '../../../models/controllerModel';
|
||||||
import { ConnectToControllerDialog } from '../../../ui/dialogs/connectControllerDialog';
|
// import { ConnectToControllerDialog } from '../../../ui/dialogs/connectControllerDialog';
|
||||||
|
|
||||||
describe('ConnectControllerDialog', function (): void {
|
// describe('ConnectControllerDialog', function (): void {
|
||||||
afterEach(function (): void {
|
// afterEach(function (): void {
|
||||||
sinon.restore();
|
// sinon.restore();
|
||||||
});
|
// });
|
||||||
|
|
||||||
(<{ info: ControllerInfo | undefined, description: string }[]>[
|
// (<{ info: ControllerInfo | undefined, description: string }[]>[
|
||||||
{ info: undefined, description: 'all input' },
|
// { info: undefined, description: 'all input' },
|
||||||
{ info: { endpoint: '127.0.0.1' }, description: 'all but URL' },
|
// { info: { endpoint: '127.0.0.1' }, description: 'all but URL' },
|
||||||
{ info: { endpoint: '127.0.0.1', username: 'sa' }, description: 'all but URL and password' }]).forEach(test => {
|
// { info: { endpoint: '127.0.0.1', username: 'sa' }, description: 'all but URL and password' }]).forEach(test => {
|
||||||
it(`Validate returns false when ${test.description} is empty`, async function (): Promise<void> {
|
// it(`Validate returns false when ${test.description} is empty`, async function (): Promise<void> {
|
||||||
const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
// const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
||||||
connectControllerDialog.showDialog(test.info, undefined);
|
// connectControllerDialog.showDialog(test.info, undefined);
|
||||||
await connectControllerDialog.isInitialized;
|
// await connectControllerDialog.isInitialized;
|
||||||
const validateResult = await connectControllerDialog.validate();
|
// const validateResult = await connectControllerDialog.validate();
|
||||||
should(validateResult).be.false();
|
// should(validateResult).be.false();
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('validate returns false if controller refresh fails', async function (): Promise<void> {
|
// it('validate returns false if controller refresh fails', async function (): Promise<void> {
|
||||||
sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.reject('Controller refresh failed'));
|
// sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.reject('Controller refresh failed'));
|
||||||
const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
// const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
||||||
const info: ControllerInfo = { id: uuid(), endpoint: 'https://127.0.0.1:30080', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] };
|
// const info: ControllerInfo = { id: uuid(), endpoint: 'https://127.0.0.1:30080', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] };
|
||||||
connectControllerDialog.showDialog(info, 'pwd');
|
// connectControllerDialog.showDialog(info, 'pwd');
|
||||||
await connectControllerDialog.isInitialized;
|
// await connectControllerDialog.isInitialized;
|
||||||
const validateResult = await connectControllerDialog.validate();
|
// const validateResult = await connectControllerDialog.validate();
|
||||||
should(validateResult).be.false('Validation should have returned false');
|
// should(validateResult).be.false('Validation should have returned false');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('validate replaces http with https', async function (): Promise<void> {
|
// it('validate replaces http with https', async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog(
|
// await validateConnectControllerDialog(
|
||||||
{ id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// { id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30081');
|
// 'https://127.0.0.1:30081');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('validate appends https if missing', async function (): Promise<void> {
|
// it('validate appends https if missing', async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog({ id: uuid(), endpoint: '127.0.0.1:30080', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// await validateConnectControllerDialog({ id: uuid(), endpoint: '127.0.0.1:30080', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30080');
|
// 'https://127.0.0.1:30080');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('validate appends default port if missing', async function (): Promise<void> {
|
// it('validate appends default port if missing', async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog({ id: uuid(), endpoint: 'https://127.0.0.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// await validateConnectControllerDialog({ id: uuid(), endpoint: 'https://127.0.0.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30080');
|
// 'https://127.0.0.1:30080');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('validate appends both port and https if missing', async function (): Promise<void> {
|
// it('validate appends both port and https if missing', async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog({ id: uuid(), endpoint: '127.0.0.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// await validateConnectControllerDialog({ id: uuid(), endpoint: '127.0.0.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'my-arc', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30080');
|
// 'https://127.0.0.1:30080');
|
||||||
});
|
// });
|
||||||
|
|
||||||
for (const name of ['', undefined]) {
|
// for (const name of ['', undefined]) {
|
||||||
it.skip(`validate display name gets set to arc instance name for user chosen name of:${name}`, async function (): Promise<void> {
|
// it.skip(`validate display name gets set to arc instance name for user chosen name of:${name}`, async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog(
|
// await validateConnectControllerDialog(
|
||||||
{ id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: name!, namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// { id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: name!, namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30081');
|
// 'https://127.0.0.1:30081');
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
it.skip(`validate display name gets set to default data controller name for user chosen name of:'' and instanceName in explicably returned as undefined from the controller endpoint`, async function (): Promise<void> {
|
// it.skip(`validate display name gets set to default data controller name for user chosen name of:'' and instanceName in explicably returned as undefined from the controller endpoint`, async function (): Promise<void> {
|
||||||
await validateConnectControllerDialog(
|
// await validateConnectControllerDialog(
|
||||||
{ id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: '', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
// { id: uuid(), endpoint: 'http://127.0.0.1:30081', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: '', namespace: 'arc-ns', username: 'sa', rememberPassword: true, resources: [] },
|
||||||
'https://127.0.0.1:30081',
|
// 'https://127.0.0.1:30081',
|
||||||
undefined);
|
// undefined);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
async function validateConnectControllerDialog(info: ControllerInfo, expectedUrl: string, arcInstanceName: string = 'arc-instance'): Promise<void> {
|
// async function validateConnectControllerDialog(info: ControllerInfo, expectedUrl: string, arcInstanceName: string = 'arc-instance'): Promise<void> {
|
||||||
const expectedControllerInfoName = info.name || arcInstanceName || loc.defaultControllerName;
|
// const expectedControllerInfoName = info.name || arcInstanceName || loc.defaultControllerName;
|
||||||
const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
// const connectControllerDialog = new ConnectToControllerDialog(undefined!);
|
||||||
// Stub out refresh calls to controllerModel - we'll test those separately
|
// // Stub out refresh calls to controllerModel - we'll test those separately
|
||||||
sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.resolve());
|
// sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.resolve());
|
||||||
// stub out controller registration response to return a known instanceName for the dc.
|
// // stub out controller registration response to return a known instanceName for the dc.
|
||||||
/*
|
// /*
|
||||||
sinon.stub(ControllerModel.prototype, 'controllerRegistration').get(() => {
|
// sinon.stub(ControllerModel.prototype, 'controllerRegistration').get(() => {
|
||||||
return <Registration>{ instanceName: arcInstanceName };
|
// return <Registration>{ instanceName: arcInstanceName };
|
||||||
});
|
// });
|
||||||
*/
|
// */
|
||||||
connectControllerDialog.showDialog(info, 'pwd');
|
// connectControllerDialog.showDialog(info, 'pwd');
|
||||||
await connectControllerDialog.isInitialized;
|
// await connectControllerDialog.isInitialized;
|
||||||
const validateResult = await connectControllerDialog.validate();
|
// const validateResult = await connectControllerDialog.validate();
|
||||||
should(validateResult).be.true('Validation should have returned true');
|
// should(validateResult).be.true('Validation should have returned true');
|
||||||
const model = await connectControllerDialog.waitForClose();
|
// const model = await connectControllerDialog.waitForClose();
|
||||||
should(model?.controllerModel.info.endpoint).equal(expectedUrl);
|
// should(model?.controllerModel.info.endpoint).equal(expectedUrl);
|
||||||
should(model?.controllerModel.info.name).equal(expectedControllerInfoName);
|
// should(model?.controllerModel.info.name).equal(expectedControllerInfoName);
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,184 +1,184 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo, ResourceType } from 'arc';
|
// import { ControllerInfo, ResourceType } from 'arc';
|
||||||
import 'mocha';
|
// import 'mocha';
|
||||||
import * as should from 'should';
|
// import * as should from 'should';
|
||||||
import * as TypeMoq from 'typemoq';
|
// import * as TypeMoq from 'typemoq';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import { v4 as uuid } from 'uuid';
|
// import { v4 as uuid } from 'uuid';
|
||||||
import * as vscode from 'vscode';
|
// import * as vscode from 'vscode';
|
||||||
import * as azdataExt from 'azdata-ext';
|
// import * as azdataExt from 'azdata-ext';
|
||||||
import * as kubeUtils from '../../../common/kubeUtils';
|
// import * as kubeUtils from '../../../common/kubeUtils';
|
||||||
import { ControllerModel } from '../../../models/controllerModel';
|
// import { ControllerModel } from '../../../models/controllerModel';
|
||||||
import { MiaaModel } from '../../../models/miaaModel';
|
// import { MiaaModel } from '../../../models/miaaModel';
|
||||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
// import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||||
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
|
// import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
|
||||||
import { MiaaTreeNode } from '../../../ui/tree/miaaTreeNode';
|
// import { MiaaTreeNode } from '../../../ui/tree/miaaTreeNode';
|
||||||
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
// import { FakeControllerModel } from '../../mocks/fakeControllerModel';
|
||||||
import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
// import { FakeAzdataApi } from '../../mocks/fakeAzdataApi';
|
||||||
|
|
||||||
interface ExtensionGlobalMemento extends vscode.Memento {
|
// interface ExtensionGlobalMemento extends vscode.Memento {
|
||||||
setKeysForSync(keys: string[]): void;
|
// setKeysForSync(keys: string[]): void;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getDefaultControllerInfo(): ControllerInfo {
|
// function getDefaultControllerInfo(): ControllerInfo {
|
||||||
return {
|
// return {
|
||||||
id: uuid(),
|
// id: uuid(),
|
||||||
endpoint: '127.0.0.1',
|
// endpoint: '127.0.0.1',
|
||||||
kubeConfigFilePath: '/path/to/.kube/config',
|
// kubeConfigFilePath: '/path/to/.kube/config',
|
||||||
kubeClusterContext: 'currentCluster',
|
// kubeClusterContext: 'currentCluster',
|
||||||
username: 'sa',
|
// username: 'sa',
|
||||||
name: 'my-arc',
|
// name: 'my-arc',
|
||||||
namespace: 'arc-ns',
|
// namespace: 'arc-ns',
|
||||||
rememberPassword: true,
|
// rememberPassword: true,
|
||||||
resources: []
|
// resources: []
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
describe('AzureArcTreeDataProvider tests', function (): void {
|
// describe('AzureArcTreeDataProvider tests', function (): void {
|
||||||
let treeDataProvider: AzureArcTreeDataProvider;
|
// let treeDataProvider: AzureArcTreeDataProvider;
|
||||||
beforeEach(function (): void {
|
// beforeEach(function (): void {
|
||||||
const mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
// const mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
const mockGlobalState = TypeMoq.Mock.ofType<ExtensionGlobalMemento>();
|
// const mockGlobalState = TypeMoq.Mock.ofType<ExtensionGlobalMemento>();
|
||||||
mockGlobalState.setup(x => x.update(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
// mockGlobalState.setup(x => x.update(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||||
mockExtensionContext.setup(x => x.globalState).returns(() => mockGlobalState.object);
|
// mockExtensionContext.setup(x => x.globalState).returns(() => mockGlobalState.object);
|
||||||
//treeDataProviderMock = TypeMoq.Mock.ofType<AzureArcTreeDataProvider>();
|
// //treeDataProviderMock = TypeMoq.Mock.ofType<AzureArcTreeDataProvider>();
|
||||||
treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
|
// treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('addOrUpdateController', function (): void {
|
// describe('addOrUpdateController', function (): void {
|
||||||
it('Multiple Controllers are added correctly', async function (): Promise<void> {
|
// it('Multiple Controllers are added correctly', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
// treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
// let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
// should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||||
const controllerModel = new FakeControllerModel();
|
// const controllerModel = new FakeControllerModel();
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
children = await treeDataProvider.getChildren();
|
// children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
// should(children.length).equal(1, 'Controller node should be added correctly');
|
||||||
|
|
||||||
// Add a couple more
|
// // Add a couple more
|
||||||
const controllerModel2 = new FakeControllerModel();
|
// const controllerModel2 = new FakeControllerModel();
|
||||||
const controllerModel3 = new FakeControllerModel();
|
// const controllerModel3 = new FakeControllerModel();
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel3, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel3, '');
|
||||||
children = await treeDataProvider.getChildren();
|
// children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(3, 'Additional Controller nodes should be added correctly');
|
// should(children.length).equal(3, 'Additional Controller nodes should be added correctly');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Adding a Controller more than once doesn\'t create duplicates', async function (): Promise<void> {
|
// it('Adding a Controller more than once doesn\'t create duplicates', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
// treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
// let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
// should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
// should(children.length).equal(1, 'Controller node should be added correctly');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
// should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Updating an existing controller works as expected', async function (): Promise<void> {
|
// it('Updating an existing controller works as expected', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
// treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
// let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
// should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||||
const originalInfo: ControllerInfo = getDefaultControllerInfo();
|
// const originalInfo: ControllerInfo = getDefaultControllerInfo();
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, originalInfo);
|
// const controllerModel = new ControllerModel(treeDataProvider, originalInfo);
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
// should(children.length).equal(1, 'Controller node should be added correctly');
|
||||||
should((<ControllerTreeNode>children[0]).model.info).deepEqual(originalInfo);
|
// should((<ControllerTreeNode>children[0]).model.info).deepEqual(originalInfo);
|
||||||
const newInfo: ControllerInfo = { id: originalInfo.id, endpoint: '1.1.1.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'new-name', namespace: 'new-namespace', username: 'admin', rememberPassword: false, resources: [] };
|
// const newInfo: ControllerInfo = { id: originalInfo.id, endpoint: '1.1.1.1', kubeConfigFilePath: '/path/to/.kube/config', kubeClusterContext: 'currentCluster', name: 'new-name', namespace: 'new-namespace', username: 'admin', rememberPassword: false, resources: [] };
|
||||||
const controllerModel2 = new ControllerModel(treeDataProvider, newInfo);
|
// const controllerModel2 = new ControllerModel(treeDataProvider, newInfo);
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||||
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
// should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
||||||
should((<ControllerTreeNode>children[0]).model.info).deepEqual(newInfo);
|
// should((<ControllerTreeNode>children[0]).model.info).deepEqual(newInfo);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('getChildren', function (): void {
|
// describe('getChildren', function (): void {
|
||||||
it('should return an empty array before loading stored controllers is completed', async function (): Promise<void> {
|
// it('should return an empty array before loading stored controllers is completed', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = true;
|
// treeDataProvider['_loading'] = true;
|
||||||
let children = await treeDataProvider.getChildren();
|
// let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'While loading we should return an empty array');
|
// should(children.length).equal(0, 'While loading we should return an empty array');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('should return no children after loading', async function (): Promise<void> {
|
// it('should return no children after loading', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
// treeDataProvider['_loading'] = false;
|
||||||
let children = await treeDataProvider.getChildren();
|
// let children = await treeDataProvider.getChildren();
|
||||||
should(children.length).equal(0, 'After loading we should have 0 children');
|
// should(children.length).equal(0, 'After loading we should have 0 children');
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('should return all children of controller after loading', async function (): Promise<void> {
|
// it('should return all children of controller after loading', async function (): Promise<void> {
|
||||||
const mockArcExtension = TypeMoq.Mock.ofType<vscode.Extension<any>>();
|
// const mockArcExtension = TypeMoq.Mock.ofType<vscode.Extension<any>>();
|
||||||
const mockArcApi = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
// const mockArcApi = TypeMoq.Mock.ofType<azdataExt.IExtension>();
|
||||||
mockArcExtension.setup(x => x.exports).returns(() => {
|
// mockArcExtension.setup(x => x.exports).returns(() => {
|
||||||
return mockArcApi.object;
|
// return mockArcApi.object;
|
||||||
});
|
// });
|
||||||
const fakeAzdataApi = new FakeAzdataApi();
|
// const fakeAzdataApi = new FakeAzdataApi();
|
||||||
const pgInstances = [{ name: 'pg1', state: '', workers: 0 }];
|
// const pgInstances = [{ name: 'pg1', state: '', workers: 0 }];
|
||||||
const miaaInstances = [{ name: 'miaa1', state: '', replicas: '', serverEndpoint: '' }];
|
// const miaaInstances = [{ name: 'miaa1', state: '', replicas: '', serverEndpoint: '' }];
|
||||||
fakeAzdataApi.postgresInstances = pgInstances;
|
// fakeAzdataApi.postgresInstances = pgInstances;
|
||||||
fakeAzdataApi.miaaInstances = miaaInstances;
|
// fakeAzdataApi.miaaInstances = miaaInstances;
|
||||||
mockArcApi.setup(x => x.azdata).returns(() => fakeAzdataApi);
|
// mockArcApi.setup(x => x.azdata).returns(() => fakeAzdataApi);
|
||||||
|
|
||||||
sinon.stub(vscode.extensions, 'getExtension').returns(mockArcExtension.object);
|
// sinon.stub(vscode.extensions, 'getExtension').returns(mockArcExtension.object);
|
||||||
sinon.stub(kubeUtils, 'getKubeConfigClusterContexts').returns([{ name: 'currentCluster', isCurrentContext: true }]);
|
// sinon.stub(kubeUtils, 'getKubeConfigClusterContexts').returns([{ name: 'currentCluster', isCurrentContext: true }]);
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo(), 'mypassword');
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo(), 'mypassword');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
const controllerNode = treeDataProvider.getControllerNode(controllerModel);
|
// const controllerNode = treeDataProvider.getControllerNode(controllerModel);
|
||||||
const children = await treeDataProvider.getChildren(controllerNode);
|
// const children = await treeDataProvider.getChildren(controllerNode);
|
||||||
should(children.filter(c => c.label === pgInstances[0].name).length).equal(1, 'Should have a Postgres child');
|
// should(children.filter(c => c.label === pgInstances[0].name).length).equal(1, 'Should have a Postgres child');
|
||||||
should(children.filter(c => c.label === miaaInstances[0].name).length).equal(1, 'Should have a MIAA child');
|
// should(children.filter(c => c.label === miaaInstances[0].name).length).equal(1, 'Should have a MIAA child');
|
||||||
should(children.length).equal(2, 'Should have exactly 2 children');
|
// should(children.length).equal(2, 'Should have exactly 2 children');
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('removeController', function (): void {
|
// describe('removeController', function (): void {
|
||||||
it('removing a controller should work as expected', async function (): Promise<void> {
|
// it('removing a controller should work as expected', async function (): Promise<void> {
|
||||||
treeDataProvider['_loading'] = false;
|
// treeDataProvider['_loading'] = false;
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
||||||
const info2 = getDefaultControllerInfo();
|
// const info2 = getDefaultControllerInfo();
|
||||||
info2.username = 'cloudsa';
|
// info2.username = 'cloudsa';
|
||||||
const controllerModel2 = new ControllerModel(treeDataProvider, info2);
|
// const controllerModel2 = new ControllerModel(treeDataProvider, info2);
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||||
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());
|
// const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());
|
||||||
await treeDataProvider.removeController(children[0]);
|
// await treeDataProvider.removeController(children[0]);
|
||||||
should((await treeDataProvider.getChildren()).length).equal(1, 'Node should have been removed');
|
// should((await treeDataProvider.getChildren()).length).equal(1, 'Node should have been removed');
|
||||||
await treeDataProvider.removeController(children[0]);
|
// await treeDataProvider.removeController(children[0]);
|
||||||
should((await treeDataProvider.getChildren()).length).equal(1, 'Removing same node again should do nothing');
|
// should((await treeDataProvider.getChildren()).length).equal(1, 'Removing same node again should do nothing');
|
||||||
await treeDataProvider.removeController(children[1]);
|
// await treeDataProvider.removeController(children[1]);
|
||||||
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node should work');
|
// should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node should work');
|
||||||
await treeDataProvider.removeController(children[1]);
|
// await treeDataProvider.removeController(children[1]);
|
||||||
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node again should do nothing');
|
// should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node again should do nothing');
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('openResourceDashboard', function (): void {
|
// describe('openResourceDashboard', function (): void {
|
||||||
it('Opening dashboard for nonexistent controller node throws', async function (): Promise<void> {
|
// it('Opening dashboard for nonexistent controller node throws', async function (): Promise<void> {
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
||||||
const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
// const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
await should(openDashboardPromise).be.rejected();
|
// await should(openDashboardPromise).be.rejected();
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Opening dashboard for nonexistent resource throws', async function (): Promise<void> {
|
// it('Opening dashboard for nonexistent resource throws', async function (): Promise<void> {
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
// const openDashboardPromise = treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
await should(openDashboardPromise).be.rejected();
|
// await should(openDashboardPromise).be.rejected();
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('Opening dashboard for existing resource node succeeds', async function (): Promise<void> {
|
// it('Opening dashboard for existing resource node succeeds', async function (): Promise<void> {
|
||||||
const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
// const controllerModel = new ControllerModel(treeDataProvider, getDefaultControllerInfo());
|
||||||
const miaaModel = new MiaaModel(controllerModel, { name: 'miaa-1', resourceType: ResourceType.sqlManagedInstances }, undefined!, treeDataProvider);
|
// const miaaModel = new MiaaModel(controllerModel, { name: 'miaa-1', resourceType: ResourceType.sqlManagedInstances }, undefined!, treeDataProvider);
|
||||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
// await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||||
const controllerNode = treeDataProvider.getControllerNode(controllerModel)!;
|
// const controllerNode = treeDataProvider.getControllerNode(controllerModel)!;
|
||||||
const resourceNode = new MiaaTreeNode(miaaModel, controllerModel);
|
// const resourceNode = new MiaaTreeNode(miaaModel, controllerModel);
|
||||||
sinon.stub(controllerNode, 'getResourceNode').returns(resourceNode);
|
// sinon.stub(controllerNode, 'getResourceNode').returns(resourceNode);
|
||||||
const showDashboardStub = sinon.stub(resourceNode, 'openDashboard');
|
// const showDashboardStub = sinon.stub(resourceNode, 'openDashboard');
|
||||||
await treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
// await treeDataProvider.openResourceDashboard(controllerModel, ResourceType.sqlManagedInstances, '');
|
||||||
should(showDashboardStub.calledOnce).be.true('showDashboard should have been called exactly once');
|
// should(showDashboardStub.calledOnce).be.true('showDashboard should have been called exactly once');
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|||||||
7
extensions/arc/src/typings/arc.d.ts
vendored
7
extensions/arc/src/typings/arc.d.ts
vendored
@@ -36,12 +36,9 @@ declare module 'arc' {
|
|||||||
export type ControllerInfo = {
|
export type ControllerInfo = {
|
||||||
id: string,
|
id: string,
|
||||||
kubeConfigFilePath: string,
|
kubeConfigFilePath: string,
|
||||||
kubeClusterContext: string
|
kubeClusterContext: string,
|
||||||
endpoint: string | undefined,
|
|
||||||
namespace: string,
|
namespace: string,
|
||||||
name: string,
|
name: string,
|
||||||
username: string,
|
|
||||||
rememberPassword: boolean,
|
|
||||||
resources: ResourceInfo[]
|
resources: ResourceInfo[]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +48,5 @@ declare module 'arc' {
|
|||||||
}
|
}
|
||||||
export interface IExtension {
|
export interface IExtension {
|
||||||
getRegisteredDataControllers(): Promise<DataController[]>;
|
getRegisteredDataControllers(): Promise<DataController[]>;
|
||||||
getControllerPassword(controllerInfo: ControllerInfo): Promise<string>;
|
|
||||||
reacquireControllerPassword(controllerInfo: ControllerInfo, password: string, retryCount?: number): Promise<string>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
extensions/arc/src/typings/refs.d.ts
vendored
2
extensions/arc/src/typings/refs.d.ts
vendored
@@ -7,5 +7,5 @@
|
|||||||
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
||||||
/// <reference path='../../../azurecore/src/azurecore.d.ts'/>
|
/// <reference path='../../../azurecore/src/azurecore.d.ts'/>
|
||||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
/// <reference path='../../../azdata/src/typings/azdata-ext.d.ts'/>
|
/// <reference path='../../../azcli/src/typings/az-ext.d.ts'/>
|
||||||
/// <reference path='../../../resource-deployment/src/typings/resource-deployment.d.ts'/>
|
/// <reference path='../../../resource-deployment/src/typings/resource-deployment.d.ts'/>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class FilePicker {
|
|||||||
) {
|
) {
|
||||||
const buttonWidth = 80;
|
const buttonWidth = 80;
|
||||||
this.filePathInputBox = modelBuilder.inputBox()
|
this.filePathInputBox = modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProps({
|
||||||
value: initialPath,
|
value: initialPath,
|
||||||
ariaLabel: ariaLabel,
|
ariaLabel: ariaLabel,
|
||||||
validationErrorMessage: validationErrorMessage,
|
validationErrorMessage: validationErrorMessage,
|
||||||
@@ -43,7 +43,7 @@ export class FilePicker {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.filePickerButton = modelBuilder.button()
|
this.filePickerButton = modelBuilder.button()
|
||||||
.withProperties<azdata.ButtonProperties>({
|
.withProps({
|
||||||
label: loc.browse,
|
label: loc.browse,
|
||||||
width: buttonWidth,
|
width: buttonWidth,
|
||||||
secondary: true
|
secondary: true
|
||||||
@@ -89,5 +89,5 @@ function createFlexContainer(modelBuilder: azdata.ModelBuilder, items: azdata.Co
|
|||||||
alignItems = alignItems || (rowLayout ? 'center' : undefined);
|
alignItems = alignItems || (rowLayout ? 'center' : undefined);
|
||||||
const itemsStyle = rowLayout ? { CSSStyles: { 'margin-right': '5px', } } : {};
|
const itemsStyle = rowLayout ? { CSSStyles: { 'margin-right': '5px', } } : {};
|
||||||
const flexLayout: azdata.FlexLayout = { flexFlow: flexFlow, height: height, width: width, alignItems: alignItems };
|
const flexLayout: azdata.FlexLayout = { flexFlow: flexFlow, height: height, width: width, alignItems: alignItems };
|
||||||
return modelBuilder.flexContainer().withItems(items, itemsStyle).withLayout(flexLayout).withProperties<azdata.ComponentProperties>({ CSSStyles: cssStyles || {} }).component();
|
return modelBuilder.flexContainer().withItems(items, itemsStyle).withLayout(flexLayout).withProps({ CSSStyles: cssStyles || {} }).component();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export abstract class KeyValue extends vscode.Disposable {
|
|||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const keyComponent = modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const keyComponent = modelBuilder.text().withProps({
|
||||||
value: key,
|
value: key,
|
||||||
CSSStyles: { ...cssStyles.text, 'font-weight': 'bold', 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'font-weight': 'bold', 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
@@ -80,7 +80,7 @@ export class TextKeyValue extends KeyValue {
|
|||||||
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string) {
|
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string) {
|
||||||
super(modelBuilder, key, value);
|
super(modelBuilder, key, value);
|
||||||
|
|
||||||
this.text = modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
this.text = modelBuilder.text().withProps({
|
||||||
value: value,
|
value: value,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
@@ -101,16 +101,17 @@ export abstract class BaseInputKeyValue extends KeyValue {
|
|||||||
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string, multiline: boolean) {
|
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string, multiline: boolean) {
|
||||||
super(modelBuilder, key, value);
|
super(modelBuilder, key, value);
|
||||||
|
|
||||||
this.input = modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({
|
this.input = modelBuilder.inputBox().withProps({
|
||||||
value: value,
|
value: value,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
multiline: multiline
|
multiline: multiline,
|
||||||
|
ariaLabel: loc.connectionString(key)
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const inputContainer = modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
const inputContainer = modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
||||||
inputContainer.addItem(this.input);
|
inputContainer.addItem(this.input);
|
||||||
|
|
||||||
const copy = modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const copy = modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.copy,
|
iconPath: IconPathHelper.copy,
|
||||||
width: '17px',
|
width: '17px',
|
||||||
height: '17px',
|
height: '17px',
|
||||||
@@ -153,7 +154,7 @@ export class LinkKeyValue extends KeyValue {
|
|||||||
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string, onClick: (e: any) => any) {
|
constructor(modelBuilder: azdata.ModelBuilder, key: string, value: string, onClick: (e: any) => any) {
|
||||||
super(modelBuilder, key, value);
|
super(modelBuilder, key, value);
|
||||||
|
|
||||||
this.link = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
this.link = modelBuilder.hyperlink().withProps({
|
||||||
label: value,
|
label: value,
|
||||||
url: ''
|
url: ''
|
||||||
}).component();
|
}).component();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export class RadioOptionsGroup {
|
|||||||
private _loadingCompleteMessage: string,
|
private _loadingCompleteMessage: string,
|
||||||
private _loadingCompleteErrorMessage: (error: any) => string
|
private _loadingCompleteErrorMessage: (error: any) => string
|
||||||
) {
|
) {
|
||||||
this._divContainer = this._modelBuilder.divContainer().withProperties<azdata.DivContainerProperties>({ clickable: false }).component();
|
this._divContainer = this._modelBuilder.divContainer().withProps({ clickable: false }).component();
|
||||||
this._loadingBuilder = this._modelBuilder.loadingComponent().withItem(this._divContainer);
|
this._loadingBuilder = this._modelBuilder.loadingComponent().withItem(this._divContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export class RadioOptionsGroup {
|
|||||||
const options = optionsInfo.values!;
|
const options = optionsInfo.values!;
|
||||||
let defaultValue: string = optionsInfo.defaultValue!;
|
let defaultValue: string = optionsInfo.defaultValue!;
|
||||||
options.forEach((option: string) => {
|
options.forEach((option: string) => {
|
||||||
const radioOption = this._modelBuilder.radioButton().withProperties<azdata.RadioButtonProperties>({
|
const radioOption = this._modelBuilder.radioButton().withProps({
|
||||||
label: option,
|
label: option,
|
||||||
checked: option === defaultValue,
|
checked: option === defaultValue,
|
||||||
name: this._groupName,
|
name: this._groupName,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class ControllerDashboard extends Dashboard {
|
|||||||
public override async showDashboard(): Promise<void> {
|
public override async showDashboard(): Promise<void> {
|
||||||
await super.showDashboard();
|
await super.showDashboard();
|
||||||
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
||||||
this._controllerModel.refresh(false).catch(err => console.log(`Error refreshing Controller dashboard ${err}`));
|
this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing Controller dashboard ${err}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
protected async registerTabs(modelView: azdata.ModelView): Promise<(azdata.DashboardTab | azdata.DashboardTabGroup)[]> {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async refresh(): Promise<void> {
|
protected async refresh(): Promise<void> {
|
||||||
await this._controllerModel.refresh();
|
await this._controllerModel.refresh(false, this._controllerModel.info.namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get container(): azdata.Component {
|
public get container(): azdata.Component {
|
||||||
@@ -67,8 +67,8 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
this._propertiesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component();
|
this._propertiesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component();
|
||||||
|
|
||||||
this._arcResourcesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component();
|
this._arcResourcesLoadingComponent = this.modelView.modelBuilder.loadingComponent().component();
|
||||||
this._arcResourcesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
this._arcResourcesTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
data: [],
|
dataValues: [],
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: '',
|
displayName: '',
|
||||||
@@ -126,7 +126,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
const arcResourcesTitle = this.modelView.modelBuilder.text()
|
const arcResourcesTitle = this.modelView.modelBuilder.text()
|
||||||
.withProperties<azdata.TextComponentProperties>({ value: loc.arcResources })
|
.withProps({ value: loc.arcResources })
|
||||||
.component();
|
.component();
|
||||||
|
|
||||||
contentContainer.addItem(arcResourcesTitle, {
|
contentContainer.addItem(arcResourcesTitle, {
|
||||||
@@ -140,7 +140,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
public get toolbarContainer(): azdata.ToolbarContainer {
|
public get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
|
|
||||||
const newInstance = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const newInstance = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.newInstance,
|
label: loc.newInstance,
|
||||||
iconPath: IconPathHelper.add
|
iconPath: IconPathHelper.add
|
||||||
}).component();
|
}).component();
|
||||||
@@ -156,7 +156,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.refresh,
|
label: loc.refresh,
|
||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
@@ -173,7 +173,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
this._openInAzurePortalButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab,
|
iconPath: IconPathHelper.openInTab,
|
||||||
enabled: !!this._controllerModel.controllerConfig
|
enabled: !!this._controllerModel.controllerConfig
|
||||||
@@ -190,7 +190,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const troubleshootButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const troubleshootButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.troubleshoot,
|
label: loc.troubleshoot,
|
||||||
iconPath: IconPathHelper.wrench
|
iconPath: IconPathHelper.wrench
|
||||||
}).component();
|
}).component();
|
||||||
@@ -224,12 +224,12 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
this.controllerProperties.instanceNamespace = config?.metadata.namespace || this.controllerProperties.instanceNamespace;
|
this.controllerProperties.instanceNamespace = config?.metadata.namespace || this.controllerProperties.instanceNamespace;
|
||||||
this.refreshDisplayedProperties();
|
this.refreshDisplayedProperties();
|
||||||
|
|
||||||
this._arcResourcesTable.data = this._controllerModel.registrations
|
let registrations: (string | azdata.ImageComponent | azdata.HyperlinkComponent)[][] = this._controllerModel.registrations
|
||||||
.filter(r => r.instanceType !== ResourceType.dataControllers)
|
.filter(r => r.instanceType !== ResourceType.dataControllers)
|
||||||
.map(r => {
|
.map(r => {
|
||||||
const iconPath = getResourceTypeIcon(r.instanceType ?? '');
|
const iconPath = getResourceTypeIcon(r.instanceType ?? '');
|
||||||
const imageComponent = this.modelView.modelBuilder.image()
|
const imageComponent = this.modelView.modelBuilder.image()
|
||||||
.withProperties<azdata.ImageComponentProperties>({
|
.withProps({
|
||||||
width: iconSize,
|
width: iconSize,
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
iconPath: iconPath,
|
iconPath: iconPath,
|
||||||
@@ -238,7 +238,7 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const nameComponent = this.modelView.modelBuilder.hyperlink()
|
const nameComponent = this.modelView.modelBuilder.hyperlink()
|
||||||
.withProperties<azdata.HyperlinkComponentProperties>({
|
.withProps({
|
||||||
label: r.instanceName || '',
|
label: r.instanceName || '',
|
||||||
url: ''
|
url: ''
|
||||||
}).component();
|
}).component();
|
||||||
@@ -249,6 +249,13 @@ export class ControllerDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
return [imageComponent, nameComponent, resourceTypeToDisplayName(r.instanceType), r.state];
|
return [imageComponent, nameComponent, resourceTypeToDisplayName(r.instanceType), r.state];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let registrationsData = registrations.map(r => {
|
||||||
|
return r.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
|
return { value: value };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this._arcResourcesTable.setDataValues(registrationsData);
|
||||||
this._arcResourcesLoadingComponent.loading = !this._controllerModel.registrationsLastUpdated;
|
this._arcResourcesLoadingComponent.loading = !this._controllerModel.registrationsLastUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as loc from '../../../localizedConstants';
|
import * as loc from '../../../localizedConstants';
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
import { DashboardPage } from '../../components/dashboardPage';
|
||||||
@@ -30,11 +30,11 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
memoryRequest?: string
|
memoryRequest?: string
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _miaaModel: MiaaModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
|
||||||
this.initializeConfigurationBoxes();
|
this.initializeConfigurationBoxes();
|
||||||
|
|
||||||
@@ -59,33 +59,33 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.computeAndStorage,
|
value: loc.computeAndStorage,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const infoComputeStorage_p1 = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const infoComputeStorage_p1 = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.miaaComputeAndStorageDescriptionPartOne,
|
value: loc.miaaComputeAndStorageDescriptionPartOne,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px', 'max-width': 'auto' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const memoryVCoreslink = this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
const memoryVCoreslink = this.modelView.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.scalingCompute,
|
label: loc.scalingCompute,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-managed-instance',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/configure-managed-instance',
|
||||||
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const infoComputeStorage_p4 = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const infoComputeStorage_p4 = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.computeAndStorageDescriptionPartFour,
|
value: loc.computeAndStorageDescriptionPartFour,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const infoComputeStorage_p5 = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const infoComputeStorage_p5 = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.computeAndStorageDescriptionPartFive,
|
value: loc.computeAndStorageDescriptionPartFive,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const infoComputeStorage_p6 = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const infoComputeStorage_p6 = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.computeAndStorageDescriptionPartSix,
|
value: loc.computeAndStorageDescriptionPartSix,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
@@ -112,7 +112,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
protected get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
// Save Edits
|
// Save Edits
|
||||||
this.saveButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
this.saveButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.saveText,
|
label: loc.saveText,
|
||||||
iconPath: IconPathHelper.save,
|
iconPath: IconPathHelper.save,
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -130,8 +130,8 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await this._azdataApi.azdata.arc.sql.mi.edit(
|
await this._azApi.az.sql.miarc.edit(
|
||||||
this._miaaModel.info.name, this.saveArgs, this._miaaModel.controllerModel.azdataAdditionalEnvVars, this._miaaModel.controllerModel.controllerContext);
|
this._miaaModel.info.name, this.saveArgs, this._miaaModel.controllerModel.info.namespace, this._miaaModel.controllerModel.azAdditionalEnvVars);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.saveButton!.enabled = true;
|
this.saveButton!.enabled = true;
|
||||||
throw err;
|
throw err;
|
||||||
@@ -153,7 +153,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Discard
|
// Discard
|
||||||
this.discardButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
this.discardButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.discardText,
|
label: loc.discardText,
|
||||||
iconPath: IconPathHelper.discard,
|
iconPath: IconPathHelper.discard,
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -179,7 +179,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeConfigurationBoxes() {
|
private initializeConfigurationBoxes() {
|
||||||
this.coresLimitBox = this.modelView.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({
|
this.coresLimitBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
min: 1,
|
min: 1,
|
||||||
inputType: 'number',
|
inputType: 'number',
|
||||||
@@ -197,7 +197,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.coresRequestBox = this.modelView.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({
|
this.coresRequestBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
min: 1,
|
min: 1,
|
||||||
inputType: 'number',
|
inputType: 'number',
|
||||||
@@ -215,7 +215,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.memoryLimitBox = this.modelView.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({
|
this.memoryLimitBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
min: 2,
|
min: 2,
|
||||||
inputType: 'number',
|
inputType: 'number',
|
||||||
@@ -233,7 +233,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.memoryRequestBox = this.modelView.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({
|
this.memoryRequestBox = this.modelView.modelBuilder.inputBox().withProps({
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
min: 2,
|
min: 2,
|
||||||
inputType: 'number',
|
inputType: 'number',
|
||||||
@@ -277,7 +277,7 @@ export class MiaaComputeAndStoragePage extends DashboardPage {
|
|||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const keyComponent = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const keyComponent = this.modelView.modelBuilder.text().withProps({
|
||||||
value: `${key} :`,
|
value: `${key} :`,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ export class MiaaConnectionStringsPage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.connectionStrings,
|
value: loc.connectionStrings,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const info = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const info = this.modelView.modelBuilder.text().withProps({
|
||||||
value: `${loc.selectConnectionString}`,
|
value: `${loc.selectConnectionString}`,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
@@ -60,7 +60,7 @@ export class MiaaConnectionStringsPage extends DashboardPage {
|
|||||||
content.addItem(this._keyValueContainer.container);
|
content.addItem(this._keyValueContainer.container);
|
||||||
|
|
||||||
this._connectionStringsMessage = this.modelView.modelBuilder.text()
|
this._connectionStringsMessage = this.modelView.modelBuilder.text()
|
||||||
.withProperties<azdata.TextComponentProperties>({ CSSStyles: { 'text-align': 'center' } })
|
.withProps({ CSSStyles: { 'text-align': 'center' } })
|
||||||
.component();
|
.component();
|
||||||
content.addItem(this._connectionStringsMessage);
|
content.addItem(this._connectionStringsMessage);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class MiaaDashboard extends Dashboard {
|
|||||||
public override async showDashboard(): Promise<void> {
|
public override async showDashboard(): Promise<void> {
|
||||||
await super.showDashboard();
|
await super.showDashboard();
|
||||||
// Kick off the model refreshes but don't wait on it since that's all handled with callbacks anyways
|
// Kick off the model refreshes but don't wait on it since that's all handled with callbacks anyways
|
||||||
this._controllerModel.refresh().catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`));
|
this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for MIAA dashboard ${err}`));
|
||||||
this._miaaModel.refresh().catch(err => console.log(`Error refreshing MIAA model for MIAA dashboard ${err}`));
|
this._miaaModel.refresh().catch(err => console.log(`Error refreshing MIAA model for MIAA dashboard ${err}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as azurecore from 'azurecore';
|
import * as azurecore from 'azurecore';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { getDatabaseStateDisplayText, promptForInstanceDeletion } from '../../../common/utils';
|
import { getDatabaseStateDisplayText, promptForInstanceDeletion } from '../../../common/utils';
|
||||||
@@ -34,7 +34,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
private _connectToServerButton!: azdata.ButtonComponent;
|
private _connectToServerButton!: azdata.ButtonComponent;
|
||||||
private _databasesTableLoading!: azdata.LoadingComponent;
|
private _databasesTableLoading!: azdata.LoadingComponent;
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
private readonly _azurecoreApi: azurecore.IExtension;
|
private readonly _azurecoreApi: azurecore.IExtension;
|
||||||
|
|
||||||
private _instanceProperties = {
|
private _instanceProperties = {
|
||||||
@@ -50,7 +50,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _miaaModel: MiaaModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
this._azurecoreApi = vscode.extensions.getExtension(azurecore.extension.name)?.exports;
|
this._azurecoreApi = vscode.extensions.getExtension(azurecore.extension.name)?.exports;
|
||||||
|
|
||||||
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
||||||
@@ -75,7 +75,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async refresh(): Promise<void> {
|
protected async refresh(): Promise<void> {
|
||||||
await Promise.all([this._controllerModel.refresh(), this._miaaModel.refresh()]);
|
await Promise.all([this._controllerModel.refresh(false, this._controllerModel.info.namespace), this._miaaModel.refresh()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get container(): azdata.Component {
|
public get container(): azdata.Component {
|
||||||
@@ -112,7 +112,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
this._databasesContainer.addItem(this._connectToServerLoading, { CSSStyles: { 'margin-top': '20px' } });
|
this._databasesContainer.addItem(this._connectToServerLoading, { CSSStyles: { 'margin-top': '20px' } });
|
||||||
|
|
||||||
this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().component();
|
this._databasesTableLoading = this.modelView.modelBuilder.loadingComponent().component();
|
||||||
this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
this._databasesTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -132,11 +132,11 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: []
|
dataValues: []
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._databasesMessage = this.modelView.modelBuilder.text()
|
this._databasesMessage = this.modelView.modelBuilder.text()
|
||||||
.withProperties<azdata.TextComponentProperties>({ CSSStyles: { 'text-align': 'center' } })
|
.withProps({ CSSStyles: { 'text-align': 'center' } })
|
||||||
.component();
|
.component();
|
||||||
|
|
||||||
// Update loaded components with data
|
// Update loaded components with data
|
||||||
@@ -162,9 +162,9 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
// Service endpoints
|
// Service endpoints
|
||||||
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
|
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
|
||||||
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.serviceEndpoints, CSSStyles: titleCSS }).component());
|
rootContainer.addItem(this.modelView.modelBuilder.text().withProps({ value: loc.serviceEndpoints, CSSStyles: titleCSS }).component());
|
||||||
|
|
||||||
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -198,15 +198,15 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [
|
dataValues: [
|
||||||
[loc.kibanaDashboard, this._kibanaLoading, loc.kibanaDashboardDescription],
|
[{ value: loc.kibanaDashboard }, { value: this._kibanaLoading }, { value: loc.kibanaDashboardDescription }],
|
||||||
[loc.grafanaDashboard, this._grafanaLoading, loc.grafanaDashboardDescription]]
|
[{ value: loc.grafanaDashboard }, { value: this._grafanaLoading }, { value: loc.grafanaDashboardDescription }]]
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
rootContainer.addItem(endpointsTable);
|
rootContainer.addItem(endpointsTable);
|
||||||
|
|
||||||
// Databases
|
// Databases
|
||||||
rootContainer.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: loc.databases, CSSStyles: titleCSS }).component());
|
rootContainer.addItem(this.modelView.modelBuilder.text().withProps({ value: loc.databases, CSSStyles: titleCSS }).component());
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this._connectToServerButton!.onDidClick(async () => {
|
this._connectToServerButton!.onDidClick(async () => {
|
||||||
this._connectToServerButton!.enabled = false;
|
this._connectToServerButton!.enabled = false;
|
||||||
@@ -227,7 +227,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
public get toolbarContainer(): azdata.ToolbarContainer {
|
public get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
|
|
||||||
const deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const deleteButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.deleteText,
|
label: loc.deleteText,
|
||||||
iconPath: IconPathHelper.delete
|
iconPath: IconPathHelper.delete
|
||||||
}).component();
|
}).component();
|
||||||
@@ -244,7 +244,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
cancellable: false
|
cancellable: false
|
||||||
},
|
},
|
||||||
async (_progress, _token) => {
|
async (_progress, _token) => {
|
||||||
return await this._azdataApi.azdata.arc.sql.mi.delete(this._miaaModel.info.name, this._controllerModel.azdataAdditionalEnvVars, this._controllerModel.controllerContext);
|
return await this._azApi.az.sql.miarc.delete(this._miaaModel.info.name, this._controllerModel.info.namespace, this._controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await this._controllerModel.refreshTreeNode();
|
await this._controllerModel.refreshTreeNode();
|
||||||
@@ -265,7 +265,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.refresh,
|
label: loc.refresh,
|
||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
@@ -285,7 +285,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
this._openInAzurePortalButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab,
|
iconPath: IconPathHelper.openInTab,
|
||||||
enabled: !!this._controllerModel.controllerConfig
|
enabled: !!this._controllerModel.controllerConfig
|
||||||
@@ -302,7 +302,7 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const troubleshootButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const troubleshootButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.troubleshoot,
|
label: loc.troubleshoot,
|
||||||
iconPath: IconPathHelper.wrench
|
iconPath: IconPathHelper.wrench
|
||||||
}).component();
|
}).component();
|
||||||
@@ -366,7 +366,13 @@ export class MiaaDashboardOverviewPage extends DashboardPage {
|
|||||||
// If we were able to get the databases it means we have a good connection so update the username too
|
// If we were able to get the databases it means we have a good connection so update the username too
|
||||||
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
this._instanceProperties.miaaAdmin = this._miaaModel.username || this._instanceProperties.miaaAdmin;
|
||||||
this.refreshDisplayedProperties();
|
this.refreshDisplayedProperties();
|
||||||
this._databasesTable.data = this._miaaModel.databases.map(d => [d.name, getDatabaseStateDisplayText(d.status)]);
|
let databaseDisplayText = this._miaaModel.databases.map(d => [d.name, getDatabaseStateDisplayText(d.status)]);
|
||||||
|
let databasesTextValues = databaseDisplayText.map(d => {
|
||||||
|
return d.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
|
return { value: value };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this._databasesTable.setDataValues(databasesTextValues);
|
||||||
this._databasesTableLoading.loading = false;
|
this._databasesTableLoading.loading = false;
|
||||||
|
|
||||||
if (this._miaaModel.databasesLastUpdated) {
|
if (this._miaaModel.databasesLastUpdated) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as loc from '../../../localizedConstants';
|
import * as loc from '../../../localizedConstants';
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
import { DashboardPage } from '../../components/dashboardPage';
|
||||||
@@ -45,11 +45,11 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
private discardButton!: azdata.ButtonComponent;
|
private discardButton!: azdata.ButtonComponent;
|
||||||
private saveButton!: azdata.ButtonComponent;
|
private saveButton!: azdata.ButtonComponent;
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
|
||||||
this.initializeConfigurationBoxes();
|
this.initializeConfigurationBoxes();
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const workerNodeslink = this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
const workerNodeslink = this.modelView.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.addingWorkerNodes,
|
label: loc.addingWorkerNodes,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/scale-up-down-postgresql-hyperscale-server-group-using-cli',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/scale-up-down-postgresql-hyperscale-server-group-using-cli',
|
||||||
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
@@ -99,7 +99,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const memoryVCoreslink = this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
const memoryVCoreslink = this.modelView.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.scalingCompute,
|
label: loc.scalingCompute,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/scale-up-down-postgresql-hyperscale-server-group-using-cli',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/scale-up-down-postgresql-hyperscale-server-group-using-cli',
|
||||||
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
@@ -165,7 +165,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{
|
{
|
||||||
workers: this.saveArgs.workers,
|
workers: this.saveArgs.workers,
|
||||||
@@ -174,7 +174,8 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
memoryRequest: this.schedulingParamsToEdit(this.saveArgs.memoryRequest!),
|
memoryRequest: this.schedulingParamsToEdit(this.saveArgs.memoryRequest!),
|
||||||
memoryLimit: this.schedulingParamsToEdit(this.saveArgs.memoryLimit!)
|
memoryLimit: this.schedulingParamsToEdit(this.saveArgs.memoryLimit!)
|
||||||
},
|
},
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars);
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If an error occurs while editing the instance then re-enable the save button since
|
// If an error occurs while editing the instance then re-enable the save button since
|
||||||
// the edit wasn't successfully applied
|
// the edit wasn't successfully applied
|
||||||
@@ -479,21 +480,14 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
|
|
||||||
const keyComponent = this.modelView.modelBuilder.text().withProps({
|
const keyComponent = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.workerNodeCount,
|
value: loc.workerNodeCount,
|
||||||
|
requiredIndicator: true,
|
||||||
|
description: loc.workerNodesInformation,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const keyContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
const keyContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
||||||
keyContainer.addItem(keyComponent, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
keyContainer.addItem(keyComponent, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
||||||
|
|
||||||
const information = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.information,
|
|
||||||
title: loc.workerNodesInformation,
|
|
||||||
width: '15px',
|
|
||||||
height: '15px',
|
|
||||||
enabled: false
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
keyContainer.addItem(information, { CSSStyles: { 'margin-left': '5px', 'margin-bottom': '15px' } });
|
|
||||||
flexContainer.addItem(keyContainer, keyFlex);
|
flexContainer.addItem(keyContainer, keyFlex);
|
||||||
|
|
||||||
const inputContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
const inputContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
||||||
@@ -547,6 +541,7 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
if (component.value === originalValue) {
|
if (component.value === originalValue) {
|
||||||
return false;
|
return false;
|
||||||
} else if ((!component.valid)) {
|
} else if ((!component.valid)) {
|
||||||
|
this.discardButton.enabled = true;
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
this.saveButton.enabled = true;
|
this.saveButton.enabled = true;
|
||||||
@@ -575,21 +570,13 @@ export class PostgresComputeAndStoragePage extends DashboardPage {
|
|||||||
|
|
||||||
const titleComponent = this.modelView.modelBuilder.text().withProps({
|
const titleComponent = this.modelView.modelBuilder.text().withProps({
|
||||||
value: title,
|
value: title,
|
||||||
|
description: description,
|
||||||
CSSStyles: { ...cssStyles.title, 'font-weight': 'bold', 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.title, 'font-weight': 'bold', 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const titleContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
const titleContainer = this.modelView.modelBuilder.flexContainer().withLayout({ alignItems: 'center' }).component();
|
||||||
titleContainer.addItem(titleComponent, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
titleContainer.addItem(titleComponent, { CSSStyles: { 'margin-right': '0px', 'margin-bottom': '15px' } });
|
||||||
|
|
||||||
const information = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.information,
|
|
||||||
title: description,
|
|
||||||
width: '15px',
|
|
||||||
height: '15px',
|
|
||||||
enabled: false
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
titleContainer.addItem(information, { CSSStyles: { 'margin-left': '5px', 'margin-bottom': '15px' } });
|
|
||||||
flexContainer.addItem(titleContainer, titleFlex);
|
flexContainer.addItem(titleContainer, titleFlex);
|
||||||
|
|
||||||
let configurationSection = this.modelView.modelBuilder.divContainer().component();
|
let configurationSection = this.modelView.modelBuilder.divContainer().component();
|
||||||
|
|||||||
@@ -38,17 +38,17 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.connectionStrings,
|
value: loc.connectionStrings,
|
||||||
CSSStyles: { ...cssStyles.title }
|
CSSStyles: { ...cssStyles.title }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const info = this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const info = this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.selectConnectionString,
|
value: loc.selectConnectionString,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const link = this.modelView.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
const link = this.modelView.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.learnAboutPostgresClients,
|
label: loc.learnAboutPostgresClients,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/get-connection-endpoints-and-connection-strings-postgres-hyperscale',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/get-connection-endpoints-and-connection-strings-postgres-hyperscale',
|
||||||
}).component();
|
}).component();
|
||||||
@@ -63,7 +63,7 @@ export class PostgresConnectionStringsPage extends DashboardPage {
|
|||||||
|
|
||||||
this.connectionStringsLoading = this.modelView.modelBuilder.loadingComponent()
|
this.connectionStringsLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withItem(this.keyValueContainer.container)
|
.withItem(this.keyValueContainer.container)
|
||||||
.withProperties<azdata.LoadingComponentProperties>({
|
.withProps({
|
||||||
loading: !this._postgresModel.configLastUpdated
|
loading: !this._postgresModel.configLastUpdated
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|||||||
@@ -36,27 +36,27 @@ export class PostgresCoordinatorNodeParametersPage extends PostgresParametersPag
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async saveParameterEdits(engineSettings: string): Promise<void> {
|
protected async saveParameterEdits(engineSettings: string): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ coordinatorEngineSettings: engineSettings },
|
{ coordinatorEngineSettings: engineSettings },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resetAllParameters(): Promise<void> {
|
protected async resetAllParameters(): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ coordinatorEngineSettings: `''`, replaceEngineSettings: true },
|
{ coordinatorEngineSettings: `''`, replaceEngineSettings: true },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resetParameter(parameterName: string): Promise<void> {
|
protected async resetParameter(parameterName: string): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ coordinatorEngineSettings: parameterName + '=' },
|
{ coordinatorEngineSettings: parameterName + '=' },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class PostgresDashboard extends Dashboard {
|
|||||||
await super.showDashboard();
|
await super.showDashboard();
|
||||||
|
|
||||||
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
// Kick off the model refresh but don't wait on it since that's all handled with callbacks anyways
|
||||||
this._controllerModel.refresh().catch(err => console.log(`Error refreshing controller model for Postgres dashboard ${err}`));
|
this._controllerModel.refresh(false, this._controllerModel.info.namespace).catch(err => console.log(`Error refreshing controller model for Postgres dashboard ${err}`));
|
||||||
this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres model for Postgres dashboard ${err}`));
|
this._postgresModel.refresh().catch(err => console.log(`Error refreshing Postgres model for Postgres dashboard ${err}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,17 +33,17 @@ export class PostgresDiagnoseAndSolveProblemsPage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.diagnoseAndSolveProblems,
|
value: loc.diagnoseAndSolveProblems,
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
|
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.clickTheTroubleshootButton('Postgres'),
|
value: loc.clickTheTroubleshootButton('Postgres'),
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const troubleshootButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const troubleshootButton = this.modelView.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.wrench,
|
iconPath: IconPathHelper.wrench,
|
||||||
label: loc.troubleshoot,
|
label: loc.troubleshoot,
|
||||||
width: '160px'
|
width: '160px'
|
||||||
|
|||||||
@@ -5,28 +5,28 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as loc from '../../../localizedConstants';
|
import * as loc from '../../../localizedConstants';
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
import { DashboardPage } from '../../components/dashboardPage';
|
||||||
import { PostgresModel } from '../../../models/postgresModel';
|
import { PostgresModel } from '../../../models/postgresModel';
|
||||||
import { AddPGExtensionsDialog } from '../../dialogs/addPGExtensionsDialog';
|
import { AddPGExtensionsDialog } from '../../dialogs/addPGExtensionsDialog';
|
||||||
import { Deferred } from '../../../common/promise';
|
|
||||||
|
|
||||||
export class PostgresExtensionsPage extends DashboardPage {
|
export class PostgresExtensionsPage extends DashboardPage {
|
||||||
|
|
||||||
private extensionNames: string[] = [];
|
private extensionNames: string[] = [];
|
||||||
|
private droppedExtensions: string[] = [];
|
||||||
private extensionsTable!: azdata.DeclarativeTableComponent;
|
private extensionsTable!: azdata.DeclarativeTableComponent;
|
||||||
private extensionsLoading!: azdata.LoadingComponent;
|
private extensionsLoading!: azdata.LoadingComponent;
|
||||||
private addExtensionsButton!: azdata.ButtonComponent;
|
private addExtensionsButton!: azdata.ButtonComponent;
|
||||||
private _dropExtPromise?: Deferred<void>;
|
private dropExtensionsButton!: azdata.ButtonComponent;
|
||||||
private extensionsLink!: azdata.HyperlinkComponent;
|
private extensionsLink!: azdata.HyperlinkComponent;
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _postgresModel: PostgresModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated())));
|
this._postgresModel.onConfigUpdated(() => this.eventuallyRunOnInitialized(() => this.handleConfigUpdated())));
|
||||||
@@ -79,23 +79,23 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: loc.extensionName,
|
displayName: '',
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
|
width: '20px',
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
width: '95%',
|
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: loc.dropText,
|
displayName: loc.extensionName,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
isReadOnly: false,
|
isReadOnly: true,
|
||||||
width: '10%',
|
width: '100%',
|
||||||
headerCssStyles: cssStyles.tableHeader,
|
headerCssStyles: cssStyles.tableHeader,
|
||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: []
|
dataValues: []
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.extensionsLoading = this.modelView.modelBuilder.loadingComponent()
|
this.extensionsLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
@@ -114,21 +114,22 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
protected get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
// Add extensions
|
// Add extensions
|
||||||
this.addExtensionsButton = this.modelView.modelBuilder.button().withProps({
|
this.addExtensionsButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.addExtensions,
|
label: loc.loadExtensions,
|
||||||
ariaLabel: loc.addExtensions,
|
ariaLabel: loc.loadExtensions,
|
||||||
iconPath: IconPathHelper.add
|
iconPath: IconPathHelper.add
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.addExtensionsButton.onDidClick(async () => {
|
this.addExtensionsButton.onDidClick(async () => {
|
||||||
const addExtDialog = new AddPGExtensionsDialog(this._postgresModel);
|
const addExtDialog = new AddPGExtensionsDialog(this._postgresModel);
|
||||||
addExtDialog.showDialog(loc.addExtensions);
|
addExtDialog.showDialog(loc.loadExtensions);
|
||||||
|
|
||||||
let extArg = await addExtDialog.waitForClose();
|
let extArg = await addExtDialog.waitForClose();
|
||||||
if (extArg) {
|
if (extArg) {
|
||||||
try {
|
try {
|
||||||
this.addExtensionsButton.enabled = false;
|
this.addExtensionsButton.enabled = false;
|
||||||
let extensionList = this.extensionNames.join() + ',' + extArg;
|
this.dropExtensionsButton.enabled = false;
|
||||||
|
let extensionList = this.extensionNames.length ? this.extensionNames.join() + ',' + extArg : extArg;
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
location: vscode.ProgressLocation.Notification,
|
location: vscode.ProgressLocation.Notification,
|
||||||
@@ -137,12 +138,13 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
},
|
},
|
||||||
async (_progress, _token): Promise<void> => {
|
async (_progress, _token): Promise<void> => {
|
||||||
|
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{
|
{
|
||||||
extensions: extensionList
|
extensions: extensionList
|
||||||
},
|
},
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars);
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._postgresModel.refresh();
|
await this._postgresModel.refresh();
|
||||||
@@ -152,7 +154,7 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
vscode.window.showInformationMessage(loc.extensionsAdded(extensionList));
|
vscode.window.showInformationMessage(loc.extensionsAdded(extArg));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.updateExtensionsFailed(error));
|
vscode.window.showErrorMessage(loc.updateExtensionsFailed(error));
|
||||||
@@ -162,43 +164,20 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
// Drop extensions
|
||||||
{ component: this.addExtensionsButton }
|
this.dropExtensionsButton = this.modelView.modelBuilder.button().withProps({
|
||||||
]).component();
|
label: loc.unloadExtensions,
|
||||||
}
|
ariaLabel: loc.unloadExtensions,
|
||||||
|
|
||||||
private refreshExtensionsTable(): void {
|
|
||||||
let extensions = this._postgresModel.config!.spec.engine.extensions;
|
|
||||||
this.extensionsTable.data = extensions.map(e => {
|
|
||||||
|
|
||||||
this.extensionNames.push(e.name);
|
|
||||||
|
|
||||||
return [e.name, this.createDropButton(e.name)];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates drop button to add to each row of extensions table.
|
|
||||||
* Allows user to drop individual extension.
|
|
||||||
* @param name name of postgres extension the drop button will be tied to.
|
|
||||||
*/
|
|
||||||
public createDropButton(name: string): azdata.ButtonComponent {
|
|
||||||
// Can drop individual extensions
|
|
||||||
let button = this.modelView.modelBuilder.button().withProps({
|
|
||||||
iconPath: IconPathHelper.delete,
|
iconPath: IconPathHelper.delete,
|
||||||
ariaLabel: loc.dropExtension,
|
enabled: false
|
||||||
title: loc.dropExtension,
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
enabled: true
|
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
button.onDidClick(async () => {
|
this.dropExtensionsButton.onDidClick(async () => {
|
||||||
try {
|
try {
|
||||||
this.addExtensionsButton.enabled = false;
|
this.addExtensionsButton.enabled = false;
|
||||||
button.enabled = false;
|
this.dropExtensionsButton.enabled = false;
|
||||||
await this.dropExtension(name);
|
await this.dropExtension();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._postgresModel.refresh();
|
await this._postgresModel.refresh();
|
||||||
@@ -206,63 +185,103 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
vscode.window.showInformationMessage(loc.extensionDropped(name));
|
vscode.window.showInformationMessage(loc.extensionsDropped(this.droppedExtensions.join()));
|
||||||
|
this.droppedExtensions = [];
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.updateExtensionsFailed(error));
|
vscode.window.showErrorMessage(loc.updateExtensionsFailed(error));
|
||||||
} finally {
|
} finally {
|
||||||
this.addExtensionsButton.enabled = true;
|
this.addExtensionsButton.enabled = true;
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([
|
||||||
|
{ component: this.addExtensionsButton },
|
||||||
|
{ component: this.dropExtensionsButton }
|
||||||
|
]).component();
|
||||||
|
}
|
||||||
|
|
||||||
|
private refreshExtensionsTable(): void {
|
||||||
|
let extensions = this._postgresModel.config!.spec.engine.extensions;
|
||||||
|
let extenesionFinalData: azdata.DeclarativeTableCellValue[][] = [];
|
||||||
|
let extensionBasicData: (string | azdata.CheckBoxComponent | azdata.ImageComponent)[][] = [];
|
||||||
|
|
||||||
|
if (extensions) {
|
||||||
|
extensionBasicData = extensions.map(e => {
|
||||||
|
this.extensionNames.push(e.name);
|
||||||
|
return [this.createDropCheckBox(e.name), e.name];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
extensionBasicData = [[this.modelView.modelBuilder.image().component(), loc.noExtensions]];
|
||||||
|
}
|
||||||
|
|
||||||
|
extenesionFinalData = extensionBasicData.map(e => {
|
||||||
|
return e.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
|
return { value: value };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.extensionsTable.setDataValues(extenesionFinalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates checkboxes to select which extensions to drop.
|
||||||
|
* Allows user to drop multiple extension.
|
||||||
|
* @param name name of postgres extension the checkbox will be tied to.
|
||||||
|
*/
|
||||||
|
public createDropCheckBox(name: string): azdata.CheckBoxComponent {
|
||||||
|
// Can select extensions to drop
|
||||||
|
let checkBox = this.modelView.modelBuilder.checkBox().withProps({
|
||||||
|
ariaLabel: loc.unloadExtensions,
|
||||||
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
if (name === 'citus') {
|
||||||
|
checkBox.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.disposables.push(
|
||||||
|
checkBox.onChanged(() => {
|
||||||
|
if (checkBox.checked) {
|
||||||
|
this.droppedExtensions.push(name);
|
||||||
|
this.dropExtensionsButton.focus();
|
||||||
|
} else {
|
||||||
|
let index = this.droppedExtensions.indexOf(name, 0);
|
||||||
|
this.droppedExtensions.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.dropExtensionsButton.enabled = this.droppedExtensions.length ? true : false;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dropping the citus extension is not supported.
|
return checkBox;
|
||||||
if (name === 'citus') {
|
|
||||||
button.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls edit on postgres extensions with an updated extensions list.
|
* Calls edit on postgres extensions with an updated extensions list.
|
||||||
* @param name name of postgres extension to not inlcude when editing list of extensions
|
|
||||||
*/
|
*/
|
||||||
public async dropExtension(name: string): Promise<void> {
|
public async dropExtension(): Promise<void> {
|
||||||
// Only allow one drop to be happening at a time
|
this.droppedExtensions.forEach(d => {
|
||||||
if (this._dropExtPromise) {
|
let index = this.extensionNames.indexOf(d, 0);
|
||||||
vscode.window.showErrorMessage(loc.dropMultipleExtensions);
|
this.extensionNames.splice(index, 1);
|
||||||
return this._dropExtPromise.promise;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this._dropExtPromise = new Deferred();
|
await vscode.window.withProgress(
|
||||||
try {
|
{
|
||||||
await vscode.window.withProgress(
|
location: vscode.ProgressLocation.Notification,
|
||||||
{
|
title: loc.updatingInstance(this._postgresModel.info.name),
|
||||||
location: vscode.ProgressLocation.Notification,
|
cancellable: false
|
||||||
title: loc.updatingInstance(this._postgresModel.info.name),
|
},
|
||||||
cancellable: false
|
async (_progress, _token): Promise<void> => {
|
||||||
},
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
async (_progress, _token): Promise<void> => {
|
this._postgresModel.info.name,
|
||||||
let index = this.extensionNames.indexOf(name, 0);
|
{
|
||||||
this.extensionNames.splice(index, 1);
|
extensions: this.extensionNames.join()
|
||||||
|
},
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.controllerModel.azAdditionalEnvVars
|
||||||
{
|
);
|
||||||
extensions: this.extensionNames.join()
|
}
|
||||||
},
|
);
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this._dropExtPromise.resolve();
|
|
||||||
} catch (err) {
|
|
||||||
this._dropExtPromise.reject(err);
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
this._dropExtPromise = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleConfigUpdated(): void {
|
private handleConfigUpdated(): void {
|
||||||
@@ -271,6 +290,7 @@ export class PostgresExtensionsPage extends DashboardPage {
|
|||||||
this.extensionsLink.url = `https://www.postgresql.org/docs/${this._postgresModel.engineVersion}/external-extensions.html`;
|
this.extensionsLink.url = `https://www.postgresql.org/docs/${this._postgresModel.engineVersion}/external-extensions.html`;
|
||||||
this.extensionNames = [];
|
this.extensionNames = [];
|
||||||
this.refreshExtensionsTable();
|
this.refreshExtensionsTable();
|
||||||
|
this.addExtensionsButton.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as loc from '../../../localizedConstants';
|
import * as loc from '../../../localizedConstants';
|
||||||
import { IconPathHelper, cssStyles, iconSize } from '../../../constants';
|
import { IconPathHelper, cssStyles, iconSize } from '../../../constants';
|
||||||
import { DashboardPage } from '../../components/dashboardPage';
|
import { DashboardPage } from '../../components/dashboardPage';
|
||||||
@@ -35,11 +35,11 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
private podStatusTable!: azdata.DeclarativeTableComponent;
|
private podStatusTable!: azdata.DeclarativeTableComponent;
|
||||||
private podStatusData: PodStatusModel[] = [];
|
private podStatusData: PodStatusModel[] = [];
|
||||||
|
|
||||||
private readonly _azdataApi: azdataExt.IExtension;
|
private readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, private _controllerModel: ControllerModel, private _postgresModel: PostgresModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this._controllerModel.onRegistrationsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())),
|
this._controllerModel.onRegistrationsUpdated(() => this.eventuallyRunOnInitialized(() => this.handleRegistrationsUpdated())),
|
||||||
@@ -65,13 +65,13 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
this.properties = this.modelView.modelBuilder.propertiesContainer()
|
this.properties = this.modelView.modelBuilder.propertiesContainer()
|
||||||
.withProperties<azdata.PropertiesContainerComponentProperties>({
|
.withProps({
|
||||||
propertyItems: this.getProperties()
|
propertyItems: this.getProperties()
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.propertiesLoading = this.modelView.modelBuilder.loadingComponent()
|
this.propertiesLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withItem(this.properties)
|
.withItem(this.properties)
|
||||||
.withProperties<azdata.LoadingComponentProperties>({
|
.withProps({
|
||||||
loading: !this._controllerModel.registrationsLastUpdated && !this._postgresModel.configLastUpdated
|
loading: !this._controllerModel.registrationsLastUpdated && !this._postgresModel.configLastUpdated
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -79,9 +79,10 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
// Service endpoints
|
// Service endpoints
|
||||||
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
|
const titleCSS = { ...cssStyles.title, 'margin-block-start': '2em', 'margin-block-end': '0' };
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.serviceEndpoints,
|
value: loc.serviceEndpoints,
|
||||||
CSSStyles: titleCSS
|
CSSStyles: titleCSS,
|
||||||
|
headingLevel: 1
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
this.kibanaLink = this.modelView.modelBuilder.hyperlink().component();
|
this.kibanaLink = this.modelView.modelBuilder.hyperlink().component();
|
||||||
@@ -89,13 +90,13 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
this.grafanaLink = this.modelView.modelBuilder.hyperlink().component();
|
this.grafanaLink = this.modelView.modelBuilder.hyperlink().component();
|
||||||
|
|
||||||
this.kibanaLoading = this.modelView.modelBuilder.loadingComponent()
|
this.kibanaLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withProperties<azdata.LoadingComponentProperties>(
|
.withProps(
|
||||||
{ loading: !this._postgresModel?.configLastUpdated }
|
{ loading: !this._postgresModel?.configLastUpdated }
|
||||||
)
|
)
|
||||||
.component();
|
.component();
|
||||||
|
|
||||||
this.grafanaLoading = this.modelView.modelBuilder.loadingComponent()
|
this.grafanaLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withProperties<azdata.LoadingComponentProperties>(
|
.withProps(
|
||||||
{ loading: !this._postgresModel?.configLastUpdated }
|
{ loading: !this._postgresModel?.configLastUpdated }
|
||||||
)
|
)
|
||||||
.component();
|
.component();
|
||||||
@@ -105,8 +106,9 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
this.kibanaLoading.component = this.kibanaLink;
|
this.kibanaLoading.component = this.kibanaLink;
|
||||||
this.grafanaLoading.component = this.grafanaLink;
|
this.grafanaLoading.component = this.grafanaLink;
|
||||||
|
|
||||||
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
const endpointsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
ariaLabel: loc.serviceEndpoints,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: loc.name,
|
displayName: loc.name,
|
||||||
@@ -139,20 +141,24 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [
|
dataValues: [
|
||||||
[loc.kibanaDashboard, this.kibanaLoading, loc.kibanaDashboardDescription],
|
[{ value: loc.kibanaDashboard }, { value: this.kibanaLoading }, { value: loc.kibanaDashboardDescription }],
|
||||||
[loc.grafanaDashboard, this.grafanaLoading, loc.grafanaDashboardDescription]]
|
[{ value: loc.grafanaDashboard }, { value: this.grafanaLoading }, { value: loc.grafanaDashboardDescription }]]
|
||||||
}).component();
|
}).component();
|
||||||
content.addItem(endpointsTable);
|
content.addItem(endpointsTable);
|
||||||
|
|
||||||
// Server Group Nodes
|
// Server Group Nodes
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.serverGroupNodes,
|
value: loc.serverGroupNodes,
|
||||||
CSSStyles: titleCSS
|
CSSStyles: titleCSS,
|
||||||
|
headingLevel: 1
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.podStatusTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
this.podStatusTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
ariaLabel: loc.serverGroupNodes,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: loc.name,
|
displayName: loc.name,
|
||||||
@@ -185,14 +191,14 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [this.podStatusData.map(p => [p.podName, p.type, p.status])]
|
dataValues: this.createPodStatusDataValues()
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.serverGroupNodesLoading = this.modelView.modelBuilder.loadingComponent()
|
this.serverGroupNodesLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withItem(this.podStatusTable)
|
.withItem(this.podStatusTable)
|
||||||
.withProperties<azdata.LoadingComponentProperties>({
|
.withProps({
|
||||||
loading: !this._postgresModel.configLastUpdated
|
loading: !this._postgresModel.configLastUpdated
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -206,7 +212,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
protected get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
// Reset password
|
// Reset password
|
||||||
const resetPasswordButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const resetPasswordButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.resetPassword,
|
label: loc.resetPassword,
|
||||||
iconPath: IconPathHelper.edit
|
iconPath: IconPathHelper.edit
|
||||||
}).component();
|
}).component();
|
||||||
@@ -217,13 +223,14 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
try {
|
try {
|
||||||
const password = await promptAndConfirmPassword(input => !input ? loc.enterANonEmptyPassword : '');
|
const password = await promptAndConfirmPassword(input => !input ? loc.enterANonEmptyPassword : '');
|
||||||
if (password) {
|
if (password) {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{
|
{
|
||||||
adminPassword: true,
|
adminPassword: true,
|
||||||
noWait: true
|
noWait: true
|
||||||
},
|
},
|
||||||
Object.assign({ 'AZDATA_PASSWORD': password }, this._controllerModel.azdataAdditionalEnvVars));
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
|
Object.assign({ 'AZDATA_PASSWORD': password }, this._controllerModel.azAdditionalEnvVars));
|
||||||
vscode.window.showInformationMessage(loc.passwordReset);
|
vscode.window.showInformationMessage(loc.passwordReset);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -234,7 +241,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Delete service
|
// Delete service
|
||||||
this.deleteButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
this.deleteButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.deleteText,
|
label: loc.deleteText,
|
||||||
iconPath: IconPathHelper.delete
|
iconPath: IconPathHelper.delete
|
||||||
}).component();
|
}).component();
|
||||||
@@ -251,7 +258,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
cancellable: false
|
cancellable: false
|
||||||
},
|
},
|
||||||
async (_progress, _token) => {
|
async (_progress, _token) => {
|
||||||
return await this._azdataApi.azdata.arc.postgres.server.delete(this._postgresModel.info.name, this._controllerModel.azdataAdditionalEnvVars, this._controllerModel.controllerContext);
|
return await this._azApi.az.postgres.arcserver.delete(this._postgresModel.info.name, this._postgresModel.controllerModel.info.namespace, this._controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await this._controllerModel.refreshTreeNode();
|
await this._controllerModel.refreshTreeNode();
|
||||||
@@ -272,7 +279,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Refresh
|
// Refresh
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.refresh,
|
label: loc.refresh,
|
||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
@@ -288,7 +295,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._postgresModel.refresh(),
|
this._postgresModel.refresh(),
|
||||||
this._controllerModel.refresh()
|
this._controllerModel.refresh(false, this._controllerModel.info.namespace)
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
@@ -299,7 +306,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Open in Azure portal
|
// Open in Azure portal
|
||||||
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const openInAzurePortalButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.openInAzurePortal,
|
label: loc.openInAzurePortal,
|
||||||
iconPath: IconPathHelper.openInTab
|
iconPath: IconPathHelper.openInTab
|
||||||
}).component();
|
}).component();
|
||||||
@@ -345,7 +352,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
let podModels: PodStatusModel[] = [];
|
let podModels: PodStatusModel[] = [];
|
||||||
const podStatus = this._postgresModel.config?.status.podsStatus;
|
const podStatus = this._postgresModel.config?.status.podsStatus;
|
||||||
|
|
||||||
podStatus?.forEach(p => {
|
podStatus?.forEach((p: { conditions: any[]; name: any; role: string; }) => {
|
||||||
// If a condition of the pod has a status of False, pod is not Ready
|
// If a condition of the pod has a status of False, pod is not Ready
|
||||||
const status = p.conditions.find(c => c.status === 'False') ? loc.notReady : loc.ready;
|
const status = p.conditions.find(c => c.status === 'False') ? loc.notReady : loc.ready;
|
||||||
|
|
||||||
@@ -389,6 +396,15 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
return podModels;
|
return podModels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createPodStatusDataValues(): azdata.DeclarativeTableCellValue[][] {
|
||||||
|
let podDataValue: (string | azdata.Component)[][] = this.podStatusData.map(p => [p.podName, p.type, p.status]);
|
||||||
|
return podDataValue.map(p => {
|
||||||
|
return p.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
|
return { value: value };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private refreshDashboardLinks(): void {
|
private refreshDashboardLinks(): void {
|
||||||
if (this._postgresModel.config) {
|
if (this._postgresModel.config) {
|
||||||
const kibanaUrl = this._postgresModel.config.status.logSearchDashboard ?? '';
|
const kibanaUrl = this._postgresModel.config.status.logSearchDashboard ?? '';
|
||||||
@@ -406,7 +422,7 @@ export class PostgresOverviewPage extends DashboardPage {
|
|||||||
private refreshServerNodes(): void {
|
private refreshServerNodes(): void {
|
||||||
if (this._postgresModel.config) {
|
if (this._postgresModel.config) {
|
||||||
this.podStatusData = this.getPodStatus();
|
this.podStatusData = this.getPodStatus();
|
||||||
this.podStatusTable.data = this.podStatusData.map(p => [p.podName, p.type, p.status]);
|
this.podStatusTable.setDataValues(this.createPodStatusDataValues());
|
||||||
this.serverGroupNodesLoading.loading = false;
|
this.serverGroupNodesLoading.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as loc from '../../../localizedConstants';
|
import * as loc from '../../../localizedConstants';
|
||||||
import { UserCancelledError } from '../../../common/api';
|
import { UserCancelledError } from '../../../common/api';
|
||||||
import { IconPathHelper, cssStyles } from '../../../constants';
|
import { IconPathHelper, cssStyles } from '../../../constants';
|
||||||
@@ -37,12 +37,12 @@ export abstract class PostgresParametersPage extends DashboardPage {
|
|||||||
private changedComponentValues: Set<string> = new Set();
|
private changedComponentValues: Set<string> = new Set();
|
||||||
private parameterUpdates: Map<string, string> = new Map();
|
private parameterUpdates: Map<string, string> = new Map();
|
||||||
|
|
||||||
protected readonly _azdataApi: azdataExt.IExtension;
|
protected readonly _azApi: azExt.IExtension;
|
||||||
|
|
||||||
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, protected _postgresModel: PostgresModel) {
|
constructor(modelView: azdata.ModelView, dashboard: azdata.window.ModelViewDashboard, protected _postgresModel: PostgresModel) {
|
||||||
super(modelView, dashboard);
|
super(modelView, dashboard);
|
||||||
|
|
||||||
this._azdataApi = vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
this._azApi = vscode.extensions.getExtension(azExt.extension.name)?.exports;
|
||||||
|
|
||||||
this.initializeSearchBox();
|
this.initializeSearchBox();
|
||||||
|
|
||||||
@@ -551,8 +551,7 @@ export abstract class PostgresParametersPage extends DashboardPage {
|
|||||||
iconPath: IconPathHelper.information,
|
iconPath: IconPathHelper.information,
|
||||||
width: '15px',
|
width: '15px',
|
||||||
height: '15px',
|
height: '15px',
|
||||||
enabled: false,
|
description: loc.rangeSetting(engineSetting.min!, engineSetting.max!)
|
||||||
title: loc.rangeSetting(engineSetting.min!, engineSetting.max!)
|
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.properties,
|
value: loc.properties,
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '25px' }
|
CSSStyles: { ...cssStyles.title, 'margin-bottom': '25px' }
|
||||||
}).component());
|
}).component());
|
||||||
@@ -55,7 +55,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
|||||||
|
|
||||||
this.loading = this.modelView.modelBuilder.loadingComponent()
|
this.loading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withItem(this.keyValueContainer.container)
|
.withItem(this.keyValueContainer.container)
|
||||||
.withProperties<azdata.LoadingComponentProperties>({
|
.withProps({
|
||||||
loading: !this._postgresModel.configLastUpdated && !this._controllerModel.registrationsLastUpdated
|
loading: !this._postgresModel.configLastUpdated && !this._controllerModel.registrationsLastUpdated
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected get toolbarContainer(): azdata.ToolbarContainer {
|
protected get toolbarContainer(): azdata.ToolbarContainer {
|
||||||
const refreshButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const refreshButton = this.modelView.modelBuilder.button().withProps({
|
||||||
label: loc.refresh,
|
label: loc.refresh,
|
||||||
iconPath: IconPathHelper.refresh
|
iconPath: IconPathHelper.refresh
|
||||||
}).component();
|
}).component();
|
||||||
@@ -77,7 +77,7 @@ export class PostgresPropertiesPage extends DashboardPage {
|
|||||||
this.loading!.loading = true;
|
this.loading!.loading = true;
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._postgresModel.refresh(),
|
this._postgresModel.refresh(),
|
||||||
this._controllerModel.refresh()
|
this._controllerModel.refresh(false, this._controllerModel.info.namespace)
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
vscode.window.showErrorMessage(loc.refreshFailed(error));
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { PostgresModel } from '../../../models/postgresModel';
|
|||||||
|
|
||||||
export type PodHealthModel = {
|
export type PodHealthModel = {
|
||||||
condition: string,
|
condition: string,
|
||||||
details?: azdata.Component,
|
details: azdata.Component,
|
||||||
lastUpdate: string
|
lastUpdate: string
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,6 +90,7 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
this.podConditionsContainer = this.modelView.modelBuilder.divContainer().component();
|
this.podConditionsContainer = this.modelView.modelBuilder.divContainer().component();
|
||||||
this.podConditionsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
this.podConditionsTable = this.modelView.modelBuilder.declarativeTable().withProps({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
ariaLabel: loc.podConditionsTable,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: loc.condition,
|
displayName: loc.condition,
|
||||||
@@ -119,10 +120,13 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
rowCssStyles: cssStyles.tableRow
|
rowCssStyles: cssStyles.tableRow
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [this.coordinatorData.map(p => [p.condition, p.details, p.lastUpdate])]
|
dataValues: this.createPodConditionsDataValues(this.coordinatorData)
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.podDropDown = this.modelView.modelBuilder.dropDown().withProps({ width: '150px' }).component();
|
this.podDropDown = this.modelView.modelBuilder.dropDown().withProps({
|
||||||
|
width: '150px',
|
||||||
|
ariaLabel: loc.podsUsedDescriptionAria
|
||||||
|
}).component();
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.podDropDown.onValueChanged(() => {
|
this.podDropDown.onValueChanged(() => {
|
||||||
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(String(this.podDropDown.value)));
|
this.podConditionsTable.setFilter(this.podConditionsTableIndexes.get(String(this.podDropDown.value)));
|
||||||
@@ -133,7 +137,7 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
this.podConditionsContainer.addItem(this.podConditionsTable);
|
this.podConditionsContainer.addItem(this.podConditionsTable);
|
||||||
this.podConditionsLoading = this.modelView.modelBuilder.loadingComponent()
|
this.podConditionsLoading = this.modelView.modelBuilder.loadingComponent()
|
||||||
.withItem(this.podConditionsContainer)
|
.withItem(this.podConditionsContainer)
|
||||||
.withProperties<azdata.LoadingComponentProperties>({
|
.withProps({
|
||||||
loading: !this._postgresModel.configLastUpdated
|
loading: !this._postgresModel.configLastUpdated
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -234,11 +238,20 @@ export class PostgresResourceHealthPage extends DashboardPage {
|
|||||||
this.podConditionsTableIndexes.set(p.name, indexes);
|
this.podConditionsTableIndexes.set(p.name, indexes);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.podConditionsTable.data = this.podsData.map(p => [p.condition, p.details, p.lastUpdate]);
|
this.podConditionsTable.setDataValues(this.createPodConditionsDataValues(this.podsData));
|
||||||
|
|
||||||
return podNames;
|
return podNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createPodConditionsDataValues(podInfo: PodHealthModel[]): azdata.DeclarativeTableCellValue[][] {
|
||||||
|
let podDataValues: (string | azdata.Component)[][] = podInfo.map(p => [p.condition, p.details, p.lastUpdate]);
|
||||||
|
return podDataValues.map(p => {
|
||||||
|
return p.map((value): azdata.DeclarativeTableCellValue => {
|
||||||
|
return { value: value };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private findPodIssues(): string[] {
|
private findPodIssues(): string[] {
|
||||||
const podStatus = this._postgresModel.config?.status.podsStatus;
|
const podStatus = this._postgresModel.config?.status.podsStatus;
|
||||||
let issueCount = 0;
|
let issueCount = 0;
|
||||||
|
|||||||
@@ -34,22 +34,22 @@ export class PostgresSupportRequestPage extends DashboardPage {
|
|||||||
const content = this.modelView.modelBuilder.divContainer().component();
|
const content = this.modelView.modelBuilder.divContainer().component();
|
||||||
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
root.addItem(content, { CSSStyles: { 'margin': '20px' } });
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.newSupportRequest,
|
value: loc.newSupportRequest,
|
||||||
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
|
CSSStyles: { ...cssStyles.title, 'margin-bottom': '20px' }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.clickTheNewSupportRequestButton,
|
value: loc.clickTheNewSupportRequestButton,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
content.addItem(this.modelView.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
content.addItem(this.modelView.modelBuilder.text().withProps({
|
||||||
value: loc.supportRequestNote,
|
value: loc.supportRequestNote,
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
CSSStyles: { ...cssStyles.text, 'margin-bottom': '20px' }
|
||||||
}).component());
|
}).component());
|
||||||
|
|
||||||
const supportRequestButton = this.modelView.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const supportRequestButton = this.modelView.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.support,
|
iconPath: IconPathHelper.support,
|
||||||
label: loc.newSupportRequest,
|
label: loc.newSupportRequest,
|
||||||
width: '205px'
|
width: '205px'
|
||||||
|
|||||||
@@ -37,26 +37,26 @@ export class PostgresWorkerNodeParametersPage extends PostgresParametersPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async saveParameterEdits(engineSettings: string): Promise<void> {
|
protected async saveParameterEdits(engineSettings: string): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ workerEngineSettings: engineSettings },
|
{ workerEngineSettings: engineSettings },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resetAllParameters(): Promise<void> {
|
protected async resetAllParameters(): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ workerEngineSettings: `''`, replaceEngineSettings: true },
|
{ workerEngineSettings: `''`, replaceEngineSettings: true },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async resetParameter(parameterName: string): Promise<void> {
|
protected async resetParameter(parameterName: string): Promise<void> {
|
||||||
await this._azdataApi.azdata.arc.postgres.server.edit(
|
await this._azApi.az.postgres.arcserver.edit(
|
||||||
this._postgresModel.info.name,
|
this._postgresModel.info.name,
|
||||||
{ workerEngineSettings: parameterName + '=' },
|
{ workerEngineSettings: parameterName + '=' },
|
||||||
this._postgresModel.controllerModel.azdataAdditionalEnvVars,
|
this._postgresModel.controllerModel.info.namespace,
|
||||||
this._postgresModel.controllerModel.controllerContext);
|
this._postgresModel.controllerModel.azAdditionalEnvVars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { cssStyles } from '../../constants';
|
|||||||
import { InitializingComponent } from '../components/initializingComponent';
|
import { InitializingComponent } from '../components/initializingComponent';
|
||||||
import { PostgresModel } from '../../models/postgresModel';
|
import { PostgresModel } from '../../models/postgresModel';
|
||||||
|
|
||||||
|
export const validExtensions = ['citus', 'pgaudit', 'pgautofailover', 'pg_cron', 'pg_partman', 'plv8', 'postgis', 'postgis_raster', 'postgis_sfcgal', 'postgis_tiger_geocoder', 'tdigest'];
|
||||||
|
|
||||||
export class AddPGExtensionsDialog extends InitializingComponent {
|
export class AddPGExtensionsDialog extends InitializingComponent {
|
||||||
protected modelBuilder!: azdata.ModelBuilder;
|
protected modelBuilder!: azdata.ModelBuilder;
|
||||||
|
|
||||||
@@ -27,12 +29,12 @@ export class AddPGExtensionsDialog extends InitializingComponent {
|
|||||||
dialog.registerContent(async view => {
|
dialog.registerContent(async view => {
|
||||||
this.modelBuilder = view.modelBuilder;
|
this.modelBuilder = view.modelBuilder;
|
||||||
|
|
||||||
const info = this.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
const info = this.modelBuilder.text().withProps({
|
||||||
value: loc.extensionsFunction,
|
value: loc.extensionsAddFunction(validExtensions.join(', ')),
|
||||||
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' }
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const link = this.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
const link = this.modelBuilder.hyperlink().withProps({
|
||||||
label: loc.extensionsLearnMore,
|
label: loc.extensionsLearnMore,
|
||||||
url: 'https://docs.microsoft.com/azure/azure-arc/data/using-extensions-in-postgresql-hyperscale-server-group',
|
url: 'https://docs.microsoft.com/azure/azure-arc/data/using-extensions-in-postgresql-hyperscale-server-group',
|
||||||
}).component();
|
}).component();
|
||||||
@@ -42,10 +44,18 @@ export class AddPGExtensionsDialog extends InitializingComponent {
|
|||||||
infoAndLink.addItem(link);
|
infoAndLink.addItem(link);
|
||||||
|
|
||||||
this.extensionsListInputBox = this.modelBuilder.inputBox()
|
this.extensionsListInputBox = this.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProps({
|
||||||
value: '',
|
value: '',
|
||||||
ariaLabel: loc.extensionsAddList,
|
ariaLabel: loc.extensionsAddList,
|
||||||
enabled: true
|
enabled: true,
|
||||||
|
validationErrorMessage: loc.extensionsAddErrorrMessage(validExtensions.join(','))
|
||||||
|
}).withValidation((component) => {
|
||||||
|
if (!component.value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newExtensions = component.value.split(',');
|
||||||
|
return newExtensions.every(e => validExtensions.includes(e));
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
let formModel = this.modelBuilder.formContainer()
|
let formModel = this.modelBuilder.formContainer()
|
||||||
@@ -56,7 +66,8 @@ export class AddPGExtensionsDialog extends InitializingComponent {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: this.extensionsListInputBox,
|
component: this.extensionsListInputBox,
|
||||||
title: loc.extensionsAddList
|
title: loc.extensionsAddList,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
title: ''
|
title: ''
|
||||||
@@ -67,7 +78,7 @@ export class AddPGExtensionsDialog extends InitializingComponent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dialog.registerCloseValidator(async () => await this.validate());
|
dialog.registerCloseValidator(async () => await this.validate());
|
||||||
dialog.okButton.label = loc.addExtensions;
|
dialog.okButton.label = loc.loadExtensions;
|
||||||
dialog.cancelButton.label = loc.cancel;
|
dialog.cancelButton.label = loc.cancel;
|
||||||
azdata.window.openDialog(dialog);
|
azdata.window.openDialog(dialog);
|
||||||
return dialog;
|
return dialog;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { ControllerInfo, ResourceInfo } from 'arc';
|
import { ControllerInfo, ResourceInfo } from 'arc';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as azdataExt from 'azdata-ext';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Deferred } from '../../common/promise';
|
import { Deferred } from '../../common/promise';
|
||||||
@@ -13,12 +12,11 @@ import * as loc from '../../localizedConstants';
|
|||||||
import { ControllerModel } from '../../models/controllerModel';
|
import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { InitializingComponent } from '../components/initializingComponent';
|
import { InitializingComponent } from '../components/initializingComponent';
|
||||||
import { AzureArcTreeDataProvider } from '../tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from '../tree/azureArcTreeDataProvider';
|
||||||
import { getErrorMessage } from '../../common/utils';
|
|
||||||
import { RadioOptionsGroup } from '../components/radioOptionsGroup';
|
import { RadioOptionsGroup } from '../components/radioOptionsGroup';
|
||||||
import { getCurrentClusterContext, getDefaultKubeConfigPath, getKubeConfigClusterContexts, KubeClusterContext } from '../../common/kubeUtils';
|
import { getCurrentClusterContext, getDefaultKubeConfigPath, getKubeConfigClusterContexts, KubeClusterContext } from '../../common/kubeUtils';
|
||||||
import { FilePicker } from '../components/filePicker';
|
import { FilePicker } from '../components/filePicker';
|
||||||
|
|
||||||
export type ConnectToControllerDialogModel = { controllerModel: ControllerModel, password: string };
|
export type ConnectToControllerDialogModel = { controllerModel: ControllerModel };
|
||||||
|
|
||||||
abstract class ControllerDialogBase extends InitializingComponent {
|
abstract class ControllerDialogBase extends InitializingComponent {
|
||||||
protected _toDispose: vscode.Disposable[] = [];
|
protected _toDispose: vscode.Disposable[] = [];
|
||||||
@@ -29,9 +27,6 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
protected kubeConfigInputBox!: FilePicker;
|
protected kubeConfigInputBox!: FilePicker;
|
||||||
protected clusterContextRadioGroup!: RadioOptionsGroup;
|
protected clusterContextRadioGroup!: RadioOptionsGroup;
|
||||||
protected nameInputBox!: azdata.InputBoxComponent;
|
protected nameInputBox!: azdata.InputBoxComponent;
|
||||||
protected usernameInputBox!: azdata.InputBoxComponent;
|
|
||||||
protected passwordInputBox!: azdata.InputBoxComponent;
|
|
||||||
protected urlInputBox!: azdata.InputBoxComponent;
|
|
||||||
|
|
||||||
private _kubeClusters: KubeClusterContext[] = [];
|
private _kubeClusters: KubeClusterContext[] = [];
|
||||||
|
|
||||||
@@ -46,13 +41,6 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
component: this.namespaceInputBox,
|
component: this.namespaceInputBox,
|
||||||
title: loc.namespace,
|
title: loc.namespace,
|
||||||
required: true
|
required: true
|
||||||
},
|
|
||||||
{
|
|
||||||
component: this.urlInputBox,
|
|
||||||
title: loc.controllerUrl,
|
|
||||||
layout: {
|
|
||||||
info: loc.controllerUrlDescription
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
component: this.kubeConfigInputBox.component(),
|
component: this.kubeConfigInputBox.component(),
|
||||||
title: loc.controllerKubeConfig,
|
title: loc.controllerKubeConfig,
|
||||||
@@ -68,14 +56,6 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
layout: {
|
layout: {
|
||||||
info: loc.controllerNameDescription
|
info: loc.controllerNameDescription
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
component: this.usernameInputBox,
|
|
||||||
title: loc.controllerUsername,
|
|
||||||
required: true
|
|
||||||
}, {
|
|
||||||
component: this.passwordInputBox,
|
|
||||||
title: loc.controllerPassword,
|
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -83,16 +63,11 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
protected abstract fieldToFocusOn(): azdata.Component;
|
protected abstract fieldToFocusOn(): azdata.Component;
|
||||||
protected readonlyFields(): azdata.Component[] { return []; }
|
protected readonlyFields(): azdata.Component[] { return []; }
|
||||||
|
|
||||||
protected initializeFields(controllerInfo: ControllerInfo | undefined, password: string | undefined) {
|
protected initializeFields(controllerInfo: ControllerInfo | undefined) {
|
||||||
this.namespaceInputBox = this.modelBuilder.inputBox()
|
this.namespaceInputBox = this.modelBuilder.inputBox()
|
||||||
.withProps({
|
.withProps({
|
||||||
value: controllerInfo?.namespace,
|
value: controllerInfo?.namespace,
|
||||||
}).component();
|
}).component();
|
||||||
this.urlInputBox = this.modelBuilder.inputBox()
|
|
||||||
.withProps({
|
|
||||||
value: controllerInfo?.endpoint,
|
|
||||||
placeHolder: loc.controllerUrlPlaceholder,
|
|
||||||
}).component();
|
|
||||||
this.kubeConfigInputBox = new FilePicker(
|
this.kubeConfigInputBox = new FilePicker(
|
||||||
this.modelBuilder,
|
this.modelBuilder,
|
||||||
controllerInfo?.kubeConfigFilePath || getDefaultKubeConfigPath(),
|
controllerInfo?.kubeConfigFilePath || getDefaultKubeConfigPath(),
|
||||||
@@ -113,15 +88,6 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
.withProps({
|
.withProps({
|
||||||
value: controllerInfo?.name
|
value: controllerInfo?.name
|
||||||
}).component();
|
}).component();
|
||||||
this.usernameInputBox = this.modelBuilder.inputBox()
|
|
||||||
.withProps({
|
|
||||||
value: controllerInfo?.username
|
|
||||||
}).component();
|
|
||||||
this.passwordInputBox = this.modelBuilder.inputBox()
|
|
||||||
.withProps({
|
|
||||||
inputType: 'password',
|
|
||||||
value: password
|
|
||||||
}).component();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected completionPromise = new Deferred<ConnectToControllerDialogModel | undefined>();
|
protected completionPromise = new Deferred<ConnectToControllerDialogModel | undefined>();
|
||||||
@@ -150,13 +116,13 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
this.namespaceInputBox.value = currentContext?.namespace;
|
this.namespaceInputBox.value = currentContext?.namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public showDialog(controllerInfo?: ControllerInfo, password: string | undefined = undefined): azdata.window.Dialog {
|
public showDialog(controllerInfo?: ControllerInfo): azdata.window.Dialog {
|
||||||
this.id = controllerInfo?.id ?? uuid();
|
this.id = controllerInfo?.id ?? uuid();
|
||||||
this.resources = controllerInfo?.resources ?? [];
|
this.resources = controllerInfo?.resources ?? [];
|
||||||
this._toDispose.push(this.dialog.cancelButton.onClick(() => this.handleCancel()));
|
this._toDispose.push(this.dialog.cancelButton.onClick(() => this.handleCancel()));
|
||||||
this.dialog.registerContent(async (view) => {
|
this.dialog.registerContent(async (view) => {
|
||||||
this.modelBuilder = view.modelBuilder;
|
this.modelBuilder = view.modelBuilder;
|
||||||
this.initializeFields(controllerInfo, password);
|
this.initializeFields(controllerInfo);
|
||||||
|
|
||||||
let formModel = this.modelBuilder.formContainer()
|
let formModel = this.modelBuilder.formContainer()
|
||||||
.withFormItems([{
|
.withFormItems([{
|
||||||
@@ -192,75 +158,37 @@ abstract class ControllerDialogBase extends InitializingComponent {
|
|||||||
return this.completionPromise.promise;
|
return this.completionPromise.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getControllerInfo(url: string, rememberPassword: boolean = false): ControllerInfo {
|
protected getControllerInfo(): ControllerInfo {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
endpoint: url || undefined,
|
|
||||||
namespace: this.namespaceInputBox.value!.trim(),
|
namespace: this.namespaceInputBox.value!.trim(),
|
||||||
kubeConfigFilePath: this.kubeConfigInputBox.value!,
|
kubeConfigFilePath: this.kubeConfigInputBox.value!,
|
||||||
kubeClusterContext: this.clusterContextRadioGroup.value!,
|
kubeClusterContext: this.clusterContextRadioGroup.value!,
|
||||||
name: this.nameInputBox.value ?? '',
|
name: this.nameInputBox.value ?? '',
|
||||||
username: this.usernameInputBox.value!,
|
|
||||||
rememberPassword: rememberPassword,
|
|
||||||
resources: this.resources
|
resources: this.resources
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConnectToControllerDialog extends ControllerDialogBase {
|
export class ConnectToControllerDialog extends ControllerDialogBase {
|
||||||
protected rememberPwCheckBox!: azdata.CheckBoxComponent;
|
|
||||||
|
|
||||||
protected fieldToFocusOn() {
|
protected fieldToFocusOn() {
|
||||||
return this.namespaceInputBox;
|
return this.namespaceInputBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override getComponents() {
|
|
||||||
return [
|
|
||||||
...super.getComponents(),
|
|
||||||
{
|
|
||||||
component: this.rememberPwCheckBox,
|
|
||||||
title: ''
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override initializeFields(controllerInfo: ControllerInfo | undefined, password: string | undefined) {
|
|
||||||
super.initializeFields(controllerInfo, password);
|
|
||||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
|
||||||
.withProperties<azdata.CheckBoxProperties>({
|
|
||||||
label: loc.rememberPassword,
|
|
||||||
checked: controllerInfo?.rememberPassword
|
|
||||||
}).component();
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(treeDataProvider: AzureArcTreeDataProvider) {
|
constructor(treeDataProvider: AzureArcTreeDataProvider) {
|
||||||
super(treeDataProvider, loc.connectToController);
|
super(treeDataProvider, loc.connectToController);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async validate(): Promise<boolean> {
|
public async validate(): Promise<boolean> {
|
||||||
if (!this.namespaceInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
if (!this.namespaceInputBox.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let url = this.urlInputBox.value?.trim() || '';
|
|
||||||
if (url) {
|
|
||||||
// Only support https connections
|
|
||||||
if (url.toLowerCase().startsWith('http://')) {
|
|
||||||
url = url.replace('http', 'https');
|
|
||||||
}
|
|
||||||
// Append https if they didn't type it in
|
|
||||||
if (!url.toLowerCase().startsWith('https://')) {
|
|
||||||
url = `https://${url}`;
|
|
||||||
}
|
|
||||||
// Append default port if one wasn't specified
|
|
||||||
if (!/.*:\d*$/.test(url)) {
|
|
||||||
url = `${url}:30080`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const controllerInfo: ControllerInfo = this.getControllerInfo(url, !!this.rememberPwCheckBox.checked);
|
const controllerInfo: ControllerInfo = this.getControllerInfo();
|
||||||
const controllerModel = new ControllerModel(this.treeDataProvider, controllerInfo, this.passwordInputBox.value);
|
const controllerModel = new ControllerModel(this.treeDataProvider, controllerInfo);
|
||||||
try {
|
try {
|
||||||
// Validate that we can connect to the controller, this also populates the controllerRegistration from the connection response.
|
// Validate that we can connect to the controller, this also populates the controllerRegistration from the connection response.
|
||||||
await controllerModel.refresh(false);
|
await controllerModel.refresh(false, this.namespaceInputBox.value);
|
||||||
// default info.name to the name of the controller instance if the user did not specify their own and to a pre-canned default if for some weird reason controller endpoint returned instanceName is also not a valid value
|
// default info.name to the name of the controller instance if the user did not specify their own and to a pre-canned default if for some weird reason controller endpoint returned instanceName is also not a valid value
|
||||||
controllerModel.info.name = controllerModel.info.name || controllerModel.controllerConfig?.metadata.name || loc.defaultControllerName;
|
controllerModel.info.name = controllerModel.info.name || controllerModel.controllerConfig?.metadata.name || loc.defaultControllerName;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -270,74 +198,7 @@ export class ConnectToControllerDialog extends ControllerDialogBase {
|
|||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.completionPromise.resolve({ controllerModel: controllerModel, password: this.passwordInputBox.value });
|
this.completionPromise.resolve({ controllerModel: controllerModel });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class PasswordToControllerDialog extends ControllerDialogBase {
|
|
||||||
|
|
||||||
constructor(treeDataProvider: AzureArcTreeDataProvider) {
|
|
||||||
super(treeDataProvider, loc.passwordToController);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fieldToFocusOn() {
|
|
||||||
return this.passwordInputBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override readonlyFields(): azdata.Component[] {
|
|
||||||
return [
|
|
||||||
this.urlInputBox,
|
|
||||||
...this.kubeConfigInputBox.items,
|
|
||||||
...this.clusterContextRadioGroup.items,
|
|
||||||
this.nameInputBox,
|
|
||||||
this.usernameInputBox
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async validate(): Promise<boolean> {
|
|
||||||
if (!this.passwordInputBox.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const controllerInfo: ControllerInfo = this.getControllerInfo(this.urlInputBox.value!, false);
|
|
||||||
const controllerModel = new ControllerModel(this.treeDataProvider, controllerInfo, this.passwordInputBox.value);
|
|
||||||
const azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
|
|
||||||
try {
|
|
||||||
await azdataApi.azdata.login(
|
|
||||||
{
|
|
||||||
endpoint: controllerInfo.endpoint,
|
|
||||||
namespace: controllerInfo.namespace
|
|
||||||
},
|
|
||||||
controllerInfo.username,
|
|
||||||
this.passwordInputBox.value,
|
|
||||||
{
|
|
||||||
'KUBECONFIG': this.kubeConfigInputBox.value!,
|
|
||||||
'KUBECTL_CONTEXT': this.clusterContextRadioGroup.value!
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (getErrorMessage(e).match(/Wrong username or password/i)) {
|
|
||||||
this.dialog.message = {
|
|
||||||
text: loc.loginFailed,
|
|
||||||
level: azdata.window.MessageLevel.Error
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
this.dialog.message = {
|
|
||||||
text: loc.errorVerifyingPassword(e),
|
|
||||||
level: azdata.window.MessageLevel.Error
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.completionPromise.resolve({ controllerModel: controllerModel, password: this.passwordInputBox.value });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override showDialog(controllerInfo?: ControllerInfo): azdata.window.Dialog {
|
|
||||||
const dialog = super.showDialog(controllerInfo);
|
|
||||||
dialog.okButton.label = loc.ok;
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,22 +35,22 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
|||||||
this.modelBuilder = view.modelBuilder;
|
this.modelBuilder = view.modelBuilder;
|
||||||
|
|
||||||
this.serverNameInputBox = this.modelBuilder.inputBox()
|
this.serverNameInputBox = this.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProps({
|
||||||
value: connectionProfile?.serverName,
|
value: connectionProfile?.serverName,
|
||||||
enabled: false
|
readOnly: true
|
||||||
}).component();
|
}).component();
|
||||||
this.usernameInputBox = this.modelBuilder.inputBox()
|
this.usernameInputBox = this.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProps({
|
||||||
value: connectionProfile?.userName
|
value: connectionProfile?.userName
|
||||||
}).component();
|
}).component();
|
||||||
this.passwordInputBox = this.modelBuilder.inputBox()
|
this.passwordInputBox = this.modelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProps({
|
||||||
inputType: 'password',
|
inputType: 'password',
|
||||||
value: connectionProfile?.password
|
value: connectionProfile?.password
|
||||||
})
|
})
|
||||||
.component();
|
.component();
|
||||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
||||||
.withProperties<azdata.CheckBoxProperties>({
|
.withProps({
|
||||||
label: loc.rememberPassword,
|
label: loc.rememberPassword,
|
||||||
checked: connectionProfile?.savePassword
|
checked: connectionProfile?.savePassword
|
||||||
}).component();
|
}).component();
|
||||||
@@ -78,7 +78,7 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
|||||||
title: ''
|
title: ''
|
||||||
}]).withLayout({ width: '100%' }).component();
|
}]).withLayout({ width: '100%' }).component();
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
this.serverNameInputBox.focus();
|
this.usernameInputBox.focus();
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ControllerInfo } from 'arc';
|
import { ControllerInfo } from 'arc';
|
||||||
import * as azdata from 'azdata';
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ControllerModel } from '../../models/controllerModel';
|
import { ControllerModel } from '../../models/controllerModel';
|
||||||
import { ControllerTreeNode } from './controllerTreeNode';
|
import { ControllerTreeNode } from './controllerTreeNode';
|
||||||
@@ -18,7 +17,6 @@ const mementoToken = 'arcDataControllers.v2';
|
|||||||
*/
|
*/
|
||||||
export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNode> {
|
export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNode> {
|
||||||
|
|
||||||
private _credentialsProvider = azdata.credentials.getProvider('arcControllerPasswords');
|
|
||||||
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
|
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
|
||||||
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
|
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
|
||||||
|
|
||||||
@@ -51,14 +49,13 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addOrUpdateController(model: ControllerModel, password: string, refreshTree = true): Promise<void> {
|
public async addOrUpdateController(model: ControllerModel, refreshTree = true): Promise<void> {
|
||||||
const controllerNode = this.getControllerNode(model);
|
const controllerNode = this.getControllerNode(model);
|
||||||
if (controllerNode) {
|
if (controllerNode) {
|
||||||
controllerNode.model.info = model.info;
|
controllerNode.model.info = model.info;
|
||||||
} else {
|
} else {
|
||||||
this._controllerNodes.push(new ControllerTreeNode(model, this._context, this));
|
this._controllerNodes.push(new ControllerTreeNode(model, this._context, this));
|
||||||
}
|
}
|
||||||
await this.updatePassword(model, password);
|
|
||||||
if (refreshTree) {
|
if (refreshTree) {
|
||||||
this._onDidChangeTreeData.fire(undefined);
|
this._onDidChangeTreeData.fire(undefined);
|
||||||
}
|
}
|
||||||
@@ -71,22 +68,10 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
|
|
||||||
public async removeController(controllerNode: ControllerTreeNode): Promise<void> {
|
public async removeController(controllerNode: ControllerTreeNode): Promise<void> {
|
||||||
this._controllerNodes = this._controllerNodes.filter(node => node !== controllerNode);
|
this._controllerNodes = this._controllerNodes.filter(node => node !== controllerNode);
|
||||||
await this.deletePassword(controllerNode.model.info);
|
|
||||||
this._onDidChangeTreeData.fire(undefined);
|
this._onDidChangeTreeData.fire(undefined);
|
||||||
await this.saveControllers();
|
await this.saveControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPassword(info: ControllerInfo): Promise<string> {
|
|
||||||
const provider = await this._credentialsProvider;
|
|
||||||
const credential = await provider.readCredential(info.id);
|
|
||||||
return credential.password;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async deletePassword(info: ControllerInfo): Promise<void> {
|
|
||||||
const provider = await this._credentialsProvider;
|
|
||||||
await provider.deleteCredential(info.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the specified node, or the entire tree if node is undefined
|
* Refreshes the specified node, or the entire tree if node is undefined
|
||||||
* @param node The node to refresh, or undefined for the whole tree
|
* @param node The node to refresh, or undefined for the whole tree
|
||||||
@@ -95,15 +80,6 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
|
|||||||
this._onDidChangeTreeData.fire(node);
|
this._onDidChangeTreeData.fire(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updatePassword(model: ControllerModel, password: string): Promise<void> {
|
|
||||||
const provider = await this._credentialsProvider;
|
|
||||||
if (model.info.rememberPassword) {
|
|
||||||
await provider.saveCredential(model.info.id, password);
|
|
||||||
} else {
|
|
||||||
await provider.deleteCredential(model.info.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadSavedControllers(): Promise<void> {
|
private async loadSavedControllers(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const controllerMementos: ControllerInfo[] = this._context.globalState.get(mementoToken) || [];
|
const controllerMementos: ControllerInfo[] = this._context.globalState.get(mementoToken) || [];
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ export class ControllerTreeNode extends TreeNode {
|
|||||||
|
|
||||||
public override async getChildren(): Promise<TreeNode[]> {
|
public override async getChildren(): Promise<TreeNode[]> {
|
||||||
try {
|
try {
|
||||||
await this.model.refresh(false);
|
await this.model.refresh(false, this.model.info.namespace);
|
||||||
this.updateChildren(this.model.registrations);
|
this.updateChildren(this.model.registrations);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vscode.window.showErrorMessage(loc.errorConnectingToController(err));
|
vscode.window.showErrorMessage(loc.errorConnectingToController(err));
|
||||||
try {
|
try {
|
||||||
await this.model.refresh(false);
|
await this.model.refresh(false, this.model.info.namespace);
|
||||||
this.updateChildren(this.model.registrations);
|
this.updateChildren(this.model.registrations);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!(err instanceof UserCancelledError)) {
|
if (!(err instanceof UserCancelledError)) {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
# Microsoft Azure Data CLI Extension for Azure Data Studio
|
# Microsoft Azure CLI Extension for Azure Data Studio
|
||||||
|
|
||||||
Welcome to Microsoft Azure Data CLI Extension for Azure Data Studio!
|
Welcome to Microsoft Azure CLI Extension for Azure Data Studio!
|
||||||
|
|
||||||
**This extension is only applicable to customers in the Azure Arc data services public preview. Other usage is not supported at this time.**
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This extension adds support for the Azure Data CLI (azdata) within Azure Data Studio.
|
This extension adds support for the Azure CLI (az) within Azure Data Studio.
|
||||||
|
|
||||||
See https://docs.microsoft.com/cli/azure/?view=azure-cli-latest for more information on the tool.
|
See https://docs.microsoft.com/cli/azure/?view=azure-cli-latest for more information on the tool.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "azcli",
|
"name": "azcli",
|
||||||
"displayName": "%azdata.displayName%",
|
"displayName": "%azcli.arc.displayName%",
|
||||||
"description": "%azdata.description%",
|
"description": "%azcli.arc.description%",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
@@ -26,104 +26,19 @@
|
|||||||
"configuration": [
|
"configuration": [
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "%azdata.config.title%",
|
"title": "%azcli.arc.config.title%",
|
||||||
"properties": {
|
"properties": {
|
||||||
"azcli.logDebugInfo": {
|
"azcli.logDebugInfo": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "%azdata.config.debug%"
|
"description": "%azcli.arc.config.debug%"
|
||||||
},
|
|
||||||
"azcli.acceptEula": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "prompt",
|
|
||||||
"enum": [
|
|
||||||
"dontPrompt",
|
|
||||||
"prompt"
|
|
||||||
],
|
|
||||||
"enumDescriptions": [
|
|
||||||
"%azdata.acceptEula.dontPrompt.description%",
|
|
||||||
"%azdata.acceptEula.prompt.description%"
|
|
||||||
],
|
|
||||||
"description": "%azdata.acceptEula.description%"
|
|
||||||
},
|
|
||||||
"azcli.install": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "prompt",
|
|
||||||
"enum": [
|
|
||||||
"dontPrompt",
|
|
||||||
"prompt"
|
|
||||||
],
|
|
||||||
"enumDescriptions": [
|
|
||||||
"%azdata.install.dontPrompt.description%",
|
|
||||||
"%azdata.install.prompt.description%"
|
|
||||||
],
|
|
||||||
"description": "%azdata.install.description%"
|
|
||||||
},
|
|
||||||
"azcli.update": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "prompt",
|
|
||||||
"enum": [
|
|
||||||
"dontPrompt",
|
|
||||||
"prompt"
|
|
||||||
],
|
|
||||||
"enumDescriptions": [
|
|
||||||
"%azdata.update.dontPrompt.description%",
|
|
||||||
"%azdata.update.prompt.description%"
|
|
||||||
],
|
|
||||||
"description": "%azdata.update.description%"
|
|
||||||
},
|
|
||||||
"azcli.requiredUpdate": {
|
|
||||||
"type": "string",
|
|
||||||
"default": "prompt",
|
|
||||||
"enum": [
|
|
||||||
"dontPrompt",
|
|
||||||
"prompt"
|
|
||||||
],
|
|
||||||
"enumDescriptions": [
|
|
||||||
"%azdata.update.dontPrompt.description%",
|
|
||||||
"%azdata.update.prompt.description%"
|
|
||||||
],
|
|
||||||
"description": "%azdata.requiredUpdate.description%"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commands": [
|
|
||||||
{
|
|
||||||
"command": "azcli.acceptEula",
|
|
||||||
"title": "%azdata.acceptEula.command.name%",
|
|
||||||
"category": "%command.category%"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "azcli.install",
|
|
||||||
"title": "%azdata.install.command.name%",
|
|
||||||
"category": "%command.category%"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "azcli.update",
|
|
||||||
"title": "%azdata.update.command.name%",
|
|
||||||
"category": "%command.category%"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"menus": {
|
|
||||||
"commandPalette": [
|
|
||||||
{
|
|
||||||
"command": "azcli.acceptEula",
|
|
||||||
"when": "!azcli.eulaAccepted"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "azcli.install",
|
|
||||||
"when": "!azcli.found"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"command": "azcli.update",
|
|
||||||
"when": "azcli.found"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"resourceDeploymentOptionsSources": [
|
"resourceDeploymentOptionsSources": [
|
||||||
{
|
{
|
||||||
"id": "arc.controller.config.profiles"
|
"id": "azcli.arc.controller.config.profiles"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,25 +1,9 @@
|
|||||||
{
|
{
|
||||||
"azdata.displayName": "Azure CLI",
|
"azcli.arc.displayName": "Azure CLI",
|
||||||
"azdata.description": "Support for Azure CLI.",
|
"azcli.arc.description": "Support for Azure CLI.",
|
||||||
"azdata.config.title": "Azure CLI Configuration",
|
"azcli.arc.config.title": "Azure CLI Configuration",
|
||||||
"azdata.config.debug": "Log debug info to the output channel for all executed az commands",
|
"azcli.arc.config.debug": "Log debug info to the output channel for all executed az commands",
|
||||||
|
|
||||||
"command.category": "Azure CLI",
|
"command.category": "Azure CLI",
|
||||||
"azdata.acceptEula.command.name": "Accept Eula",
|
"azcli.arc.category": "Azure CLI"
|
||||||
"azdata.install.command.name": "Install",
|
|
||||||
"azdata.update.command.name": "Check for Update",
|
|
||||||
"azdata.category": "Azure CLI",
|
|
||||||
|
|
||||||
"azdata.acceptEula.description": "Choose how acceptance of EULA for the Azure CLI is done",
|
|
||||||
"azdata.acceptEula.prompt.description": "The user will be prompted for acceptance of EULA for the Azure CLI",
|
|
||||||
"azdata.acceptEula.dontPrompt.description": "The user will not be prompted for acceptance of EULA for the Azure CLI",
|
|
||||||
|
|
||||||
"azdata.install.description": "Choose how install of Azure CLI is done",
|
|
||||||
"azdata.install.prompt.description": "The user will be prompted for installation of the Azure CLI",
|
|
||||||
"azdata.install.dontPrompt.description": "The user will not be prompted for installation of the Azure CLI",
|
|
||||||
|
|
||||||
"azdata.update.description": "Choose whether you will be prompted when an update of the Azure CLI is available.",
|
|
||||||
"azdata.requiredUpdate.description": "Choose whether you will be prompted when a required update of the Azure CLI is available.",
|
|
||||||
"azdata.update.prompt.description": "The user will be prompted for update of the Azure CLI",
|
|
||||||
"azdata.update.dontPrompt.description": "The user will not be prompted for update of the Azure CLI"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,200 +3,137 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as vscode from 'vscode';
|
import { IAzTool } from './az';
|
||||||
import { IAzdataTool, isEulaAccepted, MIN_AZDATA_VERSION, promptForEula } from './azdata';
|
|
||||||
import Logger from './common/logger';
|
import Logger from './common/logger';
|
||||||
import { NoAzdataError } from './common/utils';
|
import { NoAzureCLIError } from './common/utils';
|
||||||
import * as constants from './constants';
|
|
||||||
import * as loc from './localizedConstants';
|
import * as loc from './localizedConstants';
|
||||||
import { AzdataToolService } from './services/azdataToolService';
|
import { AzToolService } from './services/azToolService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that :
|
* Validates that :
|
||||||
* - Azdata is installed
|
* - Az is installed
|
||||||
* - The Azdata version is >= the minimum required version
|
* @param az The az tool to check
|
||||||
* - The Azdata CLI has been accepted
|
|
||||||
* @param azdata The azdata tool to check
|
|
||||||
* @param eulaAccepted Whether the Azdata CLI EULA has been accepted
|
|
||||||
*/
|
*/
|
||||||
async function validateAzdata(azdata: IAzdataTool | undefined, eulaAccepted: boolean): Promise<void> {
|
export function validateAz(az: IAzTool | undefined) {
|
||||||
throwIfNoAzdataOrEulaNotAccepted(azdata, eulaAccepted);
|
throwIfNoAz(az);
|
||||||
await throwIfRequiredVersionMissing(azdata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function throwIfNoAzdataOrEulaNotAccepted(azdata: IAzdataTool | undefined, eulaAccepted: boolean): asserts azdata {
|
export function throwIfNoAz(localAz: IAzTool | undefined): asserts localAz {
|
||||||
throwIfNoAzdata(azdata);
|
if (!localAz) {
|
||||||
if (!eulaAccepted) {
|
Logger.log(loc.noAzureCLI);
|
||||||
Logger.log(loc.eulaNotAccepted);
|
throw new NoAzureCLIError();
|
||||||
throw new Error(loc.eulaNotAccepted);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function throwIfRequiredVersionMissing(azdata: IAzdataTool): Promise<void> {
|
export function getExtensionApi(azToolService: AzToolService): azExt.IExtension {
|
||||||
const currentVersion = await azdata.getSemVersion();
|
|
||||||
if (currentVersion.compare(MIN_AZDATA_VERSION) < 0) {
|
|
||||||
throw new Error(loc.missingRequiredVersion(MIN_AZDATA_VERSION.raw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function throwIfNoAzdata(localAzdata: IAzdataTool | undefined): asserts localAzdata {
|
|
||||||
if (!localAzdata) {
|
|
||||||
Logger.log(loc.noAzdata);
|
|
||||||
throw new NoAzdataError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getExtensionApi(memento: vscode.Memento, azdataToolService: AzdataToolService, localAzdataDiscovered: Promise<IAzdataTool | undefined>): azdataExt.IExtension {
|
|
||||||
return {
|
return {
|
||||||
isEulaAccepted: async () => {
|
az: getAzApi(azToolService)
|
||||||
throwIfNoAzdata(await localAzdataDiscovered); // ensure that we have discovered Azdata
|
|
||||||
return !!memento.get<boolean>(constants.eulaAccepted);
|
|
||||||
},
|
|
||||||
promptForEula: async (requireUserAction: boolean = true): Promise<boolean> => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
return promptForEula(memento, true /* userRequested */, requireUserAction);
|
|
||||||
},
|
|
||||||
azdata: getAzdataApi(localAzdataDiscovered, azdataToolService, memento)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAzdataApi(localAzdataDiscovered: Promise<IAzdataTool | undefined>, azdataToolService: AzdataToolService, memento: vscode.Memento): azdataExt.IAzdataApi {
|
export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||||
return {
|
return {
|
||||||
arc: {
|
arcdata: {
|
||||||
dc: {
|
dc: {
|
||||||
create: async (
|
|
||||||
namespace: string,
|
|
||||||
name: string,
|
|
||||||
connectivityMode: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
location: string,
|
|
||||||
subscription: string,
|
|
||||||
profileName?: string,
|
|
||||||
storageClass?: string,
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars,
|
|
||||||
azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.dc.create(namespace, name, connectivityMode, resourceGroup, location, subscription, profileName, storageClass, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
endpoint: {
|
endpoint: {
|
||||||
list: async (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
await localAzdataDiscovered;
|
validateAz(azToolService.localAz);
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
return azToolService.localAz!.arcdata.dc.endpoint.list(namespace, additionalEnvVars);
|
||||||
return azdataToolService.localAzdata!.arc.dc.endpoint.list(additionalEnvVars, azdataContext);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
list: async (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
list: async (additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
await localAzdataDiscovered;
|
validateAz(azToolService.localAz);
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
return azToolService.localAz!.arcdata.dc.config.list(additionalEnvVars);
|
||||||
return azdataToolService.localAzdata!.arc.dc.config.list(additionalEnvVars, azdataContext);
|
|
||||||
},
|
},
|
||||||
show: async (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
show: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
await localAzdataDiscovered;
|
validateAz(azToolService.localAz);
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
return azToolService.localAz!.arcdata.dc.config.show(namespace, additionalEnvVars);
|
||||||
return azdataToolService.localAzdata!.arc.dc.config.show(additionalEnvVars, azdataContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postgres: {
|
|
||||||
server: {
|
|
||||||
delete: async (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.postgres.server.delete(name, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
list: async (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.postgres.server.list(additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
show: async (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.postgres.server.show(name, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
edit: async (
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
adminPassword?: boolean;
|
|
||||||
coresLimit?: string;
|
|
||||||
coresRequest?: string;
|
|
||||||
coordinatorEngineSettings?: string;
|
|
||||||
engineSettings?: string;
|
|
||||||
extensions?: string;
|
|
||||||
memoryLimit?: string;
|
|
||||||
memoryRequest?: string;
|
|
||||||
noWait?: boolean;
|
|
||||||
port?: number;
|
|
||||||
replaceEngineSettings?: boolean;
|
|
||||||
workerEngineSettings?: string;
|
|
||||||
workers?: number;
|
|
||||||
},
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars,
|
|
||||||
azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.postgres.server.edit(name, args, additionalEnvVars, azdataContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sql: {
|
|
||||||
mi: {
|
|
||||||
delete: async (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.sql.mi.delete(name, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
list: async (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.sql.mi.list(additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
show: async (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.sql.mi.show(name, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
edit: async (
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
coresLimit?: string;
|
|
||||||
coresRequest?: string;
|
|
||||||
memoryLimit?: string;
|
|
||||||
memoryRequest?: string;
|
|
||||||
noWait?: boolean;
|
|
||||||
},
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars,
|
|
||||||
azdataContext?: string
|
|
||||||
) => {
|
|
||||||
await localAzdataDiscovered;
|
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
|
||||||
return azdataToolService.localAzdata!.arc.sql.mi.edit(name, args, additionalEnvVars, azdataContext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPath: async () => {
|
postgres: {
|
||||||
await localAzdataDiscovered;
|
arcserver: {
|
||||||
throwIfNoAzdata(azdataToolService.localAzdata);
|
delete: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
return azdataToolService.localAzdata.getPath();
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.postgres.arcserver.delete(name, namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.postgres.arcserver.list(namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
show: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.postgres.arcserver.show(name, namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
edit: async (
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
adminPassword?: boolean;
|
||||||
|
coresLimit?: string;
|
||||||
|
coresRequest?: string;
|
||||||
|
coordinatorEngineSettings?: string;
|
||||||
|
engineSettings?: string;
|
||||||
|
extensions?: string;
|
||||||
|
memoryLimit?: string;
|
||||||
|
memoryRequest?: string;
|
||||||
|
noWait?: boolean;
|
||||||
|
port?: number;
|
||||||
|
replaceEngineSettings?: boolean;
|
||||||
|
workerEngineSettings?: string;
|
||||||
|
workers?: number;
|
||||||
|
},
|
||||||
|
namespace: string,
|
||||||
|
additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.postgres.arcserver.edit(name, args, namespace, additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
login: async (endpointOrNamespace: azdataExt.EndpointOrNamespace, username: string, password: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string) => {
|
sql: {
|
||||||
await validateAzdata(azdataToolService.localAzdata, isEulaAccepted(memento));
|
miarc: {
|
||||||
return azdataToolService.localAzdata!.login(endpointOrNamespace, username, password, additionalEnvVars, azdataContext);
|
delete: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.sql.miarc.delete(name, namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.sql.miarc.list(namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
show: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.sql.miarc.show(name, namespace, additionalEnvVars);
|
||||||
|
},
|
||||||
|
edit: async (
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
coresLimit?: string;
|
||||||
|
coresRequest?: string;
|
||||||
|
memoryLimit?: string;
|
||||||
|
memoryRequest?: string;
|
||||||
|
noWait?: boolean;
|
||||||
|
},
|
||||||
|
namespace: string,
|
||||||
|
additionalEnvVars?: azExt.AdditionalEnvVars
|
||||||
|
) => {
|
||||||
|
validateAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz!.sql.miarc.edit(name, args, namespace, additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getPath: async () => {
|
||||||
|
throwIfNoAz(azToolService.localAz);
|
||||||
|
return azToolService.localAz.getPath();
|
||||||
},
|
},
|
||||||
getSemVersion: async () => {
|
getSemVersion: async () => {
|
||||||
await localAzdataDiscovered;
|
throwIfNoAz(azToolService.localAz);
|
||||||
throwIfNoAzdata(azdataToolService.localAzdata);
|
return azToolService.localAz.getSemVersion();
|
||||||
return azdataToolService.localAzdata.getSemVersion();
|
|
||||||
},
|
},
|
||||||
version: async () => {
|
version: async () => {
|
||||||
await localAzdataDiscovered;
|
throwIfNoAz(azToolService.localAz);
|
||||||
throwIfNoAzdata(azdataToolService.localAzdata);
|
return azToolService.localAz.version();
|
||||||
return azdataToolService.localAzdata.version();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
320
extensions/azcli/src/az.ts
Normal file
320
extensions/azcli/src/az.ts
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azExt from 'az-ext';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import { SemVer } from 'semver';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { executeCommand, ExitCodeError, ProcessOutput } from './common/childProcess';
|
||||||
|
import Logger from './common/logger';
|
||||||
|
import { NoAzureCLIError, searchForCmd } from './common/utils';
|
||||||
|
import { azConfigSection, azFound, debugConfigKey, latestAzArcExtensionVersion } from './constants';
|
||||||
|
import * as loc from './localizedConstants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The latest Azure CLI arcdata extension version for this extension to function properly
|
||||||
|
*/
|
||||||
|
export const LATEST_AZ_ARC_EXTENSION_VERSION = new SemVer(latestAzArcExtensionVersion);
|
||||||
|
|
||||||
|
export const enum AzDeployOption {
|
||||||
|
dontPrompt = 'dontPrompt',
|
||||||
|
prompt = 'prompt'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for an object to interact with the az tool installed on the box.
|
||||||
|
*/
|
||||||
|
export interface IAzTool extends azExt.IAzApi {
|
||||||
|
/**
|
||||||
|
* Executes az with the specified arguments (e.g. --version) and returns the result
|
||||||
|
* @param args The args to pass to az
|
||||||
|
* @param parseResult A function used to parse out the raw result into the desired shape
|
||||||
|
*/
|
||||||
|
executeCommand<R>(args: string[], additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<R>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object to interact with the az tool installed on the box.
|
||||||
|
*/
|
||||||
|
export class AzTool implements azExt.IAzApi {
|
||||||
|
|
||||||
|
private _semVersion: SemVer;
|
||||||
|
|
||||||
|
constructor(private _path: string, version: string) {
|
||||||
|
this._semVersion = new SemVer(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The semVersion corresponding to this installation of az. version() method should have been run
|
||||||
|
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||||
|
* Az has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||||
|
*/
|
||||||
|
public async getSemVersion(): Promise<SemVer> {
|
||||||
|
return this._semVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the path where az tool is installed
|
||||||
|
*/
|
||||||
|
public async getPath(): Promise<string> {
|
||||||
|
return this._path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public arcdata = {
|
||||||
|
dc: {
|
||||||
|
endpoint: {
|
||||||
|
list: (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.DcEndpointListResult[]>> => {
|
||||||
|
return this.executeCommand<azExt.DcEndpointListResult[]>(['arcdata', 'dc', 'endpoint', 'list', '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
list: (additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.DcConfigListResult[]>> => {
|
||||||
|
return this.executeCommand<azExt.DcConfigListResult[]>(['arcdata', 'dc', 'config', 'list'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
show: (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.DcConfigShowResult>> => {
|
||||||
|
return this.executeCommand<azExt.DcConfigShowResult>(['arcdata', 'dc', 'config', 'show', '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public postgres = {
|
||||||
|
arcserver: {
|
||||||
|
delete: (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<void>> => {
|
||||||
|
return this.executeCommand<void>(['postgres', 'arc-server', 'delete', '-n', name, '--k8s-namespace', namespace, '--force', '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
list: (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.PostgresServerListResult[]>> => {
|
||||||
|
return this.executeCommand<azExt.PostgresServerListResult[]>(['postgres', 'arc-server', 'list', '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
show: (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.PostgresServerShowResult>> => {
|
||||||
|
return this.executeCommand<azExt.PostgresServerShowResult>(['postgres', 'arc-server', 'show', '-n', name, '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
edit: (
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
adminPassword?: boolean,
|
||||||
|
coresLimit?: string,
|
||||||
|
coresRequest?: string,
|
||||||
|
coordinatorEngineSettings?: string,
|
||||||
|
engineSettings?: string,
|
||||||
|
extensions?: string,
|
||||||
|
memoryLimit?: string,
|
||||||
|
memoryRequest?: string,
|
||||||
|
noWait?: boolean,
|
||||||
|
port?: number,
|
||||||
|
replaceEngineSettings?: boolean,
|
||||||
|
workerEngineSettings?: string,
|
||||||
|
workers?: number
|
||||||
|
},
|
||||||
|
namespace: string,
|
||||||
|
additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<void>> => {
|
||||||
|
const argsArray = ['postgres', 'arc-server', 'edit', '-n', name, '--k8s-namespace', namespace, '--use-k8s'];
|
||||||
|
if (args.adminPassword) { argsArray.push('--admin-password'); }
|
||||||
|
if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); }
|
||||||
|
if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); }
|
||||||
|
if (args.coordinatorEngineSettings) { argsArray.push('--coordinator-settings', args.coordinatorEngineSettings); }
|
||||||
|
if (args.engineSettings) { argsArray.push('--engine-settings', args.engineSettings); }
|
||||||
|
if (args.extensions) { argsArray.push('--extensions', args.extensions); }
|
||||||
|
if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); }
|
||||||
|
if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); }
|
||||||
|
if (args.noWait) { argsArray.push('--no-wait'); }
|
||||||
|
if (args.port) { argsArray.push('--port', args.port.toString()); }
|
||||||
|
if (args.replaceEngineSettings) { argsArray.push('--replace-settings'); }
|
||||||
|
if (args.workerEngineSettings) { argsArray.push('--worker-settings', args.workerEngineSettings); }
|
||||||
|
if (args.workers !== undefined) { argsArray.push('--workers', args.workers.toString()); }
|
||||||
|
return this.executeCommand<void>(argsArray, additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public sql = {
|
||||||
|
miarc: {
|
||||||
|
delete: (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<void>> => {
|
||||||
|
return this.executeCommand<void>(['sql', 'mi-arc', 'delete', '-n', name, '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
list: (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.SqlMiListResult[]>> => {
|
||||||
|
return this.executeCommand<azExt.SqlMiListResult[]>(['sql', 'mi-arc', 'list', '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
show: (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<azExt.SqlMiShowResult>> => {
|
||||||
|
return this.executeCommand<azExt.SqlMiShowResult>(['sql', 'mi-arc', 'show', '-n', name, '--k8s-namespace', namespace, '--use-k8s'], additionalEnvVars);
|
||||||
|
},
|
||||||
|
edit: (
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
coresLimit?: string,
|
||||||
|
coresRequest?: string,
|
||||||
|
memoryLimit?: string,
|
||||||
|
memoryRequest?: string,
|
||||||
|
noWait?: boolean,
|
||||||
|
},
|
||||||
|
namespace: string,
|
||||||
|
additionalEnvVars?: azExt.AdditionalEnvVars
|
||||||
|
): Promise<azExt.AzOutput<void>> => {
|
||||||
|
const argsArray = ['sql', 'mi-arc', 'edit', '-n', name, '--k8s-namespace', namespace, '--use-k8s'];
|
||||||
|
if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); }
|
||||||
|
if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); }
|
||||||
|
if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); }
|
||||||
|
if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); }
|
||||||
|
if (args.noWait) { argsArray.push('--no-wait'); }
|
||||||
|
return this.executeCommand<void>(argsArray, additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the output of running '--version' command on the az tool.
|
||||||
|
* It also updates the cachedVersion property based on the return value from the tool.
|
||||||
|
*/
|
||||||
|
public async version(): Promise<azExt.AzOutput<string>> {
|
||||||
|
const output = await executeAzCommand(`"${this._path}"`, ['--version']);
|
||||||
|
this._semVersion = new SemVer(parseVersion(output.stdout));
|
||||||
|
return {
|
||||||
|
stdout: output.stdout,
|
||||||
|
stderr: output.stderr.split(os.EOL)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the specified az command.
|
||||||
|
* @param args The args to pass to az
|
||||||
|
* @param additionalEnvVars Additional environment variables to set for this execution
|
||||||
|
*/
|
||||||
|
public async executeCommand<R>(args: string[], additionalEnvVars?: azExt.AdditionalEnvVars): Promise<azExt.AzOutput<R>> {
|
||||||
|
try {
|
||||||
|
const result = await executeAzCommand(`"${this._path}"`, args.concat(['--output', 'json']), additionalEnvVars);
|
||||||
|
|
||||||
|
let stdout = <unknown>result.stdout;
|
||||||
|
let stderr = <unknown>result.stderr;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Automatically try parsing the JSON. This is expected to fail for some az commands such as resource delete.
|
||||||
|
stdout = JSON.parse(result.stdout);
|
||||||
|
} catch (err) {
|
||||||
|
// If the output was not pure JSON, catch the error and log it here.
|
||||||
|
Logger.log(loc.azOutputParseErrorCaught(args.concat(['--output', 'json']).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
stdout: <R>stdout,
|
||||||
|
stderr: <string[]>stderr
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof ExitCodeError) {
|
||||||
|
try {
|
||||||
|
await fs.promises.access(this._path);
|
||||||
|
//this.path exists
|
||||||
|
} catch (e) {
|
||||||
|
// this.path does not exist
|
||||||
|
await vscode.commands.executeCommand('setContext', azFound, false);
|
||||||
|
throw new NoAzureCLIError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds and returns the existing installation of Azure CLI, or throws an error if it can't find it
|
||||||
|
* or encountered an unexpected error.
|
||||||
|
* The promise is rejected when Azure CLI is not found.
|
||||||
|
*/
|
||||||
|
export async function findAz(): Promise<IAzTool> {
|
||||||
|
Logger.log(loc.searchingForAz);
|
||||||
|
try {
|
||||||
|
const az = await findSpecificAz();
|
||||||
|
Logger.log(loc.foundExistingAz(await az.getPath(), (await az.getSemVersion()).raw));
|
||||||
|
return az;
|
||||||
|
} catch (err) {
|
||||||
|
Logger.log(loc.noAzureCLI);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses out the Azure CLI version from the raw az version output
|
||||||
|
* @param raw The raw version output from az --version
|
||||||
|
*/
|
||||||
|
function parseVersion(raw: string): string {
|
||||||
|
// Currently the version is a multi-line string that contains other version information such
|
||||||
|
// as the Python installation, with the first line holding the version of az itself.
|
||||||
|
//
|
||||||
|
// The output of az --version looks like:
|
||||||
|
// azure-cli 2.26.1
|
||||||
|
// ...
|
||||||
|
const start = raw.search('azure-cli');
|
||||||
|
const end = raw.search('core');
|
||||||
|
raw = raw.slice(start, end).replace('azure-cli', '');
|
||||||
|
return raw.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses out the arcdata extension version from the raw az version output
|
||||||
|
* @param raw The raw version output from az --version
|
||||||
|
*/
|
||||||
|
function parseArcExtensionVersion(raw: string): string {
|
||||||
|
// Currently the version is a multi-line string that contains other version information such
|
||||||
|
// as the Python installation and any extensions.
|
||||||
|
//
|
||||||
|
// The output of az --version looks like:
|
||||||
|
// azure-cli 2.26.1
|
||||||
|
// ...
|
||||||
|
// Extensions:
|
||||||
|
// arcdata 1.0.0
|
||||||
|
// connectedk8s 1.1.5
|
||||||
|
// ...
|
||||||
|
const start = raw.search('arcdata');
|
||||||
|
if (start === -1) {
|
||||||
|
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||||
|
//throw new AzureCLIArcExtError();
|
||||||
|
} else {
|
||||||
|
raw = raw.slice(start + 7);
|
||||||
|
raw = raw.split(os.EOL)[0].trim();
|
||||||
|
}
|
||||||
|
return raw.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeAzCommand(command: string, args: string[], additionalEnvVars: azExt.AdditionalEnvVars = {}): Promise<ProcessOutput> {
|
||||||
|
const debug = vscode.workspace.getConfiguration(azConfigSection).get(debugConfigKey);
|
||||||
|
if (debug) {
|
||||||
|
args.push('--debug');
|
||||||
|
}
|
||||||
|
return executeCommand(command, args, additionalEnvVars);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||||
|
// async function setConfig(key: string, value: string): Promise<void> {
|
||||||
|
// const config = vscode.workspace.getConfiguration(azConfigSection);
|
||||||
|
// await config.update(key, value, vscode.ConfigurationTarget.Global);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find user's local Azure CLI. Execute az --version and parse out the version number.
|
||||||
|
* If an update is needed, prompt the user to update via link. Return the AzTool.
|
||||||
|
* Currently commented out because Don't Prompt Again is not properly implemented.
|
||||||
|
*/
|
||||||
|
async function findSpecificAz(): Promise<IAzTool> {
|
||||||
|
const path = await ((process.platform === 'win32') ? searchForCmd('az.cmd') : searchForCmd('az'));
|
||||||
|
const versionOutput = await executeAzCommand(`"${path}"`, ['--version']);
|
||||||
|
const version = parseArcExtensionVersion(versionOutput.stdout);
|
||||||
|
const semVersion = new SemVer(version);
|
||||||
|
//let response: string | undefined;
|
||||||
|
|
||||||
|
if (LATEST_AZ_ARC_EXTENSION_VERSION.compare(semVersion) === 1) {
|
||||||
|
// If there is a greater version of az arc extension available, prompt to update
|
||||||
|
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||||
|
// const responses = [loc.askLater, loc.doNotAskAgain];
|
||||||
|
// response = await vscode.window.showInformationMessage(loc.requiredArcDataVersionNotAvailable(latestAzArcExtensionVersion, version), ...responses);
|
||||||
|
// if (response === loc.doNotAskAgain) {
|
||||||
|
// await setConfig(azRequiredUpdateKey, AzDeployOption.dontPrompt);
|
||||||
|
// }
|
||||||
|
} else if (LATEST_AZ_ARC_EXTENSION_VERSION.compare(semVersion) === -1) {
|
||||||
|
// Current version should not be greater than latest version
|
||||||
|
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||||
|
// vscode.window.showErrorMessage(loc.unsupportedArcDataVersion(latestAzArcExtensionVersion, version));
|
||||||
|
}
|
||||||
|
return new AzTool(path, version);
|
||||||
|
}
|
||||||
@@ -1,712 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
|
||||||
import { SemVer } from 'semver';
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import { getPlatformDownloadLink, getPlatformReleaseVersion } from './azdataReleaseInfo';
|
|
||||||
import { executeCommand, executeSudoCommand, ExitCodeError, ProcessOutput } from './common/childProcess';
|
|
||||||
import { HttpClient } from './common/httpClient';
|
|
||||||
import Logger from './common/logger';
|
|
||||||
import { getErrorMessage, NoAzdataError, searchForCmd } from './common/utils';
|
|
||||||
import { azdataAcceptEulaKey, azdataConfigSection, azdataFound, azdataInstallKey, azdataUpdateKey, azdatarequiredUpdateKey, debugConfigKey, eulaAccepted, eulaUrl, microsoftPrivacyStatementUrl } from './constants';
|
|
||||||
import * as loc from './localizedConstants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The minimum required azdata CLI version for this extension to function properly
|
|
||||||
*/
|
|
||||||
export const MIN_AZDATA_VERSION = new SemVer('20.3.4');
|
|
||||||
|
|
||||||
export const enum AzdataDeployOption {
|
|
||||||
dontPrompt = 'dontPrompt',
|
|
||||||
prompt = 'prompt'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for an object to interact with the azdata tool installed on the box.
|
|
||||||
*/
|
|
||||||
export interface IAzdataTool extends azdataExt.IAzdataApi {
|
|
||||||
/**
|
|
||||||
* Executes azdata with the specified arguments (e.g. --version) and returns the result
|
|
||||||
* @param args The args to pass to azdata
|
|
||||||
* @param parseResult A function used to parse out the raw result into the desired shape
|
|
||||||
*/
|
|
||||||
executeCommand<R>(args: string[], additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<R>>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object to interact with the azdata tool installed on the box.
|
|
||||||
*/
|
|
||||||
export class AzdataTool implements azdataExt.IAzdataApi {
|
|
||||||
|
|
||||||
private _semVersion: SemVer;
|
|
||||||
|
|
||||||
constructor(private _path: string, version: string) {
|
|
||||||
this._semVersion = new SemVer(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The semVersion corresponding to this installation of azdata. version() method should have been run
|
|
||||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
|
||||||
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
|
||||||
*/
|
|
||||||
public async getSemVersion(): Promise<SemVer> {
|
|
||||||
return this._semVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gets the path where azdata tool is installed
|
|
||||||
*/
|
|
||||||
public async getPath(): Promise<string> {
|
|
||||||
return this._path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public arc = {
|
|
||||||
dc: {
|
|
||||||
create: (
|
|
||||||
namespace: string,
|
|
||||||
name: string,
|
|
||||||
connectivityMode: string,
|
|
||||||
resourceGroup: string,
|
|
||||||
location: string,
|
|
||||||
subscription: string,
|
|
||||||
profileName?: string,
|
|
||||||
storageClass?: string,
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars,
|
|
||||||
azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> => {
|
|
||||||
const args = ['arc', 'dc', 'create',
|
|
||||||
'--namespace', namespace,
|
|
||||||
'--name', name,
|
|
||||||
'--connectivity-mode', connectivityMode,
|
|
||||||
'--resource-group', resourceGroup,
|
|
||||||
'--location', location,
|
|
||||||
'--subscription', subscription];
|
|
||||||
if (profileName) {
|
|
||||||
args.push('--profile-name', profileName);
|
|
||||||
}
|
|
||||||
if (storageClass) {
|
|
||||||
args.push('--storage-class', storageClass);
|
|
||||||
}
|
|
||||||
return this.executeCommand<void>(args, additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
endpoint: {
|
|
||||||
list: (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.DcEndpointListResult[]>> => {
|
|
||||||
return this.executeCommand<azdataExt.DcEndpointListResult[]>(['arc', 'dc', 'endpoint', 'list'], additionalEnvVars, azdataContext);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
list: (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigListResult[]>> => {
|
|
||||||
return this.executeCommand<azdataExt.DcConfigListResult[]>(['arc', 'dc', 'config', 'list'], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
show: (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.DcConfigShowResult>> => {
|
|
||||||
return this.executeCommand<azdataExt.DcConfigShowResult>(['arc', 'dc', 'config', 'show'], additionalEnvVars, azdataContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postgres: {
|
|
||||||
server: {
|
|
||||||
delete: (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> => {
|
|
||||||
return this.executeCommand<void>(['arc', 'postgres', 'server', 'delete', '-n', name, '--force'], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
list: (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerListResult[]>> => {
|
|
||||||
return this.executeCommand<azdataExt.PostgresServerListResult[]>(['arc', 'postgres', 'server', 'list'], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
show: (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.PostgresServerShowResult>> => {
|
|
||||||
return this.executeCommand<azdataExt.PostgresServerShowResult>(['arc', 'postgres', 'server', 'show', '-n', name], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
edit: (
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
adminPassword?: boolean,
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
coordinatorEngineSettings?: string,
|
|
||||||
engineSettings?: string,
|
|
||||||
extensions?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
port?: number,
|
|
||||||
replaceEngineSettings?: boolean,
|
|
||||||
workerEngineSettings?: string,
|
|
||||||
workers?: number
|
|
||||||
},
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars,
|
|
||||||
azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> => {
|
|
||||||
const argsArray = ['arc', 'postgres', 'server', 'edit', '-n', name];
|
|
||||||
if (args.adminPassword) { argsArray.push('--admin-password'); }
|
|
||||||
if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); }
|
|
||||||
if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); }
|
|
||||||
if (args.coordinatorEngineSettings) { argsArray.push('--coordinator-engine-settings', args.coordinatorEngineSettings); }
|
|
||||||
if (args.engineSettings) { argsArray.push('--engine-settings', args.engineSettings); }
|
|
||||||
if (args.extensions) { argsArray.push('--extensions', args.extensions); }
|
|
||||||
if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); }
|
|
||||||
if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); }
|
|
||||||
if (args.noWait) { argsArray.push('--no-wait'); }
|
|
||||||
if (args.port) { argsArray.push('--port', args.port.toString()); }
|
|
||||||
if (args.replaceEngineSettings) { argsArray.push('--replace-engine-settings'); }
|
|
||||||
if (args.workerEngineSettings) { argsArray.push('--worker-engine-settings', args.workerEngineSettings); }
|
|
||||||
if (args.workers !== undefined) { argsArray.push('--workers', args.workers.toString()); }
|
|
||||||
return this.executeCommand<void>(argsArray, additionalEnvVars, azdataContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sql: {
|
|
||||||
mi: {
|
|
||||||
delete: (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> => {
|
|
||||||
return this.executeCommand<void>(['arc', 'sql', 'mi', 'delete', '-n', name], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
list: (additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiListResult[]>> => {
|
|
||||||
return this.executeCommand<azdataExt.SqlMiListResult[]>(['arc', 'sql', 'mi', 'list'], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
show: (name: string, additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<azdataExt.SqlMiShowResult>> => {
|
|
||||||
return this.executeCommand<azdataExt.SqlMiShowResult>(['arc', 'sql', 'mi', 'show', '-n', name], additionalEnvVars, azdataContext);
|
|
||||||
},
|
|
||||||
edit: (
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
},
|
|
||||||
additionalEnvVars?: azdataExt.AdditionalEnvVars
|
|
||||||
): Promise<azdataExt.AzdataOutput<void>> => {
|
|
||||||
const argsArray = ['arc', 'sql', 'mi', 'edit', '-n', name];
|
|
||||||
if (args.coresLimit) { argsArray.push('--cores-limit', args.coresLimit); }
|
|
||||||
if (args.coresRequest) { argsArray.push('--cores-request', args.coresRequest); }
|
|
||||||
if (args.memoryLimit) { argsArray.push('--memory-limit', args.memoryLimit); }
|
|
||||||
if (args.memoryRequest) { argsArray.push('--memory-request', args.memoryRequest); }
|
|
||||||
if (args.noWait) { argsArray.push('--no-wait'); }
|
|
||||||
return this.executeCommand<void>(argsArray, additionalEnvVars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public async login(endpointOrNamespace: azdataExt.EndpointOrNamespace, username: string, password: string, additionalEnvVars: azdataExt.AdditionalEnvVars = {}, azdataContext?: string): Promise<azdataExt.AzdataOutput<void>> {
|
|
||||||
const args = ['login', '-u', username];
|
|
||||||
if (endpointOrNamespace.endpoint) {
|
|
||||||
args.push('-e', endpointOrNamespace.endpoint);
|
|
||||||
} else if (endpointOrNamespace.namespace) {
|
|
||||||
args.push('--namespace', endpointOrNamespace.namespace);
|
|
||||||
} else {
|
|
||||||
throw new Error(loc.endpointOrNamespaceRequired);
|
|
||||||
}
|
|
||||||
return this.executeCommand<void>(args, Object.assign({}, additionalEnvVars, { 'AZDATA_PASSWORD': password }), azdataContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the output of running '--version' command on the azdata tool.
|
|
||||||
* It also updates the cachedVersion property based on the return value from the tool.
|
|
||||||
*/
|
|
||||||
public async version(): Promise<azdataExt.AzdataOutput<string>> {
|
|
||||||
const output = await executeAzdataCommand(`"${this._path}"`, ['--version']);
|
|
||||||
this._semVersion = new SemVer(parseVersion(output.stdout));
|
|
||||||
return {
|
|
||||||
logs: [],
|
|
||||||
stdout: output.stdout.split(os.EOL),
|
|
||||||
stderr: output.stderr.split(os.EOL),
|
|
||||||
result: output.stdout
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the specified azdata command.
|
|
||||||
* @param args The args to pass to azdata
|
|
||||||
* @param additionalEnvVars Additional environment variables to set for this execution
|
|
||||||
*/
|
|
||||||
public async executeCommand<R>(args: string[], additionalEnvVars?: azdataExt.AdditionalEnvVars, azdataContext?: string): Promise<azdataExt.AzdataOutput<R>> {
|
|
||||||
try {
|
|
||||||
if (azdataContext) {
|
|
||||||
args = args.concat('--controller-context', azdataContext);
|
|
||||||
}
|
|
||||||
const output = JSON.parse((await executeAzdataCommand(`"${this._path}"`, args.concat(['--output', 'json']), additionalEnvVars)).stdout);
|
|
||||||
return {
|
|
||||||
logs: <string[]>output.log,
|
|
||||||
stdout: <string[]>output.stdout,
|
|
||||||
stderr: <string[]>output.stderr,
|
|
||||||
result: <R>output.result
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof ExitCodeError) {
|
|
||||||
try {
|
|
||||||
// For azdata internal errors the output is JSON and so we need to do some extra parsing here
|
|
||||||
// to get the correct stderr out. The actual value we get is something like
|
|
||||||
// ERROR: { stderr: '...' }
|
|
||||||
// so we also need to trim off the start that isn't a valid JSON blob
|
|
||||||
err.stderr = JSON.parse(err.stderr.substring(err.stderr.indexOf('{'), err.stderr.indexOf('}') + 1)).stderr;
|
|
||||||
} catch {
|
|
||||||
// it means this was probably some other generic error (such as command not being found)
|
|
||||||
// check if azdata still exists if it does then rethrow the original error if not then emit a new specific error.
|
|
||||||
try {
|
|
||||||
await fs.promises.access(this._path);
|
|
||||||
//this.path exists
|
|
||||||
} catch (e) {
|
|
||||||
// this.path does not exist
|
|
||||||
await vscode.commands.executeCommand('setContext', azdataFound, false);
|
|
||||||
throw new NoAzdataError();
|
|
||||||
}
|
|
||||||
throw err; // rethrow the original error
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AzdataDarwinPackageVersionInfo = {
|
|
||||||
versions: {
|
|
||||||
stable: string,
|
|
||||||
devel: string,
|
|
||||||
head: string,
|
|
||||||
bottle: boolean
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the existing installation of azdata, or throws an error if it couldn't find it
|
|
||||||
* or encountered an unexpected error.
|
|
||||||
* The promise is rejected when Azdata is not found.
|
|
||||||
*/
|
|
||||||
export async function findAzdata(): Promise<IAzdataTool> {
|
|
||||||
Logger.log(loc.searchingForAzdata);
|
|
||||||
try {
|
|
||||||
const azdata = await findSpecificAzdata();
|
|
||||||
await vscode.commands.executeCommand('setContext', azdataFound, true); // save a context key that azdata was found so that command for installing azdata is no longer available in commandPalette and that for updating it is.
|
|
||||||
Logger.log(loc.foundExistingAzdata(await azdata.getPath(), (await azdata.getSemVersion()).raw));
|
|
||||||
return azdata;
|
|
||||||
} catch (err) {
|
|
||||||
Logger.log(loc.couldNotFindAzdata(err));
|
|
||||||
Logger.log(loc.noAzdata);
|
|
||||||
await vscode.commands.executeCommand('setContext', azdataFound, false);// save a context key that azdata was not found so that command for installing azdata is available in commandPalette and that for updating it is no longer available.
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* runs the commands to install azdata, downloading the installation package if needed
|
|
||||||
*/
|
|
||||||
export async function installAzdata(): Promise<void> {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.installingAzdata);
|
|
||||||
await vscode.window.withProgress(
|
|
||||||
{
|
|
||||||
location: vscode.ProgressLocation.Notification,
|
|
||||||
title: loc.installingAzdata,
|
|
||||||
cancellable: false
|
|
||||||
},
|
|
||||||
async (_progress, _token): Promise<void> => {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await downloadAndInstallAzdataWin32();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await installAzdataDarwin();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await installAzdataLinux();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(loc.platformUnsupported(process.platform));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the azdata using os appropriate method
|
|
||||||
*/
|
|
||||||
async function updateAzdata(version: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.updatingAzdata);
|
|
||||||
await vscode.window.withProgress(
|
|
||||||
{
|
|
||||||
location: vscode.ProgressLocation.Notification,
|
|
||||||
title: loc.updatingAzdata,
|
|
||||||
cancellable: false
|
|
||||||
},
|
|
||||||
async (_progress, _token): Promise<void> => {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await downloadAndInstallAzdataWin32();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await updateAzdataDarwin();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await installAzdataLinux();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(loc.platformUnsupported(process.platform));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
vscode.window.showInformationMessage(loc.azdataUpdated(version));
|
|
||||||
Logger.log(loc.azdataUpdated(version));
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
// Windows: 1602 is User cancelling installation/update - not unexpected so don't display
|
|
||||||
if (!(err instanceof ExitCodeError) || err.code !== 1602) {
|
|
||||||
vscode.window.showWarningMessage(loc.updateError(err));
|
|
||||||
Logger.log(loc.updateError(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether azdata is installed - and if it is not then invokes the process of azdata installation.
|
|
||||||
* @param userRequested true means that this operation by was requested by a user by executing an ads command.
|
|
||||||
*/
|
|
||||||
export async function checkAndInstallAzdata(userRequested: boolean = false): Promise<IAzdataTool | undefined> {
|
|
||||||
try {
|
|
||||||
return await findAzdata(); // find currently installed Azdata
|
|
||||||
} catch (err) {
|
|
||||||
// Calls will be made to handle azdata not being installed if user declines to install on the prompt
|
|
||||||
if (await promptToInstallAzdata(userRequested)) {
|
|
||||||
return await findAzdata();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a newer version of azdata is available - and if it is then invokes the process of azdata update.
|
|
||||||
* @param currentAzdata The current version of azdata to check against
|
|
||||||
* @param userRequested true means that this operation by was requested by a user by executing an ads command.
|
|
||||||
* returns true if update was done and false otherwise.
|
|
||||||
*/
|
|
||||||
export async function checkAndUpdateAzdata(currentAzdata?: IAzdataTool, userRequested: boolean = false): Promise<boolean> {
|
|
||||||
if (currentAzdata !== undefined) {
|
|
||||||
const newSemVersion = await discoverLatestAvailableAzdataVersion();
|
|
||||||
const currentSemVersion = await currentAzdata.getSemVersion();
|
|
||||||
Logger.log(loc.foundAzdataVersionToUpdateTo(newSemVersion.raw, currentSemVersion.raw));
|
|
||||||
if (MIN_AZDATA_VERSION.compare(currentSemVersion) === 1) {
|
|
||||||
if (newSemVersion.compare(MIN_AZDATA_VERSION) >= 0) {
|
|
||||||
return await promptToUpdateAzdata(newSemVersion.raw, userRequested, true);
|
|
||||||
} else {
|
|
||||||
// This should never happen - it means that the currently available version to download
|
|
||||||
// is < the version we require. If this was to happen it'd imply something is wrong with
|
|
||||||
// the version JSON or the minimum required version.
|
|
||||||
// Regardless, there's nothing we can do and so we just bail out at this point and tell the user
|
|
||||||
// they have to install it manually (hopefully it's available and wasn't a publishing mistake)
|
|
||||||
vscode.window.showInformationMessage(loc.requiredVersionNotAvailable(MIN_AZDATA_VERSION.raw, newSemVersion.raw));
|
|
||||||
Logger.log(loc.requiredVersionNotAvailable(newSemVersion.raw, currentSemVersion.raw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (newSemVersion.compare(currentSemVersion) === 1) {
|
|
||||||
return await promptToUpdateAzdata(newSemVersion.raw, userRequested);
|
|
||||||
} else {
|
|
||||||
Logger.log(loc.currentlyInstalledVersionIsLatest((await currentAzdata.getSemVersion()).raw));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logger.log(loc.updateCheckSkipped);
|
|
||||||
Logger.log(loc.noAzdata);
|
|
||||||
await vscode.commands.executeCommand('setContext', azdataFound, false);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prompt user to install Azdata.
|
|
||||||
* @param userRequested - if true this operation was requested in response to a user issued command, if false it was issued at startup by system
|
|
||||||
* returns true if installation was done and false otherwise.
|
|
||||||
*/
|
|
||||||
async function promptToInstallAzdata(userRequested: boolean = false): Promise<boolean> {
|
|
||||||
let response: string | undefined = loc.yes;
|
|
||||||
const config = <AzdataDeployOption>getConfig(azdataInstallKey);
|
|
||||||
if (userRequested) {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.userRequestedInstall);
|
|
||||||
}
|
|
||||||
if (config === AzdataDeployOption.dontPrompt && !userRequested) {
|
|
||||||
Logger.log(loc.skipInstall(config));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const responses = userRequested
|
|
||||||
? [loc.yes, loc.no]
|
|
||||||
: [loc.yes, loc.askLater, loc.doNotAskAgain];
|
|
||||||
if (config === AzdataDeployOption.prompt) {
|
|
||||||
Logger.log(loc.promptForAzdataInstallLog);
|
|
||||||
response = await vscode.window.showErrorMessage(loc.promptForAzdataInstall, ...responses);
|
|
||||||
Logger.log(loc.userResponseToInstallPrompt(response));
|
|
||||||
}
|
|
||||||
if (response === loc.doNotAskAgain) {
|
|
||||||
await setConfig(azdataInstallKey, AzdataDeployOption.dontPrompt);
|
|
||||||
} else if (response === loc.yes) {
|
|
||||||
try {
|
|
||||||
await installAzdata();
|
|
||||||
vscode.window.showInformationMessage(loc.azdataInstalled);
|
|
||||||
Logger.log(loc.azdataInstalled);
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
// Windows: 1602 is User cancelling installation/update - not unexpected so don't display
|
|
||||||
if (!(err instanceof ExitCodeError) || err.code !== 1602) {
|
|
||||||
vscode.window.showWarningMessage(loc.installError(err));
|
|
||||||
Logger.log(loc.installError(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* prompt user to update Azdata.
|
|
||||||
* @param newVersion - provides the new version that the user will be prompted to update to
|
|
||||||
* @param userRequested - if true this operation was requested in response to a user issued command, if false it was issued at startup by system
|
|
||||||
* returns true if update was done and false otherwise.
|
|
||||||
* @param required - Whether this update is required. If true then we will always show the prompt and warn the user if they decline it
|
|
||||||
*/
|
|
||||||
async function promptToUpdateAzdata(newVersion: string, userRequested: boolean = false, required = false): Promise<boolean> {
|
|
||||||
if (required) {
|
|
||||||
let response: string | undefined = loc.yes;
|
|
||||||
const config = <AzdataDeployOption>getConfig(azdatarequiredUpdateKey);
|
|
||||||
if (userRequested) {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.userRequestedUpdate);
|
|
||||||
}
|
|
||||||
if (config === AzdataDeployOption.dontPrompt && !userRequested) {
|
|
||||||
Logger.log(loc.skipRequiredUpdate(config));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const responses = userRequested
|
|
||||||
? [loc.yes, loc.no]
|
|
||||||
: [loc.yes, loc.askLater, loc.doNotAskAgain];
|
|
||||||
Logger.log(loc.promptForRequiredAzdataUpdateLog(MIN_AZDATA_VERSION.raw, newVersion));
|
|
||||||
response = await vscode.window.showInformationMessage(loc.promptForRequiredAzdataUpdate(MIN_AZDATA_VERSION.raw, newVersion), ...responses);
|
|
||||||
Logger.log(loc.userResponseToUpdatePrompt(response));
|
|
||||||
if (response === loc.doNotAskAgain) {
|
|
||||||
await setConfig(azdatarequiredUpdateKey, AzdataDeployOption.dontPrompt);
|
|
||||||
} else if (response === loc.yes) {
|
|
||||||
return updateAzdata(newVersion);
|
|
||||||
} else {
|
|
||||||
vscode.window.showWarningMessage(loc.missingRequiredVersion(MIN_AZDATA_VERSION.raw));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let response: string | undefined = loc.yes;
|
|
||||||
const config = <AzdataDeployOption>getConfig(azdataUpdateKey);
|
|
||||||
if (userRequested) {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.userRequestedUpdate);
|
|
||||||
}
|
|
||||||
if (config === AzdataDeployOption.dontPrompt && !userRequested) {
|
|
||||||
Logger.log(loc.skipUpdate(config));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const responses = userRequested
|
|
||||||
? [loc.yes, loc.no]
|
|
||||||
: [loc.yes, loc.askLater, loc.doNotAskAgain];
|
|
||||||
if (config === AzdataDeployOption.prompt) {
|
|
||||||
Logger.log(loc.promptForAzdataUpdateLog(newVersion));
|
|
||||||
response = await vscode.window.showInformationMessage(loc.promptForAzdataUpdate(newVersion), ...responses);
|
|
||||||
Logger.log(loc.userResponseToUpdatePrompt(response));
|
|
||||||
}
|
|
||||||
if (response === loc.doNotAskAgain) {
|
|
||||||
await setConfig(azdataUpdateKey, AzdataDeployOption.dontPrompt);
|
|
||||||
} else if (response === loc.yes) {
|
|
||||||
return updateAzdata(newVersion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if Eula has been accepted.
|
|
||||||
*
|
|
||||||
* @param memento The memento that stores the eulaAccepted state
|
|
||||||
*/
|
|
||||||
export function isEulaAccepted(memento: vscode.Memento): boolean {
|
|
||||||
return !!memento.get<boolean>(eulaAccepted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
|
||||||
* @param memento - memento where the user response is stored.
|
|
||||||
* @param userRequested - if true this operation was requested in response to a user issued command, if false it was issued at startup by system
|
|
||||||
* @param requireUserAction - if the prompt is required to be acted upon by the user. This is typically 'true' when this method is called to address an Error when the EULA needs to be accepted to proceed.
|
|
||||||
* pre-requisite, the calling code has to ensure that the eula has not yet been previously accepted by the user.
|
|
||||||
* returns true if the user accepted the EULA.
|
|
||||||
*/
|
|
||||||
export async function promptForEula(memento: vscode.Memento, userRequested: boolean = false, requireUserAction: boolean = false): Promise<boolean> {
|
|
||||||
let response: string | undefined = loc.no;
|
|
||||||
const config = <AzdataDeployOption>getConfig(azdataAcceptEulaKey);
|
|
||||||
if (userRequested) {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.userRequestedAcceptEula);
|
|
||||||
}
|
|
||||||
const responses = userRequested
|
|
||||||
? [loc.accept, loc.decline]
|
|
||||||
: [loc.accept, loc.askLater, loc.doNotAskAgain];
|
|
||||||
if (config === AzdataDeployOption.prompt || userRequested) {
|
|
||||||
Logger.show();
|
|
||||||
Logger.log(loc.promptForEulaLog(microsoftPrivacyStatementUrl, eulaUrl));
|
|
||||||
response = requireUserAction
|
|
||||||
? await vscode.window.showErrorMessage(loc.promptForEula(microsoftPrivacyStatementUrl, eulaUrl), ...responses)
|
|
||||||
: await vscode.window.showInformationMessage(loc.promptForEula(microsoftPrivacyStatementUrl, eulaUrl), ...responses);
|
|
||||||
Logger.log(loc.userResponseToEulaPrompt(response));
|
|
||||||
}
|
|
||||||
if (response === loc.doNotAskAgain) {
|
|
||||||
await setConfig(azdataAcceptEulaKey, AzdataDeployOption.dontPrompt);
|
|
||||||
} else if (response === loc.accept) {
|
|
||||||
await memento.update(eulaAccepted, true); // save a memento that eula was accepted
|
|
||||||
await vscode.commands.executeCommand('setContext', eulaAccepted, true); // save a context key that eula was accepted so that command for accepting eula is no longer available in commandPalette
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads the Windows installer and runs it
|
|
||||||
*/
|
|
||||||
async function downloadAndInstallAzdataWin32(): Promise<void> {
|
|
||||||
const downLoadLink = await getPlatformDownloadLink();
|
|
||||||
const downloadFolder = os.tmpdir();
|
|
||||||
const downloadLogs = path.join(downloadFolder, 'ads_azdata_install_logs.log');
|
|
||||||
const downloadedFile = await HttpClient.downloadFile(downLoadLink, downloadFolder);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await executeSudoCommand(`msiexec /qn /i "${downloadedFile}" /lvx "${downloadLogs}"`);
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`${err.message}. See logs at ${downloadLogs} for more details.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs commands to install azdata on MacOS
|
|
||||||
*/
|
|
||||||
async function installAzdataDarwin(): Promise<void> {
|
|
||||||
await executeCommand('brew', ['tap', 'microsoft/azdata-cli-release']);
|
|
||||||
await executeCommand('brew', ['update']);
|
|
||||||
await executeCommand('brew', ['install', 'azdata-cli']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs commands to update azdata on MacOS
|
|
||||||
*/
|
|
||||||
async function updateAzdataDarwin(): Promise<void> {
|
|
||||||
await executeCommand('brew', ['tap', 'microsoft/azdata-cli-release']);
|
|
||||||
await executeCommand('brew', ['update']);
|
|
||||||
await executeCommand('brew', ['upgrade', 'azdata-cli']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs commands to install azdata on Linux
|
|
||||||
*/
|
|
||||||
async function installAzdataLinux(): Promise<void> {
|
|
||||||
// https://docs.microsoft.com/en-us/sql/big-data-cluster/deploy-install-azdata-linux-package
|
|
||||||
// Get packages needed for install process
|
|
||||||
await executeSudoCommand('apt-get update');
|
|
||||||
await executeSudoCommand('apt-get install gnupg ca-certificates curl wget software-properties-common apt-transport-https lsb-release -y');
|
|
||||||
// Download and install the signing key
|
|
||||||
await executeSudoCommand('curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null');
|
|
||||||
// Add the azdata repository information
|
|
||||||
const release = (await executeCommand('lsb_release', ['-rs'])).stdout.trim();
|
|
||||||
await executeSudoCommand(`add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/${release}/mssql-server-2019.list)"`);
|
|
||||||
// Update repository information and install azdata
|
|
||||||
await executeSudoCommand('apt-get update');
|
|
||||||
await executeSudoCommand('apt-get install -y azdata-cli');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
async function findSpecificAzdata(): Promise<IAzdataTool> {
|
|
||||||
const path = await ((process.platform === 'win32') ? searchForCmd('azdata.cmd') : searchForCmd('azdata'));
|
|
||||||
const versionOutput = await executeAzdataCommand(`"${path}"`, ['--version']);
|
|
||||||
return new AzdataTool(path, parseVersion(versionOutput.stdout));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getConfig(key: string): AzdataDeployOption | undefined {
|
|
||||||
const config = vscode.workspace.getConfiguration(azdataConfigSection);
|
|
||||||
const value = <AzdataDeployOption>config.get<AzdataDeployOption>(key);
|
|
||||||
Logger.log(loc.azdataUserSettingRead(key, value));
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setConfig(key: string, value: string): Promise<void> {
|
|
||||||
const config = vscode.workspace.getConfiguration(azdataConfigSection);
|
|
||||||
await config.update(key, value, vscode.ConfigurationTarget.Global);
|
|
||||||
Logger.log(loc.azdataUserSettingUpdated(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the latest azdata version available for a given platform
|
|
||||||
*/
|
|
||||||
export async function discoverLatestAvailableAzdataVersion(): Promise<SemVer> {
|
|
||||||
Logger.log(loc.checkingLatestAzdataVersion);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'darwin':
|
|
||||||
return await discoverLatestStableAzdataVersionDarwin();
|
|
||||||
// case 'linux':
|
|
||||||
// ideally we would not to discover linux package availability using the apt/apt-get/apt-cache package manager commands.
|
|
||||||
// However, doing discovery that way required apt update to be performed which requires sudo privileges. At least currently this code path
|
|
||||||
// gets invoked on extension start up and prompt user for sudo privileges is annoying at best. So for now basing linux discovery also on a releaseJson file.
|
|
||||||
default:
|
|
||||||
return await getPlatformReleaseVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses out the azdata version from the raw azdata version output
|
|
||||||
* @param raw The raw version output from azdata --version
|
|
||||||
*/
|
|
||||||
function parseVersion(raw: string): string {
|
|
||||||
// Currently the version is a multi-line string that contains other version information such
|
|
||||||
// as the Python installation, with the first line being the version of azdata itself.
|
|
||||||
const lines = raw.split(os.EOL);
|
|
||||||
return lines[0].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the latest azdata version for MacOs clients
|
|
||||||
*/
|
|
||||||
async function discoverLatestStableAzdataVersionDarwin(): Promise<SemVer> {
|
|
||||||
// set brew tap to azdata-cli repository
|
|
||||||
await executeCommand('brew', ['tap', 'microsoft/azdata-cli-release']);
|
|
||||||
await executeCommand('brew', ['update']);
|
|
||||||
let brewInfoAzdataCliJson;
|
|
||||||
// Get the package version 'info' about 'azdata-cli' from 'brew' as a json object
|
|
||||||
const brewInfoOutput = (await executeCommand('brew', ['info', 'azdata-cli', '--json'])).stdout;
|
|
||||||
try {
|
|
||||||
brewInfoAzdataCliJson = JSON.parse(brewInfoOutput);
|
|
||||||
} catch (e) {
|
|
||||||
throw Error(`failed to parse the JSON contents output of: 'brew info azdata-cli --json', text being parsed: '${brewInfoOutput}', error:${getErrorMessage(e)}`);
|
|
||||||
}
|
|
||||||
// Get the 'info' about 'azdata-cli' from 'brew' as a json object
|
|
||||||
const azdataPackageVersionInfo: AzdataDarwinPackageVersionInfo = brewInfoAzdataCliJson.shift();
|
|
||||||
Logger.log(loc.latestAzdataVersionAvailable(azdataPackageVersionInfo.versions.stable));
|
|
||||||
return new SemVer(azdataPackageVersionInfo.versions.stable);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function executeAzdataCommand(command: string, args: string[], additionalEnvVars: azdataExt.AdditionalEnvVars = {}): Promise<ProcessOutput> {
|
|
||||||
additionalEnvVars = Object.assign(additionalEnvVars, { 'ACCEPT_EULA': 'yes' });
|
|
||||||
const debug = vscode.workspace.getConfiguration(azdataConfigSection).get(debugConfigKey);
|
|
||||||
if (debug) {
|
|
||||||
args.push('--debug');
|
|
||||||
}
|
|
||||||
return executeCommand(command, args, additionalEnvVars);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the latest azdata version for linux clients
|
|
||||||
* This method requires sudo permission so not suitable to be run during startup.
|
|
||||||
*/
|
|
||||||
// async function discoverLatestStableAzdataVersionLinux(): Promise<SemVer> {
|
|
||||||
// // Update repository information and install azdata
|
|
||||||
// await executeSudoCommand('apt-get update');
|
|
||||||
// const output = (await executeCommand('apt', ['list', 'azdata-cli', '--upgradeable'])).stdout;
|
|
||||||
// // the packageName (with version) string is the second space delimited token on the 2nd line
|
|
||||||
// const packageName = output.split('\n')[1].split(' ')[1];
|
|
||||||
// // the version string is the first part of the package sting before '~'
|
|
||||||
// const version = packageName.split('~')[0];
|
|
||||||
// Logger.log(loc.latestAzdataVersionAvailable(version));
|
|
||||||
// return new SemVer(version);
|
|
||||||
// }
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as loc from './localizedConstants';
|
|
||||||
import { SemVer } from 'semver';
|
|
||||||
import { HttpClient } from './common/httpClient';
|
|
||||||
import Logger from './common/logger';
|
|
||||||
import { getErrorMessage } from './common/utils';
|
|
||||||
import { azdataHostname, azdataReleaseJson } from './constants';
|
|
||||||
|
|
||||||
interface PlatformReleaseInfo {
|
|
||||||
version: string; // "20.0.1"
|
|
||||||
link?: string; // "https://aka.ms/azdata-msi"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AzdataReleaseInfo {
|
|
||||||
win32: PlatformReleaseInfo,
|
|
||||||
darwin: PlatformReleaseInfo,
|
|
||||||
linux: PlatformReleaseInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlatformAzdataReleaseInfo(releaseInfo: AzdataReleaseInfo): PlatformReleaseInfo {
|
|
||||||
switch (os.platform()) {
|
|
||||||
case 'win32':
|
|
||||||
return releaseInfo.win32;
|
|
||||||
case 'linux':
|
|
||||||
return releaseInfo.linux;
|
|
||||||
case 'darwin':
|
|
||||||
return releaseInfo.darwin;
|
|
||||||
default:
|
|
||||||
Logger.log(loc.platformUnsupported(os.platform()));
|
|
||||||
throw new Error(`Unsupported AzdataReleaseInfo platform '${os.platform()}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the release version for the current platform from the release info - throwing an error if it doesn't exist.
|
|
||||||
* @param releaseInfo The AzdataReleaseInfo object
|
|
||||||
*/
|
|
||||||
export async function getPlatformReleaseVersion(): Promise<SemVer> {
|
|
||||||
const releaseInfo = await getAzdataReleaseInfo();
|
|
||||||
const platformReleaseInfo = getPlatformAzdataReleaseInfo(releaseInfo);
|
|
||||||
if (!platformReleaseInfo.version) {
|
|
||||||
Logger.log(loc.noReleaseVersion(os.platform(), JSON.stringify(releaseInfo)));
|
|
||||||
throw new Error(`No release version available for platform ${os.platform()}`);
|
|
||||||
}
|
|
||||||
Logger.log(loc.latestAzdataVersionAvailable(platformReleaseInfo.version));
|
|
||||||
return new SemVer(platformReleaseInfo.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the download link for the current platform from the release info - throwing an error if it doesn't exist.
|
|
||||||
* @param releaseInfo The AzdataReleaseInfo object
|
|
||||||
*/
|
|
||||||
export async function getPlatformDownloadLink(): Promise<string> {
|
|
||||||
const releaseInfo = await getAzdataReleaseInfo();
|
|
||||||
const platformReleaseInfo = getPlatformAzdataReleaseInfo(releaseInfo);
|
|
||||||
if (!platformReleaseInfo.link) {
|
|
||||||
Logger.log(loc.noDownloadLink(os.platform(), JSON.stringify(releaseInfo)));
|
|
||||||
throw new Error(`No download link available for platform ${os.platform()}`);
|
|
||||||
}
|
|
||||||
return platformReleaseInfo.link;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAzdataReleaseInfo(): Promise<AzdataReleaseInfo> {
|
|
||||||
const fileContents = await HttpClient.getTextContent(`${azdataHostname}/${azdataReleaseJson}`);
|
|
||||||
try {
|
|
||||||
return JSON.parse(fileContents);
|
|
||||||
} catch (e) {
|
|
||||||
Logger.log(loc.failedToParseReleaseInfo(`${azdataHostname}/${azdataReleaseJson}`, fileContents, e));
|
|
||||||
throw Error(`Failed to parse the JSON of contents at: ${azdataHostname}/${azdataReleaseJson}. Error: ${getErrorMessage(e)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { AdditionalEnvVars } from 'azdata-ext';
|
import { AdditionalEnvVars } from 'az-ext';
|
||||||
import * as cp from 'child_process';
|
import * as cp from 'child_process';
|
||||||
import * as sudo from 'sudo-prompt';
|
import * as sudo from 'sudo-prompt';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class Log {
|
|||||||
private _output: vscode.OutputChannel;
|
private _output: vscode.OutputChannel;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._output = vscode.window.createOutputChannel(loc.azdata);
|
this._output = vscode.window.createOutputChannel(loc.az);
|
||||||
}
|
}
|
||||||
|
|
||||||
log(msg: string): void {
|
log(msg: string): void {
|
||||||
|
|||||||
@@ -3,19 +3,30 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
import * as which from 'which';
|
import * as which from 'which';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
|
|
||||||
export class NoAzdataError extends Error implements azdataExt.ErrorWithLink {
|
export class NoAzureCLIError extends Error implements azExt.ErrorWithLink {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(loc.noAzdata);
|
super(loc.noAzureCLI);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get messageWithLink(): string {
|
public get messageWithLink(): string {
|
||||||
return loc.noAzdataWithLink;
|
return loc.noAzureCLI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AzureCLIArcExtError extends Error implements azExt.ErrorWithLink {
|
||||||
|
constructor() {
|
||||||
|
super(loc.arcdataExtensionNotInstalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get messageWithLink(): string {
|
||||||
|
return loc.arcdataExtensionNotInstalled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for the first instance of the specified executable in the PATH environment variable
|
* Searches for the first instance of the specified executable in the PATH environment variable
|
||||||
* @param exe The executable to search for
|
* @param exe The executable to search for
|
||||||
|
|||||||
@@ -4,21 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
// config setting keys
|
// config setting keys
|
||||||
export const azdataConfigSection: string = 'azcli';
|
export const azConfigSection: string = 'azcli';
|
||||||
export const azdataAcceptEulaKey: string = 'acceptEula';
|
|
||||||
export const azdataInstallKey: string = 'install';
|
|
||||||
export const azdataUpdateKey: string = 'update';
|
|
||||||
export const azdatarequiredUpdateKey: string = 'requiredUpdate';
|
|
||||||
export const debugConfigKey = 'logDebugInfo';
|
export const debugConfigKey = 'logDebugInfo';
|
||||||
|
export const azRequiredUpdateKey: string = 'requiredUpdate';
|
||||||
|
|
||||||
|
|
||||||
// context keys && memento keys
|
// context keys && memento keys
|
||||||
export const eulaAccepted = 'azcli.eulaAccepted';
|
export const azFound = 'az.found';
|
||||||
export const azdataFound = 'azcli.found';
|
|
||||||
|
|
||||||
// other constants
|
// other constants
|
||||||
export const azdataHostname = 'https://aka.ms';
|
export const latestAzArcExtensionVersion = '1.0.0';
|
||||||
export const azdataUri = 'azdata-msi';
|
|
||||||
export const azdataReleaseJson = 'azdata/release.json';
|
|
||||||
export const microsoftPrivacyStatementUrl = 'https://privacy.microsoft.com/privacystatement';
|
|
||||||
export const eulaUrl = 'https://aka.ms/eula-azdata-en';
|
|
||||||
|
|||||||
@@ -3,70 +3,26 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { getExtensionApi } from './api';
|
import { getExtensionApi } from './api';
|
||||||
import { checkAndInstallAzdata, checkAndUpdateAzdata, findAzdata, isEulaAccepted, promptForEula } from './azdata';
|
import { findAz } from './az';
|
||||||
import Logger from './common/logger';
|
import { ArcControllerConfigProfilesOptionsSource } from './providers/arcControllerConfigProfilesOptionsSource';
|
||||||
import * as constants from './constants';
|
import { AzToolService } from './services/azToolService';
|
||||||
import * as loc from './localizedConstants';
|
|
||||||
import { AzdataToolService } from './services/azdataToolService';
|
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<azdataExt.IExtension> {
|
export async function activate(context: vscode.ExtensionContext): Promise<azExt.IExtension> {
|
||||||
const azdataToolService = new AzdataToolService();
|
const azToolService = new AzToolService();
|
||||||
let eulaAccepted: boolean = false;
|
|
||||||
vscode.commands.registerCommand('azcli.acceptEula', async () => {
|
|
||||||
await promptForEula(context.globalState, true /* userRequested */);
|
|
||||||
});
|
|
||||||
|
|
||||||
vscode.commands.registerCommand('azcli.install', async () => {
|
azToolService.localAz = await findAz();
|
||||||
azdataToolService.localAzdata = await checkAndInstallAzdata(true /* userRequested */);
|
|
||||||
});
|
|
||||||
|
|
||||||
vscode.commands.registerCommand('azcli.update', async () => {
|
const azApi = getExtensionApi(azToolService);
|
||||||
if (await checkAndUpdateAzdata(azdataToolService.localAzdata, true /* userRequested */)) { // if an update was performed
|
|
||||||
azdataToolService.localAzdata = await findAzdata(); // find and save the currently installed azdata
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
eulaAccepted = isEulaAccepted(context.globalState); // fetch eula acceptance state from memento
|
|
||||||
await vscode.commands.executeCommand('setContext', constants.eulaAccepted, eulaAccepted); // set a context key for current value of eulaAccepted state retrieved from memento so that command for accepting eula is available/unavailable in commandPalette appropriately.
|
|
||||||
Logger.log(loc.eulaAcceptedStateOnStartup(eulaAccepted));
|
|
||||||
|
|
||||||
// Don't block on this since we want the extension to finish activating without needing user input
|
|
||||||
const localAzdataDiscovered = checkAndInstallAzdata() // install if not installed and user wants it.
|
|
||||||
.then(async azdataTool => {
|
|
||||||
if (azdataTool !== undefined) {
|
|
||||||
azdataToolService.localAzdata = azdataTool;
|
|
||||||
if (!eulaAccepted) {
|
|
||||||
// Don't block on this since we want extension to finish activating without requiring user actions.
|
|
||||||
// If EULA has not been accepted then we will check again while executing azdata commands.
|
|
||||||
promptForEula(context.globalState)
|
|
||||||
.then(async (userResponse: boolean) => {
|
|
||||||
eulaAccepted = userResponse;
|
|
||||||
})
|
|
||||||
.catch((err) => console.log(err));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
//update if available and user wants it.
|
|
||||||
if (await checkAndUpdateAzdata(azdataToolService.localAzdata)) { // if an update was performed
|
|
||||||
azdataToolService.localAzdata = await findAzdata(); // find and save the currently installed azdata
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
vscode.window.showWarningMessage(loc.updateError(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return azdataTool;
|
|
||||||
});
|
|
||||||
|
|
||||||
const azdataApi = getExtensionApi(context.globalState, azdataToolService, localAzdataDiscovered);
|
|
||||||
|
|
||||||
// register option source(s)
|
// register option source(s)
|
||||||
// TODO: Uncomment this once azdata extension is removed
|
const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
||||||
// const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
context.subscriptions.push(rdApi.registerOptionsSourceProvider(new ArcControllerConfigProfilesOptionsSource(azApi)));
|
||||||
// context.subscriptions.push(rdApi.registerOptionsSourceProvider(new ArcControllerConfigProfilesOptionsSource(azdataApi)));
|
|
||||||
|
|
||||||
return azdataApi;
|
return azApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate(): void { }
|
export function deactivate(): void { }
|
||||||
|
|||||||
@@ -5,69 +5,31 @@
|
|||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { getErrorMessage } from './common/utils';
|
import { getErrorMessage } from './common/utils';
|
||||||
import { azdataConfigSection, azdataInstallKey, azdataUpdateKey, azdatarequiredUpdateKey } from './constants';
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export const azdata = localize('azdata.azdata', "Azure Data CLI");
|
export const az = localize('az.az', "Azure CLI");
|
||||||
export const searchingForAzdata = localize('azdata.searchingForAzdata', "Searching for existing Azure Data CLI installation...");
|
export const searchingForAz = localize('az.searchingForAz', "Searching for existing Azure CLI installation...");
|
||||||
export const foundExistingAzdata = (path: string, version: string): string => localize('azdata.foundExistingAzdata', "Found existing Azure Data CLI installation of version (v{0}) at path:{1}", version, path);
|
export const foundExistingAz = (path: string, version: string): string => localize('az.foundExistingAz', "Found existing Azure CLI installation of version (v{0}) at path:{1}", version, path);
|
||||||
|
export const downloadingProgressMb = (currentMb: string, totalMb: string): string => localize('az.downloadingProgressMb', "Downloading ({0} / {1} MB)", currentMb, totalMb);
|
||||||
export const downloadingProgressMb = (currentMb: string, totalMb: string): string => localize('azdata.downloadingProgressMb', "Downloading ({0} / {1} MB)", currentMb, totalMb);
|
export const downloadFinished = localize('az.downloadFinished', "Download finished");
|
||||||
export const downloadFinished = localize('azdata.downloadFinished', "Download finished");
|
export const downloadingTo = (name: string, url: string, location: string): string => localize('az.downloadingTo', "Downloading {0} from {1} to {2}", name, url, location);
|
||||||
export const installingAzdata = localize('azdata.installingAzdata', "Installing Azure Data CLI...");
|
export const executingCommand = (command: string, args: string[]): string => localize('az.executingCommand', "Executing command: '{0} {1}'", command, args?.join(' '));
|
||||||
export const updatingAzdata = localize('azdata.updatingAzdata', "Updating Azure Data CLI...");
|
export const stdoutOutput = (stdout: string): string => localize('az.stdoutOutput', "stdout: {0}", stdout);
|
||||||
export const azdataInstalled = localize('azdata.azdataInstalled', "Azure Data CLI was successfully installed. Restarting Azure Data Studio is required to complete configuration - features will not be activated until this is done.");
|
export const stderrOutput = (stderr: string): string => localize('az.stderrOutput', "stderr: {0}", stderr);
|
||||||
export const azdataUpdated = (version: string) => localize('azdata.azdataUpdated', "Azure Data CLI was successfully updated to version: {0}.", version);
|
export const gettingTextContentsOfUrl = (url: string): string => localize('az.gettingTextContentsOfUrl', "Getting text contents of resource at URL {0}", url);
|
||||||
export const yes = localize('azdata.yes', "Yes");
|
export const promptLog = (logEntry: string) => localize('az.promptLog', "Prompting the user to accept the following: {0}", logEntry);
|
||||||
export const no = localize('azdata.no', "No");
|
export const downloadError = localize('az.downloadError', "Error while downloading");
|
||||||
export const accept = localize('azdata.accept', "Accept");
|
export const platformUnsupported = (platform: string): string => localize('az.platformUnsupported', "Platform '{0}' is currently unsupported", platform);
|
||||||
export const decline = localize('azdata.decline', "Decline");
|
export const unexpectedCommandError = (errMsg: string): string => localize('az.unexpectedCommandError', "Unexpected error executing command: {0}", errMsg);
|
||||||
export const doNotAskAgain = localize('azdata.doNotAskAgain', "Don't Ask Again");
|
export const unexpectedExitCode = (code: number, err: string): string => localize('az.unexpectedExitCode', "Unexpected exit code from command: {1} ({0})", code, err);
|
||||||
export const askLater = localize('azdata.askLater', "Ask Later");
|
export const noReleaseVersion = (platform: string, releaseInfo: string): string => localize('az.noReleaseVersion', "No release version available for platform '{0}'\nRelease info: ${1}", platform, releaseInfo);
|
||||||
export const downloadingTo = (name: string, url: string, location: string): string => localize('azdata.downloadingTo', "Downloading {0} from {1} to {2}", name, url, location);
|
export const noDownloadLink = (platform: string, releaseInfo: string): string => localize('az.noDownloadLink', "No download link available for platform '{0}'\nRelease info: ${1}", platform, releaseInfo);
|
||||||
export const executingCommand = (command: string, args: string[]): string => localize('azdata.executingCommand', "Executing command: '{0} {1}'", command, args?.join(' '));
|
export const failedToParseReleaseInfo = (url: string, fileContents: string, err: any): string => localize('az.failedToParseReleaseInfo', "Failed to parse the JSON of contents at: {0}.\nFile contents:\n{1}\nError: {2}", url, fileContents, getErrorMessage(err));
|
||||||
export const stdoutOutput = (stdout: string): string => localize('azdata.stdoutOutput', "stdout: {0}", stdout);
|
export const endpointOrNamespaceRequired = localize('az.endpointOrNamespaceRequired', "Either an endpoint or a namespace must be specified");
|
||||||
export const stderrOutput = (stderr: string): string => localize('azdata.stderrOutput', "stderr: {0}", stderr);
|
export const arcdataExtensionNotInstalled = localize('az.arcdataExtensionNotInstalled', "This extension requires the Azure CLI extension 'arcdata' to be installed. Install the latest version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.");
|
||||||
export const checkingLatestAzdataVersion = localize('azdata.checkingLatestAzdataVersion', "Checking for latest available version of Azure Data CLI");
|
export const noAzureCLI = localize('az.noAzureCLI', "No Azure CLI is available. Install the latest version manually from [here](https://docs.microsoft.com/cli/azure/install-azure-cli) and then restart Azure Data Studio.");
|
||||||
export const gettingTextContentsOfUrl = (url: string): string => localize('azdata.gettingTextContentsOfUrl', "Getting text contents of resource at URL {0}", url);
|
export const requiredArcDataVersionNotAvailable = (requiredVersion: string, currentVersion: string): string => localize('az.requiredVersionNotAvailable', "This extension requires the Azure CLI extension 'arcdata' version >= {0} to be installed, but the current version available is only {1}. Install the correct version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.", requiredVersion, currentVersion);
|
||||||
export const foundAzdataVersionToUpdateTo = (newVersion: string, currentVersion: string): string => localize('azdata.versionForUpdate', "Found version: {0} that Azure Data CLI can be updated to from current version: {1}.", newVersion, currentVersion);
|
export const unsupportedArcDataVersion = (requiredVersion: string, currentVersion: string): string => localize('az.unsupportedArcDataVersion', "Your downloaded version {1} of the Azure CLI extension 'arcdata' is not yet supported. The latest version is is {0}. Install the correct version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.", requiredVersion, currentVersion);
|
||||||
export const latestAzdataVersionAvailable = (version: string): string => localize('azdata.latestAzdataVersionAvailable', "Latest available Azure Data CLI version: {0}.", version);
|
export const doNotAskAgain = localize('az.doNotAskAgain', "Don't Ask Again");
|
||||||
export const couldNotFindAzdata = (err: any): string => localize('azdata.couldNotFindAzdata', "Could not find Azure Data CLI. Error: {0}", err.message ?? err);
|
export const askLater = localize('az.askLater', "Ask Later");
|
||||||
export const currentlyInstalledVersionIsLatest = (currentVersion: string): string => localize('azdata.currentlyInstalledVersionIsLatest', "Currently installed version of Azure Data CLI: {0} is same or newer than any other version available", currentVersion);
|
export const azOutputParseErrorCaught = (command: string): string => localize('az.azOutputParseErrorCaught', "An error occurred while parsing the output of az command: {0}. The output is not JSON.", command);
|
||||||
export const promptLog = (logEntry: string) => localize('azdata.promptLog', "Prompting the user to accept the following: {0}", logEntry);
|
|
||||||
export const promptForAzdataInstall = localize('azdata.couldNotFindAzdataWithPrompt', "Could not find Azure Data CLI, install it now? If not then some features will not be able to function.");
|
|
||||||
export const promptForAzdataInstallLog = promptLog(promptForAzdataInstall);
|
|
||||||
export const promptForAzdataUpdate = (version: string): string => localize('azdata.promptForAzdataUpdate', "A new version of Azure Data CLI ( {0} ) is available, do you wish to update to it now?", version);
|
|
||||||
export const promptForRequiredAzdataUpdate = (requiredVersion: string, latestVersion: string): string => localize('azdata.promptForRequiredAzdataUpdate', "This extension requires Azure Data CLI >= {0} to be installed, do you wish to update to the latest version ({1}) now? If you do not then some functionality may not work.", requiredVersion, latestVersion);
|
|
||||||
export const requiredVersionNotAvailable = (requiredVersion: string, currentVersion: string): string => localize('azdata.requiredVersionNotAvailable', "This extension requires Azure Data CLI >= {0} to be installed, but the current version available is only {1}. Install the correct version manually from [here](https://docs.microsoft.com/sql/azdata/install/deploy-install-azdata) and then restart Azure Data Studio.", requiredVersion, currentVersion);
|
|
||||||
export const promptForAzdataUpdateLog = (version: string): string => promptLog(promptForAzdataUpdate(version));
|
|
||||||
export const promptForRequiredAzdataUpdateLog = (requiredVersion: string, latestVersion: string): string => promptLog(promptForRequiredAzdataUpdate(requiredVersion, latestVersion));
|
|
||||||
export const missingRequiredVersion = (requiredVersion: string): string => localize('azdata.missingRequiredVersion', "Azure Data CLI >= {0} is required for this feature. Run the 'Azure Data CLI: Check for Update' command to install this and then try again.", requiredVersion);
|
|
||||||
export const downloadError = localize('azdata.downloadError', "Error while downloading");
|
|
||||||
export const installError = (err: any): string => localize('azdata.installError', "Error installing Azure Data CLI: {0}", err.message ?? err);
|
|
||||||
export const updateError = (err: any): string => localize('azdata.updateError', "Error updating Azure Data CLI: {0}", err.message ?? err);
|
|
||||||
export const platformUnsupported = (platform: string): string => localize('azdata.platformUnsupported', "Platform '{0}' is currently unsupported", platform);
|
|
||||||
export const unexpectedCommandError = (errMsg: string): string => localize('azdata.unexpectedCommandError', "Unexpected error executing command: {0}", errMsg);
|
|
||||||
export const unexpectedExitCode = (code: number, err: string): string => localize('azdata.unexpectedExitCode', "Unexpected exit code from command: {1} ({0})", code, err);
|
|
||||||
export const noAzdata = localize('azdata.noAzdata', "No Azure Data CLI is available, run the command 'Azure Data CLI: Install' to enable the features that require it.");
|
|
||||||
export const noAzdataWithLink = localize('azdata.noAzdataWithLink', "No Azure Data CLI is available, [install the Azure Data CLI](command:azdata.install) to enable the features that require it.");
|
|
||||||
export const skipInstall = (config: string): string => localize('azdata.skipInstall', "Skipping installation of Azure Data CLI, since the operation was not user requested and config option: {0}.{1} is {2}", azdataConfigSection, azdataInstallKey, config);
|
|
||||||
export const skipUpdate = (config: string): string => localize('azdata.skipUpdate', "Skipping update of Azure Data CLI, since the operation was not user requested and config option: {0}.{1} is {2}", azdataConfigSection, azdataUpdateKey, config);
|
|
||||||
export const skipRequiredUpdate = (config: string): string => localize('azdata.skipRequiredUpdate', "Skipping required update of Azure Data CLI, since the operation was not user requested and config option: {0}.{1} is {2}", azdataConfigSection, azdatarequiredUpdateKey, config);
|
|
||||||
export const noReleaseVersion = (platform: string, releaseInfo: string): string => localize('azdata.noReleaseVersion', "No release version available for platform '{0}'\nRelease info: ${1}", platform, releaseInfo);
|
|
||||||
export const noDownloadLink = (platform: string, releaseInfo: string): string => localize('azdata.noDownloadLink', "No download link available for platform '{0}'\nRelease info: ${1}", platform, releaseInfo);
|
|
||||||
export const failedToParseReleaseInfo = (url: string, fileContents: string, err: any): string => localize('azdata.failedToParseReleaseInfo', "Failed to parse the JSON of contents at: {0}.\nFile contents:\n{1}\nError: {2}", url, fileContents, getErrorMessage(err));
|
|
||||||
export const azdataUserSettingRead = (configName: string, configValue: string): string => localize('azdata.azdataUserSettingReadLog', "Azure Data CLI user setting: {0}.{1} read, value: {2}", azdataConfigSection, configName, configValue);
|
|
||||||
export const azdataUserSettingUpdated = (configName: string, configValue: string): string => localize('azdata.azdataUserSettingUpdatedLog', "Azure Data CLI user setting: {0}.{1} updated, newValue: {2}", azdataConfigSection, configName, configValue);
|
|
||||||
export const userResponseToInstallPrompt = (response: string | undefined): string => localize('azdata.userResponseInstall', "User Response on prompt to install Azure Data CLI: {0}", response);
|
|
||||||
export const userResponseToUpdatePrompt = (response: string | undefined): string => localize('azdata.userResponseUpdate', "User Response on prompt to update Azure Data CLI: {0}", response);
|
|
||||||
export const userRequestedInstall = localize('azdata.userRequestedInstall', "User requested to install Azure Data CLI using 'Azure Data CLI: Install' command");
|
|
||||||
export const userRequestedUpdate = localize('azdata.userRequestedUpdate', "User requested to update Azure Data CLI using 'Azure Data CLI: Check for Update' command");
|
|
||||||
export const userRequestedAcceptEula = localize('azdata.acceptEula', "User requested to be prompted for accepting EULA by invoking 'Azure Data CLI: Accept EULA' command");
|
|
||||||
export const updateCheckSkipped = localize('azdata.updateCheckSkipped', "No check for new Azure Data CLI version availability performed as Azure Data CLI was not found to be installed");
|
|
||||||
export const eulaNotAccepted = localize('azdata.eulaNotAccepted', "Microsoft Privacy statement and Azure Data CLI license terms have not been accepted. Execute the command: [Azure Data CLI: Accept EULA](command:azdata.acceptEula) to accept EULA to enable the features that requires Azure Data CLI.");
|
|
||||||
export const promptForEula = (privacyStatementUrl: string, eulaUrl: string) => localize('azdata.promptForEula', "It is required to accept the [Microsoft Privacy Statement]({0}) and the [Azure Data CLI license terms]({1}) to use this extension. Declining this will result in some features not working.", privacyStatementUrl, eulaUrl);
|
|
||||||
export const promptForEulaLog = (privacyStatementUrl: string, eulaUrl: string) => promptLog(promptForEula(privacyStatementUrl, eulaUrl));
|
|
||||||
export const userResponseToEulaPrompt = (response: string | undefined) => localize('azdata.promptForEulaResponse', "User response to EULA prompt: {0}", response);
|
|
||||||
export const eulaAcceptedStateOnStartup = (eulaAccepted: boolean) => localize('azdata.eulaAcceptedStateOnStartup', "'EULA Accepted' state on startup: {0}", eulaAccepted);
|
|
||||||
export const endpointOrNamespaceRequired = localize('azdata.endpointOrNamespaceRequired', "Either an endpoint or a namespace must be specified");
|
|
||||||
|
|||||||
@@ -4,19 +4,15 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as rd from 'resource-deployment';
|
import * as rd from 'resource-deployment';
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azExt from 'az-ext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that provides options sources for an Arc Data Controller
|
* Class that provides options sources for an Arc Data Controller
|
||||||
*/
|
*/
|
||||||
export class ArcControllerConfigProfilesOptionsSource implements rd.IOptionsSourceProvider {
|
export class ArcControllerConfigProfilesOptionsSource implements rd.IOptionsSourceProvider {
|
||||||
readonly id = 'arc.controller.config.profiles';
|
readonly id = 'azcli.arc.controller.config.profiles';
|
||||||
constructor(private _azdataExtApi: azdataExt.IExtension) { }
|
constructor(private _azExtApi: azExt.IExtension) { }
|
||||||
async getOptions(): Promise<string[]> {
|
async getOptions(): Promise<string[]> {
|
||||||
const isEulaAccepted = await this._azdataExtApi.isEulaAccepted();
|
return (await this._azExtApi.az.arcdata.dc.config.list()).stdout;
|
||||||
if (!isEulaAccepted) { // if eula has not yet be accepted then give user a chance to accept it
|
|
||||||
await this._azdataExtApi.promptForEula();
|
|
||||||
}
|
|
||||||
return (await this._azdataExtApi.azdata.arc.dc.config.list()).result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,25 +3,24 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { IAzdataTool } from '../azdata';
|
import { IAzTool } from '../az';
|
||||||
|
|
||||||
export class AzdataToolService {
|
export class AzToolService {
|
||||||
private _localAzdata: IAzdataTool | undefined;
|
private _localAz: IAzTool | undefined;
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the localAzdata that was last saved
|
* Gets the localAz that was last saved
|
||||||
*/
|
*/
|
||||||
get localAzdata(): IAzdataTool | undefined {
|
get localAz(): IAzTool | undefined {
|
||||||
return this._localAzdata;
|
return this._localAz;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the localAzdata object to be used for azdata operations
|
* Sets the localAz object to be used for az operations
|
||||||
*/
|
*/
|
||||||
set localAzdata(azdata: IAzdataTool | undefined) {
|
set localAz(az: IAzTool | undefined) {
|
||||||
this._localAzdata = azdata;
|
this._localAz = az;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,121 +1,77 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
// /*---------------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
// * Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
// *--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
// import * as azExt from 'az-ext';
|
||||||
import * as childProcess from '../common/childProcess';
|
// import * as childProcess from '../common/childProcess';
|
||||||
import * as sinon from 'sinon';
|
// import * as sinon from 'sinon';
|
||||||
import * as should from 'should';
|
// import * as vscode from 'vscode';
|
||||||
import * as vscode from 'vscode';
|
// import * as TypeMoq from 'typemoq';
|
||||||
import * as TypeMoq from 'typemoq';
|
// import { getExtensionApi } from '../api';
|
||||||
import { getExtensionApi, throwIfNoAzdataOrEulaNotAccepted } from '../api';
|
// import { AzToolService } from '../services/azToolService';
|
||||||
import { AzdataToolService } from '../services/azdataToolService';
|
// import { assertRejected } from './testUtils';
|
||||||
import { assertRejected } from './testUtils';
|
// import { AzTool } from '../azdata';
|
||||||
import { AzdataTool, IAzdataTool, AzdataDeployOption } from '../azdata';
|
|
||||||
|
|
||||||
describe('api', function (): void {
|
// describe('api', function (): void {
|
||||||
afterEach(function (): void {
|
// afterEach(function (): void {
|
||||||
sinon.restore();
|
// sinon.restore();
|
||||||
});
|
// });
|
||||||
describe('throwIfNoAzdataOrEulaNotAccepted', function (): void {
|
// describe('getExtensionApi', function (): void {
|
||||||
it('throws when no azdata', function (): void {
|
// it('throws when no az', async function (): Promise<void> {
|
||||||
should(() => throwIfNoAzdataOrEulaNotAccepted(undefined, false)).throw();
|
// const azToolService = new AzToolService();
|
||||||
});
|
// const api = getExtensionApi(azToolService);
|
||||||
it('throws when EULA not accepted', function (): void {
|
// await assertApiCalls(api, assertRejected);
|
||||||
should(() => throwIfNoAzdataOrEulaNotAccepted(TypeMoq.Mock.ofType<IAzdataTool>().object, false)).throw();
|
// });
|
||||||
});
|
|
||||||
it('passes with AzdataTool and EULA accepted', function (): void {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted(TypeMoq.Mock.ofType<IAzdataTool>().object, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('getExtensionApi', function (): void {
|
|
||||||
it('throws when no azdata', async function (): Promise<void> {
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const azdataToolService = new AzdataToolService();
|
|
||||||
const api = getExtensionApi(mementoMock.object, azdataToolService, Promise.resolve(undefined));
|
|
||||||
await assertRejected(api.isEulaAccepted(), 'isEulaAccepted');
|
|
||||||
await assertApiCalls(api, assertRejected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws when EULA not accepted', async function (): Promise<void> {
|
// it('succeed when az present and EULA accepted', async function (): Promise<void> {
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
// const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
||||||
mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => false);
|
// mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => true);
|
||||||
const azdataToolService = new AzdataToolService();
|
// const azTool = new AzTool('', '99.0.0');
|
||||||
// Not using a mock here because it'll hang when resolving mocked objects
|
// const azToolService = new AzToolService();
|
||||||
const api = getExtensionApi(mementoMock.object, azdataToolService, Promise.resolve(new AzdataTool('', '1.0.0')));
|
// azToolService.localAz = azTool;
|
||||||
should(await api.isEulaAccepted()).be.false('EULA should not be accepted');
|
// // Not using a mock here because it'll hang when resolving mocked objects
|
||||||
await assertApiCalls(api, assertRejected);
|
// const api = getExtensionApi(azToolService);
|
||||||
});
|
// sinon.stub(childProcess, 'executeCommand').callsFake(async (_command, args) => {
|
||||||
|
// // Version needs to be valid so it can be parsed correctly
|
||||||
|
// if (args[0] === '--version') {
|
||||||
|
// return { stdout: `99.0.0`, stderr: '' };
|
||||||
|
// }
|
||||||
|
// console.log(args[0]);
|
||||||
|
// return { stdout: `{ }`, stderr: '' };
|
||||||
|
// });
|
||||||
|
// await assertApiCalls(api, async (promise, message) => {
|
||||||
|
// try {
|
||||||
|
// await promise;
|
||||||
|
// } catch (err) {
|
||||||
|
// throw new Error(`API call to ${message} should have succeeded. ${err}`);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
it('succeed when azdata present and EULA accepted', async function (): Promise<void> {
|
// /**
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
// * Asserts that calls to the Az API behave as expected
|
||||||
mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => true);
|
// * @param api The API object to test the calls with
|
||||||
const azdataTool = new AzdataTool('', '99.0.0');
|
// * @param assertCallback The function to assert that the results are as expected
|
||||||
const azdataToolService = new AzdataToolService();
|
// */
|
||||||
azdataToolService.localAzdata = azdataTool;
|
// async function assertApiCalls(api: azExt.IExtension, assertCallback: (promise: Promise<any>, message: string) => Promise<void>): Promise<void> {
|
||||||
// Not using a mock here because it'll hang when resolving mocked objects
|
// await assertCallback(api.az.getPath(), 'getPath');
|
||||||
const api = getExtensionApi(mementoMock.object, azdataToolService, Promise.resolve(azdataTool));
|
// await assertCallback(api.az.getSemVersion(), 'getSemVersion');
|
||||||
should(await api.isEulaAccepted()).be.true('EULA should be accepted');
|
// await assertCallback(api.az.version(), 'version');
|
||||||
sinon.stub(childProcess, 'executeCommand').callsFake(async (_command, args) => {
|
|
||||||
// Version needs to be valid so it can be parsed correctly
|
|
||||||
if (args[0] === '--version') {
|
|
||||||
return { stdout: `99.0.0`, stderr: '' };
|
|
||||||
}
|
|
||||||
console.log(args[0]);
|
|
||||||
return { stdout: `{ }`, stderr: '' };
|
|
||||||
});
|
|
||||||
await assertApiCalls(api, async (promise, message) => {
|
|
||||||
try {
|
|
||||||
await promise;
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`API call to ${message} should have succeeded. ${err}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('promptForEula', async function (): Promise<void> {
|
// await assertCallback(api.az.arcdata.dc.config.list(), 'arc dc config list');
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
// await assertCallback(api.az.arcdata.dc.config.show(), 'arc dc config show');
|
||||||
mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => true);
|
|
||||||
const azdataToolService = new AzdataToolService();
|
|
||||||
// Not using a mock here because it'll hang when resolving mocked objects
|
|
||||||
const api = getExtensionApi(mementoMock.object, azdataToolService, Promise.resolve(new AzdataTool('', '1.0.0')));
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => AzdataDeployOption.dontPrompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const showErrorMessageStub = sinon.stub(vscode.window, 'showErrorMessage');
|
|
||||||
should(await api.promptForEula()).be.false();
|
|
||||||
should(showErrorMessageStub.called).be.true('User should have been prompted to accept');
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
// await assertCallback(api.az.arcdata.dc.endpoint.list(), 'arc dc endpoint list');
|
||||||
* Asserts that calls to the Azdata API behave as expected
|
|
||||||
* @param api The API object to test the calls with
|
|
||||||
* @param assertCallback The function to assert that the results are as expected
|
|
||||||
*/
|
|
||||||
async function assertApiCalls(api: azdataExt.IExtension, assertCallback: (promise: Promise<any>, message: string) => Promise<void>): Promise<void> {
|
|
||||||
await assertCallback(api.azdata.getPath(), 'getPath');
|
|
||||||
await assertCallback(api.azdata.getSemVersion(), 'getSemVersion');
|
|
||||||
await assertCallback(api.azdata.login({ endpoint: 'https://127.0.0.1' }, '', ''), 'login');
|
|
||||||
await assertCallback(api.azdata.login({ namespace: 'namespace' }, '', ''), 'login');
|
|
||||||
await assertCallback(api.azdata.version(), 'version');
|
|
||||||
|
|
||||||
await assertCallback(api.azdata.arc.dc.create('', '', '', '', '', ''), 'arc dc create');
|
|
||||||
|
|
||||||
await assertCallback(api.azdata.arc.dc.config.list(), 'arc dc config list');
|
|
||||||
await assertCallback(api.azdata.arc.dc.config.show(), 'arc dc config show');
|
|
||||||
|
|
||||||
await assertCallback(api.azdata.arc.dc.endpoint.list(), 'arc dc endpoint list');
|
|
||||||
|
|
||||||
await assertCallback(api.azdata.arc.sql.mi.list(), 'arc sql mi list');
|
|
||||||
await assertCallback(api.azdata.arc.sql.mi.delete(''), 'arc sql mi delete');
|
|
||||||
await assertCallback(api.azdata.arc.sql.mi.show(''), 'arc sql mi show');
|
|
||||||
await assertCallback(api.azdata.arc.sql.mi.edit('', {}), 'arc sql mi edit');
|
|
||||||
await assertCallback(api.azdata.arc.postgres.server.list(), 'arc sql postgres server list');
|
|
||||||
await assertCallback(api.azdata.arc.postgres.server.delete(''), 'arc sql postgres server delete');
|
|
||||||
await assertCallback(api.azdata.arc.postgres.server.show(''), 'arc sql postgres server show');
|
|
||||||
await assertCallback(api.azdata.arc.postgres.server.edit('', {}), 'arc sql postgres server edit');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// await assertCallback(api.az.sql.miarc.list(), 'arc sql mi list');
|
||||||
|
// await assertCallback(api.az.sql.miarc.delete(''), 'arc sql mi delete');
|
||||||
|
// await assertCallback(api.az.sql.miarc.show(''), 'arc sql mi show');
|
||||||
|
// await assertCallback(api.az.sql.miarc.edit('', {}), 'arc sql mi edit');
|
||||||
|
// await assertCallback(api.az.postgres.arcserver.list(), 'arc sql postgres server list');
|
||||||
|
// await assertCallback(api.az.postgres.arcserver.delete(''), 'arc sql postgres server delete');
|
||||||
|
// await assertCallback(api.az.postgres.arcserver.show(''), 'arc sql postgres server show');
|
||||||
|
// await assertCallback(api.az.postgres.arcserver.edit('', {}), 'arc sql postgres server edit');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|||||||
@@ -5,228 +5,128 @@
|
|||||||
|
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdata from '../azdata';
|
|
||||||
import * as childProcess from '../common/childProcess';
|
import * as childProcess from '../common/childProcess';
|
||||||
import { HttpClient } from '../common/httpClient';
|
import * as azdata from '../az';
|
||||||
import * as utils from '../common/utils';
|
|
||||||
import * as loc from '../localizedConstants';
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import { AzdataReleaseInfo } from '../azdataReleaseInfo';
|
|
||||||
import * as TypeMoq from 'typemoq';
|
|
||||||
import { eulaAccepted } from '../constants';
|
|
||||||
|
|
||||||
const oldAzdataMock = new azdata.AzdataTool('/path/to/azdata', azdata.MIN_AZDATA_VERSION.raw);
|
describe('az', function () {
|
||||||
const currentAzdataMock = new azdata.AzdataTool('/path/to/azdata', '9999.999.999');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This matches the schema of the JSON file used to determine the current version of
|
|
||||||
* azdata - do not modify unless also updating the corresponding JSON file
|
|
||||||
*/
|
|
||||||
const releaseJson: AzdataReleaseInfo = {
|
|
||||||
win32: {
|
|
||||||
'version': '9999.999.999',
|
|
||||||
'link': 'https://download.com/azdata-20.0.1.msi'
|
|
||||||
},
|
|
||||||
darwin: {
|
|
||||||
'version': '9999.999.999'
|
|
||||||
},
|
|
||||||
linux: {
|
|
||||||
'version': '9999.999.999'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let executeSudoCommandStub: sinon.SinonStub;
|
|
||||||
|
|
||||||
describe('azdata', function () {
|
|
||||||
afterEach(function (): void {
|
afterEach(function (): void {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
});
|
});
|
||||||
describe('azdataTool', function (): void {
|
describe('azTool', function (): void {
|
||||||
const azdataTool = new azdata.AzdataTool(os.tmpdir(), '1.0.0');
|
const azTool = new azdata.AzTool('C:/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin/az.cmd', '2.26.0');
|
||||||
let executeCommandStub: sinon.SinonStub;
|
let executeCommandStub: sinon.SinonStub;
|
||||||
const namespace = 'myNamespace';
|
const namespace = 'arc4';
|
||||||
const name = 'myName';
|
const name = 'cy-dc-4';
|
||||||
const connectivityMode = 'myConnectivityMode';
|
|
||||||
const resourceGroup = 'myResourceGroup';
|
|
||||||
const location = 'myLocation';
|
|
||||||
const subscription = 'mySubscription';
|
|
||||||
const profileName = 'myProfileName';
|
|
||||||
const storageClass = 'myStorageClass';
|
|
||||||
|
|
||||||
beforeEach(function (): void {
|
beforeEach(function (): void {
|
||||||
executeCommandStub = sinon.stub(childProcess, 'executeCommand').resolves({ stdout: '{}', stderr: '' });
|
executeCommandStub = sinon.stub(childProcess, 'executeCommand').resolves({ stdout: '{}', stderr: '' });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('arc', function (): void {
|
describe('arcdata', function (): void {
|
||||||
describe('dc', function (): void {
|
describe('dc', function (): void {
|
||||||
it('create', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.dc.create(namespace, name, connectivityMode, resourceGroup, location, subscription, profileName, storageClass);
|
|
||||||
verifyExecuteCommandCalledWithArgs([
|
|
||||||
'arc', 'dc', 'create',
|
|
||||||
namespace,
|
|
||||||
name,
|
|
||||||
connectivityMode,
|
|
||||||
resourceGroup,
|
|
||||||
location,
|
|
||||||
subscription,
|
|
||||||
profileName,
|
|
||||||
storageClass]);
|
|
||||||
});
|
|
||||||
describe('endpoint', async function (): Promise<void> {
|
describe('endpoint', async function (): Promise<void> {
|
||||||
it('list', async function (): Promise<void> {
|
it('list', async function (): Promise<void> {
|
||||||
await azdataTool.arc.dc.endpoint.list();
|
await azTool.arcdata.dc.endpoint.list(namespace);
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'endpoint', 'list']);
|
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'endpoint', 'list', '--k8s-namespace', namespace, '--use-k8s']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('config', async function (): Promise<void> {
|
describe('config', async function (): Promise<void> {
|
||||||
it('list', async function (): Promise<void> {
|
it('list', async function (): Promise<void> {
|
||||||
await azdataTool.arc.dc.config.list();
|
await azTool.arcdata.dc.config.list();
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'list']);
|
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'config', 'list']);
|
||||||
});
|
});
|
||||||
it('show', async function (): Promise<void> {
|
it('show', async function (): Promise<void> {
|
||||||
await azdataTool.arc.dc.config.show();
|
await azTool.arcdata.dc.config.show(namespace);
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'dc', 'config', 'show']);
|
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'config', 'show', '--k8s-namespace', namespace, '--use-k8s']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('postgres', function (): void {
|
|
||||||
describe('server', function (): void {
|
|
||||||
it('delete', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.postgres.server.delete(name);
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'postgres', 'server', 'delete', name]);
|
|
||||||
});
|
|
||||||
it('list', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.postgres.server.list();
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'postgres', 'server', 'list']);
|
|
||||||
});
|
|
||||||
it('show', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.postgres.server.show(name);
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'postgres', 'server', 'show', name]);
|
|
||||||
});
|
|
||||||
it('edit', async function (): Promise<void> {
|
|
||||||
const args = {
|
|
||||||
adminPassword: true,
|
|
||||||
coresLimit: 'myCoresLimit',
|
|
||||||
coresRequest: 'myCoresRequest',
|
|
||||||
engineSettings: 'myEngineSettings',
|
|
||||||
extensions: 'myExtensions',
|
|
||||||
memoryLimit: 'myMemoryLimit',
|
|
||||||
memoryRequest: 'myMemoryRequest',
|
|
||||||
noWait: true,
|
|
||||||
port: 1337,
|
|
||||||
replaceEngineSettings: true,
|
|
||||||
workers: 2
|
|
||||||
};
|
|
||||||
await azdataTool.arc.postgres.server.edit(name, args);
|
|
||||||
verifyExecuteCommandCalledWithArgs([
|
|
||||||
'arc', 'postgres', 'server', 'edit',
|
|
||||||
name,
|
|
||||||
'--admin-password',
|
|
||||||
args.coresLimit,
|
|
||||||
args.coresRequest,
|
|
||||||
args.engineSettings,
|
|
||||||
args.extensions,
|
|
||||||
args.memoryLimit,
|
|
||||||
args.memoryRequest,
|
|
||||||
'--no-wait',
|
|
||||||
args.port.toString(),
|
|
||||||
'--replace-engine-settings',
|
|
||||||
args.workers.toString()]);
|
|
||||||
});
|
|
||||||
it('edit no optional args', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.postgres.server.edit(name, {});
|
|
||||||
verifyExecuteCommandCalledWithArgs([
|
|
||||||
'arc', 'postgres', 'server', 'edit',
|
|
||||||
name]);
|
|
||||||
verifyExecuteCommandCalledWithoutArgs([
|
|
||||||
'--admin-password',
|
|
||||||
'--cores-limit',
|
|
||||||
'--cores-request',
|
|
||||||
'--engine-settings',
|
|
||||||
'--extensions',
|
|
||||||
'--memory-limit',
|
|
||||||
'--memory-request',
|
|
||||||
'--no-wait',
|
|
||||||
'--port',
|
|
||||||
'--replace-engine-settings',
|
|
||||||
'--workers']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('sql', function (): void {
|
|
||||||
describe('mi', function (): void {
|
|
||||||
it('delete', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.sql.mi.delete(name);
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'delete', name]);
|
|
||||||
});
|
|
||||||
it('list', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.sql.mi.list();
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'list']);
|
|
||||||
});
|
|
||||||
it('show', async function (): Promise<void> {
|
|
||||||
await azdataTool.arc.sql.mi.show(name);
|
|
||||||
verifyExecuteCommandCalledWithArgs(['arc', 'sql', 'mi', 'show', name]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('general error throws', async function (): Promise<void> {
|
|
||||||
const err = new Error();
|
|
||||||
executeCommandStub.throws(err);
|
|
||||||
try {
|
|
||||||
await azdataTool.arc.dc.endpoint.list();
|
|
||||||
throw new Error('command should have failed');
|
|
||||||
} catch (error) {
|
|
||||||
should(error).equal(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
it('ExitCodeError handled and parsed correctly', async function (): Promise<void> {
|
|
||||||
const errorInnerText = 'my error text';
|
|
||||||
const err = new childProcess.ExitCodeError(1, `ERROR { "stderr": "${errorInnerText}"}`);
|
|
||||||
executeCommandStub.throws(err);
|
|
||||||
try {
|
|
||||||
await azdataTool.arc.dc.endpoint.list();
|
|
||||||
throw new Error('command should have failed');
|
|
||||||
} catch (error) {
|
|
||||||
should(error).equal(err);
|
|
||||||
should((error as childProcess.ExitCodeError).stderr).equal(errorInnerText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
it('ExitCodeError general error with azdata tool existing rethrows original error', async function (): Promise<void> {
|
|
||||||
sinon.stub(fs.promises, 'access').resolves();
|
|
||||||
const err = new childProcess.ExitCodeError(1, 'some other error');
|
|
||||||
executeCommandStub.throws(err);
|
|
||||||
try {
|
|
||||||
await azdataTool.arc.dc.endpoint.list();
|
|
||||||
throw new Error('command should have failed');
|
|
||||||
} catch (error) {
|
|
||||||
should(error).equal(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
it('ExitCodeError general error with azdata tool not existing throws NoAzdataError', async function (): Promise<void> {
|
|
||||||
sinon.stub(fs.promises, 'access').throws(new Error('not found'));
|
|
||||||
const err = new childProcess.ExitCodeError(1, 'some other error');
|
|
||||||
executeCommandStub.throws(err);
|
|
||||||
try {
|
|
||||||
await azdataTool.arc.dc.endpoint.list();
|
|
||||||
throw new Error('command should have failed');
|
|
||||||
} catch (error) {
|
|
||||||
should(error instanceof utils.NoAzdataError).be.true('error should have been instance of NoAzdataError');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('login', async function (): Promise<void> {
|
describe('postgres', function (): void {
|
||||||
const endpoint = 'myEndpoint';
|
describe('arc-server', function (): void {
|
||||||
const username = 'myUsername';
|
it('delete', async function (): Promise<void> {
|
||||||
const password = 'myPassword';
|
await azTool.postgres.arcserver.delete(name, namespace);
|
||||||
await azdataTool.login({ endpoint: endpoint }, username, password);
|
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'delete', name, '--k8s-namespace', namespace]);
|
||||||
verifyExecuteCommandCalledWithArgs(['login', endpoint, username]);
|
});
|
||||||
|
it('list', async function (): Promise<void> {
|
||||||
|
await azTool.postgres.arcserver.list(namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'list', '--k8s-namespace', namespace]);
|
||||||
|
});
|
||||||
|
it('show', async function (): Promise<void> {
|
||||||
|
await azTool.postgres.arcserver.show(name, namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'show', name, '--k8s-namespace', namespace]);
|
||||||
|
});
|
||||||
|
it('edit', async function (): Promise<void> {
|
||||||
|
const args = {
|
||||||
|
adminPassword: true,
|
||||||
|
coresLimit: 'myCoresLimit',
|
||||||
|
coresRequest: 'myCoresRequest',
|
||||||
|
engineSettings: 'myEngineSettings',
|
||||||
|
extensions: 'myExtensions',
|
||||||
|
memoryLimit: 'myMemoryLimit',
|
||||||
|
memoryRequest: 'myMemoryRequest',
|
||||||
|
noWait: true,
|
||||||
|
port: 1337,
|
||||||
|
replaceEngineSettings: true,
|
||||||
|
workers: 2
|
||||||
|
};
|
||||||
|
await azTool.postgres.arcserver.edit(name, args, namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs([
|
||||||
|
'postgres', 'arc-server', 'edit',
|
||||||
|
name,
|
||||||
|
'--admin-password',
|
||||||
|
args.coresLimit,
|
||||||
|
args.coresRequest,
|
||||||
|
args.engineSettings,
|
||||||
|
args.extensions,
|
||||||
|
args.memoryLimit,
|
||||||
|
args.memoryRequest,
|
||||||
|
'--no-wait',
|
||||||
|
args.port.toString(),
|
||||||
|
'--replace-engine-settings',
|
||||||
|
args.workers.toString()]);
|
||||||
|
});
|
||||||
|
it('edit no optional args', async function (): Promise<void> {
|
||||||
|
await azTool.postgres.arcserver.edit(name, {}, namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs([
|
||||||
|
'postgres', 'arc-server', 'edit',
|
||||||
|
name]);
|
||||||
|
verifyExecuteCommandCalledWithoutArgs([
|
||||||
|
'--admin-password',
|
||||||
|
'--cores-limit',
|
||||||
|
'--cores-request',
|
||||||
|
'--engine-settings',
|
||||||
|
'--extensions',
|
||||||
|
'--memory-limit',
|
||||||
|
'--memory-request',
|
||||||
|
'--no-wait',
|
||||||
|
'--port',
|
||||||
|
'--replace-engine-settings',
|
||||||
|
'--workers']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('sql', function (): void {
|
||||||
|
describe('mi-arc', function (): void {
|
||||||
|
it('delete', async function (): Promise<void> {
|
||||||
|
await azTool.sql.miarc.delete(name, namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'delete', name, '--k8s-namespace', namespace, '--use-k8s']);
|
||||||
|
});
|
||||||
|
it('list', async function (): Promise<void> {
|
||||||
|
await azTool.sql.miarc.list(namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'list', '--k8s-namespace', namespace, '--use-k8s']);
|
||||||
|
});
|
||||||
|
it('show', async function (): Promise<void> {
|
||||||
|
await azTool.sql.miarc.show(name, namespace);
|
||||||
|
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'show', name, '--k8s-namespace', namespace, '--use-k8s']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('version', async function (): Promise<void> {
|
it('version', async function (): Promise<void> {
|
||||||
executeCommandStub.resolves({ stdout: '1.0.0', stderr: '' });
|
executeCommandStub.resolves({ stdout: '1.0.0', stderr: '' });
|
||||||
await azdataTool.version();
|
await azTool.version();
|
||||||
verifyExecuteCommandCalledWithArgs(['--version']);
|
verifyExecuteCommandCalledWithArgs(['--version']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -249,557 +149,4 @@ describe('azdata', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findAzdata', function (): void {
|
|
||||||
it('successful', async function (): Promise<void> {
|
|
||||||
// Mock searchForCmd to return a path to azdata.cmd
|
|
||||||
sinon.stub(utils, 'searchForCmd').returns(Promise.resolve('/path/to/azdata'));
|
|
||||||
// Mock call to --version to simulate azdata being installed
|
|
||||||
sinon.stub(childProcess, 'executeCommand').returns(Promise.resolve({ stdout: '1.0.0', stderr: '' }));
|
|
||||||
await should(azdata.findAzdata()).not.be.rejected();
|
|
||||||
});
|
|
||||||
it('unsuccessful', async function (): Promise<void> {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
// Mock searchForCmd to return a failure to find azdata.cmd
|
|
||||||
sinon.stub(utils, 'searchForCmd').returns(Promise.reject(new Error('Could not find azdata')));
|
|
||||||
} else {
|
|
||||||
// Mock call to executeCommand to simulate azdata --version returning error
|
|
||||||
sinon.stub(childProcess, 'executeCommand').returns(Promise.reject({ stdout: '', stderr: 'command not found: azdata' }));
|
|
||||||
}
|
|
||||||
await should(azdata.findAzdata()).be.rejected();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('installAzdata', function (): void {
|
|
||||||
|
|
||||||
let errorMessageStub: sinon.SinonStub;
|
|
||||||
beforeEach(function (): void {
|
|
||||||
errorMessageStub = sinon.stub(vscode.window, 'showErrorMessage').returns(Promise.resolve(<any>loc.yes));
|
|
||||||
sinon.stub(utils, 'searchForCmd').returns(Promise.resolve('/path/to/azdata'));
|
|
||||||
executeSudoCommandStub = sinon.stub(childProcess, 'executeSudoCommand').returns(Promise.resolve({ stdout: '', stderr: '' }));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successful install', async function (): Promise<void> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SuccessfulInstall();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSuccessfulInstall();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSuccessfulInstall();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped install - dont prompt config', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => 'dontPrompt');
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SkippedInstall();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSkippedInstall();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSkippedInstall();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped install - user chose not to prompt', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
errorMessageStub.resolves(<any>loc.doNotAskAgain);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SkippedInstall();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSkippedInstall();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSkippedInstall();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
configMock.verify(x => x.update(TypeMoq.It.isAny(), azdata.AzdataDeployOption.dontPrompt, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
it('unsuccessful download - win32', async function (): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').rejects();
|
|
||||||
sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.rejects(new Error('not Found')) // First call mock the tool not being found
|
|
||||||
.resolves({ stdout: '1.0.0', stderr: '' });
|
|
||||||
const azdataTool = await azdata.checkAndInstallAzdata();
|
|
||||||
should(azdataTool).be.undefined();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('unsuccessful install', async function (): Promise<void> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32UnsuccessfulInstall();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinUnsuccessfulInstall();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxUnsuccessfulInstall();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('updateAzdata', function (): void {
|
|
||||||
let showInformationMessageStub: sinon.SinonStub;
|
|
||||||
|
|
||||||
beforeEach(function (): void {
|
|
||||||
showInformationMessageStub = sinon.stub(vscode.window, 'showInformationMessage').returns(Promise.resolve(<any>loc.yes));
|
|
||||||
executeSudoCommandStub = sinon.stub(childProcess, 'executeSudoCommand').returns(Promise.resolve({ stdout: '', stderr: '' }));
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves(JSON.stringify(releaseJson));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successful update', async function (): Promise<void> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SuccessfulUpdate();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSuccessfulUpdate();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSuccessfulUpdate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successful update - always prompt if user requested', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SuccessfulUpdate(true);
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSuccessfulUpdate(true);
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSuccessfulUpdate(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped update - config set not to prompt', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped update - user chose to never prompt again', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
showInformationMessageStub.resolves(<any>loc.doNotAskAgain);
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSkippedUpdateDontPrompt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Config should have been updated since user chose never to prompt again
|
|
||||||
configMock.verify(x => x.update(TypeMoq.It.isAny(), azdata.AzdataDeployOption.dontPrompt, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped update - no new version', async function (): Promise<void> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32SkippedUpdate();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinSkippedUpdate();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxSkippedUpdate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skipped update - no azdata', async function (): Promise<void> {
|
|
||||||
const result = await azdata.checkAndUpdateAzdata();
|
|
||||||
should(result).be.false();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unsuccessful update', async function (): Promise<void> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
await testWin32UnsuccessfulUpdate();
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
await testDarwinUnsuccessfulUpdate();
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
await testLinuxUnsuccessfulUpdate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('discoverLatestAvailableAzdataVersion', function (): void {
|
|
||||||
it('finds latest available version of azdata successfully', async function (): Promise<void> {
|
|
||||||
await azdata.discoverLatestAvailableAzdataVersion();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('promptForEula', function (): void {
|
|
||||||
it('skipped because of config', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object);
|
|
||||||
should(result).be.false();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('always prompt if user requested', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.dontPrompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage');
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object, true);
|
|
||||||
should(result).be.false();
|
|
||||||
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('prompt if config set to do so', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage');
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object);
|
|
||||||
should(result).be.false();
|
|
||||||
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('update config if user chooses not to prompt', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage').resolves(<any>loc.doNotAskAgain);
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object);
|
|
||||||
configMock.verify(x => x.update(TypeMoq.It.isAny(), azdata.AzdataDeployOption.dontPrompt, TypeMoq.It.isAny()), TypeMoq.Times.once());
|
|
||||||
should(result).be.false('EULA should not have been accepted');
|
|
||||||
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('user accepted EULA', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const showInformationMessage = sinon.stub(vscode.window, 'showInformationMessage').resolves(<any>loc.accept);
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object);
|
|
||||||
mementoMock.verify(x => x.update(eulaAccepted, true), TypeMoq.Times.once());
|
|
||||||
should(result).be.true('EULA should have been accepted');
|
|
||||||
should(showInformationMessage.calledOnce).be.true('showInformationMessage should have been called to prompt user');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('user accepted EULA - require user action', async function (): Promise<void> {
|
|
||||||
const configMock = TypeMoq.Mock.ofType<vscode.WorkspaceConfiguration>();
|
|
||||||
configMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => azdata.AzdataDeployOption.prompt);
|
|
||||||
sinon.stub(vscode.workspace, 'getConfiguration').returns(configMock.object);
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
const showErrorMessage = sinon.stub(vscode.window, 'showErrorMessage').resolves(<any>loc.accept);
|
|
||||||
const result = await azdata.promptForEula(mementoMock.object, true, true);
|
|
||||||
mementoMock.verify(x => x.update(eulaAccepted, true), TypeMoq.Times.once());
|
|
||||||
should(result).be.true('EULA should have been accepted');
|
|
||||||
should(showErrorMessage.calledOnce).be.true('showErrorMessage should have been called to prompt user');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isEulaAccepted', function (): void {
|
|
||||||
const mementoMock = TypeMoq.Mock.ofType<vscode.Memento>();
|
|
||||||
mementoMock.setup(x => x.get(TypeMoq.It.isAny())).returns(() => true);
|
|
||||||
should(azdata.isEulaAccepted(mementoMock.object)).be.true();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function testLinuxUnsuccessfulUpdate() {
|
|
||||||
executeSudoCommandStub.rejects();
|
|
||||||
const updateDone = await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(updateDone).be.false();
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinUnsuccessfulUpdate() {
|
|
||||||
const brewInfoOutput = [{
|
|
||||||
name: 'azdata-cli',
|
|
||||||
full_name: 'microsoft/azdata-cli-release/azdata-cli',
|
|
||||||
versions: {
|
|
||||||
'stable': '9999.999.999',
|
|
||||||
'devel': null,
|
|
||||||
'head': null,
|
|
||||||
'bottle': true
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onThirdCall() //third call is brew info azdata-cli --json which needs to return json of new available azdata versions.
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.resolve({
|
|
||||||
stderr: '',
|
|
||||||
stdout: JSON.stringify(brewInfoOutput)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.onCall(5) //6th call is the first one to do actual update, the call number are 0 indexed
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.reject(new Error('not Found'));
|
|
||||||
})
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => { // by default return success
|
|
||||||
return Promise.resolve({ stderr: '', stdout: 'success' });
|
|
||||||
});
|
|
||||||
const updateDone = await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(updateDone).be.false();
|
|
||||||
should(executeCommandStub.callCount).equal(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32UnsuccessfulUpdate() {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
executeSudoCommandStub.rejects();
|
|
||||||
const updateDone = await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(updateDone).be.false('Update should not have been successful');
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxSuccessfulUpdate(userRequested = false) {
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand').returns(Promise.resolve({ stdout: '0.0.0', stderr: '' }));
|
|
||||||
executeSudoCommandStub.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock, userRequested);
|
|
||||||
should(executeSudoCommandStub.callCount).be.equal(6);
|
|
||||||
should(executeCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinSuccessfulUpdate(userRequested = false) {
|
|
||||||
const brewInfoOutput = [{
|
|
||||||
name: 'azdata-cli',
|
|
||||||
full_name: 'microsoft/azdata-cli-release/azdata-cli',
|
|
||||||
versions: {
|
|
||||||
'stable': '9999.999.999',
|
|
||||||
'devel': null,
|
|
||||||
'head': null,
|
|
||||||
'bottle': true
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onThirdCall() //third call is brew info azdata-cli --json which needs to return json of new available azdata versions.
|
|
||||||
.resolves({
|
|
||||||
stderr: '',
|
|
||||||
stdout: JSON.stringify(brewInfoOutput)
|
|
||||||
})
|
|
||||||
.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock, userRequested);
|
|
||||||
should(executeCommandStub.callCount).be.equal(6);
|
|
||||||
should(executeCommandStub.getCall(2).args[0]).be.equal('brew', '3rd call should have been to brew');
|
|
||||||
should(executeCommandStub.getCall(2).args[1]).deepEqual(['info', 'azdata-cli', '--json'], '3rd call did not have expected arguments');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function testWin32SuccessfulUpdate(userRequested = false) {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock, userRequested);
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true('executeSudoCommand should have been called once');
|
|
||||||
should(executeSudoCommandStub.getCall(0).args[0]).startWith('msiexec /qn /i');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxSkippedUpdate() {
|
|
||||||
executeSudoCommandStub.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(currentAzdataMock);
|
|
||||||
should(executeSudoCommandStub.callCount).be.equal(0, 'executeSudoCommand was not expected to be called');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinSkippedUpdateDontPrompt() {
|
|
||||||
const brewInfoOutput = [{
|
|
||||||
name: 'azdata-cli',
|
|
||||||
full_name: 'microsoft/azdata-cli-release/azdata-cli',
|
|
||||||
versions: {
|
|
||||||
'stable': '9999.999.999',
|
|
||||||
'devel': null,
|
|
||||||
'head': null,
|
|
||||||
'bottle': true
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onThirdCall() //third call is brew info azdata-cli --json which needs to return json of new available azdata versions.
|
|
||||||
.resolves({
|
|
||||||
stderr: '',
|
|
||||||
stdout: JSON.stringify(brewInfoOutput)
|
|
||||||
})
|
|
||||||
.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(executeCommandStub.callCount).be.equal(6);
|
|
||||||
should(executeCommandStub.notCalledWith(sinon.match.any, sinon.match.array.contains(['upgrade', 'azdata-cli'])));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32SkippedUpdateDontPrompt() {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(executeSudoCommandStub.notCalled).be.true(`executeSudoCommand should not have been called ${executeSudoCommandStub.getCalls().join(os.EOL)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxSkippedUpdateDontPrompt() {
|
|
||||||
sinon.stub(childProcess, 'executeCommand').returns(Promise.resolve({ stdout: '0.0.0', stderr: '' }));
|
|
||||||
executeSudoCommandStub.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(oldAzdataMock);
|
|
||||||
should(executeSudoCommandStub.callCount).be.equal(0, 'executeSudoCommand was not expected to be called');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinSkippedUpdate() {
|
|
||||||
const brewInfoOutput = [{
|
|
||||||
name: 'azdata-cli',
|
|
||||||
full_name: 'microsoft/azdata-cli-release/azdata-cli',
|
|
||||||
versions: {
|
|
||||||
'stable': '9999.999.999',
|
|
||||||
'devel': null,
|
|
||||||
'head': null,
|
|
||||||
'bottle': true
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onThirdCall() //third call is brew info azdata-cli --json which needs to return json of new available azdata versions.
|
|
||||||
.resolves({
|
|
||||||
stderr: '',
|
|
||||||
stdout: JSON.stringify(brewInfoOutput)
|
|
||||||
})
|
|
||||||
.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
await azdata.checkAndUpdateAzdata(currentAzdataMock);
|
|
||||||
should(executeCommandStub.callCount).be.equal(6);
|
|
||||||
should(executeCommandStub.notCalledWith(sinon.match.any, sinon.match.array.contains(['upgrade', 'azdata-cli'])));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32SkippedUpdate() {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
await azdata.checkAndUpdateAzdata(currentAzdataMock);
|
|
||||||
should(executeSudoCommandStub.notCalled).be.true('executeSudoCommand should not have been called');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinSkippedInstall() {
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.reject(new Error('not Found'));
|
|
||||||
})
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.resolve({ stdout: '0.0.0', stderr: '' });
|
|
||||||
});
|
|
||||||
const result = await azdata.checkAndInstallAzdata();
|
|
||||||
should(result).equal(undefined, 'result should be undefined');
|
|
||||||
should(executeCommandStub.callCount).be.equal(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxSkippedInstall() {
|
|
||||||
sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.rejects(new Error('not Found'))
|
|
||||||
.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
executeSudoCommandStub
|
|
||||||
.resolves({ stdout: 'success', stderr: '' });
|
|
||||||
const result = await azdata.checkAndInstallAzdata();
|
|
||||||
should(result).equal(undefined, 'result should be undefined');
|
|
||||||
should(executeSudoCommandStub.callCount).be.equal(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32SkippedInstall() {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.rejects(new Error('not Found')) // First call mock the tool not being found
|
|
||||||
.resolves({ stdout: '1.0.0', stderr: '' });
|
|
||||||
executeSudoCommandStub
|
|
||||||
.returns({ stdout: '', stderr: '' });
|
|
||||||
const result = await azdata.checkAndInstallAzdata();
|
|
||||||
should(result).equal(undefined, 'result should be undefined');
|
|
||||||
should(executeSudoCommandStub.notCalled).be.true('executeSudoCommand should not have been called');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32SuccessfulInstall() {
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.rejects(new Error('not Found')) // First call mock the tool not being found
|
|
||||||
.resolves({ stdout: '1.0.0', stderr: '' });
|
|
||||||
executeSudoCommandStub
|
|
||||||
.returns({ stdout: '', stderr: '' });
|
|
||||||
await azdata.checkAndInstallAzdata();
|
|
||||||
should(executeCommandStub.calledTwice).be.true(`executeCommand should have been called twice. Actual ${executeCommandStub.getCalls().length}`);
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true(`executeSudoCommand should have been called once. Actual ${executeSudoCommandStub.getCalls().length}`);
|
|
||||||
should(executeSudoCommandStub.getCall(0).args[0]).startWith('msiexec /qn /i');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinSuccessfulInstall() {
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.reject(new Error('not Found'));
|
|
||||||
})
|
|
||||||
.callsFake(async (_command: string, _args: string[]) => {
|
|
||||||
return Promise.resolve({ stdout: '0.0.0', stderr: '' });
|
|
||||||
});
|
|
||||||
await azdata.checkAndInstallAzdata();
|
|
||||||
should(executeCommandStub.callCount).be.equal(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxSuccessfulInstall() {
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand')
|
|
||||||
.onFirstCall()
|
|
||||||
.rejects(new Error('not Found'))
|
|
||||||
.resolves({ stdout: '0.0.0', stderr: '' });
|
|
||||||
executeSudoCommandStub
|
|
||||||
.resolves({ stdout: 'success', stderr: '' });
|
|
||||||
await azdata.checkAndInstallAzdata();
|
|
||||||
should(executeSudoCommandStub.callCount).be.equal(6);
|
|
||||||
should(executeCommandStub.calledThrice).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testLinuxUnsuccessfulInstall() {
|
|
||||||
executeSudoCommandStub.rejects();
|
|
||||||
const downloadPromise = azdata.installAzdata();
|
|
||||||
await should(downloadPromise).be.rejected();
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testDarwinUnsuccessfulInstall() {
|
|
||||||
const executeCommandStub = sinon.stub(childProcess, 'executeCommand').rejects();
|
|
||||||
const downloadPromise = azdata.installAzdata();
|
|
||||||
await should(downloadPromise).be.rejected();
|
|
||||||
should(executeCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testWin32UnsuccessfulInstall() {
|
|
||||||
executeSudoCommandStub.rejects();
|
|
||||||
sinon.stub(HttpClient, 'downloadFile').returns(Promise.resolve(__filename));
|
|
||||||
const downloadPromise = azdata.installAzdata();
|
|
||||||
await should(downloadPromise).be.rejected();
|
|
||||||
should(executeSudoCommandStub.calledOnce).be.true();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as should from 'should';
|
|
||||||
import * as sinon from 'sinon';
|
|
||||||
import { HttpClient } from '../common/httpClient';
|
|
||||||
import { getPlatformReleaseVersion, getPlatformDownloadLink, AzdataReleaseInfo } from '../azdataReleaseInfo';
|
|
||||||
|
|
||||||
const emptyReleaseJson = {
|
|
||||||
win32: {},
|
|
||||||
darwin: {},
|
|
||||||
linux: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const releaseVersion = '999.999.999';
|
|
||||||
const releaseLink = 'https://microsoft.com';
|
|
||||||
|
|
||||||
const validReleaseJson: AzdataReleaseInfo = {
|
|
||||||
win32: {
|
|
||||||
version: releaseVersion,
|
|
||||||
link: releaseLink
|
|
||||||
},
|
|
||||||
darwin: {
|
|
||||||
version: releaseVersion,
|
|
||||||
link: releaseLink
|
|
||||||
},
|
|
||||||
linux: {
|
|
||||||
version: releaseVersion,
|
|
||||||
link: releaseLink
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('azdataReleaseInfo', function (): void {
|
|
||||||
afterEach(function (): void {
|
|
||||||
sinon.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getPlatformReleaseVersion', function(): void {
|
|
||||||
it('gets version successfully', async function(): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves(JSON.stringify(validReleaseJson));
|
|
||||||
const version = await getPlatformReleaseVersion();
|
|
||||||
should(version.format()).equal(releaseVersion);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid JSON', async function (): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves('invalid JSON');
|
|
||||||
await should(getPlatformReleaseVersion()).be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws when no version', async function (): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves(JSON.stringify(emptyReleaseJson));
|
|
||||||
await should(getPlatformReleaseVersion()).be.rejected();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getPlatformDownloadLink', function(): void {
|
|
||||||
it('gets link successfully', async function(): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves(JSON.stringify(validReleaseJson));
|
|
||||||
const link = await getPlatformDownloadLink();
|
|
||||||
should(link).equal(releaseLink);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid JSON', async function (): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves('invalid JSON');
|
|
||||||
await should(getPlatformDownloadLink()).be.rejected();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws when no version', async function (): Promise<void> {
|
|
||||||
sinon.stub(HttpClient, 'getTextContent').resolves(JSON.stringify(emptyReleaseJson));
|
|
||||||
await should(getPlatformDownloadLink()).be.rejected();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -2,25 +2,17 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import * as should from 'should';
|
// import * as should from 'should';
|
||||||
import { NoAzdataError, searchForCmd as searchForExe } from '../../common/utils';
|
// import { searchForCmd as searchForExe } from '../../common/utils';
|
||||||
|
|
||||||
describe('utils', function () {
|
// describe('utils', function () {
|
||||||
describe('searchForExe', function (): void {
|
// describe('searchForExe', function (): void {
|
||||||
it('finds exe successfully', async function (): Promise<void> {
|
// it('finds exe successfully', async function (): Promise<void> {
|
||||||
await searchForExe('node');
|
// await searchForExe('node');
|
||||||
});
|
// });
|
||||||
it('throws for non-existent exe', async function (): Promise<void> {
|
// it('throws for non-existent exe', async function (): Promise<void> {
|
||||||
await should(searchForExe('someFakeExe')).be.rejected();
|
// await should(searchForExe('someFakeExe')).be.rejected();
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
|
|
||||||
describe('NoAzdataError', function (): void {
|
// });
|
||||||
it('error contains message with and without links', function (): void {
|
|
||||||
const error = new NoAzdataError();
|
|
||||||
should(error.message).not.be.empty();
|
|
||||||
should(error.messageWithLink).not.be.empty();
|
|
||||||
should(error.message).not.equal(error.messageWithLink, 'Messages should not be equal');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
|
||||||
import * as should from 'should';
|
|
||||||
import * as sinon from 'sinon';
|
|
||||||
import { ArcControllerConfigProfilesOptionsSource } from '../../providers/arcControllerConfigProfilesOptionsSource';
|
|
||||||
|
|
||||||
describe('arcControllerConfigProfilesOptionsSource', async function (): Promise<void> {
|
|
||||||
afterEach(function(): void {
|
|
||||||
sinon.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('eula accepted returns list', async function (): Promise<void> {
|
|
||||||
const options = ['option1', 'option2'];
|
|
||||||
const api = vscode.extensions.getExtension(azdataExt.extension.name)?.exports as azdataExt.IExtension;
|
|
||||||
sinon.stub(api, 'isEulaAccepted').resolves(true);
|
|
||||||
sinon.stub(api.azdata.arc.dc.config, 'list').resolves({ stdout: [''], stderr: [''], logs: [''], result: options});
|
|
||||||
const source = new ArcControllerConfigProfilesOptionsSource(api);
|
|
||||||
const result = await source.getOptions();
|
|
||||||
should(result).deepEqual(options);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('eula not accepted prompts for acceptance', async function (): Promise<void> {
|
|
||||||
const options = ['option1', 'option2'];
|
|
||||||
const api = vscode.extensions.getExtension(azdataExt.extension.name)?.exports as azdataExt.IExtension;
|
|
||||||
sinon.stub(api, 'isEulaAccepted').resolves(false);
|
|
||||||
const promptStub = sinon.stub(api, 'promptForEula').resolves(true);
|
|
||||||
sinon.stub(api.azdata.arc.dc.config, 'list').resolves({ stdout: [''], stderr: [''], logs: [''], result: options});
|
|
||||||
const source = new ArcControllerConfigProfilesOptionsSource(api);
|
|
||||||
const result = await source.getOptions();
|
|
||||||
should(result).deepEqual(options);
|
|
||||||
should(promptStub.calledOnce).be.true('promptForEula should have been called');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import { AzdataTool } from '../../azdata';
|
import { AzTool } from '../../az';
|
||||||
import { AzdataToolService } from '../../services/azdataToolService';
|
import { AzToolService } from '../../services/azToolService';
|
||||||
|
|
||||||
describe('azdataToolService', function (): void {
|
describe('azToolService', function (): void {
|
||||||
it('Tool should be set correctly', async function (): Promise<void> {
|
it('Tool should be set correctly', async function (): Promise<void> {
|
||||||
const service = new AzdataToolService();
|
const service = new AzToolService();
|
||||||
should(service.localAzdata).be.undefined();
|
should(service.localAz).be.undefined();
|
||||||
service.localAzdata = new AzdataTool('my path', '1.0.0');
|
service.localAz = new AzTool('my path', '1.0.0');
|
||||||
should(service).not.be.undefined();
|
should(service).not.be.undefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,18 +3,17 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
declare module 'azdata-ext' {
|
declare module 'az-ext' {
|
||||||
import { SemVer } from 'semver';
|
import { SemVer } from 'semver';
|
||||||
import * as vscode from 'vscode';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Covers defining what the azdata extension exports to other extensions
|
* Covers defining what the az extension exports to other extensions
|
||||||
*
|
*
|
||||||
* IMPORTANT: THIS IS NOT A HARD DEFINITION unlike vscode; therefore no enums or classes should be defined here
|
* IMPORTANT: THIS IS NOT A HARD DEFINITION unlike vscode; therefore no enums or classes should be defined here
|
||||||
* (const enums get evaluated when typescript -> javascript so those are fine)
|
* (const enums get evaluated when typescript -> javascript so those are fine)
|
||||||
*/
|
*/
|
||||||
export const enum extension {
|
export const enum extension {
|
||||||
name = 'Microsoft.azdata'
|
name = 'Microsoft.azcli'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdditionalEnvVars = { [key: string]: string };
|
export type AdditionalEnvVars = { [key: string]: string };
|
||||||
@@ -258,11 +257,9 @@ declare module 'azdata-ext' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AzdataOutput<R> {
|
export interface AzOutput<R> {
|
||||||
logs: string[],
|
stdout: R,
|
||||||
result: R,
|
|
||||||
stderr: string[],
|
stderr: string[],
|
||||||
stdout: string[],
|
|
||||||
code?: number
|
code?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,92 +267,75 @@ declare module 'azdata-ext' {
|
|||||||
endpoint?: string,
|
endpoint?: string,
|
||||||
namespace?: string
|
namespace?: string
|
||||||
}
|
}
|
||||||
export interface IAzdataApi {
|
export interface IAzApi {
|
||||||
arc: {
|
arcdata: {
|
||||||
dc: {
|
dc: {
|
||||||
create(namespace: string, name: string, connectivityMode: string, resourceGroup: string, location: string, subscription: string, profileName?: string, storageClass?: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<void>>,
|
|
||||||
endpoint: {
|
endpoint: {
|
||||||
list(additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<DcEndpointListResult[]>>
|
list(namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<DcEndpointListResult[]>>
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
list(additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<DcConfigListResult[]>>,
|
list(additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<DcConfigListResult[]>>,
|
||||||
show(additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<DcConfigShowResult>>
|
show(namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<DcConfigShowResult>>
|
||||||
}
|
|
||||||
},
|
|
||||||
postgres: {
|
|
||||||
server: {
|
|
||||||
delete(name: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<void>>,
|
|
||||||
list(additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<PostgresServerListResult[]>>,
|
|
||||||
show(name: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<PostgresServerShowResult>>,
|
|
||||||
edit(
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
adminPassword?: boolean,
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
coordinatorEngineSettings?: string,
|
|
||||||
engineSettings?: string,
|
|
||||||
extensions?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
port?: number,
|
|
||||||
replaceEngineSettings?: boolean,
|
|
||||||
workerEngineSettings?: string,
|
|
||||||
workers?: number
|
|
||||||
},
|
|
||||||
additionalEnvVars?: AdditionalEnvVars,
|
|
||||||
azdataContext?: string
|
|
||||||
): Promise<AzdataOutput<void>>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sql: {
|
|
||||||
mi: {
|
|
||||||
delete(name: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<void>>,
|
|
||||||
list(additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<SqlMiListResult[]>>,
|
|
||||||
show(name: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<SqlMiShowResult>>,
|
|
||||||
edit(
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
},
|
|
||||||
additionalEnvVars?: AdditionalEnvVars,
|
|
||||||
azdataContext?: string
|
|
||||||
): Promise<AzdataOutput<void>>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
postgres: {
|
||||||
|
arcserver: {
|
||||||
|
delete(name: string, namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<void>>,
|
||||||
|
list(namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<PostgresServerListResult[]>>,
|
||||||
|
show(name: string, namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<PostgresServerShowResult>>,
|
||||||
|
edit(
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
adminPassword?: boolean,
|
||||||
|
coresLimit?: string,
|
||||||
|
coresRequest?: string,
|
||||||
|
coordinatorEngineSettings?: string,
|
||||||
|
engineSettings?: string,
|
||||||
|
extensions?: string,
|
||||||
|
memoryLimit?: string,
|
||||||
|
memoryRequest?: string,
|
||||||
|
noWait?: boolean,
|
||||||
|
port?: number,
|
||||||
|
replaceEngineSettings?: boolean,
|
||||||
|
workerEngineSettings?: string,
|
||||||
|
workers?: number
|
||||||
|
},
|
||||||
|
namespace?: string,
|
||||||
|
additionalEnvVars?: AdditionalEnvVars
|
||||||
|
): Promise<AzOutput<void>>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sql: {
|
||||||
|
miarc: {
|
||||||
|
delete(name: string, namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<void>>,
|
||||||
|
list(namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<SqlMiListResult[]>>,
|
||||||
|
show(name: string, namespace?: string, additionalEnvVars?: AdditionalEnvVars): Promise<AzOutput<SqlMiShowResult>>,
|
||||||
|
edit(
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
coresLimit?: string,
|
||||||
|
coresRequest?: string,
|
||||||
|
memoryLimit?: string,
|
||||||
|
memoryRequest?: string,
|
||||||
|
noWait?: boolean,
|
||||||
|
},
|
||||||
|
namespace?: string,
|
||||||
|
additionalEnvVars?: AdditionalEnvVars
|
||||||
|
): Promise<AzOutput<void>>
|
||||||
|
}
|
||||||
|
},
|
||||||
getPath(): Promise<string>,
|
getPath(): Promise<string>,
|
||||||
login(endpointOrNamespace: EndpointOrNamespace, username: string, password: string, additionalEnvVars?: AdditionalEnvVars, azdataContext?: string): Promise<AzdataOutput<void>>,
|
|
||||||
/**
|
/**
|
||||||
* The semVersion corresponding to this installation of azdata. version() method should have been run
|
* The semVersion corresponding to this installation of az. version() method should have been run
|
||||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||||
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
* Az has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||||
*/
|
*/
|
||||||
getSemVersion(): Promise<SemVer>,
|
getSemVersion(): Promise<SemVer>,
|
||||||
version(): Promise<AzdataOutput<string>>
|
version(): Promise<AzOutput<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtension {
|
export interface IExtension {
|
||||||
azdata: IAzdataApi;
|
az: IAzApi;
|
||||||
|
|
||||||
/**
|
|
||||||
* returns true if AZDATA CLI EULA has been previously accepted by the user.
|
|
||||||
*/
|
|
||||||
isEulaAccepted(): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
|
||||||
* @param requireUserAction - if the prompt is required to be acted upon by the user. This is typically 'true' when this method is called to address an Error when the EULA needs to be accepted to proceed.
|
|
||||||
*
|
|
||||||
* pre-requisite, the calling code has to ensure that the EULA has not yet been previously accepted by the user. The code can use @see isEulaAccepted() call to ascertain this.
|
|
||||||
* returns true if the user accepted the EULA.
|
|
||||||
*/
|
|
||||||
promptForEula(requireUserAction?: boolean): Promise<boolean>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,10 +185,10 @@ declare module 'azdata-ext' {
|
|||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
engine: {
|
engine: {
|
||||||
extensions: {
|
extensions?: {
|
||||||
name: string // "citus"
|
name: string // "citus"
|
||||||
}[],
|
}[],
|
||||||
settings: {
|
settings?: {
|
||||||
default: { [key: string]: string }, // { "max_connections": "101", "work_mem": "4MB" }
|
default: { [key: string]: string }, // { "max_connections": "101", "work_mem": "4MB" }
|
||||||
roles: {
|
roles: {
|
||||||
coordinator: { [key: string]: string },
|
coordinator: { [key: string]: string },
|
||||||
@@ -208,7 +208,7 @@ declare module 'azdata-ext' {
|
|||||||
limits: SchedulingOptions
|
limits: SchedulingOptions
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
roles: {
|
roles?: {
|
||||||
coordinator: {
|
coordinator: {
|
||||||
resources: {
|
resources: {
|
||||||
requests: SchedulingOptions,
|
requests: SchedulingOptions,
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const externals = {
|
|||||||
'utf-8-validate': 'commonjs utf-8-validate',
|
'utf-8-validate': 'commonjs utf-8-validate',
|
||||||
'keytar': 'commonjs keytar',
|
'keytar': 'commonjs keytar',
|
||||||
'@azure/arm-subscriptions': 'commonjs @azure/arm-subscriptions',
|
'@azure/arm-subscriptions': 'commonjs @azure/arm-subscriptions',
|
||||||
'@azure/arm-resourcegraph': 'commonjs @azure/arm-resourcegraph'
|
'@azure/arm-resourcegraph': 'commonjs @azure/arm-resourcegraph',
|
||||||
|
'@azure/storage-blob': 'commonjs @azure/storage-blob'
|
||||||
};
|
};
|
||||||
|
|
||||||
// conditionally add ws if we are going to be running in a node environment
|
// conditionally add ws if we are going to be running in a node environment
|
||||||
|
|||||||
@@ -332,6 +332,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/arm-resourcegraph": "^4.0.0",
|
"@azure/arm-resourcegraph": "^4.0.0",
|
||||||
"@azure/arm-subscriptions": "^3.0.0",
|
"@azure/arm-subscriptions": "^3.0.0",
|
||||||
|
"@azure/storage-blob": "^12.6.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"qs": "^6.9.1",
|
"qs": "^6.9.1",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ const enum SettingIds {
|
|||||||
ossrdbms = 'ossrdbms',
|
ossrdbms = 'ossrdbms',
|
||||||
vault = 'vault',
|
vault = 'vault',
|
||||||
ado = 'ado',
|
ado = 'ado',
|
||||||
ala = 'ala'
|
ala = 'ala',
|
||||||
|
storage = 'storage'
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicAzureSettings: ProviderSettings = {
|
const publicAzureSettings: ProviderSettings = {
|
||||||
@@ -74,6 +75,12 @@ const publicAzureSettings: ProviderSettings = {
|
|||||||
endpoint: 'https://api.loganalytics.io',
|
endpoint: 'https://api.loganalytics.io',
|
||||||
azureResourceId: AzureResource.AzureLogAnalytics,
|
azureResourceId: AzureResource.AzureLogAnalytics,
|
||||||
},
|
},
|
||||||
|
azureStorageResource: {
|
||||||
|
id: SettingIds.storage,
|
||||||
|
endpoint: '',
|
||||||
|
endpointSuffix: '.core.windows.net',
|
||||||
|
azureResourceId: AzureResource.AzureStorage
|
||||||
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
scopes: [
|
scopes: [
|
||||||
'openid', 'email', 'profile', 'offline_access',
|
'openid', 'email', 'profile', 'offline_access',
|
||||||
@@ -128,6 +135,12 @@ const usGovAzureSettings: ProviderSettings = {
|
|||||||
endpoint: 'https://api.loganalytics.us',
|
endpoint: 'https://api.loganalytics.us',
|
||||||
azureResourceId: AzureResource.AzureLogAnalytics,
|
azureResourceId: AzureResource.AzureLogAnalytics,
|
||||||
},
|
},
|
||||||
|
azureStorageResource: {
|
||||||
|
id: SettingIds.storage,
|
||||||
|
endpoint: '',
|
||||||
|
endpointSuffix: '.core.usgovcloudapi.net',
|
||||||
|
azureResourceId: AzureResource.AzureStorage
|
||||||
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
scopes: [
|
scopes: [
|
||||||
'openid', 'email', 'profile', 'offline_access',
|
'openid', 'email', 'profile', 'offline_access',
|
||||||
@@ -181,6 +194,12 @@ const usNatAzureSettings: ProviderSettings = {
|
|||||||
endpoint: 'https://api.loganalytics.azure.eaglex.ic.gov',
|
endpoint: 'https://api.loganalytics.azure.eaglex.ic.gov',
|
||||||
azureResourceId: AzureResource.AzureLogAnalytics,
|
azureResourceId: AzureResource.AzureLogAnalytics,
|
||||||
},
|
},
|
||||||
|
azureStorageResource: {
|
||||||
|
id: SettingIds.storage,
|
||||||
|
endpoint: '',
|
||||||
|
endpointSuffix: '.core.eaglex.ic.gov',
|
||||||
|
azureResourceId: AzureResource.AzureStorage
|
||||||
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
scopes: [
|
scopes: [
|
||||||
'openid', 'email', 'profile', 'offline_access',
|
'openid', 'email', 'profile', 'offline_access',
|
||||||
@@ -220,7 +239,18 @@ const germanyAzureSettings: ProviderSettings = {
|
|||||||
endpoint: 'https://vault.microsoftazure.de',
|
endpoint: 'https://vault.microsoftazure.de',
|
||||||
azureResourceId: AzureResource.AzureKeyVault
|
azureResourceId: AzureResource.AzureKeyVault
|
||||||
},
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/'
|
azureStorageResource: {
|
||||||
|
id: SettingIds.storage,
|
||||||
|
endpoint: '',
|
||||||
|
endpointSuffix: '.core.cloudapi.de',
|
||||||
|
azureResourceId: AzureResource.AzureStorage
|
||||||
|
},
|
||||||
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
|
scopes: [
|
||||||
|
'openid', 'email', 'profile', 'offline_access',
|
||||||
|
'https://management.microsoftazure.de/user_impersonation'
|
||||||
|
],
|
||||||
|
portalEndpoint: 'https://portal.microsoftazure.de/'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -231,8 +261,13 @@ const chinaAzureSettings: ProviderSettings = {
|
|||||||
displayName: localize('chinaCloudDisplayName', "Azure (China)"),
|
displayName: localize('chinaCloudDisplayName', "Azure (China)"),
|
||||||
id: 'azure_chinaCloud',
|
id: 'azure_chinaCloud',
|
||||||
settings: {
|
settings: {
|
||||||
host: 'https://login.chinacloudapi.cn/',
|
host: 'https://login.partner.microsoftonline.cn/',
|
||||||
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
|
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
|
||||||
|
microsoftResource: {
|
||||||
|
id: SettingIds.marm,
|
||||||
|
endpoint: 'https://management.core.chinacloudapi.cn/',
|
||||||
|
azureResourceId: AzureResource.MicrosoftResourceManagement
|
||||||
|
},
|
||||||
graphResource: {
|
graphResource: {
|
||||||
id: SettingIds.graph,
|
id: SettingIds.graph,
|
||||||
endpoint: 'https://graph.chinacloudapi.cn',
|
endpoint: 'https://graph.chinacloudapi.cn',
|
||||||
@@ -245,9 +280,14 @@ const chinaAzureSettings: ProviderSettings = {
|
|||||||
},
|
},
|
||||||
armResource: {
|
armResource: {
|
||||||
id: SettingIds.arm,
|
id: SettingIds.arm,
|
||||||
endpoint: 'https://managemement.chinacloudapi.net',
|
endpoint: 'https://management.chinacloudapi.cn',
|
||||||
azureResourceId: AzureResource.ResourceManagement
|
azureResourceId: AzureResource.ResourceManagement
|
||||||
},
|
},
|
||||||
|
sqlResource: {
|
||||||
|
id: SettingIds.sql,
|
||||||
|
endpoint: 'https://database.chinacloudapi.cn/',
|
||||||
|
azureResourceId: AzureResource.Sql
|
||||||
|
},
|
||||||
azureKeyVaultResource: {
|
azureKeyVaultResource: {
|
||||||
id: SettingIds.vault,
|
id: SettingIds.vault,
|
||||||
endpoint: 'https://vault.azure.cn',
|
endpoint: 'https://vault.azure.cn',
|
||||||
@@ -258,8 +298,18 @@ const chinaAzureSettings: ProviderSettings = {
|
|||||||
endpoint: 'https://api.loganalytics.azure.cn',
|
endpoint: 'https://api.loganalytics.azure.cn',
|
||||||
azureResourceId: AzureResource.AzureLogAnalytics,
|
azureResourceId: AzureResource.AzureLogAnalytics,
|
||||||
},
|
},
|
||||||
redirectUri: 'https://vscode-redirect.azurewebsites.net/'
|
azureStorageResource: {
|
||||||
|
id: SettingIds.storage,
|
||||||
|
endpoint: '',
|
||||||
|
endpointSuffix: '.core.chinacloudapi.cn',
|
||||||
|
azureResourceId: AzureResource.AzureStorage
|
||||||
|
},
|
||||||
|
redirectUri: 'https://vscode-redirect.azurewebsites.net/',
|
||||||
|
scopes: [
|
||||||
|
'openid', 'email', 'profile', 'offline_access',
|
||||||
|
'https://management.chinacloudapi.cn/user_impersonation'
|
||||||
|
],
|
||||||
|
portalEndpoint: 'https://portal.azure.cn/'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
declare module 'azureResource' {
|
declare module 'azureResource' {
|
||||||
import { TreeDataProvider } from 'vscode';
|
import { TreeDataProvider } from 'vscode';
|
||||||
import { DataProvider, Account, TreeItem } from 'azdata';
|
import { DataProvider, Account, TreeItem } from 'azdata';
|
||||||
|
import { BlobItem } from '@azure/storage-blob';
|
||||||
|
|
||||||
export namespace azureResource {
|
export namespace azureResource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,5 +151,7 @@ declare module 'azureResource' {
|
|||||||
export interface BlobContainer extends AzureResource { }
|
export interface BlobContainer extends AzureResource { }
|
||||||
|
|
||||||
export interface FileShare extends AzureResource { }
|
export interface FileShare extends AzureResource { }
|
||||||
|
|
||||||
|
export interface Blob extends BlobItem { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { azureResource } from 'azureResource';
|
|||||||
|
|
||||||
export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
|
export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
|
||||||
private static readonly containerId = 'azure.resource.providers.AzureMonitorContainer';
|
private static readonly containerId = 'azure.resource.providers.AzureMonitorContainer';
|
||||||
private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Azure Monitor Workspace");
|
private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Log Analytics workspace");
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
@@ -50,7 +50,10 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<a
|
|||||||
providerName: 'LOGANALYTICS',
|
providerName: 'LOGANALYTICS',
|
||||||
saveProfile: false,
|
saveProfile: false,
|
||||||
options: {},
|
options: {},
|
||||||
azureAccount: account.key.accountId
|
azureAccount: account.key.accountId,
|
||||||
|
azureTenantId: databaseServer.tenant,
|
||||||
|
azureResourceId: databaseServer.id,
|
||||||
|
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||||
},
|
},
|
||||||
childProvider: 'LOGANALYTICS',
|
childProvider: 'LOGANALYTICS',
|
||||||
type: ExtensionNodeType.Server
|
type: ExtensionNodeType.Server
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user