Skip to content

Conversation

@jacobshandling
Copy link
Member

@jacobshandling jacobshandling commented Oct 24, 2025

Related issue: Resolves #33907

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/
  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements)

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Database migrations

  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
    - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. N/A
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

Summary by CodeRabbit

Release Notes

  • New Features

    • Windows software inventory now includes upgrade code data for better software identification and tracking.
  • Chores

    • Database schema updated to support upgrade code storage for software titles and inventory records.

@codecov
Copy link

codecov bot commented Oct 24, 2025

Codecov Report

❌ Patch coverage is 75.75758% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.86%. Comparing base (bede996) to head (7c7760c).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
cmd/osquery-perf/agent.go 0.00% 13 Missing ⚠️
.../20251028163158_AddWindowsUpgradeCodeToSoftware.go 42.85% 7 Missing and 5 partials ⚠️
server/datastore/mysql/software.go 93.75% 4 Missing and 1 partial ⚠️
server/fleet/software.go 86.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #34786      +/-   ##
==========================================
+ Coverage   66.21%   67.86%   +1.64%     
==========================================
  Files        2073     1346     -727     
  Lines      174780   153326   -21454     
  Branches     7194        0    -7194     
==========================================
- Hits       115738   104054   -11684     
+ Misses      48409    38676    -9733     
+ Partials    10633    10596      -37     
Flag Coverage Δ
backend 67.86% <75.75%> (-0.01%) ⬇️
fleetd-chrome ?
frontend ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jacobshandling
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Walkthrough

This PR implements Windows UpgradeCode collection for the Fleet software inventory system. It adds the upgrade_code field to data models, database schema, and queries. A migration adds upgrade_code columns to software and software_titles tables, updates unique_identifier generation logic, and introduces validation for upgrade codes from Windows programs source.

Changes

Cohort / File(s) Summary
Data Models
server/fleet/software.go, server/fleet/software_installer.go
Added UpgradeCode (*string) field to Software, SoftwareTitle, SoftwareTitleListResult, and HostSoftwareWithInstaller structs. Updated SoftwareFromOsqueryRow function signature to accept upgradeCode parameter with validation logic (UpgradeCodeExpectedLength constant). Updated ToUniqueStr to include UpgradeCode when present.
Database Schema
server/datastore/mysql/schema.sql
Added upgrade_code CHAR(38) column to software and software_titles tables. Updated unique_identifier generated column in software_titles to include nullif(upgrade_code, '') in COALESCE logic. Incremented migration_status_tables AUTO_INCREMENT to 435.
Database Migration
server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go, server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware_test.go
New migration adds upgrade_code columns (CHAR(38) utf8mb4) to software_titles and software tables, initializes to empty strings for programs source, and updates unique_identifier generation. Down migration is a no-op. Includes comprehensive test covering macOS/Windows entries with and without upgrade codes.
Query Updates
server/datastore/mysql/software.go, server/datastore/mysql/software_titles.go, server/datastore/mysql/software_titles_test.go
Extended SELECT queries and INSERT/UPDATE statements to propagate upgrade_code column through software ingestion pipeline. Updated ListHostSoftware and related queries to include upgrade_code. Parameter renamed from software to incomingSoftware in applyChangesForNewSoftwareDB. Updated function calls to accommodate SoftwareFromOsqueryRow signature change.
Service Layer
server/service/osquery_utils/queries.go
Updated Windows software queries to select upgrade_code (empty string where not applicable) and pass row["upgrade_code"] to SoftwareFromOsqueryRow constructor.
Load Testing
cmd/osquery-perf/agent.go
Extended softwareJSON struct to include UpgradeCode field. Updated software item processing to propagate upgrade_code through transformation logic and Windows software runtime generation.
Documentation
docs/Contributing/product-groups/orchestration/understanding-host-vitals.md
Added upgrade_code field (empty string value) to Windows software SELECT statements across programs, ie_extensions, chrome_extensions, firefox_addons, and chocolatey_packages sources.

Sequence Diagram

sequenceDiagram
    participant osquery
    participant osquery_utils
    participant SoftwareFromOsqueryRow as SoftwareFromOsqueryRow<br/>(fleet)
    participant Database as MySQL Database
    participant API as API Response

    osquery->>osquery_utils: Windows software query<br/>(includes upgrade_code)
    osquery_utils->>osquery_utils: Extract row["upgrade_code"]
    osquery_utils->>SoftwareFromOsqueryRow: SoftwareFromOsqueryRow(..., upgradeCode)
    
    rect rgb(200, 220, 255)
    Note over SoftwareFromOsqueryRow: Validation Logic
    SoftwareFromOsqueryRow->>SoftwareFromOsqueryRow: if source == "programs"<br/>validate upgradeCode length
    SoftwareFromOsqueryRow->>SoftwareFromOsqueryRow: Populate UpgradeCode field
    SoftwareFromOsqueryRow->>SoftwareFromOsqueryRow: Update unique_identifier
    end
    
    SoftwareFromOsqueryRow->>Database: Insert/Update software<br/>with upgrade_code
    Database->>Database: Generate unique_identifier<br/>via COALESCE logic
    Database->>API: Return software record
    API->>API: Include upgrade_code in response<br/>(programs source only)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Database Migration Logic: Review the unique_identifier generation in COALESCE/NULLIF logic and verify correctness across macOS (bundle_identifier) and Windows (upgrade_code) paths
  • UpgradeCode Validation: Verify SoftwareFromOsqueryRow validation enforces UpgradeCodeExpectedLength only for programs source
  • SQL Query Consistency: Confirm all SELECT statements, INSERT/UPDATE paths, and aggregations consistently handle upgrade_code columns across software.go and software_titles.go
  • Test Coverage: Verify migration test adequately covers all COALESCE branches and edge cases (nil, empty string, valid upgrade code)

Possibly related PRs

  • Fixed MySQL DB performance regressions #33184: Modifies software title lookup and query generation in server/datastore/mysql/software.go, likely affected by or related to upgrade_code propagation logic.
  • Software ingestion fixes #33399: Updates software ingestion code paths and batching logic in server/datastore/mysql/software.go, touches similar ingestion pipeline that upgrade_code propagates through.

Suggested labels

~qa-on-branch

Suggested reviewers

  • getvictor
  • lucasmrod
  • lukeheath

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ⚠️ Warning The PR description is substantially incomplete when compared to the required template. While the related issue is properly referenced (#33907) and testing-related checkboxes are marked (automated tests added and manual QA completed), several critical checklist items are either unchecked or entirely unaddressed. The "Changes file added" checkbox remains unchecked without deletion or explanation, despite the template directing submitters to delete lines that don't apply. The "Input data is properly validated" checkbox is unchecked, which is problematic given that the raw summary shows multiple SQL modifications affecting data insertion, selection, and schema changes. Additionally, important testing requirements such as "automated tests simulate multiple hosts and test for host isolation" are completely absent from the description. The database migration section is partially addressed with schema and collation checks marked, but lacks comprehensive coverage. The description provides minimal rationale for which template items were intentionally excluded versus overlooked. The author should either complete the unchecked items or explicitly delete them with clear explanations for why they don't apply to this PR. Specifically: clarify the status of the changes file (add it or confirm it's not needed), verify and check the input validation box since SQL modifications are present, address whether automated tests include multi-host and host isolation scenarios, and confirm whether endpoint backwards compatibility was considered. If items are intentionally omitted because they don't apply, delete those lines from the description per the template instructions to indicate deliberate exclusion rather than omission.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues Check ✅ Passed The PR successfully addresses all major coding requirements from issue #33907. It includes database schema migrations adding upgrade_code columns to software and software_titles tables [visible in migrations and schema.sql], updates osquery query logic to fetch upgrade_code values [server/service/osquery_utils/queries.go], implements validation ensuring upgrade_code is only returned for programs source with proper length checking [server/fleet/software.go], adds upgrade_code fields to all relevant data models [fleet/software.go, fleet/software_installer.go], propagates the field through datastore methods [software.go, software_titles.go], updates the osquery-perf tool [cmd/osquery-perf/agent.go], and includes comprehensive test coverage [migration and software tests]. All core engineering tasks from the issue are implemented.
Out of Scope Changes Check ✅ Passed The code changes are tightly focused on implementing upgrade_code support across the entire software inventory stack, from database schema through data models to API endpoints and osquery-perf tooling. All file modifications directly support the stated objective of collecting and surfacing Windows MSI UpgradeCode values. The raw summaries note minor textual updates related to terminology changes (master/slave to writer/reader), which appear to be tangential refactoring alongside the main implementation but represent only minimal additional scope that could be considered cleanup as part of the refactoring work.
Title Check ✅ Passed The PR title "Ingest, store, consider in unique_identifier, and serve upgrade_codes for Windows software" is directly and comprehensively aligned with the main change in the changeset. The title accurately captures the full scope of the implementation: adding upgrade_code support through database ingestion, storage via schema and migrations, integration into the unique_identifier calculation, and exposure through API models. The language is specific and technical, clearly communicating the primary change without vague terminology. While the title is somewhat detailed with multiple stages connected by "and", it remains clear, concrete, and descriptive enough that a developer scanning history would immediately understand the core objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 33907-windows-sw-upgrade-code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/fleet/software.go (1)

153-171: Include UpgradeCode in ComputeRawChecksum() or remove it from ToUniqueStr().

The checksum column has a UNIQUE constraint (idx_software_checksum in the database schema), making this inconsistency a data integrity issue—not merely an optimization concern. Since UpgradeCode is included in ToUniqueStr() (lines 153–157) but not in ComputeRawChecksum() (line 171), two software entries could be treated as different by the application yet share the same checksum, violating the database constraint.

Resolve by either:

  1. Adding UpgradeCode to the columns hashed in ComputeRawChecksum(), or
  2. Removing UpgradeCode from ToUniqueStr() if it shouldn't affect uniqueness.

The TODO at line 171 confirms this was previously flagged but unresolved.

🧹 Nitpick comments (11)
server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go (2)

14-31: Prefer VARCHAR(38) (or BINARY(16)) over CHAR(38).

CHAR adds padding semantics and fixed storage even for empty-string rows set for 'programs'. VARCHAR(38) avoids padding; BINARY(16) is even denser if you ever normalize GUIDs to 16‑byte form. Functionally equivalent here; consider for future migrations.

-ALTER TABLE software_titles ADD COLUMN upgrade_code CHAR(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL
+ALTER TABLE software_titles ADD COLUMN upgrade_code VARCHAR(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL
...
-ALTER TABLE software ADD COLUMN upgrade_code CHAR(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL
+ALTER TABLE software ADD COLUMN upgrade_code VARCHAR(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL

14-21: Optional: consider indexing upgrade_code later if used in lookups.

If APIs/datastore will filter by upgrade_code, a secondary index can help. Not required for this PR but worth tracking.
Would you like a follow-up issue opened to benchmark queries on upgrade_code before adding an index?

Also applies to: 28-31

server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware_test.go (1)

28-33: Add a small check for software table too.

You set defaults on both tables; consider asserting software.upgrade_code default '' mirrors titles.

+var swUC *string
+err = db.Get(&swUC, `SELECT upgrade_code FROM software WHERE source = 'programs' LIMIT 1`)
+require.NoError(t, err)
+require.Equal(t, "", *swUC)

Also applies to: 63-68

docs/Contributing/product-groups/orchestration/understanding-host-vitals.md (1)

1066-1070: Replace hard tabs with spaces in code blocks.

markdownlint MD010 flagged tabs; swap to spaces to keep checks green.

Also applies to: 1077-1081, 1088-1092, 1099-1103, 1110-1114

server/datastore/mysql/software_test.go (2)

3710-3713: Cover UpgradeCode in checksum tests (programs) once spec is finalized.

If checksum/unique_identifier now incorporates Windows MSI UpgradeCode for source="programs", add explicit cases for:

  • programs with a non-empty UpgradeCode
  • programs with an empty UpgradeCode (treated as “no value”)

This will prevent regressions and prove checksum disambiguation works as intended. Example to enable later:

- // {Name: "foo", Version: "0.0.2", Source: "programs", UpgradeCode: ptr.String("{55ac7218-24cb-4b99-9449-f28d9c59cc7e}")},
- // {Name: "foo", Version: "0.0.2", Source: "programs", UpgradeCode: ptr.String("")},
+ {Name: "foo", Version: "0.0.2", Source: "programs", UpgradeCode: ptr.String("{55ac7218-24cb-4b99-9449-f28d9c59cc7e}")},
+ {Name: "foo", Version: "0.0.2", Source: "programs", UpgradeCode: ptr.String("")},

Please confirm: Is UpgradeCode included in checksum for source="programs" only (and ignored for other sources)?


3727-3727: SELECT should project upgrade_code when validating checksum fetch.

When you switch the test to include programs+UpgradeCode, also select upgrade_code so struct comparisons remain accurate:

- `SELECT name, version, source, bundle_identifier, ` + "`release`" + `, arch, vendor, extension_for, extension_id, application_id FROM software WHERE checksum = UNHEX(?)`
+ `SELECT name, version, source, bundle_identifier, ` + "`release`" + `, arch, vendor, extension_for, extension_id, application_id, upgrade_code FROM software WHERE checksum = UNHEX(?)`
server/service/osquery_utils/queries.go (1)

1225-1226: Use COALESCE to ensure NULL upgrade_codes are returned as empty strings.

Per the PR objectives, programs entries with no upgrade_code value should return an empty string. The current query may return NULL if the upgrade_code column is NULL in the database. While osquery might convert NULLs to empty strings when building the result map, it's better to be explicit.

Apply this diff:

   publisher AS vendor,
   install_location AS installed_path,
-	upgrade_code AS upgrade_code
+	COALESCE(upgrade_code, '') AS upgrade_code
 FROM programs
server/datastore/mysql/software.go (3)

1509-1509: Document GROUP BY clause decision for upgrade_code.

The TODO questions whether s.upgrade_code should be in the GROUP BY clause. Since upgrade_code is included in other GROUP BY clauses (e.g., lines 4812, 4856, 4896), this inconsistency should be resolved or documented.


2478-2478: Clarify struct field naming convention.

The TODO questions the naming SoftwareUpgradeCode vs SoftwareUpgradeCodeList. Since the related field is SoftwareUpgradeCodeList (line 2515), this should be consistent throughout.


2528-2528: Consider including upgrade_code in installed software query.

The TODO suggests upgrade_code might need to be included in this query for completeness. Evaluate whether this is needed for the Windows software identity verification feature.

server/fleet/software.go (1)

572-574: Consider format validation for UpgradeCode GUID.

The validation only checks length (38 characters), but the comment at lines 39-40 states UpgradeCode is "a GUID, only uses hexadecimal digits, hyphens, curly braces". Consider adding format validation to ensure the upgrade code matches the expected GUID pattern (e.g., {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}).

This would catch malformed data earlier and provide better error messages.

Example validation:

import "regexp"

var upgradeCodePattern = regexp.MustCompile(`^\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\}$`)

if upgradeCode != "" {
    if len(upgradeCode) != UpgradeCodeExpectedLength {
        return nil, errors.New("host reported invalid upgrade code - unexpected length")
    }
    if !upgradeCodePattern.MatchString(upgradeCode) {
        return nil, errors.New("host reported invalid upgrade code - invalid GUID format")
    }
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3749ba and 2449384.

⛔ Files ignored due to path filters (2)
  • cmd/osquery-perf/windows_11-software.json.bz2 is excluded by !**/*.bz2
  • server/datastore/mysql/testdata/select_software_titles_sql_fixture.gz is excluded by !**/*.gz
📒 Files selected for processing (12)
  • cmd/osquery-perf/agent.go (3 hunks)
  • docs/Contributing/product-groups/orchestration/understanding-host-vitals.md (5 hunks)
  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go (1 hunks)
  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware_test.go (1 hunks)
  • server/datastore/mysql/schema.sql (3 hunks)
  • server/datastore/mysql/software.go (34 hunks)
  • server/datastore/mysql/software_test.go (3 hunks)
  • server/datastore/mysql/software_titles.go (2 hunks)
  • server/datastore/mysql/software_titles_test.go (1 hunks)
  • server/fleet/software.go (9 hunks)
  • server/fleet/software_installer.go (1 hunks)
  • server/service/osquery_utils/queries.go (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/fleet/software_installer.go
  • server/service/osquery_utils/queries.go
  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go
  • cmd/osquery-perf/agent.go
  • server/datastore/mysql/software_titles.go
  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware_test.go
  • server/datastore/mysql/software_titles_test.go
  • server/fleet/software.go
  • server/datastore/mysql/software.go
  • server/datastore/mysql/software_test.go
🧠 Learnings (5)
📚 Learning: 2025-07-08T16:13:39.114Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: server/datastore/mysql/migrations/tables/20250707095725_HostIdentitySCEPCertificates.go:53-55
Timestamp: 2025-07-08T16:13:39.114Z
Learning: In the Fleet codebase, Down migration functions are intentionally left empty/no-op. The team does not implement rollback functionality for database migrations, so empty Down_* functions in migration files are correct and should not be flagged as issues.

Applied to files:

  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go
📚 Learning: 2025-09-18T21:55:10.359Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 33184
File: server/datastore/mysql/migrations/tables/20250918154557_AddKernelHostCountsIndexForVulnQueries.go:12-27
Timestamp: 2025-09-18T21:55:10.359Z
Learning: In Fleet's codebase, schema.sql is auto-generated from all existing migrations, so there's no risk of migrations creating duplicate database objects. Migrations don't need to be made idempotent to guard against conflicts with schema.sql since they work together as part of the same system.

Applied to files:

  • server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go
  • server/datastore/mysql/schema.sql
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: Fleet repo targets Go 1.24.5 (root go.mod), which supports testing.T.Context(). Do not flag usage of t.Context() or suggest replacing it with context.Background() in tests (e.g., server/datastore/mysql/labels_test.go Line 2031 and similar).

Applied to files:

  • server/datastore/mysql/software_titles_test.go
  • server/datastore/mysql/software_test.go
📚 Learning: 2025-08-01T15:08:16.858Z
Learnt from: sgress454
Repo: fleetdm/fleet PR: 31508
File: server/datastore/mysql/schema.sql:102-116
Timestamp: 2025-08-01T15:08:16.858Z
Learning: The schema.sql file in server/datastore/mysql/ is auto-generated from migrations for use with tests, so it cannot be manually edited. Any changes must be made through migrations.

Applied to files:

  • server/datastore/mysql/schema.sql
📚 Learning: 2025-09-12T13:04:23.777Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 32823
File: server/datastore/mysql/software.go:4457-4471
Timestamp: 2025-09-12T13:04:23.777Z
Learning: In the Fleet codebase, the `status` column in the `host_software_installs` table is defined as `GENERATED ALWAYS`, meaning it's automatically computed by the database based on other field values (like exit codes) and should not be explicitly included in INSERT statements.

Applied to files:

  • server/datastore/mysql/schema.sql
  • docs/Contributing/product-groups/orchestration/understanding-host-vitals.md
🪛 markdownlint-cli2 (0.18.1)
docs/Contributing/product-groups/orchestration/understanding-host-vitals.md

1069-1069: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1080-1080: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1091-1091: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1102-1102: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1113-1113: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (19)
server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware.go (2)

33-38: Unique identifier logic looks right.

Including NULLIF(upgrade_code, "") in the COALESCE safeguards empty-string sentinels. Good change.


43-45: No-op Down migration is correct for this repo.

Empty Down is consistent with Fleet’s migration policy. (Based on learnings)

cmd/osquery-perf/agent.go (3)

122-128: Add field: OK.

Accepts upgrade_code from fixtures; backward-compatible for sources without the field.


2532-2536: Emit upgrade_code only for Windows programs.

Matches spec (“only from programs”). Looks good.


138-142: Cannot verify fixture coverage—fixture file not accessible in repository.

The code change at lines 138-142 is correct: it properly maps s.UpgradeCode to the "upgrade_code" field, and the review comment's note that "sources without values default to empty string" is accurate for Go's string type.

However, the suggested sanity check cannot be completed because the fixture file cmd/osquery-perf/windows_11-software.json.bz2 is not accessible in the repository (it may be excluded, generated at build time, or located elsewhere). Without access to the actual fixture data, I cannot verify whether the Windows fixture contains the expected upgrade_code entries as the reviewer anticipated.

server/datastore/mysql/migrations/tables/20251028163158_AddWindowsUpgradeCodeToSoftware_test.go (2)

48-62: Good coverage of default values.

Asserts '' for Windows and NULL for macOS after migration.


66-101: Solid COALESCE/NULLIF assertions.

Tests for non-empty, empty, and nil upgrade_code paths are correct and readable.

server/datastore/mysql/software_titles.go (2)

395-406: ListSoftwareTitles now projects upgrade_code.

Consistent with schema; no filtering/scope changes introduced.


51-58: No action required—struct field verified.

The SoftwareTitle struct at server/fleet/software.go:223 correctly includes UpgradeCode *string \json:"upgrade_code,omitempty" db:"upgrade_code"``, ensuring proper sqlx StructScan mapping for the new column.

docs/Contributing/product-groups/orchestration/understanding-host-vitals.md (1)

1056-1115: Windows query update matches spec.

upgrade_code returned for programs; empty for non-programs sources. Looks good.

server/datastore/mysql/software_titles_test.go (1)

2219-2221: Call-site correctly updated for SoftwareFromOsqueryRow signature change.

Verification confirms all call sites in the repository have been updated to match the 12-parameter signature with upgradeCode as the final parameter. No stale call sites remain.

server/fleet/software_installer.go (1)

636-637: UpgradeCode field implementation verified—no issues found.

The three-case requirement is correctly supported:

  1. Database migration establishes the foundation: DEFAULT NULL for all rows, then sets empty string for "programs" source, leaving non-programs as NULL.
  2. SQL queries include upgrade_code in SELECT clauses (e.g., server/datastore/mysql/software.go:354).
  3. Go's database/sql package automatically converts NULL to nil pointers and non-NULL values to pointers.
  4. The struct definition with *string and omitempty handles all cases correctly: nil (omitted from JSON), empty string (included as ""), or actual value (included).

The datastore layer properly populates this field based on source, and the implementation is sound.

server/datastore/mysql/software_test.go (1)

9167-9170: LGTM: All call sites correctly updated to new arity.

Verification confirms all four call sites to SoftwareFromOsqueryRow use the correct 12-parameter arity:

  • Test call sites in software_test.go and software_titles_test.go properly pass empty strings for new trailing parameters
  • Production call site in queries.go passes the corresponding row values for all 12 parameters

No missed call sites detected.

server/service/osquery_utils/queries.go (2)

1217-1275: LGTM! Clean implementation of upgrade_code for Windows software sources.

The UNION query correctly adds the upgrade_code column to all sources with consistent column ordering. Non-programs sources appropriately return empty strings for upgrade_code, ensuring only the programs source can have actual upgrade_code values.


1969-1969: LGTM! Correct propagation of upgrade_code to the software ingestion path.

The upgrade_code parameter is properly passed to SoftwareFromOsqueryRow, completing the data flow from the Windows software query to the software model.

server/datastore/mysql/schema.sql (3)

2408-2408: UpgradeCode column definition looks good.

The char(38) type correctly accommodates the Windows MSI UpgradeCode format (GUID with braces), and collation is consistent with schema standards.


2592-2593: GENERATED ALWAYS formula correctly handles upgrade_code logic.

The updated unique_identifier column uses COALESCE(bundle_identifier, application_id, NULLIF(upgrade_code, ''), name), which properly handles the requirement to treat empty upgrade_code values as NULL. This ensures upgrade_code is only used as a differentiator when non-empty.


1641-1641: Migration tracking updated correctly.

The AUTO_INCREMENT for migration_status_tables is correctly incremented to 435, and the new migration entry (20251028163158) is properly included in the INSERT statement.

Also applies to: 1643-1643

server/fleet/software.go (1)

571-577: No changes needed; UpgradeCode restriction to "programs" is correct.

The code properly restricts UpgradeCode to the "programs" source only. This is intentional and correct because upgrade codes are exclusive to Windows MSI (Microsoft Installer) packages, which only appear in the "programs" source. Other Windows software sources in the codebase—such as "ie_extensions", "chrome_extensions", "firefox_addons", and "chocolatey_packages"—are explicitly queried with empty upgrade codes in the osquery software inventory query, confirming they do not support upgrade codes.

@jacobshandling jacobshandling marked this pull request as ready for review October 31, 2025 00:54
@jacobshandling jacobshandling changed the title 33907 windows sw upgrade code Ingest, store, consider in unique_identifier, and serve upgrade_codes for Windows software Oct 31, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Software inventory: Collect UpgradeCode for Windows software

4 participants