Skip to content

Conversation

@awakecoding
Copy link
Contributor

Overview

Extends the Devolutions Agent auto-updater to support Devolutions Hub Service MSI updates, using the same mechanism as Gateway updates.

Changes

Core Implementation

  • Hub Service MSI upgrade code: f437046e-8e13-430a-8c8f-29fcb9023b59
  • Product ID: HubServicesbin (from productinfo.htm)
  • Windows Services:
    • Devolutions Hub PAM Service (main)
    • Devolutions Hub Encryption Service (optional)
    • Devolutions Hub Reporting Service (optional)

Code Changes

  1. crates/devolutions-agent-shared/src/windows/mod.rs: Added HUB_SERVICE_UPDATE_CODE constant
  2. crates/devolutions-agent-shared/src/update_json.rs: Extended schema with optional hub_service field
  3. devolutions-agent/src/updater/productinfo/mod.rs: Added HUB_SERVICE_PRODUCT_ID
  4. devolutions-agent/src/updater/product.rs: Added HubService product variant
  5. devolutions-agent/src/updater/detect.rs: Extended detection logic for Hub Service
  6. devolutions-agent/src/updater/package.rs: Extended MSI operations for Hub Service
  7. devolutions-agent/src/updater/product_actions.rs: Refactored to ServiceUpdateActions supporting multiple services per product
  8. devolutions-agent/src/updater/mod.rs: Added HubService to PRODUCTS array

Key Features

  • ✅ Handles multiple Windows services per product (3 Hub services)
  • ✅ Gracefully handles optional services that may not be installed
  • ✅ Preserves individual service states and startup modes during updates
  • ✅ Backward compatible - no breaking changes to Gateway updater
  • ✅ Extended JSON schema with optional fields only

Testing Instructions

Prerequisites

  1. Install Devolutions Hub Service on test machine (any combination of PAM/Encryption/Reporting features)
  2. Build and deploy Devolutions Agent with these changes
  3. Verify Agent service is running

Test 1: Manual Update Trigger

Verify current state:

# Check installed Hub services
Get-Service | Where-Object { $_.DisplayName -like "Devolutions Hub*" } | Select-Object Name, DisplayName, Status, StartType | Format-Table -AutoSize

Create update.json:

$updateJson = @{
    HubService = @{
        TargetVersion = "2025.4.0.0"  # Replace with actual newer version
        DownloadUrl = "https://cdn.devolutions.net/download/Setup.DevolutionsHubServices.2025.4.0.0.msi"
        Checksum = @{
            Algorithm = "SHA256"
            Value = "abc123..."  # Replace with actual SHA256 hash
        }
    }
} | ConvertTo-Json -Depth 10

$updateJson | Out-File -FilePath "$env:ProgramData\Devolutions\Agent\update.json" -Encoding utf8 -Force

Monitor logs:

Get-Content "$env:ProgramData\Devolutions\Agent\logs\gateway.log" -Wait -Tail 50

Expected log sequence (within 3 seconds):

[INFO] File watcher detected update.json change
[INFO] Processing update request for HubService
[INFO] Querying service states for HubService
[INFO] Service 'Devolutions Hub PAM Service' found - running: true, automatic_startup: true
[DEBUG] Service 'Devolutions Hub Encryption Service' not found (feature not installed): ...
[DEBUG] Service 'Devolutions Hub Reporting Service' not found (feature not installed): ...
[INFO] Current HubService version: X.X.X.X
[INFO] Target HubService version: Y.Y.Y.Y
[INFO] Update required for HubService
[INFO] Downloading package...
[INFO] Checksum validation passed
[INFO] Package signature valid
[INFO] Uninstalling current HubService version...
[INFO] Installing HubService version Y.Y.Y.Y...
[INFO] HubService update completed successfully

Verify results:

# Check new version installed
Get-Service | Where-Object { $_.DisplayName -like "Devolutions Hub*" } | Select-Object Name, Status, StartType | Format-Table -AutoSize

Test 2: Service State Preservation

