├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ ├── main.yml │ └── release.yml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DCO ├── Jenkinsfile ├── LICENSE ├── README.md ├── USAGE_DATA.md ├── build ├── install-vscode.ts ├── unit-tests.ts ├── update-readme.ts └── verify-tools.ts ├── coverconfig.json ├── header ├── images ├── context │ ├── EVT.svg │ ├── REV.svg │ ├── RTE.svg │ ├── SVC.svg │ ├── base-kn-logo-letters.svg │ ├── base-kn-logo.svg │ ├── broker.svg │ ├── channel.svg │ ├── folder-closed.svg │ ├── folder-opened.svg │ ├── kn-broker-name.svg │ ├── kn-broker.svg │ ├── kn-channel-name.svg │ ├── kn-channel.svg │ ├── kn-eventing-broker-trigger.svg │ ├── kn-revision-name.svg │ ├── kn-revision.svg │ ├── kn-service-name.svg │ ├── kn-service.svg │ ├── kn-source-name.svg │ ├── kn-source.svg │ ├── kn-subscription-name.svg │ ├── kn-subscription.svg │ ├── kn-trigger-name.svg │ ├── kn-trigger.svg │ ├── knative-logo.svg │ ├── link.svg │ ├── ns.svg │ ├── repo-dark.svg │ ├── repo-light.svg │ ├── revision.svg │ ├── service.svg │ ├── source-generic.svg │ ├── subscription.svg │ ├── trigger.svg │ ├── versions-dark.svg │ └── versions-light.svg ├── error.svg ├── knative-logo.png ├── pass.svg ├── running.gif ├── stop-dark.svg ├── stop-light.svg └── vscode-knative.svg ├── package-lock.json ├── package.json ├── schemas ├── knservice.json └── knservice.yaml ├── src ├── check-cluster.ts ├── cli │ ├── cli-config.json │ ├── cli-config.ts │ ├── cmdCli.ts │ ├── config.ts │ ├── execute.ts │ ├── func-api.ts │ ├── kn-api.ts │ ├── kubectl-api.ts │ └── virtualfs.ts ├── commands.ts ├── editor │ ├── knativeOpenTextDocument.ts │ ├── knativeReadonlyProvider.ts │ └── knativeSchemaRegister.ts ├── eventingTree │ ├── brokerDataProvider.ts │ ├── channelDataProvider.ts │ ├── eventingDataProvider.ts │ ├── eventingExplorer.ts │ ├── eventingTreeItem.ts │ ├── sourceDataProvider.ts │ ├── subscriptionDataProvider.ts │ └── triggerDataProvider.ts ├── extension.ts ├── functions │ ├── active-namespace.ts │ ├── active-task-view │ │ ├── active-command-tree-view.ts │ │ ├── activeExplorer.ts │ │ ├── command-node.ts │ │ ├── focusOnOutputChannel.ts │ │ └── stop-command.ts │ ├── func.ts │ ├── funcUtils.ts │ ├── function-command │ │ ├── build-and-deploy-function.ts │ │ ├── configure-function.ts │ │ ├── create-function.ts │ │ ├── get-url-function.ts │ │ ├── invoke-function-def.ts │ │ ├── invoke-function.ts │ │ ├── open-yaml-file-in-editor.ts │ │ ├── repository-command.ts │ │ ├── run-function.ts │ │ └── undeploy-function.ts │ ├── function-tree-view │ │ └── functionsTreeItem.ts │ ├── function-type.ts │ ├── functionsExplorer.ts │ ├── validate-item.ts │ └── webview-id.ts ├── git │ ├── git.d.ts │ └── git.ts ├── icon-path.ts ├── knative │ ├── apiServerSource.ts │ ├── baseSource.ts │ ├── bindingSource.ts │ ├── broker.ts │ ├── channel.ts │ ├── genericSource.ts │ ├── kEvent.ts │ ├── knativeBrokers.ts │ ├── knativeChannels.ts │ ├── knativeEvents.ts │ ├── knativeItem.ts │ ├── knativeServices.ts │ ├── knativeSources.ts │ ├── knativeSubscriptions.ts │ ├── knativeTriggers.ts │ ├── pingSource.ts │ ├── revision.ts │ ├── service.ts │ ├── sink.ts │ ├── subscription.ts │ └── trigger.ts ├── output │ └── knOutputChannel.ts ├── reportIssue.ts ├── servingTree │ ├── servingDataProvider.ts │ ├── servingExplorer.ts │ └── servingTreeItem.ts ├── telemetry.ts ├── util │ ├── archive.ts │ ├── constants.ts │ ├── credential-helper.ts │ ├── download.ts │ ├── errorable.ts │ ├── existing-workspace-folder-pick.ts │ ├── filters.ts │ ├── format.ts │ ├── hideclusterinformation.ts │ ├── multiStepInput.ts │ ├── output_channels.ts │ ├── parse.ts │ ├── platform.ts │ ├── quote.ts │ ├── stderrstring.ts │ ├── watch.ts │ └── windowUtils.ts └── version.ts ├── test ├── cli │ ├── cli-config.test.ts │ ├── cmdCli.test.ts │ ├── execute.test.ts │ ├── kn-api.test.ts │ ├── kubectl-api.test.ts │ └── virtualfs.test.ts ├── coverage.ts ├── editor │ ├── knativeOpenTextDocument.test.ts │ ├── knativeReadonlyProvider.test.ts │ └── knativeSchemaRegister.test.ts ├── eventingTree │ ├── broker.json │ ├── brokerDataProvider.test.ts │ ├── brokerEmptySpec.json │ ├── brokerIncomplete.json │ ├── brokerList.yaml │ ├── channel.json │ ├── channelDataProvider.test.ts │ ├── channelEmptySpec.json │ ├── channelIncomplete.json │ ├── channelList.yaml │ ├── eventingDataProvider.test.ts │ ├── eventingExplorer.test.ts │ ├── eventingTreeItem.test.ts │ ├── source.json │ ├── sourceDataProvider.test.ts │ ├── sourceEmptySpec.json │ ├── sourceIncomplete.json │ ├── sourceList.yaml │ ├── subscription.json │ ├── subscriptionDataProvider.test.ts │ ├── subscriptionEmptySpec.json │ ├── subscriptionIncomplete.json │ ├── subscriptionList.yaml │ ├── trigger.json │ ├── triggerDataProvider.test.ts │ ├── triggerEmptySpec.json │ ├── triggerIncomplete.json │ └── triggerList.yaml ├── extension.test.ts ├── fixtures │ ├── func-test │ │ └── func.yaml │ ├── func-test1 │ │ └── func.yaml │ ├── test.gz │ ├── test.tar.gz │ └── test.zip ├── functions │ ├── func.test.ts │ ├── function-command │ │ ├── build-and-deploy-function.test.ts │ │ ├── configure-function.test.ts │ │ ├── create-function.test.ts │ │ ├── run-function.test.ts │ │ └── undeploy-function.test.ts │ ├── functionsExplorer.test.ts │ ├── testFunctionitem.ts │ └── validate-item.test.ts ├── git │ └── git.test.ts ├── index.ts ├── knative │ ├── knativeBrokers.test.ts │ ├── knativeChannels.test.ts │ ├── knativeEvents.test.ts │ ├── knativeItem.test.ts │ ├── knativeServices.test.ts │ ├── knativeSources.test.ts │ ├── knativeSubscriptions.test.ts │ ├── knativeTriggers.test.ts │ └── revision.test.ts ├── output │ └── knOutputChannel.test.ts ├── reportIssue.test.ts ├── servingTree │ ├── aaaServiceRevisionList.json │ ├── bbbServiceRevisionList.json │ ├── cccServiceRevisionList.json │ ├── failed.yaml │ ├── multipleServiceRevisionList.json │ ├── multipleServiceServicesList.json │ ├── service.yaml │ ├── servingDataProvider.test.ts │ ├── servingExplorer.test.ts │ ├── servingTreeItem.test.ts │ ├── singleServiceFailedRevisionRevisionList.json │ ├── singleServiceFailedRevisionServiceList.json │ ├── singleServiceIncompleteServiceList.json │ ├── singleServiceRevisionList.json │ └── singleServiceServiceList.json ├── ui-test │ ├── .mocharc-debug.js │ ├── Jenkinsfile │ ├── allTestsSuite.ts │ ├── baseTestsSuite.ts │ ├── common │ │ ├── constants.ts │ │ └── testUtils.ts │ ├── custom-settings.json │ ├── extensionInstallationUITest.ts │ ├── extensionUITest.ts │ ├── initializationUITest.ts │ └── scripts │ │ ├── prepare-env.sh │ │ ├── run-crc.sh │ │ └── setup-crc.sh └── util │ ├── archive.test.ts │ ├── download.test.ts │ ├── filters.test.ts │ ├── format.test.ts │ ├── hideclusterinformation.test.ts │ ├── parse.test.ts │ ├── platform.test.ts │ ├── watch.test.ts │ └── window.test.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | __coverage__ 3 | public/dist 4 | *.min.js 5 | public/lib 6 | **/node_modules 7 | Godeps 8 | test-resources 9 | test 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "jasmine": true, 7 | "node": true 8 | }, 9 | "extends": [ 10 | "airbnb-base", 11 | "eslint:recommended", 12 | "plugin:@typescript-eslint/eslint-recommended", 13 | "plugin:@typescript-eslint/recommended", 14 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 15 | "plugin:import/recommended", 16 | "prettier" 17 | ], 18 | "parser": "@typescript-eslint/parser", 19 | "parserOptions": { 20 | "ecmaVersion": 2020, 21 | "comment": true, 22 | "project": "./tsconfig.json", 23 | "sourceType": "module" 24 | }, 25 | "plugins": ["@typescript-eslint", "prettier", "import"], 26 | "rules": { 27 | "camelcase": 2, 28 | "consistent-return": 0, 29 | "consistent-this": [1, "that"], 30 | "curly": [2, "all"], 31 | "default-case": [2], 32 | "dot-notation": [2], 33 | "no-multiple-empty-lines": [2, { "max": 2, "maxEOF": 0 }], 34 | "eqeqeq": [2, "allow-null"], 35 | "guard-for-in": 2, 36 | "import/extensions": [ 37 | "error", 38 | "ignorePackages", 39 | { 40 | "js": "never", 41 | "jsx": "never", 42 | "ts": "never", 43 | "tsx": "never", 44 | "mjs": "never" 45 | } 46 | ], 47 | "import/first": [ 48 | "off" 49 | ], 50 | "import/order": ["error", 51 | { 52 | "groups": [ 53 | "builtin", 54 | "external", 55 | "sibling", 56 | "parent", 57 | "internal", 58 | "index", 59 | "object" 60 | ], 61 | "pathGroups": [ 62 | { 63 | "pattern": "~/**", 64 | "group": "internal" 65 | } 66 | ], 67 | "alphabetize": { 68 | "order": "asc" /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, 69 | "caseInsensitive": true /* ignore case. Options: [true, false] */ 70 | } 71 | } 72 | ], 73 | "import/no-default-export": "off", 74 | "import/no-duplicates": ["error"], 75 | "import/no-unresolved": ["error"], 76 | "import/prefer-default-export": "off", 77 | "max-nested-callbacks": [1, 4], 78 | "no-alert": 2, 79 | "no-caller": 2, 80 | "no-console": 2, 81 | "no-constant-condition": 2, 82 | "no-debugger": 2, 83 | "no-else-return": ["error"], 84 | "no-global-strict": 0, 85 | "no-irregular-whitespace": ["error"], 86 | "no-shadow": ["off"], 87 | "@typescript-eslint/no-shadow": "error", 88 | "no-underscore-dangle": 0, 89 | "@typescript-eslint/no-use-before-define": 2, 90 | "@typescript-eslint/await-thenable": "error", 91 | "no-var": 2, 92 | "object-shorthand": ["error", "properties"], 93 | "prefer-const": ["error", { "destructuring": "all" }], 94 | "prefer-template": 2, 95 | "prettier/prettier": "error", 96 | "radix": 2 97 | }, 98 | "settings": { 99 | "import/core-modules": ["vscode"], 100 | "import/extensions": [".ts", ".tsx"], 101 | "import/parsers": { 102 | "@typescript-eslint/parser": [".ts", ".tsx"] 103 | }, 104 | "import/resolver": { 105 | "typescript": { 106 | "extensions": [".ts"] 107 | }, 108 | "node": { 109 | "extensions": [".ts", ".js", ".jsx"] 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [macos-latest, ubuntu-latest, windows-latest] 15 | node: [20] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Setup node 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node }} 25 | - run: npm install 26 | - run: npm run build 27 | - name: Run headless test 28 | uses: coactions/setup-xvfb@v1 29 | with: 30 | run: npm test 31 | - name: Run UI tests 32 | if: runner.os == 'Linux' 33 | uses: coactions/setup-xvfb@v1 34 | with: 35 | run: npm run base-ui-test 36 | - name: Upload screenshots 37 | uses: actions/upload-artifact@v3 38 | if: failure() 39 | with: 40 | name: screenshots-${{ matrix.os }} 41 | path: 'test-resources/**/screenshots/*.png' 42 | retention-days: 2 43 | if-no-files-found: warn 44 | - uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab #v4.1.0 45 | name: codecov-upload 46 | with: 47 | file: ./coverage/coverage-final.json 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | coverage/ 6 | test-resources/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "printWidth": 130, 4 | "singleQuote": true, 5 | "trailingComma": "all", 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin", 6 | "IBM.output-colorizer" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/out/**/*.js" 18 | ], 19 | "preLaunchTask": "${defaultBuildTask}", 20 | "env": { 21 | "VSCODE_REDHAT_TELEMETRY_DEBUG": "true" 22 | } 23 | }, 24 | { 25 | "name": "Extension Tests Debug", 26 | "type": "extensionHost", 27 | "request": "launch", 28 | "sourceMaps": true, 29 | "runtimeExecutable": "${execPath}", 30 | "args": [ 31 | "--extensionDevelopmentPath=${workspaceFolder}", 32 | "--extensionTestsPath=${workspaceFolder}/out/test" 33 | ], 34 | "outFiles": [ 35 | "${workspaceFolder}/out/test/**/*.js" 36 | ], 37 | "preLaunchTask": "npm: watch", 38 | "env": { 39 | "VST_DISABLE_COVERAGE": "true", 40 | "VSCODE_REDHAT_TELEMETRY_DEBUG": "true" 41 | } 42 | }, 43 | { 44 | "name": "Extension Tests", 45 | "type": "extensionHost", 46 | "request": "launch", 47 | "runtimeExecutable": "${execPath}", 48 | "args": [ 49 | "--extensionDevelopmentPath=${workspaceFolder}", 50 | "--extensionTestsPath=${workspaceFolder}/out/test/index" 51 | ], 52 | "outFiles": [ 53 | "${workspaceFolder}/out/test/**/*.js" 54 | ], 55 | "preLaunchTask": "${defaultBuildTask}", 56 | "env": { 57 | "VSCODE_REDHAT_TELEMETRY_DEBUG": "true" 58 | } 59 | }, 60 | { 61 | "name": "Debug UI Tests", 62 | "type": "node", 63 | "request": "launch", 64 | "program": "${workspaceFolder}/node_modules/.bin/extest", 65 | "args": [ 66 | "setup-and-run", 67 | "${workspaceFolder}/out/test/ui-test/baseTestsSuite.js", 68 | "--mocha_config", 69 | "${workspaceFolder}/test/ui-test/.mocharc-debug.js", 70 | "-c", 71 | "max", 72 | "-e", 73 | "~/notTestFolder", 74 | "-i", 75 | "-o", 76 | "test/ui-test/custom-settings.json" 77 | ], 78 | "console": "integratedTerminal", 79 | "internalConsoleOptions": "neverOpen", 80 | "env": { 81 | "VSCODE_REDHAT_TELEMETRY_DEBUG": "true" 82 | } 83 | }, 84 | { 85 | "type": "node", 86 | "request": "launch", 87 | "name": "Launch Program", 88 | "runtimeArgs": [ 89 | "--version" 90 | ], 91 | "outputCapture": "std", 92 | "runtimeVersion": "14.16.1", 93 | "env": { 94 | "VSCODE_REDHAT_TELEMETRY_DEBUG": "true" 95 | } 96 | } 97 | ] 98 | } 99 | 100 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": "explicit" 5 | }, 6 | "editor.minimap.size": "fill", 7 | "eslint.validate": [ 8 | "javascript", 9 | "typescript" 10 | ], 11 | "files.exclude": { 12 | "out": false, 13 | "**/.classpath": true, 14 | "**/.project": true, 15 | "**/.settings": true, 16 | "**/.factorypath": true 17 | }, 18 | "search.exclude": { 19 | "**/node_modules": false, 20 | "out": true 21 | }, 22 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 23 | "typescript.tsc.autoDetect": "off", 24 | "typescript.tsdk": "node_modules/typescript/lib", 25 | "[typescript]": { 26 | "editor.defaultFormatter": "esbenp.prettier-vscode", 27 | "editor.formatOnSave": false 28 | }, 29 | "cSpell.words": [ 30 | "apiextensions", 31 | "apiserver", 32 | "APIV", 33 | "autoscaler", 34 | "brushnet", 35 | "camelcase", 36 | "cobertura", 37 | "codecov", 38 | "coverconfig", 39 | "dbaeumer", 40 | "DDTHH", 41 | "decache", 42 | "devcluster", 43 | "devel", 44 | "Dexample", 45 | "Dknative", 46 | "Dockal", 47 | "Drevision", 48 | "Dservice", 49 | "Dtest", 50 | "entrypoint", 51 | "eqeqeq", 52 | "errorable", 53 | "esbenp", 54 | "extest", 55 | "finalizer", 56 | "finalizers", 57 | "fspath", 58 | "globby", 59 | "hasha", 60 | "idempotence", 61 | "instrumenter", 62 | "istio", 63 | "Knative", 64 | "knmsx", 65 | "knreadonly", 66 | "knrp", 67 | "knservice", 68 | "knvfs", 69 | "kservice", 70 | "ksvc", 71 | "kube", 72 | "kubeconfig", 73 | "Kubectl", 74 | "Kubelet", 75 | "Kubernetes", 76 | "lcov", 77 | "loadknativecore", 78 | "loadothercore", 79 | "loglevel", 80 | "mtchannel", 81 | "NOTCONNECTEDLOCALFUNCTIONS", 82 | "notifs", 83 | "Ondrej", 84 | "openshift", 85 | "Parens", 86 | "podspecable", 87 | "quarkus", 88 | "queryable", 89 | "qycgp", 90 | "redhat", 91 | "repos", 92 | "reqs", 93 | "rhdevelopers", 94 | "routable", 95 | "runtime's", 96 | "SCTP", 97 | "Serializable", 98 | "serviceaccount", 99 | "SIGKILL", 100 | "sinkbindings", 101 | "sinonjs", 102 | "spawnargs", 103 | "spawnfile", 104 | "subresources", 105 | "thenable", 106 | "triggerb", 107 | "triggercdb", 108 | "triggercef", 109 | "triggerfcdd", 110 | "Unprocessable", 111 | "USERPROFILE", 112 | "virtualfs", 113 | "vsix", 114 | "zpyvk" 115 | ] 116 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | out/**/*.map 5 | src/** 6 | .gitignore 7 | vsc-extension-quickstart.md 8 | **/tsconfig.json 9 | **/tslint.json 10 | **/*.map 11 | **/*.ts 12 | tslint.json 13 | *.vsix 14 | test/** 15 | test-resources/ 16 | coverage/** 17 | coverconfig.json 18 | Jenkinsfile 19 | .gitattributes 20 | .travis.yml 21 | CONTRIBUTING.md 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Contributions are essential for keeping this extension great. 4 | We try to keep it as easy as possible to contribute changes and we are 5 | open to suggestions for making it even easier. 6 | There are only a few guidelines that we need contributors to follow. 7 | 8 | ## First Time Setup 9 | 1. Install prerequisites: 10 | * latest [Visual Studio Code](https://code.visualstudio.com/) 11 | * [Node.js](https://nodejs.org/) v16.0.0 or higher 12 | 2. Fork and clone the repository 13 | 3. `cd vscode-knative` 14 | 4. Install the dependencies: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 5. Open the folder in VS Code 20 | 21 | ## Developing the extension 22 | We strongly suggest that you use [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) and [ESlint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) to ease the development of the extension. We are using multiple tools to keep codebase clean, maintainable and code readable. 23 | 1. Once you have all package dependencies installed (`npm install`) you can compile the extension, so you are sure you have all the dependencies installed correctly: 24 | ``` 25 | npm run build 26 | ``` 27 | 2. Now you can run unit tests suite by going to the `Run` tab and launching `Extension Tests (vscode-knative)` task. 28 | 3. Or you can run tests from the CLI: 29 | ``` 30 | npm test 31 | ``` 32 | 4. To run extension in VSCode using the code directly from the workspace, launch `Run Extension (vscode-knative)` task. 33 | 34 | ## Installing the extension from the source code 35 | 36 | 1. Install `vsce` - A command line tool you'll use to publish extensions to the Extension Marketplace. 37 | ```bash 38 | $ npm install -g vsce 39 | ``` 40 | 2. From root folder, run the below command. 41 | ```bash 42 | $ vsce package 43 | ``` 44 | 3. `vscode-knative-.vsix` file is created. Install it by following the instructions [here](https://code.visualstudio.com/docs/editor/extension-gallery#_install-from-a-vsix). 45 | 46 | 47 | 4. Once the extension is installed and reloaded, there will be an Knative Icon on the View Container. 48 | 49 | > If you have any questions or run into any problems, please post an issue - we'll be very happy to help. 50 | 51 | ### Certificate of Origin 52 | 53 | By contributing to this project you agree to the Developer Certificate of 54 | Origin (DCO). This document was created by the Linux Kernel community and is a 55 | simple statement that you, as a contributor, have the legal right to make the 56 | contribution. See the [DCO](DCO) file for details. 57 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | node('rhel8'){ 4 | stage('Checkout repo') { 5 | deleteDir() 6 | git url: "https://github.com/${params.FORK}/vscode-knative.git", branch: params.BRANCH 7 | } 8 | 9 | stage('Install requirements') { 10 | def nodeHome = tool 'nodejs-lts' 11 | env.PATH="${env.PATH}:${nodeHome}/bin" 12 | sh 'npm install -g --force "@vscode/vsce"' 13 | sh 'npm install -g typescript ovsx' 14 | } 15 | 16 | stage('Build') { 17 | sh "npm install" 18 | sh "npm run vscode:prepublish" 19 | } 20 | 21 | withEnv(['JUNIT_REPORT_PATH=report.xml']) { 22 | stage('Unit Tests') { 23 | wrap([$class: 'Xvnc']) { 24 | sh "npm test" 25 | junit 'report.xml' 26 | } 27 | } 28 | } 29 | 30 | stage('UI Tests') { 31 | wrap([$class: 'Xvnc']) { 32 | try { 33 | sh """ 34 | if [ -f \$HOME/.vs-kn/kn ]; then 35 | rm \$HOME/.vs-kn/kn 36 | fi 37 | """ 38 | sh "npm run base-ui-test" 39 | } 40 | finally { 41 | archiveArtifacts artifacts: 'test-resources/*.log,test-resources/**/*.png' 42 | } 43 | } 44 | } 45 | 46 | stage('Package') { 47 | def packageJson = readJSON file: 'package.json' 48 | packageJson.extensionDependencies = ["ms-kubernetes-tools.vscode-kubernetes-tools"] 49 | writeJSON file: 'package.json', json: packageJson, pretty: 4 50 | sh "vsce package -o knative-${packageJson.version}-${env.BUILD_NUMBER}.vsix" 51 | sh "sha256sum *.vsix > knative-${packageJson.version}-${env.BUILD_NUMBER}.vsix.sha256" 52 | } 53 | 54 | if(params.UPLOAD_LOCATION) { 55 | stage('Snapshot') { 56 | sh "sftp -C ${UPLOAD_LOCATION}/snapshots/vscode-knative/ <<< \$'put -p *.vsix*'" 57 | } 58 | } 59 | 60 | if(publishToMarketPlace.equals('true') || publishToOVSX.equals('true')){ 61 | timeout(time:5, unit:'DAYS') { 62 | input message:'Approve deployment?', submitter: 'rgrunber, msuman' 63 | } 64 | 65 | stage "Publish to Marketplaces" 66 | def vsix = findFiles(glob: '**.vsix') 67 | // VS Code Marketplace 68 | if (publishToMarketPlace.equals('true')) { 69 | withCredentials([[$class: 'StringBinding', credentialsId: 'vscode_java_marketplace', variable: 'TOKEN']]) { 70 | sh 'vsce publish -p ${TOKEN} --packagePath' + " ${vsix[0].path}" 71 | } 72 | } 73 | // Open-VSX Marketplace 74 | if (publishToOVSX.equals('true')) { 75 | withCredentials([[$class: 'StringBinding', credentialsId: 'open-vsx-access-token', variable: 'OVSX_TOKEN']]) { 76 | sh 'ovsx publish -p ${OVSX_TOKEN}' + " --packagePath ${vsix[0].path}" 77 | } 78 | } 79 | 80 | stage "Promote the build to stable" 81 | sh "sftp -C ${UPLOAD_LOCATION}/stable/vscode-knative/ <<< \$'put -p *.vsix*'" 82 | archive includes:"**.vsix*" 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Red Hat. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /USAGE_DATA.md: -------------------------------------------------------------------------------- 1 | ## [knative Extension](https://github.com/redhat-developer/vscode-knative) 2 | 3 | ### Usage Data 4 | 5 | * When the extension is activated 6 | * When following command(s) contributed by extension is executed 7 | * Command's ID 8 | * Command's error message (in case of exception) 9 | * Command's specific data like kn version, fn version and to check which command user has used. 10 | * When the extension is deactivated 11 | * Following are the commands which send data to telemetry. 12 | * Build and Deploy command. 13 | * Create function command. 14 | * Invoke function command. 15 | * Run function command. 16 | * Undeploy function command. 17 | * Configure function command. -------------------------------------------------------------------------------- /build/install-vscode.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { execSync } from 'child_process'; 7 | import { platform } from 'os'; 8 | import { dirname, join } from 'path'; 9 | import path = require('path'); 10 | import { downloadAndUnzipVSCode } from '@vscode/test-electron'; 11 | 12 | downloadAndUnzipVSCode() 13 | .then((executable: string): void => { 14 | const extensionsToInstall = ['redhat.vscode-yaml', 'ms-kubernetes-tools.vscode-kubernetes-tools']; 15 | let exe: string = executable; 16 | if (platform() === 'darwin') { 17 | exe = `'${join(exe.substring(0, exe.indexOf('.app') + 4), 'Contents', 'Resources', 'app', 'bin', 'code')}'`; 18 | } else { 19 | exe = join(dirname(exe), 'bin', 'code'); 20 | } 21 | const extensionRootPath = path.resolve(__dirname, '..', '..'); 22 | const vsCodeTest = path.resolve(path.join(extensionRootPath, '.vscode-test')); 23 | const userDataDir = path.join(vsCodeTest, 'user-data'); 24 | const extDir = path.join(vsCodeTest, 'extensions'); 25 | // eslint-disable-next-line no-restricted-syntax 26 | for (const extension of extensionsToInstall) { 27 | // eslint-disable-next-line no-console 28 | console.log('Installing extension: ', extension); 29 | // eslint-disable-next-line @typescript-eslint/no-unsafe-call 30 | execSync(`${exe} --install-extension ${extension} --user-data-dir ${userDataDir} --extensions-dir ${extDir}`); 31 | } 32 | }) 33 | // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions 34 | .catch((err) => console.log(`There was an error while downloading and unzipping, error = ${err}`)); 35 | -------------------------------------------------------------------------------- /build/unit-tests.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | 8 | import { runTests } from '@vscode/test-electron'; 9 | 10 | async function main(): Promise { 11 | try { 12 | // The folder containing the Extension Manifest package.json 13 | // Passed to `--extensionDevelopmentPath` 14 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 15 | 16 | // The path to the extension test runner script 17 | // Passed to --extensionTestsPath 18 | const extensionTestsPath = path.resolve(__dirname, '../../out/test/'); 19 | 20 | // Download VS Code, unzip it and run the integration test 21 | // console.log(extensionDevelopmentPath, extensionTestsPath); 22 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 23 | } catch (err) { 24 | // console.error(err); 25 | process.exit(1); 26 | } 27 | } 28 | 29 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 30 | main(); 31 | -------------------------------------------------------------------------------- /build/update-readme.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { readFileSync, writeFileSync } from 'fs-extra'; 7 | 8 | const readme = readFileSync('./README.md'); 9 | 10 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions 11 | const lines = `${readme}`.split('\n'); 12 | 13 | const index = lines.findIndex((line) => line.includes('## Overview')); 14 | lines.splice(0, index + 1); 15 | writeFileSync('./README.md', lines.join('\n')); 16 | -------------------------------------------------------------------------------- /build/verify-tools.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-argument */ 2 | /* eslint-disable no-console */ 3 | /* eslint-disable @typescript-eslint/no-misused-promises */ 4 | /* eslint-disable no-await-in-loop */ 5 | /* eslint-disable guard-for-in */ 6 | /* eslint-disable no-restricted-syntax */ 7 | /*----------------------------------------------------------------------------------------------- 8 | * Copyright (c) Red Hat, Inc. All rights reserved. 9 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 10 | *-----------------------------------------------------------------------------------------------*/ 11 | 12 | import { exec } from 'child_process'; 13 | import { tmpdir } from 'os'; 14 | import { join, resolve } from 'path'; 15 | import { existsSync } from 'fs-extra'; 16 | import { fromFile } from 'hasha'; 17 | import { sync } from 'mkdirp'; 18 | import { exit } from 'shelljs'; 19 | import configData = require('../src/cli/cli-config.json'); 20 | import { DownloadUtil } from '../src/util/download'; 21 | 22 | async function downloadFileAndCreateSha256( 23 | targetFolder: string, 24 | fileName: string, 25 | reqURL: string, 26 | sha256sum: string, 27 | ): Promise { 28 | if (!existsSync(targetFolder)) { 29 | sync(targetFolder); 30 | } 31 | const currentFile = join(targetFolder, fileName); 32 | console.log(`${currentFile} download started from ${reqURL}`); 33 | await DownloadUtil.downloadFile(reqURL, currentFile, (current) => console.log(`${current}%`)); 34 | const currentSHA256 = await fromFile(currentFile, { algorithm: 'sha256' }); 35 | if (currentSHA256 === sha256sum) { 36 | console.log(`[INFO] ${currentFile} is downloaded and sha256 is correct`); 37 | } else { 38 | throw Error(`${currentFile} is downloaded and sha256 is not correct`); 39 | } 40 | } 41 | 42 | async function verifyTools(): Promise { 43 | for (const key in configData) { 44 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 45 | for (const OS in configData[key].platform) { 46 | const targetFolder = resolve(tmpdir(), OS); 47 | await downloadFileAndCreateSha256( 48 | targetFolder, 49 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 50 | configData[key].platform[OS].dlFileName, 51 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 52 | configData[key].platform[OS].url, 53 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 54 | configData[key].platform[OS].sha256sum, 55 | ); 56 | } 57 | } 58 | } 59 | 60 | const fileCheckRegex = /\w*cli-config.json/; 61 | 62 | exec('git diff --name-only origin/main -- .', async (error, stdout) => { 63 | if (error) { 64 | throw error; 65 | } 66 | console.log('The changed files:'); 67 | console.log(stdout); 68 | if (fileCheckRegex.test(stdout)) { 69 | console.log('cli-config.json is changed, starting download verification'); 70 | try { 71 | await verifyTools(); 72 | } catch (err) { 73 | // eslint-disable-next-line @typescript-eslint/no-unsafe-call 74 | exit(1); 75 | } 76 | } else { 77 | console.log('cli-config.json is not changed, skipping download verification'); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /coverconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "relativeSourcePath": "../src", 3 | "relativeCoverageDir": "../../coverage", 4 | "ignorePatterns": [ 5 | "**/node_modules/**" 6 | ], 7 | "includePid": false, 8 | "reports": [ 9 | "html", 10 | "text", 11 | "cobertura", 12 | "json", 13 | "lcov" 14 | ], 15 | "verbose": false 16 | } 17 | -------------------------------------------------------------------------------- /header: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ -------------------------------------------------------------------------------- /images/context/repo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/context/repo-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/context/versions-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/context/versions-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/knative-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-knative/175be75e337facae8988fdeeea9212e9882f331c/images/knative-logo.png -------------------------------------------------------------------------------- /images/pass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /images/running.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-knative/175be75e337facae8988fdeeea9212e9882f331c/images/running.gif -------------------------------------------------------------------------------- /images/stop-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /images/stop-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/check-cluster.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 2 | /* eslint-disable import/no-cycle */ 3 | /*----------------------------------------------------------------------------------------------- 4 | * Copyright (c) Red Hat, Inc. All rights reserved. 5 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 6 | *-----------------------------------------------------------------------------------------------*/ 7 | 8 | import { knExecutor } from './cli/execute'; 9 | import { KubectlAPI } from './cli/kubectl-api'; 10 | 11 | interface clusterVersion { 12 | items: [ 13 | { 14 | status: { 15 | desired: { 16 | version: string; 17 | }; 18 | }; 19 | }, 20 | ]; 21 | } 22 | 23 | export async function checkOpenShiftCluster(): Promise { 24 | try { 25 | const result = await knExecutor.execute(KubectlAPI.checkOcpCluster(), process.cwd(), false); 26 | if (result?.stdout?.trim()) { 27 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 28 | return JSON.parse(result?.stdout); 29 | } 30 | return null; 31 | } catch (err) { 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/cli/config.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri, workspace } from 'vscode'; 7 | 8 | const EXTENSION_CONFIG_KEY = 'vs-knative'; 9 | 10 | export const enum Kind { 11 | LocalConfig, 12 | } 13 | 14 | export enum FunctionContextType { 15 | NONE = 'none', 16 | FUNCTION = 'functions', 17 | LOCALDEPLOYFUNCTION = 'localDeployFunctions', 18 | DEPLOYFUNCTION = 'deployFunctions', 19 | FAILNAMESPACENODE = 'failNamespaceNode', 20 | NAMESPACENODE = 'namespaceNode', 21 | LOCAlFUNCTIONS = 'localFunctions', 22 | LOCAlFUNCTIONSENABLEMENT = 'localFunctionsEnablement', 23 | NOTCONNECTEDLOCALFUNCTIONS = 'notConnectedLocalFunctions', 24 | NOTCONNECTEDLOCALFUNCTIONSENABLEMENT = 'notConnectedLocalFunctionsEnablement', 25 | ACTIVECOMMAND = 'activecommand', 26 | ERRORCOMMAND = 'errorcommand', 27 | PASSCOMMAND = 'passcommand', 28 | } 29 | 30 | export enum FunctionStatus { 31 | CLUSTERONLY = 'clusterOnly', 32 | LOCALONLY = 'localOnly', 33 | CLUSTERLOCALBOTH = 'clusterLocalBoth', 34 | } 35 | 36 | export enum ServingContextType { 37 | NONE = 'none', 38 | REVISION = 'revision', 39 | REVISION_TAGGED = 'revision_tagged', 40 | SERVICE = 'service', 41 | SERVICE_MODIFIED = 'service_modified', 42 | LOGIN_REQUIRED = 'login_required', 43 | } 44 | 45 | export enum EventingContextType { 46 | NONE = 'none', 47 | BROKER = 'broker', 48 | BROKER_FOLDER = 'broker_folder', 49 | CHANNEL = 'channel', 50 | CHANNEL_FOLDER = 'channel_folder', 51 | SOURCE = 'source', 52 | SOURCE_FOLDER = 'source_folder', 53 | SOURCE_APISERVER = 'source_apiserver', 54 | SOURCE_BINDING = 'source_binding', 55 | SOURCE_PING = 'source_ping', 56 | SUBSCRIPTION = 'subscription', 57 | SUBSCRIPTION_FOLDER = 'subscription_folder', 58 | TRIGGER = 'trigger', 59 | TRIGGER_FOLDER = 'trigger_folder', 60 | URI = 'uri', 61 | } 62 | 63 | export enum ComponentType { 64 | LOCAL = 'local', 65 | GIT = 'git', 66 | BINARY = 'binary', 67 | } 68 | 69 | export const enum SourceType { 70 | GIT = 'git', 71 | LOCAL = 'local', 72 | BINARY = 'binary', 73 | } 74 | 75 | export interface ComponentSettings { 76 | Type: string; 77 | SourceLocation: string; 78 | Ref: string; 79 | SourceType: SourceType; 80 | Application: string; 81 | Project: string; 82 | Name: string; 83 | ContextPath?: Uri; 84 | Ports: string[]; 85 | } 86 | 87 | export interface Config { 88 | kind: Kind; 89 | apiVersion: string; 90 | ComponentSettings: ComponentSettings; 91 | } 92 | 93 | export function getOutputFormat(): string { 94 | return workspace.getConfiguration(EXTENSION_CONFIG_KEY)['vs-knative.outputFormat'] as string; 95 | } 96 | -------------------------------------------------------------------------------- /src/cli/execute.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as path from 'path'; 8 | import { Terminal } from 'vscode'; 9 | import { CmdCliConfig } from './cli-config'; 10 | import { CmdCli, Cli, CliCommand, CliExitData, cliCommandToString } from './cmdCli'; 11 | import { WindowUtil } from '../util/windowUtils'; 12 | 13 | export class Execute { 14 | private cli: Cli = CmdCli.getInstance(); 15 | 16 | // eslint-disable-next-line class-methods-use-this 17 | public async executeInTerminal(command: CliCommand, cwd: string = process.cwd(), name = 'Knative'): Promise { 18 | // Get the first word in the command string sent. 19 | const cmd = command.cliCommand; 20 | // Get the location of the installed cli tool. 21 | let toolLocation = await CmdCliConfig.detectOrDownload(cmd); 22 | if (toolLocation) { 23 | toolLocation = path.dirname(toolLocation); 24 | } 25 | const terminal: Terminal = WindowUtil.createTerminal(name, cwd, toolLocation); 26 | terminal.sendText(cliCommandToString(command), true); 27 | terminal.show(); 28 | } 29 | 30 | public async execute(command: CliCommand, cwd?: string, fail = true): Promise { 31 | const cmd = command; 32 | // Get the location of the cli tool and add it as a path to the command so that it will run. 33 | const toolLocation = await CmdCliConfig.detectOrDownload(cmd.cliCommand); 34 | if (toolLocation) { 35 | cmd.cliCommand = toolLocation; 36 | } 37 | 38 | const ced = this.cli 39 | .execute(cmd, cwd ? { cwd } : {}) 40 | .then((result) => (result.error && result.stdout === '' && fail ? Promise.reject(result.error) : result)) 41 | .catch((err) => (fail ? Promise.reject(err) : Promise.resolve({ error: null, stdout: '', stderr: '' }))); 42 | 43 | CmdCli.resetErrorFlags(); 44 | return ced; 45 | } 46 | } 47 | 48 | /** 49 | * Convert the results of a search, putting the `item` and children into JSON format 50 | * @param result: CliExitData 51 | */ 52 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 53 | export function loadItems(result: CliExitData): any[] { 54 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 55 | let data: any[] = []; 56 | try { 57 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 58 | const { items } = JSON.parse(result.stdout); 59 | if (items) { 60 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 61 | data = items; 62 | } 63 | } catch (ignore) { 64 | // do nothing 65 | } 66 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 67 | return data; 68 | } 69 | 70 | export const knExecutor = new Execute(); 71 | -------------------------------------------------------------------------------- /src/cli/kubectl-api.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | // eslint-disable-next-line import/no-cycle 7 | import { CliCommand, createCliCommand, CmdCli } from './cmdCli'; 8 | 9 | function kubectlCliCommand(cmdArguments: string[]): CliCommand { 10 | return createCliCommand('kubectl', ...cmdArguments); 11 | } 12 | 13 | /** 14 | * A series of commands for the kubernetes cli `kubectl`. 15 | */ 16 | export class KubectlAPI { 17 | /** 18 | * Create / Update by applying a yaml file. 19 | */ 20 | static applyYAML(yamlPath: string, options: { override: boolean }): CliCommand { 21 | const a = ['apply', '-f', yamlPath]; 22 | if (options.override) { 23 | a.push('--force'); 24 | } 25 | return kubectlCliCommand(a); 26 | } 27 | 28 | static checkOcpCluster(): CliCommand { 29 | return kubectlCliCommand(['get', 'clusterversion', '-ojson']); 30 | } 31 | 32 | /** 33 | * Prints the simple version info for kubectl. 34 | */ 35 | static printVersion(): CliCommand { 36 | return kubectlCliCommand(['version', '-ojson']); 37 | } 38 | 39 | static currentNamespace(): CliCommand { 40 | return kubectlCliCommand(['config', 'view', '--minify', '-o', 'json']); 41 | } 42 | 43 | /** 44 | * Returns just the version number without the rest of the printed text. 45 | * 46 | * @param location The path of the kubectl executable 47 | */ 48 | static async getKubectlVersion(location: string): Promise { 49 | // eslint-disable-next-line prefer-regex-literals 50 | const version = new RegExp( 51 | `Client Version:\\s+v(((([0-9]+)\\.([0-9]+)\\.([0-9]+)|(([0-9]+)-([0-9a-zA-Z]+)-([0-9a-zA-Z]+)))(?:-([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?)?).*`, 52 | ); 53 | let detectedVersion: string; 54 | 55 | try { 56 | const data = await CmdCli.getInstance().execute(createCliCommand(`${location}`, `version`, '--client', '--short')); 57 | 58 | if (data.stdout) { 59 | const toolVersion: string[] = data.stdout 60 | .trim() 61 | .split('\n') 62 | // Find the line of text that has the version. 63 | .filter((value1) => version.exec(value1)) 64 | // Pull out just the version from the line from above. 65 | .map((value2) => { 66 | const regexResult = version.exec(value2); 67 | return regexResult[1]; 68 | }); 69 | if (toolVersion.length) { 70 | [detectedVersion] = toolVersion; 71 | } 72 | } 73 | return detectedVersion; 74 | } catch (error) { 75 | // eslint-disable-next-line no-console 76 | // console.log(`GetVersion had an error: ${error}`); 77 | return undefined; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { commands } from 'vscode'; 7 | 8 | export enum VSCodeCommands { 9 | SetContext = 'setContext', 10 | } 11 | 12 | export enum CommandContext { 13 | funcDisableRun = 'function:run', 14 | } 15 | 16 | export function setCommandContext(key: CommandContext | string, value: string | boolean): PromiseLike { 17 | return commands.executeCommand(VSCodeCommands.SetContext, key, value); 18 | } 19 | -------------------------------------------------------------------------------- /src/editor/knativeOpenTextDocument.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as vscode from 'vscode'; 8 | import { KN_READONLY_SCHEME } from './knativeReadonlyProvider'; 9 | import { vfsUri, KN_RESOURCE_SCHEME } from '../cli/virtualfs'; 10 | import { EventingTreeItem } from '../eventingTree/eventingTreeItem'; 11 | import { ServingTreeItem } from '../servingTree/servingTreeItem'; 12 | 13 | /** 14 | * This is set up as a Command. It can be called from a menu or by clicking on the tree item. 15 | * 16 | * @param treeItem 17 | * @param outputFormat 18 | * @param editable 19 | */ 20 | export async function openTreeItemInEditor( 21 | treeItem: ServingTreeItem | EventingTreeItem, 22 | outputFormat: string, 23 | editable: boolean, 24 | ): Promise { 25 | const schema: string = editable ? KN_RESOURCE_SCHEME : KN_READONLY_SCHEME; 26 | let contextValue: string; 27 | if (treeItem.contextValue.startsWith('source_')) { 28 | contextValue = treeItem.contextValue; 29 | } else { 30 | contextValue = treeItem.contextValue.includes('_') 31 | ? treeItem.contextValue.substr(0, treeItem.contextValue.indexOf('_')) 32 | : treeItem.contextValue; 33 | } 34 | const foundName: string = treeItem.getName(); 35 | const name: string = foundName.includes(' - ') ? foundName.slice(foundName.lastIndexOf(' - ') + 3) : foundName; 36 | const uri = vfsUri(schema, contextValue, name, outputFormat); 37 | 38 | await vscode.workspace.openTextDocument(uri).then( 39 | async (doc) => { 40 | if (doc) { 41 | await vscode.window.showTextDocument(doc, { preserveFocus: true, preview: true }); 42 | } else { 43 | throw Error(`Error loading resource located at ${uri.toString()}`); 44 | } 45 | }, 46 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions 47 | (err) => vscode.window.showErrorMessage(`Error loading document: ${err}`), 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/editor/knativeReadonlyProvider.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | // eslint-disable-next-line import/no-cycle 8 | import { KnativeResourceVirtualFileSystemProvider } from '../cli/virtualfs'; 9 | 10 | export const KN_READONLY_SCHEME = 'knreadonly'; 11 | 12 | /** 13 | * This implements the VSCode Text Document Content Provider, loading knative resources as read only yaml files in the editor. 14 | * 15 | * It is registered in the extension.ts and automatically called whenever the registered scheme is used to open a doc. 16 | */ 17 | export class KnativeReadonlyProvider implements vscode.TextDocumentContentProvider { 18 | // emitter and its event 19 | onDidChangeEmitter = new vscode.EventEmitter(); 20 | 21 | onDidChange = this.onDidChangeEmitter.event; 22 | 23 | knvfs: KnativeResourceVirtualFileSystemProvider; 24 | 25 | constructor(knv: KnativeResourceVirtualFileSystemProvider) { 26 | this.knvfs = knv; 27 | } 28 | 29 | provideTextDocumentContent(uri: vscode.Uri): Promise { 30 | return this.knvfs.loadResource(uri); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/editor/knativeSchemaRegister.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { extensions, Uri } from 'vscode'; 7 | import * as serviceSchema from '../../schemas/knservice.json'; 8 | 9 | const SCHEME = 'knmsx'; 10 | const schemaJSON = JSON.stringify(serviceSchema); 11 | 12 | function onRequestSchemaURI(resource: string): string | undefined { 13 | if (resource.search('.yaml') && !resource.startsWith('knreadonly')) { 14 | return `${SCHEME}://schema/knative`; 15 | } 16 | return undefined; 17 | } 18 | 19 | function onRequestSchemaContent(schemaUri: string): string | undefined { 20 | const parsedUri = Uri.parse(schemaUri); 21 | 22 | if (parsedUri.scheme !== SCHEME) { 23 | return undefined; 24 | } 25 | 26 | return schemaJSON; 27 | } 28 | 29 | export async function registerSchema(): Promise { 30 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 31 | const yamlExtensionAPI = await extensions.getExtension('redhat.vscode-yaml').activate(); 32 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 33 | yamlExtensionAPI.registerContributor(SCHEME, onRequestSchemaURI, onRequestSchemaContent, `apiVersion: serving.knative.dev/v1`); 34 | } 35 | -------------------------------------------------------------------------------- /src/eventingTree/eventingExplorer.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { Disposable, TreeView, window, workspace } from 'vscode'; 8 | import * as vscode from 'vscode'; 9 | // eslint-disable-next-line import/no-cycle 10 | import { EventingDataProvider } from './eventingDataProvider'; 11 | import { EventingTreeItem } from './eventingTreeItem'; 12 | import { ServingTreeItem } from '../servingTree/servingTreeItem'; 13 | import { Platform } from '../util/platform'; 14 | import { WatchUtil, FileContentChangeNotifier } from '../util/watch'; 15 | 16 | const kubeConfigFolder: string = path.join(Platform.getUserHomePath(), '.kube'); 17 | const kubeconfigParam: string[][] = [[kubeConfigFolder, 'config']]; 18 | const kubeconfigEnv: string = process.env.KUBECONFIG; 19 | let kubeconfigList: string[] = []; 20 | // check to make sure there is an ENV for KUBECONFIG 21 | if (kubeconfigEnv) { 22 | kubeconfigList = kubeconfigEnv.split(path.delimiter); 23 | } 24 | kubeconfigList.forEach((value): void => { 25 | const kubeconfigSplit: string[] = value.split(path.sep); 26 | const kubeconfigFileName: string = kubeconfigSplit.pop(); 27 | const kubeconfigDir: string = kubeconfigSplit.join(path.sep); 28 | kubeconfigParam.push([kubeconfigDir, kubeconfigFileName]); 29 | }); 30 | 31 | export class EventingExplorer implements Disposable { 32 | public treeView: TreeView; 33 | 34 | public fsw: FileContentChangeNotifier[] = []; 35 | 36 | public registeredCommands: Disposable[] = []; 37 | 38 | public treeDataProvider: EventingDataProvider; 39 | 40 | public constructor() { 41 | this.treeDataProvider = new EventingDataProvider(); 42 | 43 | // Watch the local kubeconfig locations for changes and refresh when one changes. 44 | kubeconfigParam.forEach((params) => { 45 | const l = this.fsw.push(WatchUtil.watchFileForContextChange(params[0], params[1])); 46 | this.fsw[l - 1].emitter.on('file-changed', () => this.treeDataProvider.refresh()); 47 | }); 48 | 49 | if (workspace.getConfiguration('knative').get('pollRefresh')) { 50 | this.treeDataProvider.pollRefresh(); 51 | } 52 | 53 | // Initialize the tree/explorer view by linking the reference in the package.json to this class. 54 | this.treeView = window.createTreeView('knativeEventingProjectExplorer', { treeDataProvider: this.treeDataProvider }); 55 | 56 | this.registeredCommands = [ 57 | vscode.commands.registerCommand('eventing.explorer.refresh', () => this.treeDataProvider.refresh()), 58 | ]; 59 | } 60 | 61 | dispose(): void { 62 | this.registeredCommands.forEach((command) => { 63 | command.dispose(); 64 | }); 65 | this.fsw.forEach((value) => { 66 | value.watcher.close(); 67 | }); 68 | this.treeView.dispose(); 69 | } 70 | 71 | public async reveal(item: EventingTreeItem | ServingTreeItem): Promise { 72 | await this.treeView.reveal(item); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/functions/active-namespace.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { window } from 'vscode'; 8 | import { Namespace } from './function-type'; 9 | import { knExecutor } from '../cli/execute'; 10 | import { KubectlAPI } from '../cli/kubectl-api'; 11 | import { getStderrString } from '../util/stderrstring'; 12 | 13 | export async function activeNamespace(): Promise { 14 | const result = await knExecutor.execute(KubectlAPI.currentNamespace(), process.cwd(), false); 15 | if (result.error) { 16 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 17 | window.showErrorMessage(`Fail to fetch the Namespace Error: ${getStderrString(result.error)}`); 18 | return null; 19 | } 20 | try { 21 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 22 | const currentNamespace: Namespace = JSON.parse(result.stdout); 23 | return currentNamespace.contexts[0].context.namespace; 24 | } catch (err) { 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/functions/active-task-view/active-command-tree-view.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | /* eslint-disable import/no-cycle */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-return */ 4 | /*----------------------------------------------------------------------------------------------- 5 | * Copyright (c) Red Hat, Inc. All rights reserved. 6 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 7 | *-----------------------------------------------------------------------------------------------*/ 8 | 9 | import { TreeItemCollapsibleState } from 'vscode'; 10 | import { CommandNode, ActiveCommandNodeImpl } from './command-node'; 11 | import { FunctionContextType } from '../../cli/config'; 12 | import { CACHED_OUTPUT_CHANNELS, STATUS_CACHED_OUTPUT_CHANNELS } from '../../util/output_channels'; 13 | 14 | export interface ActiveCommand { 15 | name?: string; 16 | } 17 | 18 | export function activeCommandTreeView(): CommandNode[] { 19 | const children = []; 20 | if (CACHED_OUTPUT_CHANNELS && CACHED_OUTPUT_CHANNELS.size !== 0) { 21 | // eslint-disable-next-line no-restricted-syntax 22 | for (const [key] of CACHED_OUTPUT_CHANNELS) { 23 | let obj: ActiveCommandNodeImpl; 24 | const getStatusCachedOutputChannel: string = STATUS_CACHED_OUTPUT_CHANNELS.get(key); 25 | if (getStatusCachedOutputChannel === 'successful') { 26 | obj = new ActiveCommandNodeImpl(null, key, FunctionContextType.PASSCOMMAND, TreeItemCollapsibleState.None); 27 | } else if (getStatusCachedOutputChannel === 'error') { 28 | obj = new ActiveCommandNodeImpl(null, key, FunctionContextType.ERRORCOMMAND, TreeItemCollapsibleState.None); 29 | } else { 30 | obj = new ActiveCommandNodeImpl(null, key, FunctionContextType.ACTIVECOMMAND, TreeItemCollapsibleState.None); 31 | } 32 | 33 | children.unshift(obj); 34 | } 35 | } else { 36 | const obj: ActiveCommandNodeImpl = new ActiveCommandNodeImpl( 37 | null, 38 | 'No function is currently being run (or executed).', 39 | FunctionContextType.NONE, 40 | TreeItemCollapsibleState.None, 41 | ); 42 | children.push(obj); 43 | } 44 | 45 | return children; 46 | } 47 | -------------------------------------------------------------------------------- /src/functions/active-task-view/activeExplorer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /* eslint-disable @typescript-eslint/no-floating-promises */ 3 | /* eslint-disable class-methods-use-this */ 4 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 5 | /*----------------------------------------------------------------------------------------------- 6 | * Copyright (c) Red Hat, Inc. All rights reserved. 7 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 8 | *-----------------------------------------------------------------------------------------------*/ 9 | 10 | import { Disposable, Event, EventEmitter, ProviderResult, TreeDataProvider, TreeItem, TreeView, window } from 'vscode'; 11 | import { activeCommandTreeView } from './active-command-tree-view'; 12 | import { CommandNode } from './command-node'; 13 | 14 | export class ActiveCommandExplorer implements TreeDataProvider, Disposable { 15 | private treeView: TreeView; 16 | 17 | private onDidChangeTreeDataEmitter: EventEmitter = new EventEmitter(); 18 | 19 | readonly onDidChangeTreeData: Event = this.onDidChangeTreeDataEmitter.event; 20 | 21 | constructor() { 22 | this.treeView = window.createTreeView('activeTaskFunction', { treeDataProvider: this, canSelectMany: false }); 23 | } 24 | 25 | getTreeItem(element: CommandNode): TreeItem | Thenable { 26 | return element; 27 | } 28 | 29 | getChildren(element?: CommandNode): ProviderResult { 30 | if (element) { 31 | return element.getChildren(); 32 | } 33 | return activeCommandTreeView(); 34 | } 35 | 36 | getParent?(element: CommandNode): CommandNode { 37 | return element.getParent(); 38 | } 39 | 40 | async refresh(target?: CommandNode): Promise { 41 | if (target) { 42 | await target.refresh(); 43 | } 44 | this.onDidChangeTreeDataEmitter.fire(target); 45 | } 46 | 47 | dispose(): void { 48 | this.treeView.dispose(); 49 | } 50 | 51 | async reveal(item: CommandNode): Promise { 52 | this.refresh(item.getParent()); 53 | // double call of reveal is workaround for possible upstream issue 54 | // https://github.com/redhat-developer/vscode-openshift-tools/issues/762 55 | await this.treeView.reveal(item); 56 | this.treeView.reveal(item); 57 | } 58 | 59 | isVisible(): boolean { 60 | return this.treeView.visible; 61 | } 62 | } 63 | 64 | export const activeCommandExplorer = new ActiveCommandExplorer(); 65 | -------------------------------------------------------------------------------- /src/functions/active-task-view/focusOnOutputChannel.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { CACHED_OUTPUT_CHANNELS } from '../../util/output_channels'; 8 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 9 | 10 | export function focusOnOutputChannel(context: FunctionNode): void { 11 | if (!context) { 12 | return null; 13 | } 14 | const getOutputChannel = CACHED_OUTPUT_CHANNELS.get(context.getName()); 15 | getOutputChannel.show(); 16 | } 17 | -------------------------------------------------------------------------------- /src/functions/active-task-view/stop-command.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { CACHED_CHILDPROCESS } from '../../util/output_channels'; 8 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 9 | 10 | export function stopCommand(context: FunctionNode): void { 11 | if (!context) { 12 | return null; 13 | } 14 | CACHED_CHILDPROCESS.get(context.getName()).kill('SIGTERM'); 15 | } 16 | -------------------------------------------------------------------------------- /src/functions/funcUtils.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import * as fs from 'fs-extra'; 8 | import * as yaml from 'js-yaml'; 9 | import { FuncContent } from './function-type'; 10 | 11 | export async function getFuncYamlContent(dir: string): Promise { 12 | let funcData: FuncContent[]; 13 | try { 14 | const funcYaml: string = await fs.readFile(path.join(dir, 'func.yaml'), 'utf-8'); 15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 16 | funcData = yaml.safeLoadAll(funcYaml); 17 | } catch (error) { 18 | // ignore 19 | } 20 | return funcData?.[0]; 21 | } 22 | -------------------------------------------------------------------------------- /src/functions/function-command/configure-function.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as vscode from 'vscode'; 8 | import { selectFunctionFolder } from './build-and-deploy-function'; 9 | import { CliCommand } from '../../cli/cmdCli'; 10 | import { knExecutor } from '../../cli/execute'; 11 | import { FuncAPI } from '../../cli/func-api'; 12 | import { telemetryLog } from '../../telemetry'; 13 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 14 | 15 | export const enum ConfigAction { 16 | Add, 17 | Remove, 18 | } 19 | 20 | export const ENV_VARIABLES = 'Environment Variables'; 21 | export const VOLUMES = 'Volumes'; 22 | 23 | function getConfigEnvsCliCommand(action: ConfigAction, funcPath: string) { 24 | if (action === ConfigAction.Add) { 25 | telemetryLog('Function_add_environment_variable', 'Click on add environment variable command'); 26 | return FuncAPI.addEnvironmentVariable(funcPath); 27 | } 28 | telemetryLog('Function_remove_environment_variable', 'Click on remove environment variable command'); 29 | return FuncAPI.removeEnvironmentVariable(funcPath); 30 | } 31 | 32 | function getConfigVolumesCliCommand(action: ConfigAction, funcPath: string) { 33 | if (action === ConfigAction.Add) { 34 | telemetryLog('Function_add_volume', 'Click on add volume command'); 35 | return FuncAPI.addVolumes(funcPath); 36 | } 37 | telemetryLog('Function_remove_volume', 'Click on remove volume command'); 38 | return FuncAPI.removeVolumes(funcPath); 39 | } 40 | 41 | function getCliCommand(action: ConfigAction, objectToConfigure: string, funcPath: string): CliCommand { 42 | switch (objectToConfigure) { 43 | case ENV_VARIABLES: { 44 | return getConfigEnvsCliCommand(action, funcPath); 45 | } 46 | case VOLUMES: { 47 | return getConfigVolumesCliCommand(action, funcPath); 48 | } 49 | default: { 50 | return null; 51 | } 52 | } 53 | } 54 | 55 | async function getFuncPath(context?: FunctionNode) { 56 | if (!context || !context.contextPath) { 57 | const selectedFolderPick = await selectFunctionFolder(); 58 | if (!selectedFolderPick) { 59 | return null; 60 | } 61 | return selectedFolderPick.workspaceFolder.uri.fsPath; 62 | } 63 | return context.contextPath.fsPath; 64 | } 65 | 66 | export async function configureFunction(action: ConfigAction, objectToConfigure?: string, context?: FunctionNode): Promise { 67 | const funcPath = await getFuncPath(context); 68 | if (!funcPath) { 69 | return null; 70 | } 71 | if (!objectToConfigure) { 72 | // eslint-disable-next-line no-param-reassign 73 | objectToConfigure = await vscode.window.showQuickPick([ENV_VARIABLES, VOLUMES], { 74 | placeHolder: 'Configure your function', 75 | }); 76 | if (!objectToConfigure) { 77 | return null; 78 | } 79 | } 80 | 81 | await knExecutor.executeInTerminal(getCliCommand(action, objectToConfigure, funcPath)); 82 | } 83 | 84 | export async function configureEnvs(action: ConfigAction, context?: FunctionNode): Promise { 85 | await configureFunction(action, ENV_VARIABLES, context); 86 | } 87 | 88 | export async function configureVolumes(action: ConfigAction, context?: FunctionNode): Promise { 89 | await configureFunction(action, VOLUMES, context); 90 | } 91 | -------------------------------------------------------------------------------- /src/functions/function-command/get-url-function.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { commands, Uri, window } from 'vscode'; 7 | // eslint-disable-next-line import/no-cycle 8 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 9 | 10 | export async function urlFunction(context?: FunctionNode): Promise { 11 | if (!context) { 12 | return null; 13 | } 14 | if (!context.url) { 15 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 16 | window.showErrorMessage('Failed to get URL'); 17 | return null; 18 | } 19 | return commands.executeCommand('vscode.open', Uri.parse(context.url)); 20 | } 21 | -------------------------------------------------------------------------------- /src/functions/function-command/open-yaml-file-in-editor.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as path from 'path'; 8 | import { Uri, window, workspace } from 'vscode'; 9 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 10 | 11 | export function openInEditor(context: FunctionNode): void { 12 | if (!context) { 13 | return null; 14 | } 15 | const uriPath = Uri.file(path.join(context.contextPath.fsPath, 'func.yaml')); 16 | workspace.openTextDocument(uriPath).then( 17 | (doc) => { 18 | if (doc) { 19 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 20 | window.showTextDocument(doc, { preserveFocus: true, preview: true }); 21 | } 22 | }, 23 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions 24 | (err) => window.showErrorMessage(`Error loading document: ${err}`), 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/functions/function-command/undeploy-function.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as vscode from 'vscode'; 8 | import { CliExitData, executeCmdCli } from '../../cli/cmdCli'; 9 | import { FuncAPI } from '../../cli/func-api'; 10 | import { telemetryLog, telemetryLogError } from '../../telemetry'; 11 | import { getStderrString } from '../../util/stderrstring'; 12 | import { FunctionNode } from '../function-tree-view/functionsTreeItem'; 13 | import { functionExplorer } from '../functionsExplorer'; 14 | 15 | export async function undeployFunction(context: FunctionNode): Promise { 16 | if (!context) { 17 | return null; 18 | } 19 | const response = await vscode.window.showWarningMessage(`Do you want to undeploy Function: ${context.getName()}`, 'Yes', 'No'); 20 | if (response === 'No') { 21 | return null; 22 | } 23 | await vscode.window.withProgress( 24 | { 25 | cancellable: false, 26 | location: vscode.ProgressLocation.Notification, 27 | title: `Undeploying function ${context.getName()}...`, 28 | }, 29 | async () => { 30 | const result: CliExitData = await executeCmdCli.executeExec(FuncAPI.deleteFunc(context.getName())); 31 | if (result.error) { 32 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 33 | vscode.window.showErrorMessage( 34 | `Failed to undeploy function:${context.getName()} with the following error: ${getStderrString(result.error)}`, 35 | ); 36 | telemetryLogError('Function_undeploy_error', getStderrString(result.error)); 37 | return null; 38 | } 39 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 40 | vscode.window.showInformationMessage(`Function ${context.getName()} successfully undeployed`); 41 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 42 | functionExplorer.refresh(); 43 | telemetryLog('Function_successfully_undeploy', context.getName()); 44 | return null; 45 | }, 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/functions/function-type.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | 8 | export interface FunctionList { 9 | name: string; 10 | namespace?: string; 11 | runtime?: string; 12 | url?: string; 13 | ready?: string; 14 | } 15 | 16 | export interface FuncContent { 17 | name?: string; 18 | deploy?: { 19 | namespace: string; 20 | }; 21 | runtime?: string; 22 | image?: string; 23 | imageDigest?: string; 24 | builder?: string; 25 | invocation?: { format?: string }; 26 | } 27 | 28 | export interface FolderPick extends vscode.QuickPickItem { 29 | test?: string; 30 | workspaceFolder?: vscode.WorkspaceFolder; 31 | } 32 | 33 | export interface ImageAndBuild { 34 | image?: string; 35 | builder?: string; 36 | autoGenerateImage?: boolean; 37 | } 38 | 39 | export interface ContextList { 40 | name?: string; 41 | context?: { namespace: string }; 42 | } 43 | 44 | export interface Namespace { 45 | contexts?: [ContextList]; 46 | } 47 | 48 | export interface FunctionInfo { 49 | name?: string; 50 | image?: string; 51 | namespace?: string; 52 | routes?: [string]; 53 | } 54 | -------------------------------------------------------------------------------- /src/functions/functionsExplorer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { TreeDataProvider, TreeView, Event, EventEmitter, TreeItem, ProviderResult, Disposable, window, workspace } from 'vscode'; 8 | import { func } from './func'; 9 | import { FunctionNode } from './function-tree-view/functionsTreeItem'; 10 | 11 | export class FunctionExplorer implements TreeDataProvider, Disposable { 12 | public treeView: TreeView; 13 | 14 | private onDidChangeTreeDataEmitter: EventEmitter = new EventEmitter(); 15 | 16 | readonly onDidChangeTreeData: Event = this.onDidChangeTreeDataEmitter.event; 17 | 18 | public registeredCommands: Disposable[] = []; 19 | 20 | constructor() { 21 | this.treeView = window.createTreeView('knativeFunctionProjectExplorer', { treeDataProvider: this, canSelectMany: true }); 22 | workspace.onDidChangeWorkspaceFolders(() => { 23 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 24 | this.refresh(); 25 | }); 26 | } 27 | 28 | // eslint-disable-next-line class-methods-use-this 29 | getTreeItem(element: FunctionNode): TreeItem | Thenable { 30 | return element; 31 | } 32 | 33 | // eslint-disable-next-line class-methods-use-this 34 | getChildren(element?: FunctionNode): ProviderResult { 35 | if (element) { 36 | return element.getChildren(); 37 | } 38 | return func.getFunctionNodes(); 39 | } 40 | 41 | // eslint-disable-next-line class-methods-use-this 42 | getParent?(element: FunctionNode): FunctionNode { 43 | return element.getParent(); 44 | } 45 | 46 | async refresh(target?: FunctionNode): Promise { 47 | if (target) { 48 | await target.refresh(); 49 | } 50 | this.onDidChangeTreeDataEmitter.fire(target); 51 | } 52 | 53 | dispose(): void { 54 | this.registeredCommands.forEach((command) => { 55 | command.dispose(); 56 | }); 57 | this.treeView.dispose(); 58 | } 59 | 60 | async reveal(item: FunctionNode): Promise { 61 | await this.refresh(item.getParent()); 62 | await this.treeView.reveal(item); 63 | } 64 | 65 | isVisible(): boolean { 66 | return this.treeView.visible; 67 | } 68 | } 69 | 70 | export const functionExplorer = new FunctionExplorer(); 71 | -------------------------------------------------------------------------------- /src/functions/validate-item.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { SEVERITY, ValidatorResponse, ValidatorResponseItem } from '@redhat-developer/vscode-wizard'; 8 | import * as fs from 'fs-extra'; 9 | import { Platform } from '../util/platform'; 10 | 11 | export const pathValidation = new Map(); 12 | 13 | const pathWindowsRegex = /^[a-zA-Z]:\\(?:[^#%\\/:*?"<>|\r\n\\[\]{}]+\\)*[^#%\\/:*?"<>|\r\n\\[\]{}]*$/; 14 | 15 | export interface content { 16 | value: string; 17 | message: string; 18 | id: string; 19 | } 20 | 21 | export function createValidationItem(sev: SEVERITY, id: string, content: string): ValidatorResponseItem { 22 | return { 23 | severity: sev, 24 | template: { 25 | id, 26 | content, 27 | }, 28 | }; 29 | } 30 | 31 | export function inputFieldValidation(inputField: content, items: ValidatorResponseItem[]): ValidatorResponse { 32 | items.push(createValidationItem(SEVERITY.ERROR, inputField.id, inputField.message)); 33 | return { items }; 34 | } 35 | 36 | export function selectLocationValidation(selectLocation: content, items: ValidatorResponseItem[]): ValidatorResponse { 37 | if (!selectLocation.value.trim()) { 38 | items.push(createValidationItem(SEVERITY.ERROR, selectLocation.id, selectLocation.message)); 39 | } 40 | if (selectLocation.value.trim() && Platform.OS === 'win32') { 41 | if (!pathWindowsRegex.test(selectLocation.value)) { 42 | items.push(createValidationItem(SEVERITY.ERROR, selectLocation.id, 'Selected path has invalid format.')); 43 | pathValidation.set('path_validation', false); 44 | } else { 45 | pathValidation.set('path_validation', true); 46 | } 47 | } 48 | // eslint-disable-next-line no-extra-boolean-cast 49 | if (selectLocation.value.trim()) { 50 | if (!path.isAbsolute(selectLocation.value)) { 51 | items.push(createValidationItem(SEVERITY.ERROR, selectLocation.id, 'The selection is not a valid absolute path.')); 52 | pathValidation.set('path_validation', false); 53 | } else { 54 | pathValidation.set('path_validation', true); 55 | } 56 | } 57 | // eslint-disable-next-line no-extra-boolean-cast 58 | if (selectLocation.value.trim()) { 59 | const stats = path.parse(selectLocation.value); 60 | if (stats.root.length === 0 && !fs.existsSync(stats.root)) { 61 | items.push(createValidationItem(SEVERITY.ERROR, selectLocation.id, 'Selected disk does not exist.')); 62 | pathValidation.set('path_validation', false); 63 | } else { 64 | pathValidation.set('path_validation', true); 65 | } 66 | } 67 | return { items }; 68 | } 69 | -------------------------------------------------------------------------------- /src/functions/webview-id.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export const createFunctionID = { 7 | function_name: 'functionName', 8 | language_name: 'languageName', 9 | template_inputText: 'templateInputText', 10 | select_template: 'selectTemplate', 11 | select_language: 'selectLanguage', 12 | select_location: 'selectLocation', 13 | }; 14 | 15 | export const invokeFunctionID = { 16 | invoke_Instance: 'invokeInstance', 17 | invoke_namespace: 'invokeNamespace', 18 | invoke_ID: 'invokeId', 19 | invoke_Url: 'invokeUrl', 20 | invoke_Url_Check: 'invokeUrlCheck', 21 | invoke_Url_Def: 'invokeUrlDef', 22 | invoke_path: 'invokePath', 23 | invoke_data_text: 'invokeDataText', 24 | invoke_data_file: 'invokeDataFile', 25 | invoke_data_desc: 'invokeDataDesc', 26 | invoke_data_mode: 'invokeDataMode', 27 | invoke_context_type: 'invokeContextType', 28 | invoke_source: 'invokeSource', 29 | invoke_type: 'invokeType', 30 | invoke_format: 'invokeFormat', 31 | }; 32 | -------------------------------------------------------------------------------- /src/icon-path.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export const IMAGES = '../../../../images'; 7 | -------------------------------------------------------------------------------- /src/knative/apiServerSource.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { BaseSource, Metadata } from './baseSource'; 8 | 9 | export type sourceOptions = Array>; 10 | 11 | export class APIServerSource extends BaseSource { 12 | constructor( 13 | public name: string, 14 | public parent: string, 15 | public resource: string, 16 | public sink: string, 17 | public details?: Items, 18 | ) { 19 | super(name, parent, details); 20 | } 21 | 22 | static JSONToSource(value: Items): APIServerSource { 23 | let controller: string; 24 | if (value.spec.resources) { 25 | controller = value.spec.resources[0].controllerSelector?.name; 26 | } 27 | let sinkNameOrUri: string; 28 | if (value.spec.sink) { 29 | if (value.spec.sink.ref) { 30 | sinkNameOrUri = value.spec.sink.ref.name; 31 | } 32 | if (value.spec.sink.uri) { 33 | sinkNameOrUri = value.spec.sink.uri; 34 | } 35 | } 36 | const source = new APIServerSource(value.metadata.name, 'Sources', controller, sinkNameOrUri, value); 37 | return source; 38 | } 39 | } 40 | 41 | export interface Items { 42 | apiVersion: string; 43 | kind: string; 44 | metadata: Metadata; 45 | spec: Spec; 46 | status: Status; 47 | } 48 | export interface Spec { 49 | mode?: string | null; 50 | resources?: Resources[] | null; 51 | serviceAccountName?: string | null; 52 | sink: Sink; 53 | jsonData?: string | null; 54 | schedule?: string | null; 55 | subject?: RefOrSubject | null; 56 | } 57 | export interface Resources { 58 | apiVersion: string; 59 | controller: boolean; 60 | controllerSelector: ControllerSelector; 61 | kind: string; 62 | labelSelector: Record; 63 | } 64 | export interface ControllerSelector { 65 | apiVersion: string; 66 | kind: string; 67 | name: string; 68 | uid: string; 69 | } 70 | export interface Sink { 71 | ref?: RefOrSubject | null; 72 | uri?: string | null; 73 | } 74 | export interface RefOrSubject { 75 | apiVersion: string; 76 | kind: string; 77 | name: string; 78 | namespace: string; 79 | } 80 | export interface Status { 81 | conditions?: Conditions[] | null; 82 | observedGeneration: number; 83 | sinkUri: string; 84 | ceAttributes?: CeAttributes[] | null; 85 | } 86 | export interface Conditions { 87 | lastTransitionTime: string; 88 | status: string; 89 | type: string; 90 | message?: string | null; 91 | } 92 | export interface CeAttributes { 93 | source: string; 94 | type: string; 95 | } 96 | -------------------------------------------------------------------------------- /src/knative/baseSource.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | // import { Sink } from './event'; 8 | import { KnativeItem } from './knativeItem'; 9 | import { Sink as sinkType } from './sink'; 10 | 11 | export type sourceOptions = Array>; 12 | 13 | export class BaseSource extends KnativeItem { 14 | constructor( 15 | public name: string, 16 | public parent: string, 17 | public details?: Items, 18 | ) { 19 | super(); 20 | } 21 | 22 | childSink: sinkType; 23 | 24 | annotation?: Map; 25 | 26 | label?: Map; 27 | 28 | namespace?: string; 29 | 30 | modified?: boolean; 31 | } 32 | 33 | export interface Items { 34 | apiVersion: string; 35 | kind: string; 36 | metadata: Metadata; 37 | spec: Spec; 38 | status: Status; 39 | } 40 | export interface Metadata { 41 | annotations: Annotations; 42 | creationTimestamp: string; 43 | generation: number; 44 | managedFields?: ManagedFields[] | null; 45 | name: string; 46 | namespace: string; 47 | resourceVersion: string; 48 | selfLink: string; 49 | uid: string; 50 | finalizers?: string[] | null; 51 | } 52 | export interface Annotations { 53 | 'sources.knative.dev/creator': string; 54 | 'sources.knative.dev/lastModifier': string; 55 | } 56 | export interface ManagedFields { 57 | apiVersion: string; 58 | fieldsType: string; 59 | fieldsV1: FieldsV1; 60 | manager: string; 61 | operation: string; 62 | time: string; 63 | } 64 | export interface FieldsV1 { 65 | 'f:metadata'?: Record | null; 66 | 'f:spec'?: Record | null; 67 | 'f:status'?: Record | null; 68 | } 69 | export interface Spec { 70 | mode?: string | null; 71 | resources?: Resources[] | null; 72 | serviceAccountName?: string | null; 73 | sink: Sink; 74 | jsonData?: string | null; 75 | schedule?: string | null; 76 | subject?: RefOrSubject | null; 77 | } 78 | export interface Resources { 79 | apiVersion: string; 80 | controller: boolean; 81 | controllerSelector: ControllerSelector; 82 | kind: string; 83 | labelSelector: Record; 84 | } 85 | export interface ControllerSelector { 86 | apiVersion: string; 87 | kind: string; 88 | name: string; 89 | uid: string; 90 | } 91 | export interface Sink { 92 | ref?: RefOrSubject | null; 93 | uri?: string | null; 94 | } 95 | export interface RefOrSubject { 96 | apiVersion: string; 97 | kind: string; 98 | name: string; 99 | namespace: string; 100 | } 101 | export interface Status { 102 | conditions?: Conditions[] | null; 103 | observedGeneration: number; 104 | sinkUri: string; 105 | ceAttributes?: CeAttributes[] | null; 106 | } 107 | export interface Conditions { 108 | lastTransitionTime: string; 109 | status: string; 110 | type: string; 111 | message?: string | null; 112 | } 113 | export interface CeAttributes { 114 | source: string; 115 | type: string; 116 | } 117 | -------------------------------------------------------------------------------- /src/knative/bindingSource.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { BaseSource, Metadata } from './baseSource'; 8 | import { Sink as sinkType } from './sink'; 9 | 10 | export type sourceOptions = Array>; 11 | 12 | export class BindingSource extends BaseSource { 13 | constructor( 14 | public name: string, 15 | public parent: string, 16 | public subject: string, 17 | public sink: string, 18 | public details?: Items, 19 | ) { 20 | super(name, parent, details); 21 | } 22 | 23 | childSink: sinkType; 24 | 25 | static JSONToSource(value: Items): BindingSource { 26 | let sinkNameOrUri: string; 27 | if (value.spec.sink) { 28 | if (value.spec.sink.ref) { 29 | sinkNameOrUri = value.spec.sink.ref.name; 30 | } 31 | if (value.spec.sink.uri) { 32 | sinkNameOrUri = value.spec.sink.uri; 33 | } 34 | } 35 | const source = new BindingSource(value.metadata.name, 'Sources', value.spec.subject?.name, sinkNameOrUri, value); 36 | return source; 37 | } 38 | } 39 | 40 | export interface Items { 41 | apiVersion: string; 42 | kind: string; 43 | metadata: Metadata; 44 | spec: Spec; 45 | status: Status; 46 | } 47 | export interface Spec { 48 | mode?: string | null; 49 | resources?: Resources[] | null; 50 | serviceAccountName?: string | null; 51 | sink: Sink; 52 | jsonData?: string | null; 53 | schedule?: string | null; 54 | subject?: RefOrSubject | null; 55 | } 56 | export interface Resources { 57 | apiVersion: string; 58 | controller: boolean; 59 | controllerSelector: ControllerSelector; 60 | kind: string; 61 | labelSelector: Record; 62 | } 63 | export interface ControllerSelector { 64 | apiVersion: string; 65 | kind: string; 66 | name: string; 67 | uid: string; 68 | } 69 | export interface Sink { 70 | ref?: RefOrSubject | null; 71 | uri?: string | null; 72 | } 73 | export interface RefOrSubject { 74 | apiVersion: string; 75 | kind: string; 76 | name: string; 77 | namespace: string; 78 | } 79 | export interface Status { 80 | conditions?: Conditions[] | null; 81 | observedGeneration: number; 82 | sinkUri: string; 83 | ceAttributes?: CeAttributes[] | null; 84 | } 85 | export interface Conditions { 86 | lastTransitionTime: string; 87 | status: string; 88 | type: string; 89 | message?: string | null; 90 | } 91 | export interface CeAttributes { 92 | source: string; 93 | type: string; 94 | } 95 | -------------------------------------------------------------------------------- /src/knative/broker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { KnativeItem } from './knativeItem'; 8 | 9 | export class Broker extends KnativeItem { 10 | constructor( 11 | public name: string, 12 | public parent: string, 13 | public details?: Items, 14 | ) { 15 | super(); 16 | } 17 | 18 | annotation?: Map; 19 | 20 | label?: Map; 21 | 22 | namespace?: string; 23 | 24 | modified?: boolean; 25 | 26 | static JSONToBroker(value: Items): Broker { 27 | const broker = new Broker(value.metadata.name, 'Brokers', value); 28 | return broker; 29 | } 30 | } 31 | 32 | export interface Items { 33 | apiVersion: string; 34 | kind: string; 35 | metadata: Metadata; 36 | spec: Spec; 37 | status: Status; 38 | } 39 | export interface Metadata { 40 | annotations: Annotations; 41 | creationTimestamp: string; 42 | finalizers?: string[] | null; 43 | generation: number; 44 | managedFields?: ManagedFields[] | null; 45 | name: string; 46 | namespace: string; 47 | resourceVersion: string; 48 | selfLink: string; 49 | uid: string; 50 | } 51 | export interface Annotations { 52 | 'eventing.knative.dev/broker.class': string; 53 | 'eventing.knative.dev/creator': string; 54 | 'eventing.knative.dev/lastModifier': string; 55 | } 56 | export interface ManagedFields { 57 | apiVersion: string; 58 | fieldsType: string; 59 | fieldsV1: FieldsV1; 60 | manager: string; 61 | operation: string; 62 | time: string; 63 | } 64 | export interface FieldsV1 { 65 | 'f:metadata'?: Record | null; 66 | 'f:spec'?: Record | null; 67 | 'f:status'?: Record | null; 68 | } 69 | export interface Spec { 70 | config: Config; 71 | } 72 | export interface Config { 73 | apiVersion: string; 74 | kind: string; 75 | name: string; 76 | namespace: string; 77 | } 78 | export interface Status { 79 | address: Address; 80 | conditions?: Conditions[] | null; 81 | observedGeneration: number; 82 | } 83 | export interface Address { 84 | url: string; 85 | } 86 | export interface Conditions { 87 | lastTransitionTime: string; 88 | status: string; 89 | type: string; 90 | } 91 | -------------------------------------------------------------------------------- /src/knative/channel.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { KnativeItem } from './knativeItem'; 8 | 9 | export class Channel extends KnativeItem { 10 | constructor( 11 | public name: string, 12 | public parent: string, 13 | public channelType: string, 14 | public details?: Items, 15 | ) { 16 | super(); 17 | } 18 | 19 | annotation?: Map; 20 | 21 | label?: Map; 22 | 23 | namespace?: string; 24 | 25 | modified?: boolean; 26 | 27 | static JSONToChannel(value: Items): Channel { 28 | const channel = new Channel(value.metadata.name, 'Channels', value.spec.channelTemplate?.kind, value); 29 | return channel; 30 | } 31 | } 32 | 33 | export interface Items { 34 | apiVersion: string; 35 | kind: string; 36 | metadata: Metadata; 37 | spec: Spec; 38 | status: Status; 39 | } 40 | export interface Metadata { 41 | annotations: Annotations; 42 | creationTimestamp: string; 43 | generation: number; 44 | managedFields?: ManagedFields[] | null; 45 | name: string; 46 | namespace: string; 47 | resourceVersion: string; 48 | selfLink: string; 49 | uid: string; 50 | } 51 | export interface Annotations { 52 | 'messaging.knative.dev/creator': string; 53 | 'messaging.knative.dev/lastModifier': string; 54 | 'messaging.knative.dev/subscribable': string; 55 | } 56 | export interface ManagedFields { 57 | apiVersion: string; 58 | fieldsType: string; 59 | fieldsV1: FieldsV1; 60 | manager: string; 61 | operation: string; 62 | time: string; 63 | } 64 | export interface FieldsV1 { 65 | 'f:metadata'?: Record | null; 66 | 'f:spec'?: Record | null; 67 | 'f:status'?: Record | null; 68 | } 69 | export interface Spec { 70 | channelTemplate: ChannelTemplate; 71 | } 72 | export interface ChannelTemplate { 73 | apiVersion: string; 74 | kind: string; 75 | } 76 | export interface Status { 77 | address: Address; 78 | channel: Channel1; 79 | conditions?: Conditions[] | null; 80 | observedGeneration: number; 81 | } 82 | export interface Address { 83 | url: string; 84 | } 85 | export interface Channel1 { 86 | apiVersion: string; 87 | kind: string; 88 | name: string; 89 | namespace: string; 90 | } 91 | export interface Conditions { 92 | lastTransitionTime: string; 93 | status: string; 94 | type: string; 95 | } 96 | -------------------------------------------------------------------------------- /src/knative/genericSource.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { BaseSource, Items } from './baseSource'; 7 | 8 | export type sourceOptions = Array>; 9 | 10 | export class GenericSource extends BaseSource { 11 | constructor( 12 | public name: string, 13 | public parent: string, 14 | public sourceType: string, 15 | public sink: string, 16 | public options?: sourceOptions, 17 | public details?: Items, 18 | ) { 19 | super(name, parent, details); 20 | } 21 | 22 | static JSONToSource(value: Items): GenericSource { 23 | let sinkNameOrUri: string; 24 | if (value.spec.sink) { 25 | if (value.spec.sink.ref) { 26 | sinkNameOrUri = value.spec.sink.ref.name; 27 | } 28 | if (value.spec.sink.uri) { 29 | sinkNameOrUri = value.spec.sink.uri; 30 | } 31 | } 32 | const source = new GenericSource(value.metadata.name, 'Sources', value.kind, sinkNameOrUri, null, value); 33 | return source; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/knative/kEvent.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Broker } from './broker'; 7 | import { Channel } from './channel'; 8 | import { KnativeItem } from './knativeItem'; 9 | import { SourceTypes } from './knativeSources'; 10 | import { Subscription } from './subscription'; 11 | import { Trigger } from './trigger'; 12 | 13 | export class KEvent extends KnativeItem { 14 | constructor(public name: string) { 15 | super(); 16 | } 17 | 18 | // public children: Broker[] | Channel[] | Source[] | Subscription[] | Trigger[]; 19 | public children: Array; 20 | } 21 | -------------------------------------------------------------------------------- /src/knative/knativeBrokers.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Broker } from './broker'; 7 | 8 | /** 9 | * A singleton to hold the Brokers. 10 | * Public methods to control the list of Brokers. 11 | */ 12 | export class KnativeBrokers { 13 | // eslint-disable-next-line no-use-before-define 14 | private static instance: KnativeBrokers; 15 | 16 | // eslint-disable-next-line no-useless-constructor 17 | private constructor() { 18 | // do something if needed, but this is private for the singleton 19 | } 20 | 21 | public static get Instance(): KnativeBrokers { 22 | if (!KnativeBrokers.instance) { 23 | KnativeBrokers.instance = new KnativeBrokers(); 24 | } 25 | return KnativeBrokers.instance; 26 | } 27 | 28 | private brokers: Broker[] = []; 29 | 30 | // eslint-disable-next-line class-methods-use-this 31 | private updateTree(): void { 32 | // TODO: 33 | // tell the tree view to refresh it's look at the data in 'brokers' 34 | // convert brokers to tree objects and then sort them 35 | } 36 | 37 | public getBrokers(): Broker[] { 38 | return this.brokers; 39 | } 40 | 41 | public findBroker(brokerName: string): Broker { 42 | return this.brokers[this.brokers.findIndex((s) => s.name === brokerName)]; 43 | } 44 | 45 | public addBroker(broker: Broker): Broker { 46 | this.brokers.push(broker); 47 | // this.brokers.sort(compareNodes); 48 | this.updateTree(); 49 | return broker; 50 | } 51 | 52 | public addBrokers(brokers: Broker[]): Broker[] { 53 | this.brokers = brokers; 54 | // this.brokers.sort(compareNodes); 55 | this.updateTree(); 56 | return this.brokers; 57 | } 58 | 59 | public updateBroker(broker: Broker): Broker[] { 60 | const updated: Broker[] = this.brokers.map((s) => { 61 | if (s.name === broker.name) { 62 | return broker; 63 | } 64 | return s; 65 | }); 66 | // passing this through a variable to ensure we don't change values while working on them 67 | this.brokers = updated; 68 | return this.brokers; 69 | } 70 | 71 | public removeBroker(name: string): Broker[] { 72 | // find the index of the broker passed in. 73 | const brokerIndex: number = this.brokers.findIndex((s) => s.name === name); 74 | // remove the broker 75 | this.brokers.splice(brokerIndex, 1); 76 | 77 | this.updateTree(); 78 | return this.brokers; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/knative/knativeChannels.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Channel } from './channel'; 7 | 8 | /** 9 | * A singleton to hold the Channels. 10 | * Public methods to control the list of Channels. 11 | */ 12 | export class KnativeChannels { 13 | // eslint-disable-next-line no-use-before-define 14 | private static instance: KnativeChannels; 15 | 16 | // eslint-disable-next-line no-useless-constructor 17 | private constructor() { 18 | // do something if needed, but this is private for the singleton 19 | } 20 | 21 | public static get Instance(): KnativeChannels { 22 | if (!KnativeChannels.instance) { 23 | KnativeChannels.instance = new KnativeChannels(); 24 | } 25 | return KnativeChannels.instance; 26 | } 27 | 28 | private channels: Channel[] = []; 29 | 30 | // eslint-disable-next-line class-methods-use-this 31 | private updateTree(): void { 32 | // TODO: 33 | // tell the tree view to refresh it's look at the data in 'channels' 34 | // convert channels to tree objects and then sort them 35 | } 36 | 37 | public getChannels(): Channel[] { 38 | return this.channels; 39 | } 40 | 41 | public findChannel(channelName: string): Channel { 42 | return this.channels[this.channels.findIndex((s) => s.name === channelName)]; 43 | } 44 | 45 | public addChannel(channel: Channel): Channel { 46 | this.channels.push(channel); 47 | // this.channels.sort(compareNodes); 48 | this.updateTree(); 49 | return channel; 50 | } 51 | 52 | public addChannels(channels: Channel[]): Channel[] { 53 | this.channels = channels; 54 | // this.channels.sort(compareNodes); 55 | this.updateTree(); 56 | return this.channels; 57 | } 58 | 59 | public updateChannel(channel: Channel): Channel[] { 60 | const updated: Channel[] = this.channels.map((s) => { 61 | if (s.name === channel.name) { 62 | return channel; 63 | } 64 | return s; 65 | }); 66 | // passing this through a variable to ensure we don't change values while working on them 67 | this.channels = updated; 68 | return this.channels; 69 | } 70 | 71 | public removeChannel(name: string): Channel[] { 72 | // find the index of the channel passed in. 73 | const channelIndex: number = this.channels.findIndex((s) => s.name === name); 74 | // remove the channel 75 | this.channels.splice(channelIndex, 1); 76 | 77 | this.updateTree(); 78 | return this.channels; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/knative/knativeItem.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { TreeItem } from 'vscode'; 7 | 8 | export class KnativeItem {} 9 | 10 | /** 11 | * The Sort compare function will compare the context type first, then compare the label. 12 | * 13 | * All non-undefined array elements are sorted according to the return value of the compare function (all undefined elements are sorted to the end of the array, with no call to compareFunction). If a and b are two elements being compared, then: 14 | * 15 | * If compareFunction(a, b) returns less than 0, sort a to an index lower than b (i.e. a comes first). 16 | * If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAScript standard does not guarantee this behavior, thus, not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this. 17 | * If compareFunction(a, b) returns greater than 0, sort b to an index lower than a (i.e. b comes first). 18 | * compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If inconsistent results are returned, then the sort order is undefined. 19 | * 20 | * @param a TreeObject 21 | * @param b TreeObject 22 | */ 23 | export function compareNodes(a: TreeItem, b: TreeItem): number { 24 | if (!a.contextValue) { 25 | return -1; 26 | } 27 | if (!b.contextValue) { 28 | return 1; 29 | } 30 | // We do not want to consider sorting on anything after the underscore. 31 | const aContext = a.contextValue.includes('_') ? a.contextValue.substr(0, a.contextValue.indexOf('_')) : a.contextValue; 32 | const bContext = b.contextValue.includes('_') ? b.contextValue.substr(0, b.contextValue.indexOf('_')) : b.contextValue; 33 | const t = aContext.localeCompare(bContext); 34 | const labelA = typeof a.label === 'string' ? a.label : a.label.label; 35 | const labelB = typeof b.label === 'string' ? b.label : b.label.label; 36 | return t || labelA.localeCompare(labelB); 37 | } 38 | -------------------------------------------------------------------------------- /src/knative/pingSource.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-use-before-define */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { BaseSource, Metadata } from './baseSource'; 8 | import { Sink as sinkType } from './sink'; 9 | 10 | export type sourceOptions = Array>; 11 | 12 | export class PingSource extends BaseSource { 13 | constructor( 14 | public name: string, 15 | public parent: string, 16 | public schedule: string, 17 | public data: string, 18 | public sink: string, 19 | public details?: Items, 20 | ) { 21 | super(name, parent, details); 22 | } 23 | 24 | childSink: sinkType; 25 | 26 | static JSONToSource(value: Items): PingSource { 27 | let sinkNameOrUri: string; 28 | if (value.spec.sink) { 29 | if (value.spec.sink.ref) { 30 | sinkNameOrUri = value.spec.sink.ref.name; 31 | } 32 | if (value.spec.sink.uri) { 33 | sinkNameOrUri = value.spec.sink.uri; 34 | } 35 | } 36 | const source = new PingSource(value.metadata.name, 'Sources', value.spec.schedule, value.spec.jsonData, sinkNameOrUri, value); 37 | return source; 38 | } 39 | } 40 | 41 | export interface Items { 42 | apiVersion: string; 43 | kind: string; 44 | metadata: Metadata; 45 | spec: Spec; 46 | status: Status; 47 | } 48 | export interface Spec { 49 | mode?: string | null; 50 | resources?: Resources[] | null; 51 | serviceAccountName?: string | null; 52 | sink: Sink; 53 | jsonData?: string | null; 54 | schedule?: string | null; 55 | subject?: RefOrSubject | null; 56 | } 57 | export interface Resources { 58 | apiVersion: string; 59 | controller: boolean; 60 | controllerSelector: ControllerSelector; 61 | kind: string; 62 | labelSelector: Record; 63 | } 64 | export interface ControllerSelector { 65 | apiVersion: string; 66 | kind: string; 67 | name: string; 68 | uid: string; 69 | } 70 | export interface Sink { 71 | ref?: RefOrSubject | null; 72 | uri?: string | null; 73 | } 74 | export interface RefOrSubject { 75 | apiVersion: string; 76 | kind: string; 77 | name: string; 78 | namespace: string; 79 | } 80 | export interface Status { 81 | conditions?: Conditions[] | null; 82 | observedGeneration: number; 83 | sinkUri: string; 84 | ceAttributes?: CeAttributes[] | null; 85 | } 86 | export interface Conditions { 87 | lastTransitionTime: string; 88 | status: string; 89 | type: string; 90 | message?: string | null; 91 | } 92 | export interface CeAttributes { 93 | source: string; 94 | type: string; 95 | } 96 | -------------------------------------------------------------------------------- /src/knative/sink.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { Broker } from './broker'; 8 | import { Channel } from './channel'; 9 | import { Service } from './service'; 10 | 11 | export type Sink = Broker | Channel | Service | vscode.Uri; 12 | -------------------------------------------------------------------------------- /src/knative/trigger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-argument */ 2 | /* eslint-disable no-use-before-define */ 3 | /*----------------------------------------------------------------------------------------------- 4 | * Copyright (c) Red Hat, Inc. All rights reserved. 5 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 6 | *-----------------------------------------------------------------------------------------------*/ 7 | 8 | import { Broker } from './broker'; 9 | import { KnativeItem } from './knativeItem'; 10 | import { Sink } from './sink'; 11 | 12 | export class Trigger extends KnativeItem { 13 | constructor( 14 | public name: string, 15 | public parent: string, 16 | public broker: string, 17 | public filter: Map, 18 | public sink: string, 19 | public details?: Items, 20 | ) { 21 | super(); 22 | } 23 | 24 | childBroker: Broker; 25 | 26 | childSink: Sink; 27 | 28 | annotation?: Map; 29 | 30 | label?: Map; 31 | 32 | namespace?: string; 33 | 34 | modified?: boolean; 35 | 36 | static JSONToTrigger(value: Items): Trigger { 37 | // create a map of the filter key/value pairs 38 | const filters = new Map(); 39 | if (value.spec.filter?.attributes) { 40 | // eslint-disable-next-line no-restricted-syntax, guard-for-in 41 | for (const f in value.spec.filter.attributes) { 42 | filters.set(f, value.spec.filter.attributes[f]); 43 | } 44 | } 45 | let sub: string; 46 | if (value.spec.subscriber) { 47 | if (value.spec.subscriber.ref) { 48 | sub = value.spec.subscriber.ref.name; 49 | } 50 | if (value.spec.subscriber.uri) { 51 | sub = value.spec.subscriber.uri; 52 | } 53 | } 54 | const trigger = new Trigger(value.metadata.name, 'Triggers', value.spec.broker, filters, sub, value); 55 | return trigger; 56 | } 57 | } 58 | 59 | export interface Items { 60 | apiVersion: string; 61 | kind: string; 62 | metadata: Metadata; 63 | spec: Spec; 64 | status: Status; 65 | } 66 | export interface Metadata { 67 | annotations: Annotations; 68 | creationTimestamp: string; 69 | generation: number; 70 | labels: Labels; 71 | managedFields?: ManagedFields[] | null; 72 | name: string; 73 | namespace: string; 74 | resourceVersion: string; 75 | selfLink: string; 76 | uid: string; 77 | } 78 | export interface Annotations { 79 | 'eventing.knative.dev/creator': string; 80 | 'eventing.knative.dev/lastModifier': string; 81 | } 82 | export interface Labels { 83 | 'eventing.knative.dev/broker': string; 84 | } 85 | export interface ManagedFields { 86 | apiVersion: string; 87 | fieldsType: string; 88 | fieldsV1: FieldsV1; 89 | manager: string; 90 | operation: string; 91 | time: string; 92 | } 93 | export interface FieldsV1 { 94 | 'f:metadata'?: Record | null; 95 | 'f:spec'?: Record | null; 96 | 'f:status'?: Record | null; 97 | } 98 | export interface Spec { 99 | broker: string; 100 | filter: Filter; 101 | subscriber: Subscriber; 102 | } 103 | export interface Filter { 104 | attributes: Attributes; 105 | } 106 | export interface Attributes { 107 | name: string; 108 | type: string; 109 | } 110 | export interface Subscriber { 111 | ref?: Ref | null; 112 | uri?: string | null; 113 | } 114 | export interface Ref { 115 | apiVersion: string; 116 | kind: string; 117 | name: string; 118 | namespace: string; 119 | } 120 | export interface Status { 121 | conditions?: Conditions[] | null; 122 | observedGeneration: number; 123 | subscriberUri: string; 124 | } 125 | export interface Conditions { 126 | lastTransitionTime: string; 127 | status: string; 128 | type: string; 129 | } 130 | -------------------------------------------------------------------------------- /src/output/knOutputChannel.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { prettifyJson } from '../util/format'; 8 | 9 | /** 10 | * An interface that requires the implementation of: 11 | * @function print 12 | * @function show 13 | */ 14 | export interface OutputChannel { 15 | print(text: string): void; 16 | show(): void; 17 | } 18 | 19 | /** 20 | * Write to and display information in the Knative Output window/channel. 21 | * An output channel is a container for readonly textual information. 22 | * 23 | * @function print 24 | * @function show 25 | */ 26 | export class KnOutputChannel implements OutputChannel { 27 | private readonly channel: vscode.OutputChannel = vscode.window.createOutputChannel('Knative'); 28 | 29 | /** 30 | * Display the output channel. 31 | */ 32 | show(): void { 33 | this.channel.show(); 34 | } 35 | 36 | /** 37 | * Take and display it in the output channel. 38 | * 39 | * If it has `--token=xxx` convert it to `--token= *****`. 40 | * 41 | * Open the Knative Output channel if set in config. 42 | * @param text 43 | */ 44 | public print(text: string): void { 45 | const textData: string = prettifyJson(text); 46 | this.channel.append(textData); 47 | if (!textData.endsWith('\n')) { 48 | this.channel.append('\n'); 49 | } 50 | if (vscode.workspace.getConfiguration('knative').get('showChannelOnOutput')) { 51 | this.channel.show(); 52 | } 53 | } 54 | } 55 | 56 | export const knOutputChannel: OutputChannel = new KnOutputChannel(); 57 | -------------------------------------------------------------------------------- /src/reportIssue.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-return-await */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 4 | /*----------------------------------------------------------------------------------------------- 5 | * Copyright (c) Red Hat, Inc. All rights reserved. 6 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 7 | *-----------------------------------------------------------------------------------------------*/ 8 | 9 | import * as vscode from 'vscode'; 10 | import { extensions, version } from 'vscode'; 11 | import { Platform } from './util/platform'; 12 | 13 | function issueUrl(): string { 14 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 15 | const { packageJSON } = extensions.getExtension('redhat.vscode-knative'); 16 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access 17 | const body = [`VS Code version: ${version}`, `OS: ${Platform.OS}`, `Extension version: ${packageJSON.version}`].join('\n'); 18 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-unsafe-member-access 19 | return `${packageJSON.bugs}/new?labels=kind/bug&title=&body=**Environment**\n${body}\n**Description**`; 20 | } 21 | 22 | export async function reportIssue(): Promise { 23 | return await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(issueUrl())); 24 | } 25 | -------------------------------------------------------------------------------- /src/servingTree/servingExplorer.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { Disposable, TreeView, window, workspace } from 'vscode'; 8 | import * as vscode from 'vscode'; 9 | // eslint-disable-next-line import/no-cycle 10 | import { ServingDataProvider } from './servingDataProvider'; 11 | import { ServingTreeItem } from './servingTreeItem'; 12 | import { EventingTreeItem } from '../eventingTree/eventingTreeItem'; 13 | import { Platform } from '../util/platform'; 14 | import { WatchUtil, FileContentChangeNotifier } from '../util/watch'; 15 | 16 | const kubeConfigFolder: string = path.join(Platform.getUserHomePath(), '.kube'); 17 | const kubeconfigParam: string[][] = [[kubeConfigFolder, 'config']]; 18 | const kubeconfigEnv: string = process.env.KUBECONFIG; 19 | let kubeconfigList: string[] = []; 20 | // check to make sure there is an ENV for KUBECONFIG 21 | if (kubeconfigEnv) { 22 | kubeconfigList = kubeconfigEnv.split(path.delimiter); 23 | } 24 | kubeconfigList.forEach((value): void => { 25 | const kubeconfigSplit: string[] = value.split(path.sep); 26 | const kubeconfigFileName: string = kubeconfigSplit.pop(); 27 | const kubeconfigDir: string = kubeconfigSplit.join(path.sep); 28 | kubeconfigParam.push([kubeconfigDir, kubeconfigFileName]); 29 | }); 30 | 31 | export class ServingExplorer implements Disposable { 32 | public treeView: TreeView; 33 | 34 | public fsw: FileContentChangeNotifier[] = []; 35 | 36 | public registeredCommands: Disposable[] = []; 37 | 38 | public treeDataProvider: ServingDataProvider; 39 | 40 | public constructor() { 41 | this.treeDataProvider = new ServingDataProvider(); 42 | 43 | // Watch the local kubeconfig locations for changes and refresh when one changes. 44 | kubeconfigParam.forEach((params) => { 45 | const l = this.fsw.push(WatchUtil.watchFileForContextChange(params[0], params[1])); 46 | this.fsw[l - 1].emitter.on('file-changed', () => this.treeDataProvider.refresh()); 47 | }); 48 | 49 | if (workspace.getConfiguration('knative').get('pollRefresh')) { 50 | this.treeDataProvider.pollRefresh(); 51 | } 52 | 53 | // Initialize the tree/explorer view by linking the reference in the package.json to this class. 54 | this.treeView = window.createTreeView('knativeServingProjectExplorer', { treeDataProvider: this.treeDataProvider }); 55 | 56 | this.registeredCommands = [ 57 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 58 | vscode.commands.registerCommand('service.output', () => this.treeDataProvider.showOutputChannel()), 59 | vscode.commands.registerCommand('service.explorer.create', () => this.treeDataProvider.addService()), 60 | vscode.commands.registerCommand('service.explorer.delete', (treeItem: ServingTreeItem) => 61 | this.treeDataProvider.deleteFeature(treeItem), 62 | ), 63 | vscode.commands.registerCommand('service.explorer.tag', (treeItem: ServingTreeItem) => 64 | this.treeDataProvider.addTag(treeItem), 65 | ), 66 | vscode.commands.registerCommand('service.explorer.refresh', () => this.treeDataProvider.refresh()), 67 | ]; 68 | } 69 | 70 | dispose(): void { 71 | this.registeredCommands.forEach((command) => { 72 | command.dispose(); 73 | }); 74 | this.fsw.forEach((value) => { 75 | value.watcher.close(); 76 | }); 77 | this.treeView.dispose(); 78 | } 79 | 80 | public async reveal(item: ServingTreeItem): Promise { 81 | await this.treeView.reveal(item); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/telemetry.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-floating-promises */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { rejects } from 'assert'; 8 | import { ExtensionContext } from 'vscode'; 9 | import { getRedHatService, TelemetryEvent, TelemetryService } from '@redhat-developer/vscode-redhat-telemetry'; 10 | import { hideClusterInfo } from './util/hideclusterinformation'; 11 | import { getStderrString } from './util/stderrstring'; 12 | 13 | let telemetryService: TelemetryService; 14 | 15 | export interface TelemetryProperties { 16 | identifier?: string; 17 | version?: string; 18 | duration?: string; 19 | runtime?: string; 20 | error?: string; 21 | runtimeversion?: string; 22 | mission?: string; 23 | message?: string; 24 | } 25 | 26 | export async function startTelemetry(context: ExtensionContext): Promise { 27 | try { 28 | const redHatService = await getRedHatService(context); 29 | telemetryService = await redHatService.getTelemetryService(); 30 | } catch (error) { 31 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument 32 | rejects(error); 33 | } 34 | return telemetryService?.sendStartupEvent(); 35 | } 36 | 37 | export function telemetryProperties(commandId: string): TelemetryProperties { 38 | return { 39 | identifier: commandId, 40 | }; 41 | } 42 | 43 | export function createTrackingEvent(name: string, properties = {}): TelemetryEvent { 44 | return { 45 | type: 'track', 46 | name, 47 | properties, 48 | }; 49 | } 50 | 51 | export async function sendTelemetry(actionName: string, properties?: TelemetryProperties): Promise { 52 | return telemetryService?.send(createTrackingEvent(actionName, properties)); 53 | } 54 | 55 | export function telemetryLogError(identifier: string, result: string | Error): void { 56 | if (identifier) { 57 | const telemetryProps: TelemetryProperties = telemetryProperties(`${identifier}_error`); 58 | const message = getStderrString(result); 59 | telemetryProps.error = hideClusterInfo(message); 60 | sendTelemetry(`${identifier}_error`, telemetryProps); 61 | } 62 | } 63 | 64 | export function telemetryLog(identifier: string, message?: string): void { 65 | if (identifier) { 66 | const telemetryProps: TelemetryProperties = telemetryProperties(identifier); 67 | if (message) { 68 | telemetryProps.message = message; 69 | } 70 | sendTelemetry(identifier, telemetryProps); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/util/archive.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 4 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 5 | /* eslint-disable no-unused-expressions */ 6 | /*----------------------------------------------------------------------------------------------- 7 | * Copyright (c) Red Hat, Inc. All rights reserved. 8 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 9 | *-----------------------------------------------------------------------------------------------*/ 10 | 11 | import * as fs from 'fs'; 12 | import * as zlib from 'zlib'; 13 | import * as tar from 'tar-fs'; 14 | import targz = require('targz'); 15 | import unzipm = require('unzip-stream'); 16 | 17 | export class Archive { 18 | static unzip(zipFile: string, extractTo: string, prefix?: string): Promise { 19 | return new Promise((resolve, reject) => { 20 | if (zipFile.endsWith('.tar.gz')) { 21 | targz.decompress( 22 | { 23 | src: zipFile, 24 | dest: extractTo, 25 | tar: { 26 | map: (header: tar.Headers) => { 27 | // eslint-disable-next-line no-param-reassign 28 | prefix && header.name.startsWith(prefix) ? (header.name = header.name.substring(prefix.length)) : header; 29 | return header; 30 | }, 31 | }, 32 | }, 33 | (err) => { 34 | err ? reject(err) : resolve(); 35 | }, 36 | ); 37 | } else if (zipFile.endsWith('.gz')) { 38 | Archive.gunzip(zipFile, extractTo).then(resolve).catch(reject); 39 | } else if (zipFile.endsWith('.zip')) { 40 | fs.createReadStream(zipFile) 41 | .pipe(unzipm.Extract({ path: extractTo })) 42 | .on('error', reject) 43 | .on('close', resolve); 44 | } else { 45 | // eslint-disable-next-line prefer-promise-reject-errors 46 | reject(`Unsupported extension for '${zipFile}'`); 47 | } 48 | }); 49 | } 50 | 51 | static gunzip(source: fs.PathLike, destination: fs.PathLike): Promise { 52 | return new Promise((res, rej) => { 53 | const src = fs.createReadStream(source); 54 | const dest = fs.createWriteStream(destination); 55 | src.pipe(zlib.createGunzip()).pipe(dest); 56 | dest.on('close', res); 57 | dest.on('error', rej); 58 | src.on('error', rej); 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/util/constants.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export enum GlyphChars { 7 | Asterisk = '\u2217', 8 | Check = '\u2713', 9 | Space = '\u00a0', 10 | Push = '\u25C9', 11 | NotPushed = '\u25CE', 12 | NoContext = '\u25CB', 13 | LocallyDeleted = '\u2297', 14 | } 15 | -------------------------------------------------------------------------------- /src/util/credential-helper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import * as path from 'path'; 8 | import { readdir } from 'fs-extra'; 9 | 10 | export async function getHelpers(): Promise> { 11 | const isCredHelper = (f: string) => f.startsWith('docker-credential'); 12 | 13 | return process.env.PATH.split(path.delimiter) 14 | .map(async (dir) => { 15 | try { 16 | return (await readdir(dir)).filter(isCredHelper); 17 | } catch (e) { 18 | // maybe log error (as warning) via some other facility than `console` 19 | return []; 20 | } 21 | }) 22 | .reduce(async (a, b) => (await a).concat(await b), Promise.resolve>([])); 23 | } 24 | -------------------------------------------------------------------------------- /src/util/download.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 6 | 7 | import * as fs from 'fs-extra'; 8 | import request = require('request'); 9 | import progress = require('request-progress'); 10 | 11 | export class DownloadUtil { 12 | static downloadFile( 13 | fromUrl: string, 14 | toFile: string, 15 | progressCallBack?: (current: number, increment: number) => void, 16 | throttle?: number, 17 | ): Promise { 18 | return new Promise((resolve, reject) => { 19 | let previous = 0; 20 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 21 | progress(request(fromUrl), { 22 | throttle: throttle || 250, 23 | delay: 0, 24 | lengthHeader: 'content-length', 25 | }) 26 | // eslint-disable-next-line @typescript-eslint/no-empty-function 27 | .on('response', () => {}) 28 | .on('progress', (state: { percent: number }) => { 29 | const current = Math.round(state.percent * 100); 30 | if (current !== previous && progressCallBack) { 31 | progressCallBack(current, current - previous); 32 | } 33 | previous = current; 34 | }) 35 | .on('error', reject) 36 | .on('end', () => progressCallBack && progressCallBack(100, 100 - previous)) 37 | .pipe(fs.createWriteStream(toFile)) 38 | .on('close', resolve) 39 | .on('error', reject); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/util/errorable.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | // Copied from https://github.com/Azure/vscode-kubernetes-tools/blob/master/src/errorable.ts 3 | 4 | /*----------------------------------------------------------------------------------------------- 5 | * Copyright (c) Microsoft Corporation. All rights reserved. 6 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 7 | *-----------------------------------------------------------------------------------------------*/ 8 | 9 | export interface Succeeded { 10 | readonly succeeded: true; 11 | readonly result: T; 12 | } 13 | 14 | export interface Failed { 15 | readonly succeeded: false; 16 | readonly error: string[]; 17 | } 18 | 19 | export type Errorable = Succeeded | Failed; 20 | 21 | // export function succeeded(e: Errorable): e is Succeeded { 22 | // return e.succeeded; 23 | // } 24 | 25 | // export function failed(e: Errorable): e is Failed { 26 | // return !e.succeeded; 27 | // } 28 | 29 | // eslint-disable-next-line @typescript-eslint/no-namespace 30 | export namespace Errorable { 31 | // eslint-disable-next-line no-inner-declarations 32 | export function succeeded(e: Errorable): e is Succeeded { 33 | return e.succeeded; 34 | } 35 | 36 | // eslint-disable-next-line no-inner-declarations 37 | export function failed(e: Errorable): e is Failed { 38 | return !e.succeeded; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/util/existing-workspace-folder-pick.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | 8 | export class ExistingWorkspaceFolderPick { 9 | // eslint-disable-next-line no-useless-constructor, no-empty-function 10 | constructor(public readonly workspaceFolder: vscode.WorkspaceFolder) {} 11 | 12 | get label(): string { 13 | return this.workspaceFolder.name; 14 | } 15 | 16 | get description(): string { 17 | return this.workspaceFolder.uri.fsPath; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/util/filters.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export class Filters { 7 | static readonly tokenRegex = /--token=[^\s]*/; 8 | 9 | static filterToken(value: string): string { 10 | return value ? value.replace(Filters.tokenRegex, '--token **********') : value; 11 | } 12 | 13 | static readonly passwordRegex = /-p\s+'([^']+)'/; 14 | 15 | static filterPassword(value: string): string { 16 | return value ? value.replace(Filters.passwordRegex, '-p **********') : value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/util/format.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Filters } from './filters'; 7 | 8 | export function prettifyJson(str: string): string { 9 | let jsonData: string; 10 | try { 11 | jsonData = JSON.stringify(JSON.parse(str), null, 2); 12 | } catch (ignore) { 13 | const hidePass = Filters.filterToken(str); 14 | return Filters.filterPassword(hidePass); 15 | } 16 | return jsonData; 17 | } 18 | -------------------------------------------------------------------------------- /src/util/hideclusterinformation.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | const IPV6Address = 7 | /((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}/gm; 8 | export function hideIPV6Address(message: string): string { 9 | if (IPV6Address.test(message)) { 10 | const ipv6Address = message.match(IPV6Address); 11 | if (ipv6Address && ipv6Address.length !== 0) { 12 | ipv6Address.forEach((data) => { 13 | // eslint-disable-next-line no-param-reassign 14 | message = message.replace(data, 'IPV6 Address '); 15 | }); 16 | } 17 | } 18 | return message; 19 | } 20 | 21 | const IpAddress = 22 | /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)+/gm; 23 | export function hideIPAddress(message: string): string { 24 | if (IpAddress.test(message)) { 25 | const ipAddress = message.match(IpAddress); 26 | if (ipAddress && ipAddress.length !== 0) { 27 | ipAddress.forEach((data) => { 28 | // eslint-disable-next-line no-param-reassign 29 | message = message.replace(data, 'IP Address '); 30 | }); 31 | } 32 | } 33 | return hideIPV6Address(message); 34 | } 35 | 36 | // eslint-disable-next-line no-useless-escape 37 | const pathMatch = /(?:"([a-zA-Z]\:(\\|\/|\\\\)|\/([^\/]+\/)|.\/|([^\/"]+\/)))([^\\\/\:\*\?\<\>\"\|]+(\\|\/){0,2})+/gm; 38 | export function hidePath(message: string): string { 39 | if (pathMatch.test(message)) { 40 | const paths = message.match(pathMatch); 41 | if (paths && paths.length !== 0) { 42 | paths.forEach((path) => { 43 | // eslint-disable-next-line no-param-reassign 44 | message = message.replace(path, '"local path'); 45 | }); 46 | } 47 | } 48 | return hideIPAddress(message); 49 | } 50 | 51 | const urlCheck = /(https?:\/\/[^\s]+)/g; 52 | export function hideClusterUrl(message: string): string { 53 | if (urlCheck.test(message)) { 54 | // eslint-disable-next-line no-param-reassign 55 | message = message.replace(urlCheck, 'cluster info '); 56 | } 57 | return hidePath(message); 58 | } 59 | 60 | const clusterInfo = /lookup\s+([^']+):/; 61 | export function hideClusterInfo(message: string): string { 62 | if (clusterInfo.test(message)) { 63 | // eslint-disable-next-line no-param-reassign 64 | message = message.replace(clusterInfo, 'lookup cluster info: '); 65 | } 66 | return hideClusterUrl(message); 67 | } 68 | -------------------------------------------------------------------------------- /src/util/parse.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { readFile } from 'fs'; 7 | import { Uri } from 'vscode'; 8 | 9 | /** 10 | * Promisify fs.readFile to parse JSON from a file. 11 | * 12 | * @param filePath Takes the path to a JSON file. 13 | * @returns Promise 14 | */ 15 | export function loadJSON(filePath: string): Promise { 16 | return new Promise((resolve, reject) => { 17 | readFile(require.resolve(filePath), 'utf-8', (err, data) => { 18 | if (err) { 19 | reject(err); 20 | } else { 21 | try { 22 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument 23 | resolve(JSON.parse(data)); 24 | } catch (parseErr) { 25 | reject(parseErr); 26 | } 27 | } 28 | }); 29 | }); 30 | } 31 | 32 | /** 33 | * Take a potential URI string, check if it starts with `http://` or `https://` and create URI for it. 34 | * @param uriString 35 | * @returns vscode.Uri 36 | */ 37 | export function convertStringToURI(uriString: string): Uri { 38 | let uri: Uri; 39 | if (uriString.startsWith('http://') || uriString.startsWith('https://')) { 40 | uri = Uri.parse(uriString); 41 | } 42 | return uri; 43 | } 44 | -------------------------------------------------------------------------------- /src/util/platform.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export class Platform { 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents 8 | static identify(map: { [x: string]: () => any; win32?: () => string; default: any }): any | undefined { 9 | if (map[Platform.OS]) { 10 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 11 | return map[Platform.OS](); 12 | } 13 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call 14 | return map.default ? map.default() : undefined; 15 | } 16 | 17 | static getOS(): string { 18 | return process.platform; 19 | } 20 | 21 | static get OS(): string { 22 | return Platform.getOS(); 23 | } 24 | 25 | static get ENV(): NodeJS.ProcessEnv { 26 | return Platform.getEnv(); 27 | } 28 | 29 | static getEnv(): NodeJS.ProcessEnv { 30 | return process.env; 31 | } 32 | 33 | static getUserHomePath(): string { 34 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 35 | return Platform.identify({ 36 | win32: () => Platform.ENV.USERPROFILE, 37 | default: () => Platform.ENV.HOME, 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/util/quote.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Platform } from './platform'; 7 | 8 | export const quote = Platform.OS === 'win32' ? '"' : "'"; 9 | -------------------------------------------------------------------------------- /src/util/stderrstring.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | export function getStderrString(data: string | Error): string { 7 | if (data instanceof Error) { 8 | return data.message; 9 | } 10 | if (typeof data === 'string') { 11 | return data; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/util/watch.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { EventEmitter } from 'events'; 7 | import { createReadStream, FSWatcher } from 'fs'; 8 | import { join } from 'path'; 9 | import byline = require('byline'); 10 | import { ensureDirSync, watch } from 'fs-extra'; 11 | 12 | export interface FileContentChangeNotifier { 13 | readonly watcher: FSWatcher; 14 | readonly emitter: EventEmitter; 15 | } 16 | 17 | export class WatchUtil { 18 | static watchFileForContextChange(location: string, filename: string): FileContentChangeNotifier { 19 | const emitter: EventEmitter = new EventEmitter(); 20 | let timer: NodeJS.Timer; 21 | let context = ''; 22 | ensureDirSync(location); 23 | const watcher: FSWatcher = watch(location, (_eventType, changedFile) => { 24 | if (filename === changedFile) { 25 | if (timer) { 26 | clearTimeout(timer); 27 | } 28 | timer = setTimeout(() => { 29 | timer = undefined; 30 | WatchUtil.grep(join(location, filename), /current-context:.*/) 31 | .then((newContext: string) => { 32 | if (context !== newContext) { 33 | emitter.emit('file-changed'); 34 | context = newContext; 35 | } 36 | }) 37 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions 38 | .catch((error) => `There was an error looking for the file. ${error}`); 39 | }, 500); 40 | } 41 | }); 42 | return { watcher, emitter }; 43 | } 44 | 45 | static grep(fileLocation: string, rx: RegExp): Promise { 46 | return new Promise((resolve, reject) => { 47 | const fileStream = createReadStream(fileLocation, { encoding: 'utf8' }); 48 | byline(fileStream) 49 | .on('data', (line: string) => { 50 | if (rx.test(line)) { 51 | fileStream.close(); 52 | resolve(line); 53 | } 54 | }) 55 | .on('error', reject) 56 | .on('end', resolve); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/util/windowUtils.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { window, Terminal, TerminalOptions } from 'vscode'; 8 | 9 | /** 10 | * Utility for VSCode windows. 11 | */ 12 | export class WindowUtil { 13 | /** 14 | * Create a new Terminal in VSCode with the CLI tool location on the PATH. 15 | * 16 | * @param name 17 | * @param cwd 18 | * @param toolLocation 19 | * @param env 20 | * 21 | * @returns terminal 22 | */ 23 | static createTerminal(name: string, cwd: string, toolLocation?: string, env: NodeJS.ProcessEnv = process.env): Terminal { 24 | const finalEnv: NodeJS.ProcessEnv = {}; 25 | // Copy everything from 'env' to 'finalEnv' so that we don't change the param 'env' 26 | Object.assign(finalEnv, env); 27 | // Check if we are on a 'win32' machine and set the key to Path, if not then use PATH 28 | const key = process.platform === 'win32' ? 'Path' : 'PATH'; 29 | 30 | // If there is a toolLocation and and env.PATH and the env.PATH doesn't have the toolLocation on it. 31 | // Then set the toolLocation on the PATH in the new finalEnv. 32 | if (toolLocation && env[key] && !env[key].includes(toolLocation)) { 33 | finalEnv[key] = `${toolLocation}${path.delimiter}${env[key]}`; 34 | } 35 | // Set up the options to be passed into the new terminal. 36 | const options: TerminalOptions = { 37 | cwd, 38 | name, 39 | env: finalEnv, 40 | shellPath: process.platform === 'win32' ? undefined : '/bin/bash', 41 | }; 42 | // Make a new terminal in VSCode with the toolLocation on the PATH. 43 | return window.createTerminal(options); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-cycle */ 2 | /*----------------------------------------------------------------------------------------------- 3 | * Copyright (c) Red Hat, Inc. All rights reserved. 4 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 5 | *-----------------------------------------------------------------------------------------------*/ 6 | 7 | import { knExecutor } from './cli/execute'; 8 | import { FuncAPI } from './cli/func-api'; 9 | import { KnAPI } from './cli/kn-api'; 10 | import { telemetryLog } from './telemetry'; 11 | 12 | export function knativeVersion(): void { 13 | telemetryLog('knative.version', 'Knative Version command click'); 14 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 15 | knExecutor.executeInTerminal(KnAPI.printKnVersion()); 16 | } 17 | 18 | export function functionVersion(): void { 19 | telemetryLog('function.version', 'Function Version command click'); 20 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 21 | knExecutor.executeInTerminal(FuncAPI.printFunctionVersion()); 22 | } 23 | -------------------------------------------------------------------------------- /test/cli/kubectl-api.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as chai from 'chai'; 3 | import rewire = require('rewire'); 4 | import * as sinon from 'sinon'; 5 | import * as sinonChai from 'sinon-chai'; 6 | import { CliCommand, CmdCli } from '../../src/cli/cmdCli'; 7 | import { KubectlAPI } from '../../src/cli/kubectl-api'; 8 | 9 | chai.use(sinonChai); 10 | 11 | suite('Kubectl CLI Command', () => { 12 | test('should create a proper command string', () => { 13 | const api = rewire('../../src/cli/kubectl-api'); 14 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 15 | const kubectlCliCommand = api.__get__('kubectlCliCommand'); 16 | const kubectlArguments: string[] = ['version', '--short', '--client']; 17 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call 18 | // eslint-disable-next-line 19 | const commandApi: CliCommand = kubectlCliCommand(kubectlArguments); 20 | const command: CliCommand = { 21 | cliArguments: ['version', '--short', '--client'], 22 | cliCommand: 'kubectl', 23 | }; 24 | 25 | expect(commandApi).to.deep.equal(command); 26 | }); 27 | }); 28 | 29 | suite('Kubectl API commands that will', () => { 30 | suite('Apply a file', () => { 31 | test('should apply a YAML file', () => { 32 | const command: CliCommand = { 33 | cliArguments: ['apply', '-f', './path.yaml'], 34 | cliCommand: 'kubectl', 35 | }; 36 | const commandAPI = KubectlAPI.applyYAML('./path.yaml', { override: false }); 37 | expect(commandAPI.cliArguments).to.deep.equal(command.cliArguments); 38 | }); 39 | }); 40 | 41 | suite('Print version', () => { 42 | test('should return command for printing the version of Kubectl', () => { 43 | const command: CliCommand = { 44 | cliArguments: ['version', '-ojson'], 45 | cliCommand: 'kubectl', 46 | }; 47 | expect(KubectlAPI.printVersion().cliArguments).to.deep.equal(command.cliArguments); 48 | }); 49 | }); 50 | 51 | const sandbox = sinon.createSandbox(); 52 | 53 | teardown(() => { 54 | sandbox.restore(); 55 | }); 56 | 57 | suite('Get version', () => { 58 | test('should return the version number', async () => { 59 | sandbox.stub(CmdCli.getInstance(), 'execute').resolves({ error: undefined, stdout: 'Client Version: v1.18.3' }); 60 | const version = await KubectlAPI.getKubectlVersion('path/to/kubectl'); 61 | expect(version).to.equal('1.18.3'); 62 | }); 63 | test('should return Undefined when the version is not the correct text', async () => { 64 | sandbox.stub(CmdCli.getInstance(), 'execute').resolves({ error: undefined, stdout: 'not the version text' }); 65 | const version = await KubectlAPI.getKubectlVersion('path/to/kubectl'); 66 | expect(version).to.equal(undefined); 67 | }); 68 | test('should return Undefined when the version is not returned', async () => { 69 | sandbox.stub(CmdCli.getInstance(), 'execute').resolves({ error: undefined, stdout: undefined }); 70 | const version = await KubectlAPI.getKubectlVersion('path/to/kubectl'); 71 | expect(version).to.equal(undefined); 72 | }); 73 | test('should return Undefined for errors', async () => { 74 | sandbox.stub(CmdCli.getInstance(), 'execute').throws({ error: 'Error generated by a test', stdout: undefined }); 75 | const version = await KubectlAPI.getKubectlVersion('path/to/kubectl'); 76 | expect(version).to.equal(undefined); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/editor/knativeSchemaRegister.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 3 | import { expect } from 'chai'; 4 | import rewire = require('rewire'); 5 | import * as serviceSchema from '../../schemas/knservice.json'; 6 | 7 | const rewiredSchemaRegister = rewire('../../src/editor/knativeSchemaRegister'); 8 | const schemaJSON = JSON.stringify(serviceSchema); 9 | 10 | suite('Register Schema', () => { 11 | const uriMock = rewiredSchemaRegister.__get__('onRequestSchemaURI'); 12 | const contentMock = rewiredSchemaRegister.__get__('onRequestSchemaContent'); 13 | const knmsxUriString = 14 | 'knmsx://loadknativecore/service-example.yaml?contextValue%3Dservice%26name%3Dexample%26_%3D1594328823824'; 15 | const knreadonlyUriString = 16 | 'knreadonly://loadknativecore/service-example.yaml?contextValue%3Dservice%26name%3Dexample%26_%3D1594328823824'; 17 | 18 | test('should return the schema URI when requested', () => { 19 | const returnedSchema: string = uriMock(knmsxUriString); 20 | expect(returnedSchema, `knmsx://schema/knative`); 21 | }); 22 | test('should return undefined when the schema is knreadonly instead of returning the URI', () => { 23 | const returnedSchema: string = uriMock(knreadonlyUriString); 24 | expect(returnedSchema, undefined); 25 | }); 26 | test('should return the schema content', () => { 27 | const returnedSchema: string = contentMock(knmsxUriString); 28 | expect(returnedSchema, schemaJSON); 29 | }); 30 | test('should return undefined when the schema ia knreadonly instead of returning the schema content', () => { 31 | const returnedSchema: string = contentMock(knreadonlyUriString); 32 | expect(returnedSchema, undefined); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/eventingTree/eventingExplorer.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { expect } from 'chai'; 3 | import * as chai from 'chai'; 4 | import { beforeEach } from 'mocha'; 5 | import * as sinon from 'sinon'; 6 | import * as sinonChai from 'sinon-chai'; 7 | import { EventingContextType } from '../../src/cli/config'; 8 | import { EventingExplorer } from '../../src/eventingTree/eventingExplorer'; 9 | import { EventingTreeItem } from '../../src/eventingTree/eventingTreeItem'; 10 | 11 | chai.use(sinonChai); 12 | 13 | let eventingExplorer: EventingExplorer; 14 | 15 | const eventingFolderNodes = [ 16 | new EventingTreeItem( 17 | null, 18 | null, 19 | { label: 'Brokers' }, 20 | EventingContextType.BROKER_FOLDER, 21 | vscode.TreeItemCollapsibleState.Expanded, 22 | null, 23 | null, 24 | ), 25 | new EventingTreeItem( 26 | null, 27 | null, 28 | { label: 'Channels' }, 29 | EventingContextType.CHANNEL_FOLDER, 30 | vscode.TreeItemCollapsibleState.Expanded, 31 | null, 32 | null, 33 | ), 34 | new EventingTreeItem( 35 | null, 36 | null, 37 | { label: 'Sources' }, 38 | EventingContextType.SOURCE_FOLDER, 39 | vscode.TreeItemCollapsibleState.Expanded, 40 | null, 41 | null, 42 | ), 43 | new EventingTreeItem( 44 | null, 45 | null, 46 | { label: 'Subscriptions' }, 47 | EventingContextType.SUBSCRIPTION_FOLDER, 48 | vscode.TreeItemCollapsibleState.Expanded, 49 | null, 50 | null, 51 | ), 52 | new EventingTreeItem( 53 | null, 54 | null, 55 | { label: 'Triggers' }, 56 | EventingContextType.TRIGGER_FOLDER, 57 | vscode.TreeItemCollapsibleState.Expanded, 58 | null, 59 | null, 60 | ), 61 | ]; 62 | 63 | suite('EventingExplorer', () => { 64 | const sandbox = sinon.createSandbox(); 65 | 66 | beforeEach(() => { 67 | sandbox.stub(vscode.window, 'showErrorMessage').resolves(); 68 | }); 69 | 70 | teardown(() => { 71 | sandbox.restore(); 72 | }); 73 | 74 | test('should add registered commands to', () => { 75 | // This test allows us to create a new EventingExplorer after the one in Extension is called 76 | // giving the extension test enough time to dispose of it. Otherwise we would have 2 registered 77 | // commands for each one in EventingExplorer. 78 | eventingExplorer = new EventingExplorer(); 79 | expect(eventingExplorer.registeredCommands).to.be.lengthOf(1); 80 | }); 81 | 82 | test('should connect the output command to refreshing the tree', async () => { 83 | const stub = sandbox.stub(eventingExplorer.treeDataProvider, 'refresh').returns(null); 84 | await vscode.commands.executeCommand('eventing.explorer.refresh'); 85 | sinon.assert.calledOnce(stub); 86 | }); 87 | 88 | test('should reveal the tree view', async () => { 89 | const stub = sandbox.stub(eventingExplorer.treeView, 'reveal').resolves(); 90 | await eventingExplorer.reveal(eventingFolderNodes[0]); 91 | sinon.assert.calledOnce(stub); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/fixtures/func-test/func.yaml: -------------------------------------------------------------------------------- 1 | name: node-test 2 | namespace: "test" 3 | runtime: go 4 | image: docker.io/test/node-test:latest 5 | imageDigest: sha256:6a064d4134d53baebcf11caea45585cd288839646d7ce7a42c8f8d582c6b954a 6 | builder: gcr.io/paketo-buildpacks/builder:base 7 | builders: 8 | base: gcr.io/paketo-buildpacks/builder:base 9 | default: gcr.io/paketo-buildpacks/builder:base 10 | full: gcr.io/paketo-buildpacks/builder:full 11 | buildpacks: 12 | - paketo-buildpacks/go-dist 13 | - ghcr.io/boson-project/go-function-buildpack:tip 14 | healthEndpoints: 15 | liveness: /health/liveness 16 | readiness: /health/readiness 17 | volumes: [] 18 | buildEnvs: [] 19 | envs: [] 20 | annotations: {} 21 | options: {} 22 | labels: [] 23 | -------------------------------------------------------------------------------- /test/fixtures/func-test1/func.yaml: -------------------------------------------------------------------------------- 1 | name: node-test 2 | namespace: "" 3 | runtime: go 4 | image: "" 5 | imageDigest: sha256:6a064d4134d53baebcf11caea45585cd288839646d7ce7a42c8f8d582c6b954a 6 | builder: "" 7 | builders: 8 | base: gcr.io/paketo-buildpacks/builder:base 9 | default: gcr.io/paketo-buildpacks/builder:base 10 | full: gcr.io/paketo-buildpacks/builder:full 11 | buildpacks: 12 | - paketo-buildpacks/go-dist 13 | - ghcr.io/boson-project/go-function-buildpack:tip 14 | healthEndpoints: 15 | liveness: /health/liveness 16 | readiness: /health/readiness 17 | volumes: [] 18 | buildEnvs: [] 19 | envs: [] 20 | annotations: {} 21 | options: {} 22 | labels: [] 23 | -------------------------------------------------------------------------------- /test/fixtures/test.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-knative/175be75e337facae8988fdeeea9212e9882f331c/test/fixtures/test.gz -------------------------------------------------------------------------------- /test/fixtures/test.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-knative/175be75e337facae8988fdeeea9212e9882f331c/test/fixtures/test.tar.gz -------------------------------------------------------------------------------- /test/fixtures/test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redhat-developer/vscode-knative/175be75e337facae8988fdeeea9212e9882f331c/test/fixtures/test.zip -------------------------------------------------------------------------------- /test/functions/function-command/run-function.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri, window } from 'vscode'; 7 | import * as chai from 'chai'; 8 | import * as sinon from 'sinon'; 9 | import * as sinonChai from 'sinon-chai'; 10 | import { FunctionContextType } from '../../../src/cli/config'; 11 | import { knExecutor } from '../../../src/cli/execute'; 12 | import { FuncImpl } from '../../../src/functions/func'; 13 | import { runFunction } from '../../../src/functions/function-command/run-function'; 14 | import { TestItem } from '../testFunctionitem'; 15 | 16 | const { expect } = chai; 17 | chai.use(sinonChai); 18 | 19 | suite('Function/Run', () => { 20 | const sandbox = sinon.createSandbox(); 21 | let showInformationMessageStub: sinon.SinonStub; 22 | const data: Uri = { 23 | authority: '', 24 | fragment: '', 25 | path: 'test', 26 | query: '', 27 | scheme: 'file', 28 | fsPath: 'test', 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 30 | with: (change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri => { 31 | throw new Error('Function not implemented.'); 32 | }, 33 | toJSON: () => { 34 | throw new Error('Function not implemented.'); 35 | }, 36 | }; 37 | const taskRunNode = new TestItem(FuncImpl.ROOT, 'func1', FunctionContextType.FUNCTION, null, data); 38 | 39 | setup(() => { 40 | sandbox.stub(knExecutor, 'executeInTerminal'); 41 | showInformationMessageStub = sandbox.stub(window, 'showInformationMessage').resolves(); 42 | }); 43 | 44 | teardown(() => { 45 | sandbox.restore(); 46 | }); 47 | 48 | test('return null if empty context', async () => { 49 | const result = await runFunction(undefined); 50 | expect(result).equal(null); 51 | }); 52 | 53 | test('delete function from tree view', async () => { 54 | await runFunction(taskRunNode, false); 55 | // eslint-disable-next-line no-unused-expressions 56 | expect(showInformationMessageStub).calledOnce; 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/functions/function-command/undeploy-function.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri, window } from 'vscode'; 7 | import * as chai from 'chai'; 8 | import * as fsExtra from 'fs-extra'; 9 | import * as sinon from 'sinon'; 10 | import * as sinonChai from 'sinon-chai'; 11 | import { executeCmdCli } from '../../../src/cli/cmdCli'; 12 | import { FunctionContextType, FunctionStatus } from '../../../src/cli/config'; 13 | import { FuncImpl } from '../../../src/functions/func'; 14 | import { undeployFunction } from '../../../src/functions/function-command/undeploy-function'; 15 | import { TestItem } from '../testFunctionitem'; 16 | 17 | const { expect } = chai; 18 | chai.use(sinonChai); 19 | 20 | suite('Function/undeploy', () => { 21 | const sandbox = sinon.createSandbox(); 22 | let executeExecStub: sinon.SinonStub; 23 | let showWarningMessageStub: sinon.SinonStub; 24 | let showErrorMessageStub: sinon.SinonStub; 25 | const data: Uri = { 26 | authority: '', 27 | fragment: '', 28 | path: 'test', 29 | query: '', 30 | scheme: 'file', 31 | fsPath: 'test', 32 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 33 | with: (change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri => { 34 | throw new Error('Function not implemented.'); 35 | }, 36 | toJSON: () => { 37 | throw new Error('Function not implemented.'); 38 | }, 39 | }; 40 | const taskRunNode = new TestItem( 41 | FuncImpl.ROOT, 42 | 'func1', 43 | FunctionContextType.FUNCTION, 44 | null, 45 | data, 46 | null, 47 | FunctionStatus.CLUSTERLOCALBOTH, 48 | ); 49 | 50 | setup(() => { 51 | executeExecStub = sandbox.stub(executeCmdCli, 'executeExec'); 52 | sandbox.stub(fsExtra, 'remove').resolves(); 53 | showWarningMessageStub = sandbox.stub(window, 'showWarningMessage'); 54 | showErrorMessageStub = sandbox.stub(window, 'showErrorMessage'); 55 | }); 56 | 57 | teardown(() => { 58 | sandbox.restore(); 59 | }); 60 | 61 | test('return null if empty context', async () => { 62 | const result = await undeployFunction(undefined); 63 | expect(result).equal(null); 64 | }); 65 | 66 | test('return null if use select no', async () => { 67 | showWarningMessageStub.onFirstCall().resolves('No'); 68 | const result = await undeployFunction(taskRunNode); 69 | expect(result).equal(null); 70 | }); 71 | 72 | test('undeploy function from tree view', async () => { 73 | showWarningMessageStub.onFirstCall().resolves('Yes'); 74 | executeExecStub.onFirstCall().resolves({ error: null, stdout: 'successful' }); 75 | await undeployFunction(taskRunNode); 76 | // eslint-disable-next-line no-unused-expressions 77 | expect(executeExecStub).calledOnce; 78 | // eslint-disable-next-line no-unused-expressions 79 | expect(showWarningMessageStub).calledOnce; 80 | }); 81 | 82 | test('show error if it fails to undeploy function', async () => { 83 | showWarningMessageStub.onFirstCall().resolves('Yes'); 84 | executeExecStub.onFirstCall().resolves({ error: 'error', stdout: null }); 85 | await undeployFunction(taskRunNode); 86 | // eslint-disable-next-line no-unused-expressions 87 | expect(showErrorMessageStub).calledOnce; 88 | // eslint-disable-next-line no-unused-expressions 89 | expect(showWarningMessageStub).calledOnce; 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /test/functions/functionsExplorer.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as chai from 'chai'; 7 | import * as sinon from 'sinon'; 8 | import * as sinonChai from 'sinon-chai'; 9 | import { TestItem } from './testFunctionitem'; 10 | import { FunctionContextType } from '../../src/cli/config'; 11 | import { func, FuncImpl } from '../../src/functions/func'; 12 | import { functionExplorer } from '../../src/functions/functionsExplorer'; 13 | 14 | const { expect } = chai; 15 | chai.use(sinonChai); 16 | 17 | suite('Function/FunctionExplorer', () => { 18 | const sandbox = sinon.createSandbox(); 19 | const element = new TestItem(FuncImpl.ROOT, 'namespace', FunctionContextType.NAMESPACENODE); 20 | 21 | teardown(() => { 22 | sandbox.restore(); 23 | }); 24 | 25 | test('return tree item', () => { 26 | const result = functionExplorer.getTreeItem(element); 27 | expect(result).equal(element); 28 | }); 29 | 30 | test('return children node', () => { 31 | const result = functionExplorer.getChildren(element); 32 | expect(result).equal(element.getChildren()); 33 | }); 34 | 35 | test('return children node from function', async () => { 36 | sandbox.stub(func, 'getFunctionNodes').resolves([element]); 37 | const result = await functionExplorer.getChildren(); 38 | expect(result).deep.equal([element]); 39 | }); 40 | 41 | test('return parent node', () => { 42 | const result = functionExplorer.getParent(element); 43 | expect(result).equal(element.getParent()); 44 | }); 45 | 46 | test('tree view is visible', () => { 47 | sandbox.stub(functionExplorer.treeView, 'visible').value(true); 48 | const result = functionExplorer.isVisible(); 49 | expect(result).equal(true); 50 | }); 51 | 52 | test('reveal node', async () => { 53 | const revealStub = sandbox.stub(functionExplorer.treeView, 'reveal').resolves(); 54 | await functionExplorer.reveal(element); 55 | // eslint-disable-next-line no-unused-expressions 56 | revealStub.calledTwice; 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/functions/testFunctionitem.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { Uri } from 'vscode'; 7 | import { FunctionContextType } from '../../src/cli/config'; 8 | import { FunctionNode } from '../../src/functions/function-tree-view/functionsTreeItem'; 9 | 10 | export class TestItem implements FunctionNode { 11 | // eslint-disable-next-line no-useless-constructor 12 | constructor( 13 | private parent: FunctionNode, 14 | private name: string, 15 | public readonly contextValue: FunctionContextType, 16 | private children = [], 17 | public contextPath?: Uri, 18 | public runtime?: string, 19 | public functionStatus?: string, 20 | ) {} 21 | 22 | getName(): string { 23 | return this.name; 24 | } 25 | 26 | getChildren(): FunctionNode[] { 27 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 28 | return this.children; 29 | } 30 | 31 | getParent(): FunctionNode { 32 | return this.parent; 33 | } 34 | 35 | get label(): string { 36 | return this.name; 37 | } 38 | 39 | // eslint-disable-next-line class-methods-use-this 40 | refresh(): Promise { 41 | return Promise.resolve(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/knative/knativeBrokers.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { expect } from 'chai'; 3 | import * as chai from 'chai'; 4 | import { beforeEach } from 'mocha'; 5 | import * as sinon from 'sinon'; 6 | import * as sinonChai from 'sinon-chai'; 7 | import { Broker } from '../../src/knative/broker'; 8 | import { KnativeBrokers } from '../../src/knative/knativeBrokers'; 9 | import * as brokerData from '../eventingTree/broker.json'; 10 | 11 | chai.use(sinonChai); 12 | 13 | suite('Knative Brokers', () => { 14 | const sandbox = sinon.createSandbox(); 15 | const knativeBrokers: KnativeBrokers = KnativeBrokers.Instance; 16 | const testBroker0: Broker = new Broker('example-broker0', 'Brokers', JSON.parse(JSON.stringify(brokerData.items[0]))); 17 | const testBroker1: Broker = new Broker('example-broker1', 'Brokers', JSON.parse(JSON.stringify(brokerData.items[1]))); 18 | let testBrokers: Broker[]; 19 | 20 | beforeEach(() => { 21 | sandbox.stub(vscode.window, 'showErrorMessage').resolves(); 22 | testBrokers = [testBroker0, testBroker1]; 23 | knativeBrokers.addBrokers(testBrokers); 24 | }); 25 | 26 | teardown(() => { 27 | sandbox.restore(); 28 | }); 29 | 30 | suite('Getting an instance', () => { 31 | test('should return an instance of the singleton', () => { 32 | const instance: KnativeBrokers = KnativeBrokers.Instance; 33 | expect(instance).to.deep.equal(knativeBrokers); 34 | }); 35 | }); 36 | suite('Getting a Broker', () => { 37 | test('should return a list of brokers from the instance', () => { 38 | const returnedBroker: Broker = knativeBrokers.getBrokers()[0]; 39 | expect(returnedBroker).to.deep.equal(testBroker0); 40 | }); 41 | }); 42 | suite('Finding a Broker', () => { 43 | test('should return a broker using the broker name', () => { 44 | const returnedBroker: Broker = knativeBrokers.findBroker('example-broker0'); 45 | expect(returnedBroker).to.deep.equal(testBroker0); 46 | }); 47 | }); 48 | suite('Adding a Broker', () => { 49 | test('should add a broker and return the broker added', () => { 50 | const remainingBrokers: Broker[] = knativeBrokers.removeBroker('example-broker1'); 51 | expect(remainingBrokers).to.have.lengthOf(1); 52 | const returnedBroker: Broker = knativeBrokers.addBroker(testBroker1); 53 | expect(returnedBroker).to.deep.equal(testBroker1); 54 | }); 55 | }); 56 | suite('Adding multiple Brokers', () => { 57 | test('should add a list of brokers return a list of brokers added', () => { 58 | const remainingBrokers: Broker[] = knativeBrokers.removeBroker('example-broker1'); 59 | expect(remainingBrokers).to.have.lengthOf(1); 60 | const returnedBrokers: Broker[] = knativeBrokers.addBrokers(testBrokers); 61 | expect(returnedBrokers).to.deep.equal(testBrokers); 62 | }); 63 | }); 64 | suite('Updating a Broker', () => { 65 | test('should return a list of brokers, including the updated one', () => { 66 | const returnedBrokers: Broker[] = knativeBrokers.updateBroker(testBroker1); 67 | expect(returnedBrokers).to.deep.equal(testBrokers); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/knative/knativeChannels.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { expect } from 'chai'; 3 | import * as chai from 'chai'; 4 | import { beforeEach } from 'mocha'; 5 | import * as sinon from 'sinon'; 6 | import * as sinonChai from 'sinon-chai'; 7 | import { Channel } from '../../src/knative/channel'; 8 | import { KnativeChannels } from '../../src/knative/knativeChannels'; 9 | import * as channelData from '../eventingTree/channel.json'; 10 | 11 | chai.use(sinonChai); 12 | 13 | suite('Knative Channels', () => { 14 | const sandbox = sinon.createSandbox(); 15 | const knativeChannels: KnativeChannels = KnativeChannels.Instance; 16 | const testChannel0: Channel = new Channel( 17 | 'example-channel0', 18 | 'Channels', 19 | 'InMemoryChannel', 20 | JSON.parse(JSON.stringify(channelData.items[0])), 21 | ); 22 | const testChannel1: Channel = new Channel( 23 | 'example-channel1', 24 | 'Channels', 25 | 'InMemoryChannel', 26 | JSON.parse(JSON.stringify(channelData.items[1])), 27 | ); 28 | let testChannels: Channel[]; 29 | 30 | beforeEach(() => { 31 | sandbox.stub(vscode.window, 'showErrorMessage').resolves(); 32 | testChannels = [testChannel0, testChannel1]; 33 | knativeChannels.addChannels(testChannels); 34 | }); 35 | 36 | teardown(() => { 37 | sandbox.restore(); 38 | }); 39 | 40 | suite('Getting an instance', () => { 41 | test('should return an instance of the singleton', () => { 42 | const instance: KnativeChannels = KnativeChannels.Instance; 43 | expect(instance).to.deep.equal(knativeChannels); 44 | }); 45 | }); 46 | suite('Getting a Channel', () => { 47 | test('should return a list of channels from the instance', () => { 48 | const returnedChannel: Channel = knativeChannels.getChannels()[0]; 49 | expect(returnedChannel).to.deep.equal(testChannel0); 50 | }); 51 | }); 52 | suite('Finding a Channel', () => { 53 | test('should return a channel using the channel name', () => { 54 | const returnedChannel: Channel = knativeChannels.findChannel('example-channel0'); 55 | expect(returnedChannel).to.deep.equal(testChannel0); 56 | }); 57 | }); 58 | suite('Adding a Channel', () => { 59 | test('should add a channel and return the channel added', () => { 60 | const remainingChannels: Channel[] = knativeChannels.removeChannel('example-channel1'); 61 | expect(remainingChannels).to.have.lengthOf(1); 62 | const returnedChannel: Channel = knativeChannels.addChannel(testChannel1); 63 | expect(returnedChannel).to.deep.equal(testChannel1); 64 | }); 65 | }); 66 | suite('Adding multiple channels', () => { 67 | test('should add a list of channels return a list of channels added', () => { 68 | const remainingChannels: Channel[] = knativeChannels.removeChannel('example-channel1'); 69 | expect(remainingChannels).to.have.lengthOf(1); 70 | const returnedChannels: Channel[] = knativeChannels.addChannels(testChannels); 71 | expect(returnedChannels).to.deep.equal(testChannels); 72 | }); 73 | }); 74 | suite('Updating a Channel', () => { 75 | test('should return a list of channels, including the updated one', () => { 76 | const returnedChannels: Channel[] = knativeChannels.updateChannel(testChannel1); 77 | expect(returnedChannels).to.deep.equal(testChannels); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/knative/knativeItem.test.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { expect } from 'chai'; 3 | import * as chai from 'chai'; 4 | import { beforeEach } from 'mocha'; 5 | import * as sinon from 'sinon'; 6 | import * as sinonChai from 'sinon-chai'; 7 | import { compareNodes } from '../../src/knative/knativeItem'; 8 | 9 | chai.use(sinonChai); 10 | 11 | suite('Knative Brokers', () => { 12 | const sandbox = sinon.createSandbox(); 13 | 14 | const treeItemStringLabel1: vscode.TreeItem = new vscode.TreeItem('first'); 15 | treeItemStringLabel1.contextValue = 'alpha'; 16 | const treeItemStringLabel2: vscode.TreeItem = new vscode.TreeItem('second'); 17 | treeItemStringLabel2.contextValue = 'beta'; 18 | const treeItemObjectLabel1: vscode.TreeItem = new vscode.TreeItem({ label: 'first' }); 19 | treeItemObjectLabel1.contextValue = 'alpha'; 20 | const treeItemObjectLabel2: vscode.TreeItem = new vscode.TreeItem({ label: 'second' }); 21 | treeItemObjectLabel2.contextValue = 'beta'; 22 | 23 | beforeEach(() => { 24 | sandbox.stub(vscode.window, 'showErrorMessage').resolves(); 25 | }); 26 | 27 | teardown(() => { 28 | sandbox.restore(); 29 | }); 30 | 31 | suite('Compare Tree Items', () => { 32 | test('should return less than zero when comparing tree nodes with string labels that are in order', () => { 33 | const result = compareNodes(treeItemStringLabel1, treeItemStringLabel2); 34 | expect(result).to.be.lessThan(0); 35 | }); 36 | test('should return greater than zero when comparing tree nodes with string labels that are not in order', () => { 37 | const result = compareNodes(treeItemStringLabel2, treeItemStringLabel1); 38 | expect(result).to.be.greaterThan(0); 39 | }); 40 | test('should return less than zero when comparing tree nodes with object labels that are in order', () => { 41 | const result = compareNodes(treeItemObjectLabel1, treeItemObjectLabel2); 42 | expect(result).to.be.lessThan(0); 43 | }); 44 | test('should return greater than zero when comparing tree nodes with object labels that are not in order', () => { 45 | const result = compareNodes(treeItemObjectLabel2, treeItemObjectLabel1); 46 | expect(result).to.be.greaterThan(0); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/knative/revision.test.ts: -------------------------------------------------------------------------------- 1 | import { URL } from 'url'; 2 | import { expect } from 'chai'; 3 | import * as revision from '../../src/knative/revision'; 4 | 5 | suite('Revision class', () => { 6 | let instance: revision.Revision; 7 | const annotationObj: revision.Annotations = null; 8 | const labelsObj: revision.Labels = null; 9 | const ownerRefObj: revision.OwnerReferencesEntity[] = [ 10 | { 11 | apiVersion: '1.3.0', 12 | blockOwnerDeletion: false, 13 | controller: false, 14 | kind: 'kind', 15 | name: 'name', 16 | uid: 'uid-3', 17 | }, 18 | ]; 19 | const metadataObj: revision.Metadata = { 20 | name: 'myMeta', 21 | uid: 'x123y', 22 | annotations: annotationObj, 23 | labels: labelsObj, 24 | namespace: 'myNamespace', 25 | creationTimestamp: '2020-06-15', 26 | generation: 8, 27 | resourceVersion: '18', 28 | selfLink: 'https://points.somewhere.out', 29 | ownerReferences: ownerRefObj, 30 | }; 31 | const specObj: revision.Spec = null; 32 | const statusObj: revision.Status = null; 33 | const urlObj: URL = null; 34 | const itemsObj: revision.Items = { 35 | apiVersion: '1.0.0', 36 | kind: 'type', 37 | metadata: metadataObj, 38 | spec: specObj, 39 | status: statusObj, 40 | }; 41 | const trafficObj: revision.Traffic[] = [ 42 | { 43 | configurationName: 'myConfiguration', 44 | latestRevision: true, 45 | percent: 50, 46 | revisionName: 'late night rev', 47 | tag: 'latest', 48 | url: urlObj, 49 | }, 50 | ]; 51 | setup(() => { 52 | instance = new revision.Revision('myRevision', 'myService'); 53 | }); 54 | test('toRevision should provide proper revision based on give Items and Traffic objects', () => { 55 | instance = new revision.Revision('x', 'y', itemsObj, trafficObj); 56 | expect(instance.name).to.equal('x'); 57 | expect(instance.details).to.not.equal(undefined); 58 | expect(instance.details.apiVersion).to.equal('1.0.0'); 59 | expect(instance.traffic).to.not.equal(undefined); 60 | expect(instance.traffic[0].revisionName).to.equal('late night rev'); 61 | const newRevision = revision.Revision.toRevision(itemsObj, trafficObj); 62 | expect(newRevision).to.be.instanceOf(revision.Revision); 63 | expect(newRevision.name).to.equal('myMeta'); 64 | expect(newRevision.service).to.equal('name'); 65 | expect(newRevision.details.kind).to.equal('type'); 66 | expect(newRevision.traffic).to.equal(trafficObj); 67 | }); 68 | test('toRevision should fail when Items metadata object contains null or undefined in ownerReferences', () => { 69 | const itemsObj2 = itemsObj; 70 | itemsObj2.metadata.ownerReferences = null; 71 | try { 72 | revision.Revision.toRevision(itemsObj2, trafficObj); 73 | } catch (err) { 74 | expect(err).to.be.instanceOf(TypeError); 75 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 76 | expect(err.message).matches(new RegExp('Cannot read property|Cannot read properties')); 77 | } 78 | itemsObj2.metadata.ownerReferences = undefined; 79 | try { 80 | revision.Revision.toRevision(itemsObj2, trafficObj); 81 | } catch (err) { 82 | expect(err).to.be.instanceOf(TypeError); 83 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 84 | expect(err.message).matches(new RegExp('Cannot read property|Cannot read properties')); 85 | } 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/output/knOutputChannel.test.ts: -------------------------------------------------------------------------------- 1 | import rewire = require('rewire'); 2 | import * as sinon from 'sinon'; 3 | 4 | const rewiredKnOutputChannel = rewire('../../src/output/knOutputChannel'); 5 | 6 | suite('Output Channel', () => { 7 | const sandbox = sinon.createSandbox(); 8 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call 9 | const op = new rewiredKnOutputChannel.KnOutputChannel(); 10 | 11 | teardown(() => { 12 | sandbox.restore(); 13 | }); 14 | 15 | test('should print text', () => { 16 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 17 | const channelSpy = sandbox.spy(op.channel, 'append'); 18 | const text = `hello\n`; 19 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 20 | op.print(text); 21 | sandbox.assert.calledOnce(channelSpy); 22 | }); 23 | 24 | test('should add a new line if none exists when it prints text', () => { 25 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 26 | const channelSpy = sandbox.spy(op.channel, 'append'); 27 | const text = `hello`; 28 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 29 | op.print(text); 30 | sandbox.assert.calledTwice(channelSpy); 31 | }); 32 | 33 | // test('should call show if config is set to show', () => { 34 | // sandbox.stub(vscode.workspace.getConfiguration('knative')).returns(false); 35 | // const channelSpy = sandbox.spy(op.channel, 'show'); 36 | // const text = `hello`; 37 | // op.print(text); 38 | // sandbox.assert.calledOnce(channelSpy); 39 | // }); 40 | 41 | // test('should NOT call show if config is set to NOT show', () => { 42 | // sandbox.stub(vscode.workspace.getConfiguration('knative')).returns(false); 43 | // const channelSpy = sandbox.spy(op.channel, 'show'); 44 | // const text = `hello`; 45 | // op.print(text); 46 | // sandbox.assert.notCalled(channelSpy); 47 | // }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/reportIssue.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import * as chai from 'chai'; 8 | import { beforeEach } from 'mocha'; 9 | import * as sinon from 'sinon'; 10 | import * as sinonChai from 'sinon-chai'; 11 | import * as report from '../src/reportIssue'; 12 | 13 | chai.use(sinonChai); 14 | 15 | suite('Report issue', () => { 16 | const sandbox = sinon.createSandbox(); 17 | 18 | beforeEach(() => { 19 | sandbox.stub(vscode.window, 'showErrorMessage').resolves(); 20 | }); 21 | 22 | teardown(() => { 23 | sandbox.restore(); 24 | }); 25 | 26 | test('should open a browser with a link to report an issue', async () => { 27 | const executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand').resolves(); 28 | await report.reportIssue(); 29 | sinon.assert.calledOnce(executeCommandStub); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/servingTree/failed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | serving.knative.dev/creator: system:admin 6 | serving.knative.dev/lastModifier: system:admin 7 | creationTimestamp: "2021-02-24T22:32:46Z" 8 | generation: 1 9 | managedFields: 10 | - apiVersion: serving.knative.dev/v1 11 | fieldsType: FieldsV1 12 | fieldsV1: 13 | f:status: 14 | .: {} 15 | f:conditions: {} 16 | f:latestCreatedRevisionName: {} 17 | f:observedGeneration: {} 18 | f:url: {} 19 | manager: controller 20 | operation: Update 21 | time: "2021-02-24T22:32:46Z" 22 | - apiVersion: serving.knative.dev/v1 23 | fieldsType: FieldsV1 24 | fieldsV1: 25 | f:spec: 26 | .: {} 27 | f:template: 28 | .: {} 29 | f:metadata: 30 | .: {} 31 | f:annotations: 32 | .: {} 33 | f:client.knative.dev/user-image: {} 34 | f:creationTimestamp: {} 35 | f:name: {} 36 | f:spec: 37 | .: {} 38 | f:containers: {} 39 | manager: kn 40 | operation: Update 41 | time: "2021-02-24T22:32:46Z" 42 | name: aaa 43 | namespace: josh 44 | resourceVersion: "707360" 45 | uid: 9ac18760-90a1-4b52-a522-c615e5591dfa 46 | spec: 47 | template: 48 | metadata: 49 | annotations: 50 | client.knative.dev/user-image: quay.io/rhdevelopers 51 | creationTimestamp: null 52 | name: aaa-zpyvk-1 53 | spec: 54 | containerConcurrency: 0 55 | containers: 56 | - image: quay.io/rhdevelopers 57 | name: user-container 58 | readinessProbe: 59 | successThreshold: 1 60 | tcpSocket: 61 | port: 0 62 | resources: {} 63 | enableServiceLinks: false 64 | timeoutSeconds: 300 65 | traffic: 66 | - latestRevision: true 67 | percent: 100 68 | status: 69 | conditions: 70 | - lastTransitionTime: "2021-02-24T22:32:46Z" 71 | message: 'Revision "aaa-zpyvk-1" failed with message: Unable to fetch image "quay.io/rhdevelopers": failed to resolve image to digest: HEAD https://quay.io/v2/rhdevelopers/manifests/latest: unsupported status code 404.' 72 | reason: RevisionFailed 73 | status: "False" 74 | type: ConfigurationsReady 75 | - lastTransitionTime: "2021-02-24T22:32:46Z" 76 | message: Configuration "aaa" does not have any ready Revision. 77 | reason: RevisionMissing 78 | status: "False" 79 | type: Ready 80 | - lastTransitionTime: "2021-02-24T22:32:46Z" 81 | message: Configuration "aaa" does not have any ready Revision. 82 | reason: RevisionMissing 83 | status: "False" 84 | type: RoutesReady 85 | latestCreatedRevisionName: aaa-zpyvk-1 86 | observedGeneration: 1 87 | url: http://aaa-josh.apps.rhamilto.devcluster.openshift.com 88 | -------------------------------------------------------------------------------- /test/servingTree/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | serving.knative.dev/creator: system:admin 6 | serving.knative.dev/lastModifier: system:admin 7 | creationTimestamp: "2020-07-23T22:53:04Z" 8 | generation: 5 9 | managedFields: 10 | - apiVersion: serving.knative.dev/v1 11 | fieldsType: FieldsV1 12 | fieldsV1: 13 | f:status: 14 | .: {} 15 | f:conditions: {} 16 | f:latestCreatedRevisionName: {} 17 | f:observedGeneration: {} 18 | f:url: {} 19 | manager: controller 20 | operation: Update 21 | time: "2020-07-23T23:23:59Z" 22 | - apiVersion: serving.knative.dev/v1 23 | fieldsType: FieldsV1 24 | fieldsV1: 25 | f:spec: 26 | .: {} 27 | f:template: 28 | .: {} 29 | f:metadata: 30 | .: {} 31 | f:annotations: 32 | .: {} 33 | f:client.knative.dev/user-image: {} 34 | f:creationTimestamp: {} 35 | f:name: {} 36 | f:spec: 37 | .: {} 38 | f:containers: {} 39 | manager: kn 40 | operation: Update 41 | time: "2020-07-23T23:23:59Z" 42 | name: example 43 | namespace: a-serverless-example 44 | resourceVersion: "81373" 45 | uid: b643305a-c4b1-4c45-8efb-f8edb1c86623 46 | spec: 47 | template: 48 | metadata: 49 | annotations: 50 | client.knative.dev/user-image: quay.io/rhdevelopers 51 | creationTimestamp: null 52 | name: example-zpyvk-1 53 | spec: 54 | containerConcurrency: 0 55 | containers: 56 | - image: quay.io/rhdevelopers 57 | name: user-container 58 | readinessProbe: 59 | successThreshold: 1 60 | tcpSocket: 61 | port: 0 62 | resources: {} 63 | enableServiceLinks: false 64 | timeoutSeconds: 300 65 | traffic: 66 | - latestRevision: true 67 | percent: 100 68 | status: 69 | conditions: 70 | - lastTransitionTime: "2020-07-23T23:23:08Z" 71 | message: 'Revision "example-zpyvk-1" failed with message: Unable to fetch image "quay.io/rhdevelopers": failed to resolve image to digest: HEAD https://quay.io/v2/rhdevelopers/manifests/latest: unsupported status code 404.' 72 | reason: RevisionFailed 73 | status: "False" 74 | type: ConfigurationsReady 75 | - lastTransitionTime: "2020-07-23T23:23:59Z" 76 | message: Configuration "example" does not have any ready Revision. 77 | reason: RevisionMissing 78 | status: "False" 79 | type: Ready 80 | - lastTransitionTime: "2020-07-23T23:23:59Z" 81 | message: Configuration "example" does not have any ready Revision. 82 | reason: RevisionMissing 83 | status: "False" 84 | type: RoutesReady 85 | latestCreatedRevisionName: example-75w7v 86 | observedGeneration: 5 87 | url: http://example-a-serverless-example.apps.devcluster.openshift.com 88 | -------------------------------------------------------------------------------- /test/ui-test/.mocharc-debug.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | timeout: 99999999, 3 | fullTrace: true 4 | } -------------------------------------------------------------------------------- /test/ui-test/allTestsSuite.ts: -------------------------------------------------------------------------------- 1 | import { extensionInstallationUITest } from './extensionInstallationUITest'; 2 | import { extensionsUITest } from './extensionUITest'; 3 | import { knativeInitializationUITest } from './initializationUITest'; 4 | 5 | /** 6 | * @author Ondrej Dockal 7 | */ 8 | describe('VSCode KNative Extension - UI tests', () => { 9 | extensionInstallationUITest(); 10 | extensionsUITest(true); 11 | knativeInitializationUITest(); 12 | }); 13 | -------------------------------------------------------------------------------- /test/ui-test/baseTestsSuite.ts: -------------------------------------------------------------------------------- 1 | import { extensionInstallationUITest } from './extensionInstallationUITest'; 2 | import { extensionsUITest } from './extensionUITest'; 3 | 4 | /** 5 | * @author Ondrej Dockal 6 | */ 7 | describe('VSCode KNative Extension - UI tests', () => { 8 | extensionInstallationUITest(); 9 | extensionsUITest(false); 10 | }); 11 | -------------------------------------------------------------------------------- /test/ui-test/common/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Ondrej Dockal 3 | */ 4 | export class KNativeConstants { 5 | public static readonly KUBERNETES_EXTENSION_NAME = 'Kubernetes'; 6 | 7 | public static readonly KNATIVE_EXTENSION_NAME = 'Knative'; 8 | 9 | public static readonly YAML_EXTENSION_NAME = 'YAML'; 10 | 11 | public static readonly KNATIVE_EXTENSION_BAR_NAME = 'KNATIVE'; 12 | 13 | public static readonly NO_SERVICE_FOUND = 'No Service Found'; 14 | 15 | public static readonly ACTION_ITEM_ADD_SERVICE = 'Add Service'; 16 | 17 | public static readonly ACTION_ITEM_REFRESH = 'Refresh View'; 18 | 19 | public static readonly ACTION_ITEM_REFRESH_LABEL = 'Refresh View (Shift+Alt+R)'; 20 | 21 | public static readonly ACTION_ITEM_REPORT_ISSUE = 'Report Extension Issue on GitHub'; 22 | 23 | public static readonly ACTION_ITEM_CREATE_FUNCTION = 'Create Function'; 24 | 25 | public static readonly ACTION_ITEM_FUNCTION_VERSION = 'Function version'; 26 | 27 | public static readonly REPOSITORY = 'Repository'; 28 | 29 | public static readonly SECTION_SERVING = 'Serving'; 30 | 31 | public static readonly SECTION_EVENTING = 'Eventing'; 32 | 33 | public static readonly SECTION_FUNCTION = 'Functions'; 34 | 35 | public static readonly SECTION_ACTIVE_COMMAND = 'Function Sessions'; 36 | } 37 | -------------------------------------------------------------------------------- /test/ui-test/custom-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.simpleDialog.enable": true, 3 | "window.dialogStyle": "custom", 4 | "redhat.telemetry.enabled": false 5 | } -------------------------------------------------------------------------------- /test/ui-test/extensionInstallationUITest.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { ActivityBar, ExtensionsViewItem, ExtensionsViewSection } from 'vscode-extension-tester'; 3 | import { KNativeConstants } from './common/constants'; 4 | 5 | /** 6 | * @author Ondrej Dockal 7 | */ 8 | export function extensionInstallationUITest(): void { 9 | let extensions: ExtensionsViewItem[]; 10 | 11 | before(async function context() { 12 | this.timeout(5000); 13 | const viewBar = await new ActivityBar().getViewControl('Extensions'); 14 | const sideBar = await viewBar.openView(); 15 | const section = (await sideBar.getContent().getSection('Installed')) as ExtensionsViewSection; 16 | extensions = await section.getVisibleItems(); 17 | }); 18 | 19 | describe('Knative extension', () => { 20 | it('should be installed among extensions', async function context() { 21 | this.timeout(3000); 22 | expect(await Promise.all(extensions.map((item) => item.getTitle()))).to.include(KNativeConstants.KNATIVE_EXTENSION_NAME); 23 | }); 24 | 25 | describe('dependencies', () => { 26 | it('Yaml, should be installed among extensions', async function context() { 27 | this.timeout(3000); 28 | expect(await Promise.all(extensions.map((item) => item.getTitle()))).to.include(KNativeConstants.YAML_EXTENSION_NAME); 29 | }); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /test/ui-test/initializationUITest.ts: -------------------------------------------------------------------------------- 1 | import { expect, assert } from 'chai'; 2 | import { ActivityBar, VSBrowser, NotificationType, WebDriver } from 'vscode-extension-tester'; 3 | import { KNativeConstants } from './common/constants'; 4 | import { getNotifications, cleanUpNotifications } from './common/testUtils'; 5 | 6 | /** 7 | * @author Ondrej Dockal 8 | */ 9 | export function knativeInitializationUITest(): void { 10 | let driver: WebDriver; 11 | 12 | before(async () => { 13 | await cleanUpNotifications(); 14 | // process.env.KUBECONFIG = '/home/odockal/kubeconfig'; 15 | driver = VSBrowser.instance.driver; 16 | }); 17 | 18 | describe('Knative view', () => { 19 | it('should be ready for usage, requires access to the cluster', async function context() { 20 | this.timeout(90000); 21 | // eslint-disable-next-line @typescript-eslint/await-thenable 22 | const view = await new ActivityBar().getViewControl(KNativeConstants.KNATIVE_EXTENSION_NAME); 23 | const sideBar = await view.openView(); 24 | // check that no notification error appeared 25 | try { 26 | await driver.wait(async () => { 27 | const notifs = await getNotifications(NotificationType.Error, NotificationType.Warning); 28 | return notifs === null || notifs === undefined || notifs.length === 0; 29 | }, 5000); 30 | } catch (error) { 31 | assert.fail(`Error notification appeared during cluster loading`); 32 | } 33 | await driver.wait(async () => !(await sideBar.getContent().hasProgress()), 3000); 34 | const servingSection = await sideBar.getContent().getSection(KNativeConstants.SECTION_SERVING); 35 | if (!(await servingSection.isExpanded())) { 36 | await servingSection.expand(); 37 | } 38 | await driver.wait(async () => (await servingSection.getVisibleItems()).length > 0, 30000); 39 | const items = await servingSection.getVisibleItems(); 40 | expect(await items[0].getText()).to.equal(KNativeConstants.NO_SERVICE_FOUND); 41 | const eventingSection = await sideBar.getContent().getSection(KNativeConstants.SECTION_EVENTING); 42 | if (!(await eventingSection.isExpanded())) { 43 | await eventingSection.expand(); 44 | } 45 | await driver.wait(async () => (await eventingSection.getVisibleItems()).length > 0, 30000); 46 | const brokersTree = await eventingSection.getVisibleItems(); 47 | expect(brokersTree.length).to.be.greaterThan(0); 48 | expect(await Promise.all(brokersTree.map(async (item) => item.getText()))).to.include.members([ 49 | 'Brokers', 50 | 'Channels', 51 | 'Sources', 52 | 'Subscriptions', 53 | 'Triggers', 54 | ]); 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /test/ui-test/scripts/prepare-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Test slave's virtualization 4 | ${WORKSPACE}/cdk-scripts/scripts/cdk3-virt.sh 5 | 6 | # export workspace location that is os dependent - due to cygwin on windows 7 | cd ${WORKSPACE} 8 | export WORKSPACE2=$(pwd) 9 | CONTENT="WORKSPACE2=${WORKSPACE2}" 10 | echo "$CONTENT" > ${WORKSPACE2}/local_env.properties 11 | 12 | # Set os - dependent values 13 | HYPERVISOR= 14 | BASEFILE_NAME="crc" 15 | if [[ $(uname) == *Darwin* ]]; then 16 | HYPERVISOR="xhyve" 17 | elif [[ $(uname) == *Linux* ]]; then 18 | HYPERVISOR="kvm" 19 | elif [[ $(uname) == *CYGWIN* ]]; then 20 | HYPERVISOR="virtualbox" 21 | BASEFILE_NAME="crc.exe" 22 | fi 23 | echo "HYPERVISOR=$HYPERVISOR" >> ${WORKSPACE2}/local_env.properties 24 | echo "BASEFILE_NAME=$BASEFILE_NAME" >> ${WORKSPACE2}/local_env.properties 25 | 26 | URL="URL=" 27 | 28 | FILE_ENDING= 29 | OS= 30 | if [[ $(uname) == *Darwin* ]]; then 31 | OS="macos" 32 | FILE_ENDING="crc-$OS-amd64.tar.xz" 33 | elif [[ $(uname) == *Linux* ]]; then 34 | OS="linux" 35 | FILE_ENDING="crc-$OS-amd64.tar.xz" 36 | elif [[ $(uname) == *CYGWIN* ]]; then 37 | OS="windows" 38 | FILE_ENDING="crc-$OS-amd64.zip" 39 | fi 40 | URL="${URL}${CRC_URL}/${FILE_ENDING}" 41 | 42 | echo "${URL}" >> ${WORKSPACE2}/local_env.properties 43 | echo "OS=${OS}" >> ${WORKSPACE2}/local_env.properties 44 | echo "FILE_ENDING=${FILE_ENDING}" >> ${WORKSPACE2}/local_env.properties -------------------------------------------------------------------------------- /test/ui-test/scripts/setup-crc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # inject env. variables from env properties file 4 | set -a 5 | cd ${WORKSPACE} 6 | . ./local_env.properties 7 | set +a 8 | # Download and setup CRC and pull secret 9 | cd ${WORKSPACE2} 10 | mkdir -p crc 11 | cd crc 12 | if [ -f ./crc ]; then 13 | ls 14 | echo "crc binary exists, skipping" 15 | else 16 | wget ${URL} 17 | tar xvf ${FILE_ENDING} 18 | mv */crc* . 19 | chmod +x crc* 20 | fi 21 | 22 | # workaround for https://github.com/code-ready/crc/issues/2653 23 | ./crc config set skip-check-daemon-systemd-unit true 24 | ./crc config set skip-check-daemon-systemd-sockets true 25 | # Switch off telemetry 26 | ./crc config set consent-telemetry no 27 | ./crc setup 28 | 29 | ./${BASEFILE_NAME} status || true 30 | ./${BASEFILE_NAME} version || true -------------------------------------------------------------------------------- /test/util/download.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { EventEmitter } from 'events'; 7 | import * as os from 'os'; 8 | import * as path from 'path'; 9 | import { expect } from 'chai'; 10 | import * as chai from 'chai'; 11 | import * as pq from 'proxyquire'; 12 | import * as sinon from 'sinon'; 13 | import * as sinonChai from 'sinon-chai'; 14 | 15 | chai.use(sinonChai); 16 | 17 | suite('Download Util', () => { 18 | let progressMock; 19 | const sandbox: sinon.SinonSandbox = sinon.createSandbox(); 20 | let requestEmitter: EventEmitter; 21 | let streamEmitter: EventEmitter; 22 | 23 | setup(() => { 24 | requestEmitter = new EventEmitter(); 25 | streamEmitter = new EventEmitter(); 26 | // eslint-disable-next-line dot-notation 27 | requestEmitter['pipe'] = (): EventEmitter => streamEmitter; 28 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 29 | progressMock = pq('../../src/util/download', { 30 | 'request-progress': () => requestEmitter, 31 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return 32 | request: (_: any) => _, 33 | }).DownloadUtil; 34 | }); 35 | 36 | teardown(() => { 37 | sandbox.restore(); 38 | }); 39 | 40 | test('reports download progress', () => { 41 | const callback = sandbox.stub(); 42 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 43 | const result = progressMock.downloadFile('url', path.join(os.tmpdir(), 'toFile'), callback); 44 | requestEmitter.emit('progress', { percent: 0.33 }); 45 | requestEmitter.emit('progress', { percent: 0.66 }); 46 | requestEmitter.emit('end'); 47 | streamEmitter.emit('close'); 48 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 49 | return result.then(() => { 50 | expect(callback).calledWith(33, 33); 51 | expect(callback).calledWith(66, 33); 52 | expect(callback).calledWith(100, 34); 53 | }); 54 | }); 55 | 56 | test('fails when download fails', () => { 57 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 58 | const result = progressMock.downloadFile('url', path.join(os.tmpdir(), 'toFile')); 59 | requestEmitter.emit('error', new Error('failure')); 60 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 61 | return result 62 | .then(() => Promise.reject(Error('No failure reported'))) 63 | .catch((err: Error) => { 64 | expect(err.message).to.equal('failure'); 65 | }); 66 | }); 67 | 68 | test('fails when stream fails', () => { 69 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 70 | const result = progressMock.downloadFile('url', path.join(os.tmpdir(), 'toFile')); 71 | streamEmitter.emit('error', new Error('failure')); 72 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 73 | return result 74 | .then(() => Promise.reject(Error('No failure reported'))) 75 | .catch((err: Error) => { 76 | expect(err.message).to.equal('failure'); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/util/filters.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { expect } from 'chai'; 7 | import * as sinon from 'sinon'; 8 | import { Filters } from '../../src/util/filters'; 9 | 10 | suite('Filters utility class', () => { 11 | const sandbox = sinon.createSandbox(); 12 | 13 | teardown(() => { 14 | sandbox.restore(); 15 | }); 16 | 17 | test('should replace token string in given text', () => { 18 | const spy = sandbox.spy(Filters, 'filterToken'); 19 | expect(Filters.filterToken('some string and given --token=xxx, should result in filtered string')).to.include( 20 | '--token **********', 21 | ); 22 | expect(spy.calledOnce); 23 | }); 24 | test('should not replace similar token-like expression string in given text', () => { 25 | expect( 26 | Filters.filterToken('some string and given token=xxx or --token xxx or -- token=xxx, should not result in filtered string'), 27 | ).not.to.include('--token **********'); 28 | }); 29 | test('should replace password string in given text', () => { 30 | const spy = sandbox.spy(Filters, 'filterToken'); 31 | expect( 32 | Filters.filterPassword(`some string and given password -p 'MySecretT3xt?*', should result in filtered string`), 33 | ).to.include('-p **********'); 34 | expect(spy.calledOnce); 35 | }); 36 | test('should not replace similar password-like string or parameter annotation in given text', () => { 37 | expect( 38 | Filters.filterPassword( 39 | `some string and given parameter --p 'not exactlyPass' or --p='again not a password' neither -p=someValue`, 40 | ), 41 | ).to.include('-p **********'); 42 | }); 43 | test('should return same falsy object if not full string', () => { 44 | expect(Filters.filterToken('')).to.equal(''); 45 | expect(Filters.filterToken(null)).to.equal(null); 46 | expect(Filters.filterToken(undefined)).to.equal(undefined); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/util/format.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { expect } from 'chai'; 7 | import * as chai from 'chai'; 8 | import * as sinonChai from 'sinon-chai'; 9 | import { prettifyJson } from '../../src/util/format'; 10 | 11 | chai.use(sinonChai); 12 | 13 | suite('Format', () => { 14 | const startingJSON = '{"name":"John", "age":31, "city":"New York"}'; 15 | const expectedJSON = `{ 16 | "name": "John", 17 | "age": 31, 18 | "city": "New York" 19 | }`; 20 | const startingTokenJSON = `/bin/kn revision list --token=xxx -o json -s knative-tut`; 21 | const expectedTokenJSON = `/bin/kn revision list --token ********** -o json -s knative-tut`; 22 | 23 | test('should convert unstructured JSON into nicely formatted JSON', () => { 24 | const convertedJson = prettifyJson(startingJSON); 25 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 26 | expect(convertedJson).to.equal(expectedJSON); 27 | }); 28 | test('should catch when a token is included', () => { 29 | const convertedTokenJson = prettifyJson(startingTokenJSON); 30 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call 31 | expect(convertedTokenJson).to.equal(expectedTokenJSON); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/util/hideclusterinformation.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as chai from 'chai'; 7 | import * as sinonChai from 'sinon-chai'; 8 | import { hideClusterInfo, hideIPAddress, hidePath } from '../../src/util/hideclusterinformation'; 9 | 10 | const { expect } = chai; 11 | chai.use(sinonChai); 12 | 13 | suite('Hide user information', () => { 14 | const ipAddress = 15 | 'undefinedUnable to connect to the server: dial tcp 192.168.1.240:6443: connect: no route to host 192.168.1.240:6443 2001:0000:3238:DFE1:63::FEFB 2001:0:3238:DFE1:63::FEFB'; 16 | 17 | const userPath = `undefinedUnable to connect to the server: error executing access token command "C:\\!Users\\test\\AppData\\Local\\test-code\\installer\\test-cloud-sdk\\bin\\test.cmd config config-helper --format=json": err=exec: "C:\\Users\test\\AppData\\Local\\test-code\\installer\\test-cloud-sdk\\₭ǝ℞ᖭ\\₭ǝ℞ᖭ.cmd": file does not exist output= stderr= 18 | "/₭℞ᖭ/apple/₭℞ᖭ/" "./test/test" "test/test2" "test" `; 19 | 20 | const hideClusterData = 21 | 'undefinedUnable to connect to the server: dial tcp 192.168.1.240:6443: connect: no route to host 192.168.1.240:6443 2001:0000:3238:DFE1:63::FEFB https://test.com connect to the server: dial tcp: lookup https://test.com:10.10.20.30: no such host'; 22 | 23 | test('hide cluster information', () => { 24 | const result = hideClusterInfo(hideClusterData); 25 | expect(result).deep.equals( 26 | 'undefinedUnable to connect to the server: dial tcp IP Address :6443: connect: no route to host IP Address :6443 IPV6 Address cluster info connect to the server: dial tcp: lookup cluster info: no such host', 27 | ); 28 | }); 29 | 30 | test('Hide local user path', () => { 31 | const result = hidePath(userPath); 32 | expect(result).deep 33 | .equals(`undefinedUnable to connect to the server: error executing access token command "local path": err=exec: "local path": file does not exist output= stderr= 34 | "local path" "local path" "local path" "test" `); 35 | }); 36 | 37 | test('return string if local user path not found', () => { 38 | const message = 'undefinedUnable to connect to the server: error executing access token command'; 39 | const result = hidePath(message); 40 | expect(result).deep.equals(message); 41 | }); 42 | 43 | test('Hide user ipAddress', () => { 44 | const result = hideIPAddress(ipAddress); 45 | expect(result).deep.equals( 46 | 'undefinedUnable to connect to the server: dial tcp IP Address :6443: connect: no route to host IP Address :6443 IPV6 Address IPV6 Address ', 47 | ); 48 | }); 49 | 50 | test('return string if user ipAddress not found', () => { 51 | const message = 'undefinedUnable to connect to the server: dial tcp connect: no route to host'; 52 | const result = hideIPAddress(message); 53 | expect(result).deep.equals(message); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/util/parse.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as fs from 'fs'; 7 | import * as path from 'path'; 8 | import { expect, assert } from 'chai'; 9 | import { loadJSON } from '../../src/util/parse'; 10 | 11 | suite('Parse utility class', () => { 12 | const json = { 13 | parent: { 14 | child: 'foo', 15 | }, 16 | }; 17 | const filePath = path.join(__dirname, 'valid.json'); 18 | const invalidJsonFilePath = path.join(__dirname, 'invalid.json'); 19 | 20 | suiteSetup(() => { 21 | fs.writeFileSync(filePath, JSON.stringify(json), 'utf8'); 22 | fs.writeFileSync(invalidJsonFilePath, 'Invalid json', 'utf8'); 23 | }); 24 | test('should parse valid JSON from existing file', async () => { 25 | const jsonFromFile = await loadJSON(filePath); 26 | const expected = { 27 | parent: { 28 | child: 'foo', 29 | }, 30 | }; 31 | const expectedStr = JSON.stringify(expected); 32 | const actual = JSON.stringify(jsonFromFile); 33 | assert.deepInclude(actual, expectedStr, `Comparison failed, expected ${expectedStr} but got: ${actual}`); 34 | }); 35 | test('should throw an error when processing invalid JSON file', async () => { 36 | try { 37 | await loadJSON(invalidJsonFilePath); 38 | } catch (err) { 39 | expect(err).to.be.an('error', 'Unexpected token'); 40 | } 41 | }); 42 | test('should throw an error when passing non-existing file', async () => { 43 | try { 44 | await loadJSON('/not/really/a/path.json'); 45 | } catch (err) { 46 | expect(err).to.be.an('error', 'Cannot find module'); 47 | } 48 | }); 49 | suiteTeardown(() => { 50 | fs.unlinkSync(filePath); 51 | fs.unlinkSync(invalidJsonFilePath); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/util/platform.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import { expect } from 'chai'; 7 | import * as chai from 'chai'; 8 | import * as sinon from 'sinon'; 9 | import * as sinonChai from 'sinon-chai'; 10 | import { Platform } from '../../src/util/platform'; 11 | 12 | chai.use(sinonChai); 13 | 14 | suite('Platform Utility', () => { 15 | const sandbox = sinon.createSandbox(); 16 | teardown(() => { 17 | sandbox.restore(); 18 | }); 19 | 20 | test('getOS returns the platform name', () => { 21 | const os = Platform.getOS(); 22 | expect(os).to.equal(process.platform); 23 | }); 24 | 25 | test('OS delegates to getOS', () => { 26 | const spy = sandbox.spy(Platform, 'getOS'); 27 | const os = Platform.OS; 28 | 29 | expect(spy.calledOnce); 30 | expect(os).to.equal(process.platform); 31 | }); 32 | 33 | test('getEnv returns the platform environment', () => { 34 | const env = Platform.getEnv(); 35 | expect(env).to.equal(process.env); 36 | }); 37 | 38 | test('ENV delegates to getENV', () => { 39 | const spy = sandbox.spy(Platform, 'getEnv'); 40 | const env = Platform.ENV; 41 | 42 | expect(spy.calledOnce); 43 | expect(env).to.equal(process.env); 44 | }); 45 | 46 | test('getUserHomePath returns the path to user home', () => { 47 | const home = Platform.getUserHomePath(); 48 | if (process.platform === 'win32') { 49 | expect(home).to.equal(process.env.USERPROFILE); 50 | } else { 51 | expect(home).to.equal(process.env.HOME); 52 | } 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/util/watch.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { expect } from 'chai'; 8 | import * as chai from 'chai'; 9 | import * as fs from 'fs-extra'; 10 | import * as sinon from 'sinon'; 11 | import * as sinonChai from 'sinon-chai'; 12 | import * as tmp from 'tmp'; 13 | import { WatchUtil } from '../../src/util/watch'; 14 | 15 | chai.use(sinonChai); 16 | 17 | suite('File Watch Utility', () => { 18 | const sandbox = sinon.createSandbox(); 19 | let ensureStub: sinon.SinonStub; 20 | let watchStub: sinon.SinonStub; 21 | const location = 'location'; 22 | const filename = 'file'; 23 | 24 | setup(() => { 25 | ensureStub = sandbox.stub(fs, 'ensureDirSync'); 26 | watchStub = sandbox.stub(fs, 'watch'); 27 | }); 28 | 29 | teardown(() => { 30 | sandbox.restore(); 31 | }); 32 | 33 | test('watchFileForContextChange ensures the location exists', () => { 34 | WatchUtil.watchFileForContextChange(location, filename); 35 | 36 | expect(ensureStub).calledOnceWith(location); 37 | }); 38 | 39 | test('watchFileForContextChange creates a file watcher for the given path', () => { 40 | WatchUtil.watchFileForContextChange(location, filename); 41 | 42 | expect(watchStub).calledOnceWith(location, sinon.match.func); 43 | }); 44 | 45 | test('watchFileForContextChange returns a content change notifier', () => { 46 | const result = WatchUtil.watchFileForContextChange(location, filename); 47 | 48 | expect(result).has.ownProperty('watcher'); 49 | expect(result).has.ownProperty('emitter'); 50 | }); 51 | 52 | test('emits change message when context changes', async () => { 53 | ensureStub.restore(); 54 | watchStub.restore(); 55 | const fileToWatch = fs.realpathSync(tmp.fileSync().name); 56 | fs.ensureFileSync(fileToWatch); 57 | const notifier = WatchUtil.watchFileForContextChange(path.dirname(fileToWatch), path.basename(fileToWatch)); 58 | setTimeout(() => { 59 | fs.writeFileSync(fileToWatch, 'current-context:test2'); 60 | }, 1000); 61 | return new Promise((res) => { 62 | notifier.emitter.on('file-changed', (file) => { 63 | expect(file).to.equal(undefined); 64 | res(null); 65 | }); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/util/window.test.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------------------------- 2 | * Copyright (c) Red Hat, Inc. All rights reserved. 3 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 4 | *-----------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import { window, TerminalOptions } from 'vscode'; 8 | import { expect } from 'chai'; 9 | import * as chai from 'chai'; 10 | import * as sinon from 'sinon'; 11 | import * as sinonChai from 'sinon-chai'; 12 | import { WindowUtil } from '../../src/util/windowUtils'; 13 | 14 | chai.use(sinonChai); 15 | 16 | suite('Window Utility', () => { 17 | const sandbox = sinon.createSandbox(); 18 | let termStub: sinon.SinonStub; 19 | 20 | setup(() => { 21 | termStub = sandbox.stub(window, 'createTerminal'); 22 | }); 23 | 24 | teardown(() => { 25 | sandbox.restore(); 26 | }); 27 | 28 | test('createTerminal creates a terminal object', () => { 29 | WindowUtil.createTerminal('name', process.cwd()); 30 | expect(termStub.calledOnce); 31 | }); 32 | 33 | test('createTerminal adds tools location and shell path to the environment', () => { 34 | const toolLocationDir = path.dirname(path.join('dir', 'where', 'tool', 'is', 'located', 'tool')); 35 | const env: NodeJS.ProcessEnv = {}; 36 | const key = process.platform === 'win32' ? 'Path' : 'PATH'; 37 | Object.assign(env, process.env); 38 | env[key] = `${toolLocationDir}${path.delimiter}${process.env[key]}`; 39 | 40 | const options: TerminalOptions = { 41 | cwd: process.cwd(), 42 | name: 'terminal', 43 | shellPath: process.platform === 'win32' ? undefined : '/bin/bash', 44 | env, 45 | }; 46 | WindowUtil.createTerminal('terminal', process.cwd(), toolLocationDir); 47 | 48 | expect(termStub).calledOnceWith(options); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "downlevelIteration": true, 4 | "skipLibCheck": true, 5 | "experimentalDecorators": true, 6 | "lib": [ "es6" ], 7 | "module": "commonjs", 8 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 9 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 10 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 11 | "noUnusedLocals": true, 12 | "outDir": "out", 13 | "resolveJsonModule": true, 14 | "rootDir": ".", 15 | "sourceMap": true, 16 | "strict": false, /* enable all strict type-checking options */ 17 | "target": "es6", 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | ".vscode-test", 22 | "test-resources" 23 | ], 24 | "include": [ 25 | "src", 26 | "build", 27 | "test", 28 | "schemas" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------