diff --git a/CHANGELOG.md b/CHANGELOG.md index 985590b..6d3268c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Changed -- Use NODE_TLS_REJECT_UNAUTHORIZED environment variable instead of passing TLS options to SDK when tls_insecure is enabled. ([#46](https://github.com/opsmill/infrahub-vscode/issues/46)) +- Use NODE_TLS_REJECT_UNAUTHORIZED environment variable instead of passing TLS options to SDK when tls_insecure is enabled. ([#46](https://github.com/opsmill/infrahub-vscode/issues/46)) ## [1.0.5](https://github.com/opsmill/infrahub-vscode/tree/v1.0.5) - 2025-11-07 diff --git a/changelog/30.added.md b/changelog/30.added.md new file mode 100644 index 0000000..b6839e5 --- /dev/null +++ b/changelog/30.added.md @@ -0,0 +1 @@ +Added transform execution support for Infrahub YAML configuration files. The extension now detects and displays transform types (Jinja2 or Python) in the YAML tree view, automatically determines the appropriate `infrahubctl` command (`render` for Jinja2 transforms, `transform` for Python transforms), and provides context-sensitive run commands for artifact definitions. Enhanced tree view provider with improved code structure and better type detection logic. \ No newline at end of file diff --git a/docs/docs/guides/configure-multiple-servers.mdx b/docs/docs/guides/configure-multiple-servers.mdx index 7f9a17d..c79344d 100644 --- a/docs/docs/guides/configure-multiple-servers.mdx +++ b/docs/docs/guides/configure-multiple-servers.mdx @@ -88,11 +88,10 @@ Restart VSCode after setting environment variables. When executing GraphQL queries: -1. Right-click on a query in the Infrahub YAML tree view -2. Select "Execute GraphQL Query" -3. You'll be prompted to select a server from your configured list -4. Choose the appropriate environment -5. Select the branch to query against +1. Select the play icon next to the query +2. You'll be prompted to select a server from your configured list +3. Choose the appropriate environment +4. Select the branch to query against ## Advanced configuration @@ -215,4 +214,5 @@ If you encounter certificate errors with development servers: - [How to Execute GraphQL Queries](./execute-graphql-queries.mdx) - [How to Manage Branches](./manage-branches.mdx) +- [Security Configuration and Best Practices](../topics/security-configuration.mdx) - [Extension Commands Reference](../reference/commands-settings.mdx) \ No newline at end of file diff --git a/docs/docs/guides/running-transforms.mdx b/docs/docs/guides/running-transforms.mdx new file mode 100644 index 0000000..32e7b15 --- /dev/null +++ b/docs/docs/guides/running-transforms.mdx @@ -0,0 +1,269 @@ +--- +title: How to Run Transforms and Artifacts +description: Execute Jinja2 and Python transforms directly from VSCode with automatic command selection and variable support +--- + +This guide shows you how to execute Infrahub transforms (both Jinja2 and Python) directly from VSCode. The extension automatically detects transform types and uses the appropriate `infrahubctl` command for execution. + +## Prerequisites + +- Infrahub VSCode extension installed and configured +- At least one Infrahub server configured +- A workspace with `.infrahub.yml` file containing transforms and artifact definitions +- `infrahubctl` CLI tool installed and available in your system PATH + +## Understanding Transform Types + +The extension supports two types of transforms: + +- **Jinja2 Transforms**: Template-based transforms using Jinja2 syntax (executed with `infrahubctl render`) +- **Python Transforms**: Code-based transforms using Python classes (executed with `infrahubctl transform`) + +## Step 1: Configure transforms in .infrahub.yml + +### Jinja2 Transforms + +Define Jinja2 transforms in your `.infrahub.yml`: + +```yaml +jinja2_transforms: + - name: topology_clab + description: Template to generate a containerlab topology + query: topology_simulator + template_path: templates/clab_topology.j2 +``` + +### Python Transforms + +Define Python transforms in your `.infrahub.yml`: + +```yaml +python_transforms: + - name: leaf + class_name: Leaf + file_path: transforms/leaf.py + - name: spine + class_name: Spine + file_path: transforms/spine.py + - name: edge + class_name: Edge + file_path: transforms/edge.py +``` + +### Artifact Definitions + +Define artifact definitions that reference your transforms: + +```yaml +artifact_definitions: + - name: leaf_config + artifact_name: leaf + content_type: text/plain + targets: leafs + transformation: leaf # References python_transforms + parameters: + device: name__value + + - name: Containerlab Topology + artifact_name: containerlab-topology + content_type: text/plain + targets: topologies_clab + transformation: topology_clab # References jinja2_transforms + parameters: + name: name__value +``` + +## Step 2: Execute transforms from VSCode + +### Method 1: From Artifact Definitions + +1. Open the **Infrahub YAML** tree view in VSCode +2. Expand your `.infrahub.yml` file +3. Navigate to **artifact_definitions** +4. You'll see each artifact with its transform type displayed in parentheses: + - `leaf_config (python)` + - `Containerlab Topology (jinja)` +5. Click the play icon next to the desired artifact + +### Method 2: From Transform Definitions + +1. In the **Infrahub YAML** tree view, navigate to: + - **jinja2_transforms** for Jinja2 templates, or + - **python_transforms** for Python transforms +2. Select the play icon to run the transform directly + +### Transform Execution Process + +When you run a transform, the extension will: + +1. **Auto-detect transform type**: The extension automatically determines whether to use `infrahubctl render` (Jinja2) or `infrahubctl transform` (Python) + +2. **Prompt for branch selection**: Choose which Infrahub branch to execute against + +3. **Collect transform variables**: Enter any required variables in `key=value` format: + ``` + site=nyc + device=router01 + environment=production + ``` + +4. **Execute the appropriate command**: + - For Jinja2: `infrahubctl render topology_clab site=nyc --branch main` + - For Python: `infrahubctl transform leaf device=router01 --branch main` + +## Step 3: Working with transform variables + +### Adding Variables + +When prompted for variables: + +1. Enter each variable in `key=value` format +2. Press Enter to add another variable +3. Leave empty and press Enter to finish + +### Variable Examples + +``` +# Network configuration +site=atl01 +rack=A12 +vlan=100 + +# Device specifics +device=spine01 +role=spine +asn=65001 + +# Environment settings +environment=production +region=us-east +``` + +### Variable Validation + +The extension validates variable format: +- ✅ `device=router01` (valid) +- ✅ `site=nyc` (valid) +- ❌ `device=` (invalid - empty value) +- ❌ `=router01` (invalid - empty key) +- ❌ `devicerouter01` (invalid - missing =) + +## Step 4: Understanding command execution + +### Automatic Command Selection + +The extension intelligently chooses the correct `infrahubctl` command: + +| Transform Type | Command Used | Example | +|---|---|---| +| Jinja2 | `infrahubctl render` | `infrahubctl render topology_clab --branch main` | +| Python | `infrahubctl transform` | `infrahubctl transform leaf device=spine01 --branch main` | + +### Transform Type Detection + +The extension determines transform types by: + +1. **For artifact definitions**: Looking up the `transformation` field in both `jinja2_transforms` and `python_transforms` sections +2. **For direct transforms**: Using the section they're defined in (`jinja2_transforms` vs `python_transforms`) + +### Terminal Integration + +Commands execute in the VSCode integrated terminal, allowing you to: + +- See real-time output +- Monitor progress +- Debug any errors +- Access command history + +## Step 5: Example workflow + +Here's a complete example of setting up and running transforms: + +### 1. Create directory structure +```bash +mkdir -p transforms templates +``` + +### 2. Define transforms in .infrahub.yml +```yaml +--- +jinja2_transforms: + - name: device_config + description: Generate device configuration + query: device_query + template_path: templates/device.j2 + +python_transforms: + - name: topology_builder + class_name: TopologyBuilder + file_path: transforms/topology.py + +artifact_definitions: + - name: router_config + artifact_name: router-config + content_type: text/plain + targets: routers + transformation: device_config # Jinja2 transform + parameters: + device: name__value + + - name: network_topology + artifact_name: topology + content_type: application/json + targets: networks + transformation: topology_builder # Python transform + parameters: + network: name__value +``` + +### 3. Execute from VSCode + +1. Navigate to **artifact_definitions** → **router_config (jinja)** + +2. Click play icon +3. Select branch: `main` +4. Add variables: `device=router01`, `site=nyc` +5. Command executes: `infrahubctl render device_config device=router01 site=nyc --branch main` + +## Troubleshooting + +### Common Issues + +**"Transform type not determined"** +- Verify the `transformation` field matches a name in `jinja2_transforms` or `python_transforms` +- Check YAML syntax and indentation + +**"infrahubctl command not found"** +- Ensure `infrahubctl` is installed: `pip install infrahubctl` +- Verify it's in your system PATH +- Restart VSCode after installation + +**"No transform selected"** +- Ensure your artifact definition has a valid `transformation` field +- Verify the referenced transform exists in your configuration + +**Transform execution fails** +- Check the terminal output for specific error messages +- Verify branch exists and is accessible +- Ensure required variables are provided +- Check transform syntax (Jinja2 templates or Python code) + +### Best Practices + +1. **Organize transforms logically**: Group related transforms in clearly named sections +2. **Use descriptive names**: Transform names should clearly indicate their purpose +3. **Document variables**: Add comments in your `.infrahub.yml` describing expected variables +4. **Test incrementally**: Start with simple transforms and add complexity gradually +5. **Version control**: Keep transform files and `.infrahub.yml` in version control + +## Next Steps + +- **[Managing Branches](./manage-branches.mdx)**: Learn how to work with different Infrahub branches +- **[Configure Multiple Servers](./configure-multiple-servers.mdx)**: Set up development, staging, and production environments +- **[Extension Commands Reference](../reference/commands-settings.mdx)**: Complete list of available commands + +## Further Resources + +- [Infrahub Transforms Documentation](https://docs.infrahub.app/topics/transforms) +- [infrahubctl CLI Reference](https://docs.infrahub.app/reference/cli) +- [Jinja2 Template Documentation](https://jinja.palletsprojects.com/) \ No newline at end of file diff --git a/docs/docs/guides/snippets.mdx b/docs/docs/guides/snippets.mdx index 0a7cb2b..1f8235a 100644 --- a/docs/docs/guides/snippets.mdx +++ b/docs/docs/guides/snippets.mdx @@ -1,9 +1,9 @@ --- -title: How to Use Infrahub Snippets in VSCode +title: How to use Infrahub snippets in VSCode description: Step-by-step guide to inserting and customizing Infrahub YAML and automation snippets in Visual Studio Code --- -# How to Use Infrahub Snippets in VSCode +# How to use Infrahub snippets in VSCode This guide shows you how to quickly insert and customize Infrahub YAML objects and automation scripts using built-in snippets in Visual Studio Code. By following these steps, you’ll save time and reduce errors when authoring Infrahub resources. @@ -15,7 +15,7 @@ This guide shows you how to quickly insert and customize Infrahub YAML objects a ## Steps -### 1. Insert a Infrahub Object Snippet +### 1. Insert an Infrahub object snippet 1. Open any `.yaml` or `.yml` file in your project. 2. Type `infrahubobject` and select the snippet from the suggestion list. @@ -43,7 +43,7 @@ This guide shows you how to quickly insert and customize Infrahub YAML objects a - `infrahubcheck` for a check 3. Fill in the placeholders as needed to scaffold your automation script. -## Related Resources +## Related resources - [How to Configure Multiple Servers](./configure-multiple-servers.mdx) - [How to Execute GraphQL Queries](./execute-graphql-queries.mdx) \ No newline at end of file diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index cd69a5a..6a6828d 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -7,7 +7,7 @@ Welcome to the comprehensive documentation for the Infrahub VSCode Extension. Th ## What is the Infrahub VSCode extension? -The Infrahub VSCode Extension provides intelligent tooling that connects directly to your Infrahub servers, enabling you to develop infrastructure schemas, execute GraphQL queries, and manage branch-based workflows without leaving your IDE. It brings the power of Infrahub's graph database and version control directly into your development workflow. +The Infrahub VSCode Extension provides intelligent tooling that connects directly to your Infrahub servers, enabling you to develop infrastructure schemas, execute GraphQL queries, run Jinja2 and Python transforms, and manage branch-based workflows without leaving your IDE. It brings the power of Infrahub's graph database and version control directly into your development workflow. ## Documentation structure @@ -25,6 +25,7 @@ This documentation is organized following the [Diataxis framework](https://diata - [How to Configure Multiple Infrahub Servers](./guides/configure-multiple-servers.mdx) - Set up dev, staging, and production environments - [How to Execute GraphQL Queries](./guides/execute-graphql-queries.mdx) - Run queries with variables and branch selection +- [How to Run Transforms and Artifacts](./guides/running-transforms.mdx) - Execute Jinja2 and Python transforms with automatic command selection - [How to Manage Branches](./guides/manage-branches.mdx) - Create, delete, and work with branches - [How to use Infrahub Snippets](./guides/snippets.mdx) - Insert and customize Infrahub YAML and automation snippets diff --git a/docs/docs/reference/commands-settings.mdx b/docs/docs/reference/commands-settings.mdx index d52e28b..1dcc5c3 100644 --- a/docs/docs/reference/commands-settings.mdx +++ b/docs/docs/reference/commands-settings.mdx @@ -16,6 +16,7 @@ The extension registers the following commands that can be executed via the Comm | `infrahub.editInfrahubYaml` | Edit file | Opens the selected YAML file at a specific location | Tree view item | | `infrahub.editGqlQuery` | Edit GraphQL Query | Opens the GraphQL query file for editing | Query tree item | | `infrahub.executeGraphQLQuery` | Execute GraphQL Query | Runs a GraphQL query against selected server/branch | Query tree item | +| `infrahub.runTransform` | Run Transform | Executes a Jinja2 or Python transform with automatic command selection | Transform/Artifact tree item | | `infrahub.newBranch` | New Branch | Creates a new branch on the selected server | Server tree item | | `infrahub.deleteBranch` | Delete Branch | Deletes the selected branch | Branch tree item | @@ -34,6 +35,7 @@ Right-click on tree view items to access contextual commands: - **Server Items**: New Branch - **Branch Items**: Delete Branch - **Query Items**: Execute Query, Edit Query +- **Transform/Artifact Items**: Run Transform (automatically detects Jinja2 vs Python) - **YAML Items**: Edit File #### Programmatic Execution @@ -65,20 +67,20 @@ interface ServerConfig { name: string; // Display name for the server address: string; // Server URL (http/https) api_token?: string; // Optional API token for authentication - tls_insecure?: boolean; // Optional: Disable TLS certificate verification (default: false) + tls_insecure?: boolean; // Optional: Disable TLS certificate verification (default: false) } ``` -#### TLS Configuration Details +#### TLS configuration details -The `tls_insecure` property controls TLS certificate verification behavior: +The `tls_insecure` property controls TLS certificate verification behavior: - **Default value**: `false` (secure, certificates are verified) - **When `true`**: Disables certificate verification for development environments - **Scope**: Affects all HTTPS connections when any server has this enabled - **Security impact**: Makes connections vulnerable to man-in-the-middle attacks -**Use cases for `tls_insecure: true`**: +**Use cases for `tls_insecure: true`**: - Development servers with self-signed certificates - Internal testing environments with custom CA certificates - Docker containers with self-signed certificates @@ -103,7 +105,7 @@ The `tls_insecure` property controls TLS certificate verification behavior: "name": "Development (Self-Signed)", "address": "https://dev.infrahub.local", "api_token": "inf_dev_token", - "tls_insecure": true + "tls_insecure": true } ], "infrahub-vscode.schemaDirectory": "infrastructure/schemas" @@ -111,7 +113,7 @@ The `tls_insecure` property controls TLS certificate verification behavior: ``` :::warning -Setting `tls_insecure: true` disables certificate verification and is **not recommended for production environments**. Only use this option in development/testing environments with self-signed certificates. +Setting `tls_insecure: true` disables certificate verification and is **not recommended for production environments**. Only use this option in development/testing environments with self-signed certificates. ::: ### Environment Variable Substitution @@ -313,7 +315,7 @@ inf_[32-character-alphanumeric-string] ### Token Configuration Methods -#### Method 1: Direct in Settings (Not Recommended) +#### Method 1: Direct in settings (not recommended) ```json { @@ -325,7 +327,7 @@ inf_[32-character-alphanumeric-string] } ``` -#### Method 2: Environment Variables (Recommended) +#### Method 2: Environment variables (recommended) ```json { @@ -337,7 +339,7 @@ inf_[32-character-alphanumeric-string] } ``` -#### Method 3: VSCode Secrets (Future) +#### Method 3: VSCode secrets (future) Planned support for VSCode's secret storage API. @@ -352,19 +354,19 @@ Planned support for VSCode's secret storage API. | Commands not appearing | Wrong context | Ensure correct tree view item is selected | | Validation not working | Missing YAML extension | Install Red Hat YAML extension | | Token not working | Incorrect format or permissions | Verify token format and permissions | -| TLS certificate errors | Self-signed or invalid certificates | Add `"tls_insecure": true` for development servers | -| CERT_HAS_EXPIRED | Expired SSL certificate | Renew certificate or use `tls_insecure` for dev | -| SELF_SIGNED_CERT_IN_CHAIN | Self-signed certificate | Use `"tls_insecure": true` for development | +| TLS certificate errors | Self-signed or invalid certificates | Add `"tls_insecure": true` for development servers | +| CERT_HAS_EXPIRED | Expired SSL certificate | Renew certificate or use `tls_insecure` for dev | +| SELF_SIGNED_CERT_IN_CHAIN | Self-signed certificate | Use `"tls_insecure": true` for development | -### TLS Error Messages +### TLS error messages + The extension provides specific error messages for common TLS issues: - - **"TLS Certificate expired - check tls_insecure setting"**: The server's certificate has expired - **"Self-signed certificate - check tls_insecure setting"**: The server uses a self-signed certificate - **"TLS Verification failed - check tls_insecure setting"**: General certificate verification failure - These messages appear in the server tree view when connection attempts fail due to certificate issues. + ### Debug Output @@ -452,4 +454,5 @@ The extension doesn't define default keyboard shortcuts, but you can add custom - [Getting Started Tutorial](../tutorials/getting-started.mdx) - [Understanding the Extension Architecture](../topics/extension-architecture.mdx) +- [Security Configuration and Best Practices](../topics/security-configuration.mdx) - [GitHub Repository](https://github.com/opsmill/infrahub-vscode) \ No newline at end of file diff --git a/docs/docs/topics/extension-architecture.mdx b/docs/docs/topics/extension-architecture.mdx index d70df66..41d1f0e 100644 --- a/docs/docs/topics/extension-architecture.mdx +++ b/docs/docs/topics/extension-architecture.mdx @@ -183,7 +183,7 @@ User Action → Command Handler → Client Method → Server API Example: Executing a GraphQL query: -1. User right-clicks query in tree view +1. User selects play icon next to query in tree view 2. Command handler prompts for variables 3. Client sends query to selected server/branch 4. Results are formatted and displayed in webview diff --git a/docs/docs/topics/security-configuration.mdx b/docs/docs/topics/security-configuration.mdx index e520755..1882cf6 100644 --- a/docs/docs/topics/security-configuration.mdx +++ b/docs/docs/topics/security-configuration.mdx @@ -9,13 +9,13 @@ This document covers security considerations, TLS configuration, API token manag The Infrahub VSCode extension handles connections to Infrahub servers which may contain sensitive infrastructure data. Proper security configuration ensures your connections are encrypted and authenticated appropriately for your environment. -## TLS Certificate Configuration +## TLS certificate configuration -### Understanding TLS in the Extension +### Understanding TLS in the extension The extension connects to Infrahub servers over HTTPS using TLS encryption. By default, the extension verifies server certificates to ensure secure connections. However, development environments often use self-signed certificates that fail standard verification. -### Production Environment (Default Secure Settings) +### Production environment (default secure settings) For production environments, always use properly signed certificates: @@ -26,7 +26,7 @@ For production environments, always use properly signed certificates: "name": "Production", "address": "https://infrahub.company.com", "api_token": "${env:INFRAHUB_PROD_TOKEN}" - // tls_insecure defaults to false - secure mode + // tls_insecure defaults to false - secure mode } ] } @@ -38,7 +38,7 @@ For production environments, always use properly signed certificates: - Maintains encryption integrity - Complies with enterprise security policies -### Development Environment (Insecure Mode) +### Development environment (insecure mode) For development environments with self-signed certificates: @@ -49,13 +49,13 @@ For development environments with self-signed certificates: "name": "Development", "address": "https://dev.infrahub.local", "api_token": "${env:INFRAHUB_DEV_TOKEN}", - "tls_insecure": true + "tls_insecure": true } ] } ``` -**When to Use `tls_insecure: true`:** +**When to Use `tls_insecure: true`:** - Local development servers with self-signed certificates - Docker containers with self-generated certificates - Internal testing environments with custom CA certificates @@ -67,7 +67,7 @@ For development environments with self-signed certificates: - Should never be used in production - Affects all HTTPS connections in the VSCode process -### Mixed Environment Configuration +### Mixed environment configuration You can configure different TLS settings for different environments: @@ -90,16 +90,16 @@ You can configure different TLS settings for different environments: "name": "Development", "address": "https://dev.infrahub.local:8000", "api_token": "${env:INFRAHUB_DEV_TOKEN}", - "tls_insecure": true + "tls_insecure": true // Allows self-signed certificates } ] } ``` -### How TLS Configuration Works Internally +### How TLS configuration works internally -When any server has `tls_insecure: true`: +When any server has `tls_insecure: true`: 1. **Environment Variable**: Sets `NODE_TLS_REJECT_UNAUTHORIZED = '0'` 2. **Global Effect**: Affects all HTTPS connections in the VSCode process @@ -108,11 +108,11 @@ When any server has `tls_insecure: true`: This approach was chosen to ensure compatibility with the Infrahub SDK and provide reliable certificate bypassing when needed. -## API Token Security +## API token security -### Token Storage Best Practices +### Token storage best practices -#### ❌ Don't Store Tokens Directly +#### ❌ Don't store tokens directly ```json // NEVER do this - tokens visible in settings @@ -138,7 +138,7 @@ This approach was chosen to ensure compatibility with the Infrahub SDK and provi } ``` -### Setting Up Environment Variables +### Setting up environment variables #### macOS and Linux @@ -166,9 +166,9 @@ Using PowerShell: [System.Environment]::SetEnvironmentVariable('INFRAHUB_PROD_TOKEN', 'inf_prod_1234567890abcdef', 'User') ``` -### Token Management Best Practices +### Token management best practices -#### Token Rotation +#### Token rotation Regularly rotate API tokens: @@ -185,7 +185,7 @@ export INFRAHUB_PROD_TOKEN="$NEW_TOKEN" echo "export INFRAHUB_PROD_TOKEN=\"$NEW_TOKEN\"" >> ~/.zshrc ``` -#### Minimal Permissions +#### Minimal permissions Create tokens with minimal required permissions: @@ -194,7 +194,7 @@ Create tokens with minimal required permissions: - **Separate tokens** for each environment - **Short-lived tokens** for temporary access -#### Token Security Checklist +#### Token security checklist - [ ] Tokens stored in environment variables, not settings files - [ ] Different tokens for each environment (dev, staging, prod) @@ -203,11 +203,11 @@ Create tokens with minimal required permissions: - [ ] Tokens excluded from version control (`.env` files in `.gitignore`) - [ ] Access to tokens limited to authorized personnel -## Network Security Considerations +## Network security considerations -### Firewall and Proxy Configuration +### Firewall and proxy configuration -#### Corporate Proxy Settings +#### Corporate proxy settings If behind a corporate proxy, VSCode inherits system proxy settings. For manual configuration: @@ -219,15 +219,15 @@ If behind a corporate proxy, VSCode inherits system proxy settings. For manual c } ``` -#### Network Access Requirements +#### Network access requirements The extension requires outbound HTTPS access to: - Infrahub server endpoints (configured addresses) - Schema validation endpoints (`https://schema.infrahub.app/`) -### VPN and Network Isolation +### VPN and network isolation -#### VPN-Protected Servers +#### VPN-protected servers For servers behind VPN: @@ -244,16 +244,16 @@ For servers behind VPN: } ``` -#### Network Segmentation +#### Network segmentation Consider network segmentation best practices: - Development servers on isolated networks - Production servers with restricted access - API tokens with network-based restrictions -## Compliance and Audit Considerations +## Compliance and audit considerations -### Logging and Monitoring +### Logging and monitoring The extension logs connection attempts and errors. Monitor logs for: @@ -262,7 +262,7 @@ The extension logs connection attempts and errors. Monitor logs for: - Unusual connection patterns - Token usage patterns -### Compliance Requirements +### Compliance requirements For regulated environments: @@ -273,16 +273,16 @@ For regulated environments: - Log security-relevant events - Regular security configuration reviews -#### GDPR / Data Privacy +#### GDPR / data privacy - Understand what data is transmitted to servers - Ensure proper encryption in transit - Document data processing activities - Implement data retention policies -### Security Scanning Integration +### Security scanning integration -#### VS Code Security Extensions +#### VS Code security extensions Consider installing complementary security extensions: @@ -296,26 +296,26 @@ Consider installing complementary security extensions: } ``` -#### Static Analysis +#### Static analysis Regularly scan configuration files for: - Hardcoded tokens or secrets - Insecure TLS configurations - Overly permissive settings -## Troubleshooting Security Issues +## Troubleshooting security issues -### Common TLS Errors and Solutions +### Common TLS errors and solutions -#### Certificate Expired +#### Certificate expired **Error**: `CERT_HAS_EXPIRED` **Solutions**: 1. Renew the server certificate (production) -2. Add `"tls_insecure": true` (development only) +2. Add `"tls_insecure": true` (development only) -#### Self-Signed Certificate +#### Self-signed certificate **Error**: `SELF_SIGNED_CERT_IN_CHAIN` @@ -324,7 +324,7 @@ Regularly scan configuration files for: 2. Add `"tls_insecure": true` (development) 3. Add certificate to system trust store -#### Certificate Verification Failed +#### Certificate verification failed **Error**: `UNABLE_TO_VERIFY_LEAF_SIGNATURE` @@ -333,9 +333,9 @@ Regularly scan configuration files for: 2. Update system CA certificate store 3. Use `"tls_insecure": true` for development -### Authentication Issues +### Authentication issues -#### Token Not Working +#### Token not working **Symptoms**: `401 Unauthorized` errors @@ -345,7 +345,7 @@ Regularly scan configuration files for: 3. Token expiration date 4. Environment variable substitution -#### Environment Variable Not Found +#### Environment variable not found **Error**: Token resolves to literal `${env:VARIABLE_NAME}` @@ -354,29 +354,29 @@ Regularly scan configuration files for: 2. Restart VSCode after setting variables 3. Check variable name spelling -### Network Connectivity Issues +### Network connectivity issues -#### Proxy Interference +#### Proxy interference **Symptoms**: Connection timeouts or SSL errors **Solutions**: 1. Configure VSCode proxy settings -2. Add infrahub server to proxy bypass list +2. Add Infrahub server to proxy bypass list 3. Verify proxy supports HTTPS CONNECT -#### Firewall Blocking +#### Firewall blocking **Symptoms**: Connection refused or timeouts **Solutions**: 1. Verify outbound HTTPS (443) access -2. Add Infrahub server to firewall allowlist +2. Add Infrahub server to firewall allowlist 3. Check for deep packet inspection interference -## Security Best Practices Summary +## Security best practices summary -### Development Environment +### Development environment - ✅ Use `tls_insecure: true` for self-signed certificates - ✅ Use separate development API tokens @@ -384,7 +384,7 @@ Regularly scan configuration files for: - ✅ Regular token rotation - ❌ Never use production tokens in development -### Staging Environment +### Staging environment - ✅ Use proper certificates when possible - ✅ Separate staging API tokens @@ -392,7 +392,7 @@ Regularly scan configuration files for: - ✅ Test certificate configurations - ❌ Don't use `tls_insecure` unless necessary -### Production Environment +### Production environment - ✅ Always use proper CA-signed certificates - ✅ Never use `tls_insecure: true` @@ -401,7 +401,7 @@ Regularly scan configuration files for: - ✅ Regular security reviews - ❌ Never compromise on certificate verification -### Token Management +### Token management - ✅ Store in environment variables - ✅ Use minimal required permissions @@ -410,7 +410,7 @@ Regularly scan configuration files for: - ❌ Never commit tokens to version control - ❌ Don't share tokens between environments -## Related Documentation +## Related documentation - [Configure Multiple Servers Guide](../guides/configure-multiple-servers.mdx) - [Commands and Settings Reference](../reference/commands-settings.mdx) diff --git a/docs/docs/tutorials/getting-started.mdx b/docs/docs/tutorials/getting-started.mdx index c9db4b5..10864b5 100644 --- a/docs/docs/tutorials/getting-started.mdx +++ b/docs/docs/tutorials/getting-started.mdx @@ -32,7 +32,7 @@ Before starting this tutorial, ensure you have: 4. Find the extension published by "opsmill" 5. Click the **Install** button -### Option B: VSCode Based Editors +### Option B: VSCode based editors 1. We publish the extension to [Open VSX](https://open-vsx.org/extension/opsmill/infrahub) which is often used by VSCode Forks like Cursor. 2. In your editor of choice search Infrahub. @@ -95,7 +95,7 @@ If your server requires authentication, add an API token: > "api_token": "${env:INFRAHUB_API_TOKEN}" > ``` -### TLS Configuration for HTTPS Servers +### TLS configuration for HTTPS servers If you're connecting to an HTTPS server with a self-signed certificate (common in development environments), you may need to disable certificate verification: @@ -264,8 +264,10 @@ Congratulations! You've successfully: ### What to explore next +- **[How to Run Transforms and Artifacts](../guides/running-transforms.mdx)**: Execute Jinja2 and Python transforms directly from VSCode - **[How to Configure Multiple Servers](../guides/configure-multiple-servers.mdx)**: Work with development, staging, and production environments - **[Understanding Schema Validation](../topics/schema-validation.mdx)**: Deep dive into how the extension validates your schemas +- **[Security Configuration and Best Practices](../topics/security-configuration.mdx)**: Comprehensive guide to TLS settings, API tokens, and security practices - **[Extension Commands Reference](../reference/commands-settings.mdx)**: Complete list of available commands and settings ### Troubleshooting tips diff --git a/docs/sidebars.ts b/docs/sidebars.ts index af19068..f3f8caa 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -19,6 +19,7 @@ const sidebars: SidebarsConfig = { 'guides/configure-multiple-servers', 'guides/execute-graphql-queries', 'guides/manage-branches', + 'guides/running-transforms', 'guides/snippets', ], }, diff --git a/package.json b/package.json index 62c7fd6..89865d4 100644 --- a/package.json +++ b/package.json @@ -146,6 +146,12 @@ "title": "Load All Schema Files", "icon": "$(cloud-upload)", "category": "Infrahub" + }, + { + "command": "infrahub.runTransform", + "title": "Run Transform", + "icon": "$(run)", + "category": "Infrahub" } ], "viewsContainers": { @@ -225,6 +231,11 @@ "when": "view == infrahubYamlTreeView && viewItem =~ /queries/", "group": "inline" }, + { + "command": "infrahub.runTransform", + "when": "view == infrahubYamlTreeView && viewItem =~ /transforms/", + "group": "inline" + }, { "command": "infrahub.loadSchemaFile", "when": "view == infrahubSchemaTreeView", diff --git a/src/common/commands.ts b/src/common/commands.ts index fea4a2f..2901ceb 100644 --- a/src/common/commands.ts +++ b/src/common/commands.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { InfrahubYamlTreeItem } from '../treeview/infrahubYamlTreeViewProvider'; import { promptForVariables, searchForConfigSchemaFiles } from '../common/infrahub'; import { BranchCreateInput } from 'infrahub-sdk/src/graphql/branch'; -import { showError, showInfo, escapeHtml, showConfirm, promptBranchAndRunInfrahubctl, getBranchPrompt, getServerPrompt, getGraphQLResultHtml } from '../common/utilities'; +import { showError, showInfo, escapeHtml, showConfirm, promptBranchAndRunInfrahubctl, getBranchPrompt, getServerPrompt, getGraphQLResultHtml, runInfrahubctlInTerminal } from '../common/utilities'; /** @@ -281,3 +281,90 @@ export async function checkSchemaFile(filePath: string) { export async function loadSchemaFile(filePath: string) { await promptBranchAndRunInfrahubctl('load', filePath); } + +/** + * Runs a transform (Jinja2 or Python) from the Infrahub YAML tree item. + * Prompts for branch selection and any required variables. + */ +export async function runTransformCommand(item: InfrahubYamlTreeItem): Promise { + if (!item || !item.transformation?.name) { + vscode.window.showErrorMessage('No transform selected or transformation name not found.'); + return; + } + + const transformationName = item.transformation.name; + const transformType = item.transform_type; + + if (!transformType) { + vscode.window.showErrorMessage('Transform type not determined. Cannot run transform.'); + return; + } + + // Prompt for branch selection + const branchResult = await getBranchPrompt(); + if (!branchResult) { + vscode.window.showInformationMessage('Transform run cancelled: No branch selected.'); + return; + } + + // Prompt for transform variables + const variables: string[] = []; + let addMore = true; + + while (addMore) { + const varInput = await vscode.window.showInputBox({ + prompt: 'Enter variable in key=value format (or leave empty to finish)', + placeHolder: 'e.g., site=nyc or device=router01', + ignoreFocusOut: true, + }); + + if (varInput === undefined) { + // User cancelled (pressed Escape) + vscode.window.showInformationMessage('Transform run cancelled.'); + return; + } + + if (varInput.trim() === '') { + addMore = false; + } else { + // Validate format: must have at least one character before '=' and the rest after + const trimmedInput = varInput.trim(); + const equalIndex = trimmedInput.indexOf('='); + if (equalIndex > 0 && equalIndex < trimmedInput.length - 1) { + variables.push(trimmedInput); + } else { + vscode.window.showWarningMessage('Invalid format. Variables must be in key=value format with non-empty key and value.'); + } + } + } + + // Build the command based on transform type + const branchArg = `--branch "${branchResult.branch.name}"`; + const variablesArg = variables.length > 0 ? variables.join(' ') : ''; + + let commandArgs: string; + let actionDescription: string; + + if (transformType === 'jinja') { + // Use render command for jinja transforms + commandArgs = variablesArg + ? `render ${transformationName} ${variablesArg} ${branchArg}`.trim() + : `render ${transformationName} ${branchArg}`.trim(); + actionDescription = `Rendering jinja transform: ${transformationName}`; + } else if (transformType === 'python') { + // Use transform command for python transforms + commandArgs = variablesArg + ? `transform ${transformationName} ${variablesArg} ${branchArg}`.trim() + : `transform ${transformationName} ${branchArg}`.trim(); + actionDescription = `Running python transform: ${transformationName}`; + } else { + vscode.window.showErrorMessage(`Unknown transform type: ${transformType}. Expected 'jinja' or 'python'.`); + return; + } + + await runInfrahubctlInTerminal( + commandArgs, + actionDescription, + branchResult + ); +} diff --git a/src/extension.ts b/src/extension.ts index 0b9480e..9966065 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,7 +12,7 @@ import { InfrahubSchemaProvider, InfrahubSchemaTreeItem } from './treeview/infra // Extension Utilities import { openFileAtLocation, searchForConfigSchemaFiles } from './common/infrahub'; import { InfrahubClient, InfrahubClientOptions } from 'infrahub-sdk'; -import { executeInfrahubGraphQLQuery, checkAllSchemaFiles, loadAllSchemaFiles, checkSchemaFile, loadSchemaFile } from './common/commands'; +import { executeInfrahubGraphQLQuery, checkAllSchemaFiles, loadAllSchemaFiles, checkSchemaFile, loadSchemaFile, runTransformCommand } from './common/commands'; import { newBranchCommand, deleteBranchCommand } from './common/commands'; let statusBar: vscode.StatusBarItem; @@ -150,6 +150,11 @@ export function activate(context: vscode.ExtensionContext) { await executeInfrahubGraphQLQuery(item); }), ); + context.subscriptions.push( + vscode.commands.registerCommand('infrahub.runTransform', async (item: InfrahubYamlTreeItem) => { + await runTransformCommand(item); + }), + ); // =============================================== // Status Bar diff --git a/src/treeview/infrahubYamlTreeViewProvider.ts b/src/treeview/infrahubYamlTreeViewProvider.ts index febd9c9..9300a62 100644 --- a/src/treeview/infrahubYamlTreeViewProvider.ts +++ b/src/treeview/infrahubYamlTreeViewProvider.ts @@ -9,12 +9,16 @@ import * as yaml from 'yaml'; import { load, YAMLMapping, YAMLSequence, YAMLNode, YamlMap } from 'yaml-ast-parser'; import { parseGraphQLQuery } from '../common/infrahub'; + + export class InfrahubYamlTreeItem extends vscode.TreeItem { label: string; filePath?: string = undefined; lineNumber?: number = undefined; gqlFilePath?: string = undefined; gqlInfo?: { [key: string]: any } = undefined; + transformation?: { [key: string]: any } = undefined; + transform_type?: string = undefined; constructor( label: string, @@ -39,6 +43,7 @@ export class infrahubTreeViewProvider implements vscode.TreeDataProvider { + console.log(`Infrahub: Getting values for key: ${key}`); if (!this.infrahubFile) { return []; } + try { await fs.promises.access(this.infrahubFile, fs.constants.F_OK); } catch { return []; } + try { const fileContents = await fs.promises.readFile(this.infrahubFile, 'utf8'); const ast = load(fileContents); - // Find the mapping for the given key at the root - if (ast && ast.kind === 2 /* MAP */) { - const rootMap = ast as YamlMap; - const mapping = rootMap.mappings.find((m: YAMLMapping) => m.key.value === key); - if (mapping && mapping.value && mapping.value.kind === 3 /* SEQ */) { - const seq = mapping.value as YAMLSequence; - const items = await Promise.all(seq.items.map(async (item: YAMLNode) => { - // item.startPosition gives the offset in the file - const line = fileContents.substring(0, item.startPosition).split('\n').length; - // Try to get the 'name' property if it's a mapping - let label = 'item'; - let file_path: string | undefined; - if (item.kind === 2 /* MAP */) { - const mapItem = item as YamlMap; - const nameMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'name'); - if (nameMapping && nameMapping.value) { - label = nameMapping.value.value; - } - const pathMapping = mapItem.mappings.find( - (m: YAMLMapping) => m.key.value === 'file_path', - ); - if (pathMapping && pathMapping.value) { - file_path = pathMapping.value.value; - } - } - const itemNode = new InfrahubYamlTreeItem( - label, - vscode.TreeItemCollapsibleState.None, - this.infrahubFile, - line, - ); - itemNode.tooltip = new vscode.MarkdownString( - `\`\`\`yaml${yaml.stringify(fileContents.substring(item.startPosition - 4, item.endPosition)).trim()}`, - ); - itemNode.command = { - command: 'infrahub.editInfrahubYaml', - title: 'Edit in .infrahub.yml', - arguments: [itemNode], - }; - if (key === 'queries' && file_path) { - const absolutePath = path.join(this.workspaceRoot || '', file_path); - itemNode.gqlFilePath = absolutePath; - const gqlQueryVars = await parseGraphQLQuery(absolutePath); - itemNode.gqlInfo = gqlQueryVars; - itemNode.contextValue = 'queries'; - console.log('Parsed GraphQL query variables:', itemNode.gqlInfo); - } - - return itemNode; - })); - return items; - } + if (!ast || ast.kind !== 2 /* MAP */) { + return []; + } + + const rootMap = ast as YamlMap; + const mapping = rootMap.mappings.find((m: YAMLMapping) => m.key.value === key); + + if (!mapping?.value || mapping.value.kind !== 3 /* SEQ */) { + return []; } + + const seq = mapping.value as YAMLSequence; + const items = await Promise.all(seq.items.map(async (item: YAMLNode) => { + return this.createTreeItemFromYamlNode(item, key, fileContents, ast); + })); + + return items; } catch (e) { - console.error('Error in getValuesWithAstParser:', e); + console.error('Error in getValues:', e); + return []; } - return []; + } + + /** + * Creates a tree item from a YAML node with appropriate context and metadata + */ + private async createTreeItemFromYamlNode( + item: YAMLNode, + key: string, + fileContents: string, + ast: any + ): Promise { + const line = fileContents.substring(0, item.startPosition).split('\n').length; + const { label, file_path } = this.extractBasicProperties(item); + + const itemNode = new InfrahubYamlTreeItem( + label, + vscode.TreeItemCollapsibleState.None, + this.infrahubFile, + line, + ); + + // Set common properties + itemNode.tooltip = this.createTooltip(fileContents, item); + itemNode.command = this.createEditCommand(itemNode); + + // Apply key-specific processing + await this.applyKeySpecificProcessing(itemNode, key, item, ast, file_path); + + console.log(`Infrahub: Created tree item for value: ${label} at line ${line}`, itemNode); + return itemNode; + } + + /** + * Extracts basic properties (name, file_path) from a YAML map item + */ + private extractBasicProperties(item: YAMLNode): { label: string; file_path?: string } { + let label = 'item'; + let file_path: string | undefined; + + if (item.kind === 2 /* MAP */) { + const mapItem = item as YamlMap; + const nameMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'name'); + const pathMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'file_path'); + + if (nameMapping?.value) { + label = nameMapping.value.value; + } + if (pathMapping?.value) { + file_path = pathMapping.value.value; + } + } + + return { label, file_path }; + } + + /** + * Creates tooltip for tree item + */ + private createTooltip(fileContents: string, item: YAMLNode): vscode.MarkdownString { + const yamlContent = fileContents.substring( + Math.max(0, item.startPosition - 4), + item.endPosition + ).trim(); + return new vscode.MarkdownString(`\`\`\`yaml\n${yaml.stringify(yamlContent)}\`\`\``); + } + + /** + * Creates edit command for tree item + */ + private createEditCommand(itemNode: InfrahubYamlTreeItem): vscode.Command { + return { + command: 'infrahub.editInfrahubYaml', + title: 'Edit in .infrahub.yml', + arguments: [itemNode], + }; + } + + /** + * Applies key-specific processing to tree items + */ + private async applyKeySpecificProcessing( + itemNode: InfrahubYamlTreeItem, + key: string, + item: YAMLNode, + ast: any, + file_path?: string + ): Promise { + switch (key) { + case 'queries': + await this.processQueriesItem(itemNode, file_path); + break; + case 'jinja2_transforms': + case 'python_transforms': + this.processTransformItem(itemNode, key, item); + break; + case 'artifact_definitions': + this.processArtifactDefinitionItem(itemNode, item, ast); + break; + } + } + + /** + * Processes query items with GraphQL parsing + */ + private async processQueriesItem(itemNode: InfrahubYamlTreeItem, file_path?: string): Promise { + if (!file_path) { + return; + } + + const absolutePath = path.join(this.workspaceRoot || '', file_path); + itemNode.gqlFilePath = absolutePath; + itemNode.gqlInfo = await parseGraphQLQuery(absolutePath); + itemNode.contextValue = 'queries'; + console.log('Parsed GraphQL query variables:', itemNode.gqlInfo); + } + + /** + * Processes transform items (jinja2_transforms and python_transforms) + */ + private processTransformItem(itemNode: InfrahubYamlTreeItem, key: string, item: YAMLNode): void { + if (item.kind !== 2 /* MAP */) { + return; + } + + const mapItem = item as YamlMap; + const nameMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'name'); + + if (!nameMapping?.value) { + return; + } + + itemNode.contextValue = 'transforms'; + itemNode.transform_type = key === 'jinja2_transforms' ? 'jinja' : 'python'; + itemNode.transformation = { name: nameMapping.value.value }; + } + + /** + * Processes artifact definition items with transform type detection + */ + private processArtifactDefinitionItem(itemNode: InfrahubYamlTreeItem, item: YAMLNode, ast: any): void { + if (item.kind !== 2 /* MAP */) { + return; + } + + const mapItem = item as YamlMap; + const transformationMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'transformation'); + + if (!transformationMapping?.value) { + return; + } + + const transformationName = transformationMapping.value.value; + itemNode.contextValue = 'transforms'; + itemNode.transformation = { name: transformationName }; + itemNode.transform_type = this.getTransformType(transformationName, ast); + + // Update label to show transform type + if (itemNode.transform_type) { + itemNode.label = `${itemNode.label} (${itemNode.transform_type})`; + } + + console.log('Determined transformation and type:', itemNode); + } + + /** + * Determines the transform type (python or jinja) by searching for the transformation name + * in the python_transforms or jinja2_transforms sections of the YAML file + */ + private getTransformType(transformationName: string, ast: any): string | undefined { + if (!ast || ast.kind !== 2 /* MAP */) { + return undefined; + } + + const rootMap = ast as YamlMap; + const transformSections = [ + { key: 'python_transforms', type: 'python' }, + { key: 'jinja2_transforms', type: 'jinja' } + ]; + + for (const section of transformSections) { + const sectionMapping = rootMap.mappings.find((m: YAMLMapping) => m.key.value === section.key); + + if (sectionMapping?.value?.kind === 3 /* SEQ */) { + const sequence = sectionMapping.value as YAMLSequence; + const found = sequence.items.some((item: YAMLNode) => { + if (item.kind !== 2 /* MAP */) { + return false; + } + const mapItem = item as YamlMap; + const nameMapping = mapItem.mappings.find((m: YAMLMapping) => m.key.value === 'name'); + return nameMapping?.value?.value === transformationName; + }); + + if (found) { + return section.type; + } + } + } + + return undefined; } }