- Add documentation links in main README overview section - Create new Documentation section listing all README files - Ensure all README files are properly referenced - All server documentation is now easily discoverable 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
516 lines
15 KiB
Markdown
516 lines
15 KiB
Markdown
# GitOps Implementation Fix for Portainer MCP Servers
|
|
|
|
## Problem
|
|
The Portainer MCP servers were unable to enable GitOps on existing Git-based stacks. The `enable_stack_gitops` function in `portainer_gitops_server.py` was failing with "Invalid stack file content" errors.
|
|
|
|
## Root Cause
|
|
The Portainer API's stack update endpoint (`PUT /api/stacks/{stack_id}`) has limitations:
|
|
1. It requires `StackFileContent` to be included in update requests
|
|
2. When `StackFileContent` is included, it converts Git-based stacks to file-based stacks
|
|
3. This sets `IsDetachedFromGit: true` and clears the `GitConfig`
|
|
|
|
## Solution
|
|
Enable GitOps during stack creation rather than as a separate step. The API supports including `autoUpdate` configuration when creating stacks from Git repositories.
|
|
|
|
### Implementation Changes
|
|
|
|
#### Updated `portainer_stacks_server.py`
|
|
Added GitOps parameters to the `create_compose_stack_from_git` tool:
|
|
|
|
```python
|
|
# New parameters in the tool schema:
|
|
"enable_gitops": {
|
|
"type": "boolean",
|
|
"description": "Enable GitOps automatic updates",
|
|
"default": False
|
|
},
|
|
"gitops_interval": {
|
|
"type": "string",
|
|
"description": "GitOps polling interval (e.g., '5m', '1h')",
|
|
"default": "5m"
|
|
},
|
|
"gitops_mechanism": {
|
|
"type": "string",
|
|
"enum": ["polling", "webhook"],
|
|
"description": "GitOps update mechanism",
|
|
"default": "polling"
|
|
},
|
|
"gitops_pull_image": {
|
|
"type": "boolean",
|
|
"description": "Pull latest images on GitOps update",
|
|
"default": True
|
|
},
|
|
"gitops_force_update": {
|
|
"type": "boolean",
|
|
"description": "Force redeployment even if no changes",
|
|
"default": False
|
|
}
|
|
```
|
|
|
|
The implementation now adds `autoUpdate` to the request when creating stacks:
|
|
|
|
```python
|
|
if arguments.get("enable_gitops", False):
|
|
auto_update = {
|
|
"interval": arguments.get("gitops_interval", "5m"),
|
|
"forcePullImage": arguments.get("gitops_pull_image", True),
|
|
"forceUpdate": arguments.get("gitops_force_update", False)
|
|
}
|
|
|
|
if arguments.get("gitops_mechanism") == "webhook":
|
|
auto_update["webhook"] = arguments.get("gitops_webhook_id", "")
|
|
|
|
data["autoUpdate"] = auto_update
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Creating a Stack with GitOps Enabled
|
|
|
|
```python
|
|
# Using the MCP server
|
|
await tool.call("create_compose_stack_from_git", {
|
|
"environment_id": "6",
|
|
"name": "nginx-gitops",
|
|
"repository_url": "https://github.com/example/repo",
|
|
"repository_ref": "main",
|
|
"compose_path": "docker-compose.yml",
|
|
"enable_gitops": True,
|
|
"gitops_interval": "5m",
|
|
"gitops_mechanism": "polling",
|
|
"gitops_pull_image": True
|
|
})
|
|
```
|
|
|
|
### Direct API Call
|
|
|
|
```bash
|
|
curl -X POST "https://portainer.example.com/api/stacks/create/standalone/repository?endpointId=6" \
|
|
-H "X-API-Key: your-api-key" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "nginx-gitops",
|
|
"repositoryURL": "https://github.com/example/repo",
|
|
"repositoryReferenceName": "main",
|
|
"composeFilePathInRepository": "docker-compose.yml",
|
|
"repositoryAuthentication": false,
|
|
"autoUpdate": {
|
|
"interval": "5m",
|
|
"forcePullImage": true,
|
|
"forceUpdate": false
|
|
}
|
|
}'
|
|
```
|
|
|
|
## Alternative Approaches
|
|
|
|
If you need to enable GitOps on existing stacks:
|
|
|
|
1. **Delete and Recreate**: Delete the existing stack and recreate it with GitOps enabled
|
|
2. **Use Webhooks**: Configure webhooks at the Git repository level
|
|
3. **External Automation**: Use external tools to trigger stack updates via the Git redeploy endpoint
|
|
|
|
## Testing
|
|
|
|
Use the provided `test_gitops_create.py` script to verify GitOps functionality:
|
|
|
|
```bash
|
|
python test_gitops_create.py
|
|
```
|
|
|
|
This will:
|
|
1. Create a new stack with GitOps enabled
|
|
2. Verify the GitOps configuration
|
|
3. Display the stack details including AutoUpdate settings
|
|
|
|
## Limitations
|
|
|
|
- Cannot enable GitOps on existing Git-based stacks without detaching them from Git
|
|
- Cannot update GitOps settings (like polling interval) on existing stacks without detaching them from Git
|
|
- The Portainer API does not provide a way to update Git-based stacks while maintaining Git connection
|
|
- Any update that includes `StackFileContent` converts the stack from Git-based to file-based
|
|
- This appears to be a fundamental limitation in Portainer's API design
|
|
|
|
### Why You Cannot Update GitOps Interval
|
|
|
|
When attempting to update the GitOps interval (e.g., from 5m to 3m) on an existing stack:
|
|
|
|
1. **API Limitation**: The stack update endpoint (`PUT /api/stacks/{id}`) requires `StackFileContent`
|
|
2. **Side Effect**: Including `StackFileContent` in the update request:
|
|
- Sets `IsDetachedFromGit: true`
|
|
- Clears `GitConfig: null`
|
|
- Clears `AutoUpdate: null`
|
|
3. **No Partial Updates**: The API doesn't support updating only the `AutoUpdate` configuration
|
|
|
|
### Workaround
|
|
|
|
To change the GitOps interval, you must:
|
|
|
|
1. **Delete the existing stack**
|
|
2. **Recreate it with the new interval**
|
|
|
|
Example:
|
|
```bash
|
|
# Delete existing stack
|
|
curl -X DELETE "https://portainer.example.com/api/stacks/{stack_id}?endpointId={env_id}" \
|
|
-H "X-API-Key: your-api-key"
|
|
|
|
# Recreate with new interval
|
|
curl -X POST "https://portainer.example.com/api/stacks/create/standalone/repository?endpointId={env_id}" \
|
|
-H "X-API-Key: your-api-key" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"name": "stack-name",
|
|
"repositoryURL": "https://github.com/example/repo",
|
|
"repositoryReferenceName": "refs/heads/main",
|
|
"composeFilePathInRepository": "docker-compose.yml",
|
|
"autoUpdate": {
|
|
"interval": "3m",
|
|
"forcePullImage": true,
|
|
"forceUpdate": false
|
|
}
|
|
}'
|
|
```
|
|
|
|
## Recommendations
|
|
|
|
1. Always enable GitOps during stack creation if needed
|
|
2. Consider opening a feature request with Portainer for enabling GitOps on existing stacks
|
|
3. Use the Git redeploy endpoint for manual updates of Git-based stacks
|
|
|
|
## API Field Reference
|
|
|
|
The complete `autoUpdate` object structure:
|
|
|
|
```json
|
|
{
|
|
"autoUpdate": {
|
|
"interval": "5m", // Polling interval (e.g., "1m30s", "5m", "1h")
|
|
"forcePullImage": true, // Pull latest images on update
|
|
"forceUpdate": false, // Force redeployment even if no changes
|
|
"webhook": "webhook-id", // Webhook ID (optional, for webhook mechanism)
|
|
"jobID": "15" // Job ID (managed by Portainer)
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
# Original GitOps MCP Server Documentation
|
|
|
|
## Features
|
|
|
|
- **GitOps Configuration**: Enable/disable automatic updates for stacks
|
|
- **Update Mechanisms**: Support for both webhook and polling-based updates
|
|
- **Edge Stack Support**: GitOps for edge computing environments
|
|
- **Webhook Management**: Generate and manage webhook URLs for CI/CD integration
|
|
- **Git Credentials**: Manage authentication for private repositories
|
|
- **Manual Triggers**: Trigger updates on-demand
|
|
- **Update Windows**: Configure deployment windows (Business Edition)
|
|
|
|
## Installation
|
|
|
|
1. Ensure you have the Portainer MCP servers repository:
|
|
```bash
|
|
git clone https://github.com/yourusername/portainer-mcp.git
|
|
cd portainer-mcp
|
|
```
|
|
|
|
2. Install dependencies:
|
|
```bash
|
|
pip install mcp httpx aiohttp
|
|
# Or use the requirements file:
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
3. Configure environment variables:
|
|
```bash
|
|
cp .env.example .env
|
|
# Edit .env with your Portainer URL and API key
|
|
```
|
|
|
|
4. Make the server executable:
|
|
```bash
|
|
chmod +x portainer_gitops_server.py
|
|
```
|
|
|
|
## Configuration
|
|
|
|
Add to your Claude Desktop configuration:
|
|
|
|
```json
|
|
{
|
|
"portainer-gitops": {
|
|
"command": "python",
|
|
"args": ["/path/to/portainer-mcp/portainer_gitops_server.py"],
|
|
"env": {
|
|
"PORTAINER_URL": "https://your-portainer-instance.com",
|
|
"PORTAINER_API_KEY": "your-api-key"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Available Tools
|
|
|
|
### Stack GitOps Management
|
|
|
|
#### list_gitops_stacks
|
|
List all stacks with GitOps configurations.
|
|
- **Parameters**:
|
|
- `environment_id` (optional): Filter by environment ID
|
|
|
|
#### get_stack_gitops_config
|
|
Get GitOps configuration for a specific stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
|
|
#### enable_stack_gitops
|
|
Enable GitOps automatic updates for a stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
- `mechanism` (optional): Update mechanism - "webhook" or "polling" (default: "polling")
|
|
- `interval` (optional): Polling interval like "5m", "1h" (default: "5m")
|
|
- `force_update` (optional): Force redeployment even if no changes (default: false)
|
|
- `pull_image` (optional): Pull latest images on update (default: true)
|
|
|
|
#### disable_stack_gitops
|
|
Disable GitOps automatic updates for a stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
|
|
#### trigger_stack_update
|
|
Manually trigger a GitOps update for a stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
- `pull_image` (optional): Pull latest images (default: true)
|
|
|
|
### Webhook Management
|
|
|
|
#### get_stack_webhook
|
|
Get webhook URL for a stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
|
|
#### regenerate_stack_webhook
|
|
Regenerate webhook URL for a stack.
|
|
- **Parameters**:
|
|
- `stack_id` (required): Stack ID
|
|
|
|
### Edge Stack GitOps
|
|
|
|
#### list_gitops_edge_stacks
|
|
List all edge stacks with GitOps configurations.
|
|
|
|
#### enable_edge_stack_gitops
|
|
Enable GitOps for an edge stack.
|
|
- **Parameters**:
|
|
- `edge_stack_id` (required): Edge Stack ID
|
|
- `mechanism` (optional): Update mechanism (default: "polling")
|
|
- `interval` (optional): Polling interval (default: "5m")
|
|
- `force_update` (optional): Force redeployment (default: false)
|
|
|
|
#### get_edge_stack_webhook
|
|
Get webhook URL for an edge stack.
|
|
- **Parameters**:
|
|
- `edge_stack_id` (required): Edge Stack ID
|
|
|
|
### Git Credential Management
|
|
|
|
#### create_git_credential
|
|
Create Git credentials for private repositories.
|
|
- **Parameters**:
|
|
- `name` (required): Credential name
|
|
- `username` (required): Git username
|
|
- `password` (required): Git password or personal access token
|
|
|
|
#### list_git_credentials
|
|
List all Git credentials.
|
|
|
|
#### delete_git_credential
|
|
Delete a Git credential.
|
|
- **Parameters**:
|
|
- `credential_id` (required): Credential ID
|
|
|
|
### GitOps Settings
|
|
|
|
#### get_gitops_settings
|
|
Get global GitOps settings.
|
|
|
|
#### update_gitops_settings
|
|
Update global GitOps settings.
|
|
- **Parameters**:
|
|
- `default_interval` (optional): Default polling interval
|
|
- `concurrent_updates` (optional): Max concurrent updates
|
|
- `update_window` (optional): Update window configuration
|
|
- `enabled`: Enable update window
|
|
- `start_time`: Window start time
|
|
- `end_time`: Window end time
|
|
- `timezone`: Timezone for window
|
|
|
|
#### validate_git_repository
|
|
Validate access to a Git repository.
|
|
- **Parameters**:
|
|
- `repository_url` (required): Git repository URL
|
|
- `reference` (optional): Branch or tag (default: "main")
|
|
- `credential_id` (optional): Credential ID for private repos
|
|
|
|
## Usage Examples
|
|
|
|
### Enable GitOps with Polling
|
|
|
|
```javascript
|
|
// Enable polling-based GitOps
|
|
await use_mcp_tool("portainer-gitops", "enable_stack_gitops", {
|
|
stack_id: "5",
|
|
mechanism: "polling",
|
|
interval: "10m",
|
|
force_update: false,
|
|
pull_image: true
|
|
});
|
|
|
|
// Check configuration
|
|
await use_mcp_tool("portainer-gitops", "get_stack_gitops_config", {
|
|
stack_id: "5"
|
|
});
|
|
```
|
|
|
|
### Enable GitOps with Webhooks
|
|
|
|
```javascript
|
|
// Enable webhook-based GitOps
|
|
await use_mcp_tool("portainer-gitops", "enable_stack_gitops", {
|
|
stack_id: "7",
|
|
mechanism: "webhook",
|
|
pull_image: true
|
|
});
|
|
|
|
// Get webhook URL
|
|
await use_mcp_tool("portainer-gitops", "get_stack_webhook", {
|
|
stack_id: "7"
|
|
});
|
|
```
|
|
|
|
### Manage Git Credentials
|
|
|
|
```javascript
|
|
// Create credential for private repository
|
|
await use_mcp_tool("portainer-gitops", "create_git_credential", {
|
|
name: "GitHub PAT",
|
|
username: "myusername",
|
|
password: "ghp_xxxxxxxxxxxx"
|
|
});
|
|
|
|
// List all credentials
|
|
await use_mcp_tool("portainer-gitops", "list_git_credentials", {});
|
|
```
|
|
|
|
### Edge Stack GitOps
|
|
|
|
```javascript
|
|
// Enable GitOps for edge stack
|
|
await use_mcp_tool("portainer-gitops", "enable_edge_stack_gitops", {
|
|
edge_stack_id: "3",
|
|
mechanism: "polling",
|
|
interval: "15m"
|
|
});
|
|
|
|
// List all GitOps edge stacks
|
|
await use_mcp_tool("portainer-gitops", "list_gitops_edge_stacks", {});
|
|
```
|
|
|
|
### Manual Updates
|
|
|
|
```javascript
|
|
// Trigger immediate update
|
|
await use_mcp_tool("portainer-gitops", "trigger_stack_update", {
|
|
stack_id: "5",
|
|
pull_image: true
|
|
});
|
|
```
|
|
|
|
## GitOps Workflow
|
|
|
|
### 1. **Setup Git Repository**
|
|
- Store your Docker Compose or Kubernetes manifests in Git
|
|
- Use branches or tags for different environments
|
|
- Configure CI/CD pipelines to update manifests
|
|
|
|
### 2. **Deploy Stack from Git**
|
|
- Use the stacks server to create a stack from Git repository
|
|
- Provide credentials if using a private repository
|
|
|
|
### 3. **Enable GitOps**
|
|
- Choose between polling or webhook mechanism
|
|
- Configure update intervals for polling
|
|
- Set force update if you want Git as single source of truth
|
|
|
|
### 4. **Webhook Integration**
|
|
- Add webhook URL to your Git repository
|
|
- Configure to trigger on push events
|
|
- Use in GitHub Actions or other CI/CD tools
|
|
|
|
### 5. **Monitor Updates**
|
|
- Check GitOps status for stacks
|
|
- Review update logs in Portainer
|
|
- Use manual triggers for testing
|
|
|
|
## Update Mechanisms
|
|
|
|
### Polling
|
|
- Portainer periodically checks Git repository for changes
|
|
- Default interval: 5 minutes
|
|
- Suitable for: Regular updates, less critical deployments
|
|
- Pros: Simple setup, no external configuration
|
|
- Cons: Delayed updates, resource usage
|
|
|
|
### Webhooks
|
|
- Git repository notifies Portainer of changes
|
|
- Immediate updates on push
|
|
- Suitable for: CI/CD pipelines, immediate deployments
|
|
- Pros: Instant updates, event-driven
|
|
- Cons: Requires webhook configuration in Git
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Webhooks for Production**: Faster response to changes
|
|
2. **Set Appropriate Intervals**: Balance between responsiveness and resource usage
|
|
3. **Enable Force Update Carefully**: Can overwrite local changes
|
|
4. **Secure Credentials**: Use personal access tokens, not passwords
|
|
5. **Test First**: Use manual triggers to test deployments
|
|
6. **Monitor Logs**: Check Portainer logs for update status
|
|
7. **Version Control**: Use Git tags for production deployments
|
|
|
|
## Security Considerations
|
|
|
|
- Git credentials are stored encrypted in Portainer
|
|
- Use personal access tokens with minimal permissions
|
|
- Webhook URLs contain unique IDs for security
|
|
- Enable HTTPS for webhook endpoints
|
|
- Regularly rotate Git credentials
|
|
- Use read-only tokens when possible
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Updates not triggering**: Check Git credentials and repository access
|
|
2. **Webhook failures**: Verify webhook URL and network connectivity
|
|
3. **Authentication errors**: Ensure credentials have repository access
|
|
4. **Polling delays**: Check interval settings and Portainer logs
|
|
|
|
### Debug Mode
|
|
|
|
Enable debug logging by setting in your environment:
|
|
```bash
|
|
DEBUG=true
|
|
LOG_LEVEL=DEBUG
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- Python 3.8+
|
|
- Portainer Business Edition 2.19+ (for full GitOps features)
|
|
- Valid Portainer API token
|
|
- Git repository with Docker Compose or Kubernetes manifests
|
|
- Network access between Portainer and Git repository |