Set PAM service to Manual startup:

Set-Service -Name "Devolutions Hub PAM Service" -StartupType Manual
Stop-Service -Name "Devolutions Hub PAM Service"
Start-Service -Name "Devolutions Hub PAM Service"

Trigger update (same as Test 1)

Expected behavior:

  • Service should be restarted after update (because it was running with Manual startup)
  • StartType should remain Manual (not changed to Automatic)

Verify:

Get-Service "Devolutions Hub PAM Service" | Select-Object Status, StartType
# Expected: Status=Running, StartType=Manual

Test 3: Multiple Services Installed

Install all Hub Service features (PAM, Encryption, Reporting)

Set different startup types:

Set-Service "Devolutions Hub PAM Service" -StartupType Automatic
Set-Service "Devolutions Hub Encryption Service" -StartupType Manual
Set-Service "Devolutions Hub Reporting Service" -StartupType Automatic
Start-Service "Devolutions Hub PAM Service", "Devolutions Hub Encryption Service", "Devolutions Hub Reporting Service"

Trigger update

Expected:

  • All 3 services detected in logs
  • Encryption service manually restarted (Manual startup + was running)
  • PAM and Reporting services auto-restarted (Automatic startup)
  • All startup types preserved after update

Test 4: Already Up-to-Date

Create update.json with current version (same as installed)

Expected log:

[INFO] HubService is already up-to-date

No installation should occur.

Test 5: Backward Compatibility

Create update.json for Gateway only (no hub_service field):

$updateJson = @{
    Gateway = @{
        TargetVersion = "2025.3.0.0"
        DownloadUrl = "..."
        Checksum = @{ Algorithm = "SHA256"; Value = "..." }
    }
} | ConvertTo-Json -Depth 10

Expected: Gateway updates normally, Hub Service ignored (backward compatible).

Test 6: Negative Test - Invalid Checksum

Create update.json with wrong checksum:

$updateJson = @{
    HubService = @{
        TargetVersion = "2025.4.0.0"
        DownloadUrl = "https://cdn.devolutions.net/download/Setup.DevolutionsHubServices.2025.4.0.0.msi"
        Checksum = @{
            Algorithm = "SHA256"
            Value = "0000000000000000000000000000000000000000000000000000000000000000"
        }
    }
} | ConvertTo-Json -Depth 10

Expected:

[ERROR] Checksum validation failed for HubService package
[ERROR] Update aborted for HubService

Installation should NOT proceed.

Validation Script

# Test-HubServiceUpdater.ps1
function Test-HubServiceUpdater {
    Write-Host "=== Hub Service Updater Test Suite ===" -ForegroundColor Cyan
    
    # Check Agent
    $agent = Get-Service DevolutionsAgent -ErrorAction SilentlyContinue
    if (-not $agent -or $agent.Status -ne 'Running') {
        Write-Host "ERROR: Devolutions Agent not running" -ForegroundColor Red
        return
    }
    Write-Host "✓ Devolutions Agent is running" -ForegroundColor Green
    
    # Check Hub Services
    $hubServices = Get-Service | Where-Object { $_.DisplayName -like "Devolutions Hub*" }
    if ($hubServices.Count -eq 0) {
        Write-Host "ERROR: No Hub Services found" -ForegroundColor Red
        return
    }
    Write-Host "✓ Found $($hubServices.Count) Hub Service(s):" -ForegroundColor Green
    $hubServices | Format-Table Name, DisplayName, Status, StartType -AutoSize
    
    Write-Host "`n✓ Ready to test!" -ForegroundColor Cyan
    Write-Host "  Create update.json at: $env:ProgramData\Devolutions\Agent\update.json"
    Write-Host "  Watch logs at: $env:ProgramData\Devolutions\Agent\logs\gateway.log"
}

Test-HubServiceUpdater

Notes

  • No breaking changes to existing Gateway updater functionality
  • JSON schema extended with optional hub_service field (backward compatible)
  • All MSI operations use the same proven logic as Gateway (signature validation, checksum verification)
  • Service management generalized to support products with multiple Windows services
  • Graceful degradation when optional Hub Service features are not installed

