-
Notifications
You must be signed in to change notification settings - Fork 1.4k
SW inventory for MacOS #45533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
SW inventory for MacOS #45533
Changes from 8 commits
a4415c5
0291f92
2a05318
7b47710
a4c884a
fe8b555
6fb15f4
f1b955f
38f6fa8
04475d7
b6e04b2
3f22692
a95bf2a
703da49
bc14af8
0f681cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,9 +8,11 @@ package softwareinventoryimpl | |
| import ( | ||
| "embed" | ||
| "io" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/DataDog/datadog-agent/comp/core/status" | ||
| "github.com/DataDog/datadog-agent/pkg/inventory/software" | ||
| ) | ||
|
|
||
| //go:embed status_templates | ||
|
|
@@ -66,20 +68,41 @@ func formatYYYYMMDD(ts string) (string, error) { | |
|
|
||
| // populateStatus populates the status map with software inventory data. | ||
| // This method processes the cached inventory data and formats it for display | ||
| // in the status output. It handles date formatting and organizes the data | ||
| // by software ID for easy lookup. | ||
| // in the status output. It handles date formatting, computes statistics by | ||
| // software type, and organizes the data by software ID for easy lookup. | ||
| // Note: Stats are computed from deduplicated entries to ensure consistency | ||
| // between the total count and the breakdown by type. | ||
| func (is *softwareInventory) populateStatus(status map[string]interface{}) { | ||
| data := map[string]interface{}{} | ||
|
|
||
| is.cachedInventoryMu.RLock() | ||
| cachedInventory := is.cachedInventory | ||
| is.cachedInventoryMu.RUnlock() | ||
|
|
||
| // First pass: deduplicate entries by ID and format dates | ||
| data := map[string]interface{}{} | ||
|
Comment on lines
+83
to
+84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why there is duplication?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have multiple inventory collectors which could report the same software from different sources, and the same software can appear in multiple locations. For example, Microsoft Word could appear in both application category and package category. The implemented duplication removal is the best effort and might not catch all cases. |
||
| for _, inventory := range cachedInventory { | ||
| inventory.InstallDate, _ = formatYYYYMMDD(inventory.InstallDate) | ||
| data[inventory.GetID()] = inventory | ||
| } | ||
|
|
||
| // Second pass: compute stats from deduplicated entries | ||
| // This ensures stats sum matches the total count | ||
| stats := map[string]int{} | ||
| brokenCount := 0 | ||
| for _, v := range data { | ||
| inventory := v.(software.Entry) | ||
| stats[inventory.Source]++ | ||
| if strings.Contains(inventory.Status, "broken") { | ||
| brokenCount++ | ||
| } | ||
| } | ||
|
|
||
| status["software_inventory_metadata"] = data | ||
| status["software_inventory_stats"] = stats | ||
| status["software_inventory_total"] = len(data) | ||
| // Only include broken count if there are broken entries | ||
| if brokenCount > 0 { | ||
| status["software_inventory_broken"] = brokenCount | ||
| } | ||
| } | ||
|
|
||
| // getStatusInfo returns the status information map for the software inventory. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,57 @@ | ||
| <div class="stat"> | ||
| <span class="stat_title">Software Inventory Metadata</span> | ||
| <div class="stat_data inventory-scrollbox" style="max-height: 400px; overflow-y: auto;"> | ||
| {{- $swMap := index . "software_inventory_metadata" }} | ||
| {{- if index $swMap "error" }} | ||
| <div>Error refreshing software inventory: {{ index $swMap "error" }}</div> | ||
| {{- else }} | ||
| {{- range $productCode, $meta := $swMap }} | ||
| <details> | ||
| <summary> | ||
| {{- if $meta.DisplayName }}{{ $meta.DisplayName }}{{ if $meta.Version }} {{ $meta.Version }}{{ end }}{{- else }}{{ $meta.ProductCode }}{{- end }} | ||
| {{- if contains $meta.Status "broken" }} | ||
| <span class="source-bubble broken">broken</span> | ||
| {{- end }} | ||
| </summary> | ||
| <ul style="margin:1em 0; padding-left:2em;"> | ||
| {{- if $meta.DisplayName }}<li><strong>Display Name:</strong> {{ $meta.DisplayName }}</li>{{- end }} | ||
| {{- if $meta.Version }}<li><strong>Version:</strong> {{ $meta.Version }}</li>{{- end }} | ||
| {{- if $meta.InstallDate }}<li><strong>Install Date:</strong> {{ $meta.InstallDate }}</li>{{- end }} | ||
| {{- if $meta.Publisher }}<li><strong>Publisher:</strong> {{ $meta.Publisher }}</li>{{- end }} | ||
| {{- if $meta.ProductCode }}<li><strong>Product code:</strong> {{ $meta.ProductCode }}</li>{{- end }} | ||
| {{- if $meta.Source }}<li><strong>Source:</strong> {{ $meta.Source }}</li>{{- end }} | ||
| {{- if $meta.Status }}<li><strong>Status:</strong> {{ $meta.Status }}</li>{{- end }} | ||
| {{- if $meta.UserSID }}<li><strong>User SID:</strong> {{ $meta.UserSID }}</li>{{- end }} | ||
| <li><strong>64-bit:</strong> {{ $meta.Is64Bit }}</li> | ||
| </ul> | ||
| </details> | ||
| {{- $swMap := index . "software_inventory_metadata" }} | ||
| {{- $total := index . "software_inventory_total" }} | ||
| {{- $stats := index . "software_inventory_stats" }} | ||
| {{- $broken := index . "software_inventory_broken" }} | ||
| {{- if index $swMap "error" }} | ||
| <div>Error refreshing software inventory: {{ index $swMap "error" }}</div> | ||
| {{- else }} | ||
| <div style="margin-bottom: 1em; padding: 0.5em; background: #f5f5f5; border-radius: 4px;"> | ||
| <strong>Summary:</strong> {{ $total }} entries{{- if $broken }} (<span style="color: #d00;">{{ $broken }} broken</span>){{- end }} | ||
| <br/> | ||
| <strong>By type:</strong> | ||
| <ul style="margin: 0.5em 0; padding-left: 2em; list-style: none;"> | ||
| {{- range $type, $count := $stats }} | ||
| <li>{{ $type }}: {{ $count }}</li> | ||
| {{- end }} | ||
| </ul> | ||
| </div> | ||
| <div style="margin-bottom: 1em;"> | ||
| <label for="sw-type-filter"><strong>Filter by type:</strong></label> | ||
| <select id="sw-type-filter" style="margin-left: 0.5em; padding: 0.25em 0.5em; border-radius: 4px; border: 1px solid #ccc;"> | ||
| <option value="">All ({{ $total }})</option> | ||
| {{- range $type, $count := $stats }} | ||
| <option value="{{ $type }}">{{ $type }} ({{ $count }})</option> | ||
| {{- end }} | ||
| </select> | ||
| <span id="sw-filter-count" style="margin-left: 1em; color: #666;"></span> | ||
| </div> | ||
| <div id="sw-entries-container" class="stat_data inventory-scrollbox" style="max-height: 400px; overflow-y: auto;"> | ||
| {{- range $productCode, $meta := $swMap }} | ||
| <details class="sw-entry" data-sw-type="{{ $meta.Source }}"> | ||
| <summary> | ||
| {{- if $meta.DisplayName }}{{ $meta.DisplayName }}{{ if $meta.Version }} {{ $meta.Version }}{{ end }}{{- else }}{{ $meta.ProductCode }}{{- end }} | ||
| {{- if contains $meta.Status "broken" }} | ||
| <span class="source-bubble broken">broken</span> | ||
| {{- end }} | ||
| </summary> | ||
| <ul style="margin:1em 0; padding-left:2em;"> | ||
| {{- if $meta.DisplayName }}<li><strong>Display Name:</strong> {{ $meta.DisplayName }}</li>{{- end }} | ||
| {{- if $meta.Version }}<li><strong>Version:</strong> {{ $meta.Version }}</li>{{- end }} | ||
| {{- if $meta.InstallDate }}<li><strong>Install Date:</strong> {{ $meta.InstallDate }}</li>{{- end }} | ||
| {{- if $meta.Publisher }}<li><strong>Publisher:</strong> {{ $meta.Publisher }}</li>{{- end }} | ||
| {{- if $meta.ProductCode }}<li><strong>Product code:</strong> {{ $meta.ProductCode }}</li>{{- end }} | ||
| {{- if $meta.Source }}<li><strong>Source:</strong> {{ $meta.Source }}</li>{{- end }} | ||
| {{- if $meta.Status }}<li><strong>Status:</strong> {{ $meta.Status }}</li>{{- end }} | ||
| {{- if $meta.BrokenReason }}<li><strong>Broken Reason:</strong> <span style="color: #d00;">{{ $meta.BrokenReason }}</span></li>{{- end }} | ||
| {{- if $meta.UserSID }}<li><strong>User SID:</strong> {{ $meta.UserSID }}</li>{{- end }} | ||
| {{- if $meta.InstallPath }}<li><strong>Install Path:</strong> {{ $meta.InstallPath }}</li>{{- end }} | ||
| {{- if $meta.InstallPaths }}<li><strong>Install Paths:</strong><ul style="margin: 0.25em 0; padding-left: 1.5em;">{{- range $meta.InstallPaths }}<li>{{ . }}</li>{{- end }}</ul></li>{{- end }} | ||
| <li><strong>64-bit:</strong> {{ $meta.Is64Bit }}</li> | ||
| </ul> | ||
| </details> | ||
| {{- end }} | ||
| </div> | ||
| {{- end }} | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure about this file name, should not it have darwin in the name? Does it mean it will be executed on Linux?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was in response to #45533 (comment)
See: fe8b555