Skip to content
This repository has been archived by the owner on Oct 9, 2024. It is now read-only.

Commit

Permalink
feat: add execution environment auto completion (#42)
Browse files Browse the repository at this point in the history
* feat: add execution environment auto completion

*  Add support for execution environment (EE) configuration
   options received from client.
   Example client `settings.json` for EE options (default)
   ```
   {
    "ansible.executionEnvironment.enabled": false,
    "ansible.executionEnvironment.image": "quay.io/ansible/ansible-navigator-demo-ee:0.6.0",
    "ansible.executionEnvironment.containerEngine": "auto",
    "ansible.executionEnvironment.pullPolicy: "missing"
   }
   ```

*  Add executionEnvironment service to handle initilization of EE
   and pulling plugins from within EE into cache path on local system

*  In case EE is enabled update docsLibrary service to read plugin
   docs from local cache path after executionEnvironment service
   is initialized

*  Add commandRunner utility to run command on local host or within
   EE based on settings passed from client

*  Update ansibleConfig service to use commandRunner utility
   to run ansible-config and related commands

* remove stale logs and unused imports

* update package.json node type version

* updates to package-lock.json file

* remove debug lods

* chore: remove unused inputs

* chore: readd unrelated removed file

* chore: fix review comments

*  Add semicolon and fix indetation using Prettier extesnion
*  Use vscode-uri package to get document path in commandRunner utility
*  Add progess status for downloaing EE
*  Change user message to log messeage at all applicable places
*  Add comments on code for better readability
*  Simplify logic to add `plugins` folder in `site-packages` path
*  Fix type on executionEnvironment service
*  Remove `--interactive` flag from container run command

* chore: Update log messages

* chore: Move container engine assignment out of try/catch based on review comment

* chore: add progress tracker to fetch plugin docs
  • Loading branch information
ganeshrn authored Oct 5, 2021
1 parent 8183fd2 commit cefd599
Show file tree
Hide file tree
Showing 11 changed files with 865 additions and 258 deletions.
418 changes: 232 additions & 186 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"globby": "^11.0.4",
"ini": "^1.3.8",
"lodash": "^4.17.21",
"uuid": "^8.3.2",
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.1",
"vscode-uri": "^3.0.2",
Expand All @@ -56,13 +57,15 @@
"@types/lodash": "^4.14.171",
"@types/mocha": "^8.2.3",
"@types/node": "^14.17.5",
"@types/uuid": "^8.3.1",
"@types/vscode": "^1.56.0",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"chai": "^4.3.4",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"mocha": "^8.4.0",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"ts-node": "^9.1.1",
"typescript": "^4.3.5"
Expand Down
12 changes: 12 additions & 0 deletions src/interfaces/extensionSettings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
export type IContainerEngine = 'auto' | 'podman' | 'docker';

export type IPullPolicy = 'always' | 'missing' | 'never' | 'tag';

export interface ExtensionSettings {
ansible: { path: string; useFullyQualifiedCollectionNames: boolean };
ansibleLint: { enabled: boolean; path: string; arguments: string };
executionEnvironment: ExecutionEnvironmentSettings;
python: { interpreterPath: string; activationScript: string };
}

interface ExecutionEnvironmentSettings {
containerEngine: IContainerEngine;
enabled: boolean;
image: string;
pullPolicy: IPullPolicy;
}
64 changes: 28 additions & 36 deletions src/services/ansibleConfig.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import * as child_process from 'child_process';
import * as ini from 'ini';
import * as _ from 'lodash';
import * as path from 'path';
import { URI } from 'vscode-uri';
import { Connection } from 'vscode-languageserver';
import { withInterpreter } from '../utils/misc';
import { WorkspaceFolderContext } from './workspaceManager';
import { CommandRunner } from '../utils/commandRunner';

export class AnsibleConfig {
private connection: Connection;
Expand All @@ -25,41 +23,32 @@ export class AnsibleConfig {
this.context.workspaceFolder.uri
);

// get Ansible configuration
const [ansibleConfigCommand, ansibleConfigEnv] = withInterpreter(
`${settings.ansible.path}-config`,
'dump',
settings.python.interpreterPath,
settings.python.activationScript
const commandRunner = new CommandRunner(
this.connection,
this.context,
settings
);

const ansibleConfigResult = child_process.execSync(ansibleConfigCommand, {
encoding: 'utf-8',
cwd: URI.parse(this.context.workspaceFolder.uri).path,
env: ansibleConfigEnv,
});
// get Ansible configuration
const ansibleConfigResult = await commandRunner.runCommand(
'ansible-config',
'dump'
);

let config = ini.parse(ansibleConfigResult);
let config = ini.parse(ansibleConfigResult.stdout);
config = _.mapKeys(
config,
(_, key) => key.substring(0, key.indexOf('(')) // remove config source in parenthesis
);
this._collection_paths = parsePythonStringArray(config.COLLECTIONS_PATHS);

// get Ansible basic information
const [ansibleCommand, ansibleEnv] = withInterpreter(
`${settings.ansible.path}`,
'--version',
settings.python.interpreterPath,
settings.python.activationScript
const ansibleVersionResult = await commandRunner.runCommand(
'ansible',
'--version'
);

const ansibleVersionResult = child_process.execSync(ansibleCommand, {
encoding: 'utf-8',
env: ansibleEnv,
});

const versionInfo = ini.parse(ansibleVersionResult);
const versionInfo = ini.parse(ansibleVersionResult.stdout);
this._module_locations = parsePythonStringArray(
versionInfo['configured module search path']
);
Expand All @@ -71,18 +60,13 @@ export class AnsibleConfig {

// get Python sys.path
// this is needed to get the pre-installed collections to work
const [pythonPathCommand, pythonPathEnv] = withInterpreter(
const pythonPathResult = await commandRunner.runCommand(
'python3',
' -c "import sys; print(sys.path, end=\\"\\")"',
settings.python.interpreterPath,
settings.python.activationScript
' -c "import sys; print(sys.path, end=\\"\\")"'
);
this._collection_paths.push(
...parsePythonStringArray(pythonPathResult.stdout)
);

const pythonPathResult = child_process.execSync(pythonPathCommand, {
encoding: 'utf-8',
env: pythonPathEnv,
});
this._collection_paths.push(...parsePythonStringArray(pythonPathResult));
} catch (error) {
if (error instanceof Error) {
this.connection.window.showErrorMessage(error.message);
Expand All @@ -94,10 +78,18 @@ export class AnsibleConfig {
}
}

set collections_paths(updatedCollectionPath: string[]) {
this._collection_paths = updatedCollectionPath;
}

get collections_paths(): string[] {
return this._collection_paths;
}

set module_locations(updatedModulesPath: string[]) {
this._module_locations = updatedModulesPath;
}

get module_locations(): string[] {
return this._module_locations;
}
Expand Down
90 changes: 55 additions & 35 deletions src/services/docsLibrary.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Connection } from 'vscode-languageserver';
import { Node } from 'yaml/types';
import { getDeclaredCollections } from '../utils/yaml';
import { findDocumentation, findPluginRouting } from '../utils/docsFinder';
Expand All @@ -13,6 +14,7 @@ import {
} from '../utils/docsParser';
import { IModuleMetadata } from '../interfaces/module';
export class DocsLibrary {
private connection: Connection;
private modules = new Map<string, IModuleMetadata>();
private _moduleFqcns = new Set<string>();
private docFragments = new Map<string, IModuleMetadata>();
Expand All @@ -22,55 +24,73 @@ export class DocsLibrary {
IPluginRoutesByType
>();

constructor(context: WorkspaceFolderContext) {
constructor(connection: Connection, context: WorkspaceFolderContext) {
this.connection = connection;
this.context = context;
}

public async initialize(): Promise<void> {
const ansibleConfig = await this.context.ansibleConfig;
for (const modulesPath of ansibleConfig.module_locations) {
(await findDocumentation(modulesPath, 'builtin')).forEach((doc) => {
this.modules.set(doc.fqcn, doc);
this.moduleFqcns.add(doc.fqcn);
});

(await findDocumentation(modulesPath, 'builtin_doc_fragment')).forEach(
(doc) => {
this.docFragments.set(doc.fqcn, doc);
}
try {
const settings = await this.context.documentSettings.get(
this.context.workspaceFolder.uri
);
}

(
await findPluginRouting(ansibleConfig.ansible_location, 'builtin')
).forEach((r, collection) => this.pluginRouting.set(collection, r));

for (const collectionsPath of ansibleConfig.collections_paths) {
(await findDocumentation(collectionsPath, 'collection')).forEach(
(doc) => {
const ansibleConfig = await this.context.ansibleConfig;
if (settings.executionEnvironment.enabled) {
// ensure plugin/module cache is established
await this.context.executionEnvironment;
}
for (const modulesPath of ansibleConfig.module_locations) {
(await findDocumentation(modulesPath, 'builtin')).forEach((doc) => {
this.modules.set(doc.fqcn, doc);
this.moduleFqcns.add(doc.fqcn);
}
);
});

(await findDocumentation(modulesPath, 'builtin_doc_fragment')).forEach(
(doc) => {
this.docFragments.set(doc.fqcn, doc);
}
);
}

(
await findDocumentation(collectionsPath, 'collection_doc_fragment')
).forEach((doc) => {
this.docFragments.set(doc.fqcn, doc);
});
await findPluginRouting(ansibleConfig.ansible_location, 'builtin')
).forEach((r, collection) => this.pluginRouting.set(collection, r));

for (const collectionsPath of ansibleConfig.collections_paths) {
(await findDocumentation(collectionsPath, 'collection')).forEach(
(doc) => {
this.modules.set(doc.fqcn, doc);
this.moduleFqcns.add(doc.fqcn);
}
);

(await findPluginRouting(collectionsPath, 'collection')).forEach(
(r, collection) => this.pluginRouting.set(collection, r)
);
(
await findDocumentation(collectionsPath, 'collection_doc_fragment')
).forEach((doc) => {
this.docFragments.set(doc.fqcn, doc);
});

(await findPluginRouting(collectionsPath, 'collection')).forEach(
(r, collection) => this.pluginRouting.set(collection, r)
);

// add all valid redirect routes as possible FQCNs
for (const [collection, routesByType] of this.pluginRouting) {
for (const [name, route] of routesByType.get('modules') || []) {
if (route.redirect && !route.tombstone) {
this.moduleFqcns.add(`${collection}.${name}`);
// add all valid redirect routes as possible FQCNs
for (const [collection, routesByType] of this.pluginRouting) {
for (const [name, route] of routesByType.get('modules') || []) {
if (route.redirect && !route.tombstone) {
this.moduleFqcns.add(`${collection}.${name}`);
}
}
}
}
} catch (error) {
if (error instanceof Error) {
this.connection.window.showErrorMessage(error.message);
} else {
this.connection.console.error(
`Exception in DocsLibrary service: ${JSON.stringify(error)}`
);
}
}
}

Expand Down
Loading

0 comments on commit cefd599

Please sign in to comment.