- Add Hub Service MSI upgrade code (f437046e-8e13-430a-8c8f-29fcb9023b59)
- Add HubService product variant to updater system
- Support three Hub Service Windows services (PAM, Encryption, Reporting)
- Gracefully handle optional services that may not be installed
- Refactor ServiceUpdateActions to support multiple services per product
- Preserve individual service states and startup modes during updates
- Extend update.json schema with hub_service field (backward compatible)
@github-actions
Copy link

github-actions bot commented Nov 3, 2025

Let maintainers know that an action is required on their side

  • Add the label release-required Please cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module) when you request a maintainer to cut a new release (Devolutions Gateway, Devolutions Agent, Jetsocat, PowerShell module)

  • Add the label release-blocker Follow-up is required before cutting a new release if a follow-up is required before cutting a new release

  • Add the label publish-required Please publish libraries (`Devolutions.Gateway.Utils`, OpenAPI clients, etc) when you request a maintainer to publish libraries (Devolutions.Gateway.Utils, OpenAPI clients, etc.)

  • Add the label publish-blocker Follow-up is required before publishing libraries if a follow-up is required before publishing libraries

@awakecoding
Copy link
Contributor Author

First shot with GitHub Copilot for https://devolutions.atlassian.net/browse/ARC-417

@awakecoding awakecoding marked this pull request as draft November 3, 2025 20:03
Add missing hub_service field (set to None) when Gateway API creates
update.json for Gateway-only updates.
Copy link
Member

@CBenoit CBenoit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall.

I think we also need to update the /jet/update endpoint of the Devolutions Gateway (found in devolutions-gateway/src/api/update.rs), to support the product selection.

Likely need a quick manual test before merging as well. I assume you didn’t get a chance to try it out yet.

@CBenoit CBenoit changed the title Add Hub Service auto-updater support feat(agent,dgw): add Hub Service auto-updater support Nov 4, 2025
@CBenoit
Copy link
Member

CBenoit commented Nov 4, 2025

Follow up.

We use the title of the PR and the body to generate the changelog. The title must follow the conventional commit specification, and the body must include (optional) details about the features. This can be technical to some extend (in-repository changelog for technically-inclined users), but crucially, it should still be used-oriented. (Does not apply to chore, ci, and other commit types we do not include in the changelog). The Jira ticket must be specified using Issue: KEY-ID; in this case: Issue: ARC-417. I recommend creating a DGW ticket dedicated to tracking this feature on Devolutions Gateway side.

This is stuff I’ll add in the agents instructions so you don’t have to remember when using Copilot, but maybe you could try providing it with instructions above?

This commit addresses several issues with the updater's MSI installation and Hub Service update process:

1. Strip UTF-8 BOM from update.json parsing

   - Some editors add UTF-8 BOM (0xEF 0xBB 0xBF) which caused parse failures

   - Now strips BOM before JSON deserialization if present

2. Validate MSI exit codes properly

   - Previously only checked if msiexec launched, not if it succeeded

   - Now validates exit codes: 0 = success, 3010/1641 = success with reboot

     (logged as warning since our installers shouldn't require reboot)

   - Other codes properly treated as failures

   - Applied to both install and uninstall operations

3. Hub Service installer parameter improvements

   - Removed P.SERVICESTART parameter for Hub Service (not supported)

   - Added ADDLOCAL parameter generation based on installed services

   - Maps service names to MSI features: PAM, Encryption, Reporting

   - Preserves user's feature selection during updates

4. Enhanced service restart logic for Hub Service

   - Gateway: only restarts services with manual startup that were running

   - Hub Service: restarts all services that were running (since we can't

     control startup mode via installer parameters)

These changes ensure Hub Service updates preserve the installed features and properly handle installation success/failure cases.
@CBenoit
Copy link
Member

CBenoit commented Nov 7, 2025

I opened a PR with agent instructions: #1564
You may run against it again once merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants