update: expose publish date of updates (#2050)

This commit is contained in:
Jan-Otto Kröpke
2025-05-23 21:18:29 +02:00
committed by GitHub
parent 92f213ca7c
commit 6dd21a8e00
2 changed files with 104 additions and 23 deletions

View File

@@ -60,11 +60,14 @@ type Collector struct {
mu sync.RWMutex
ctxCancelFn context.CancelFunc
logger *slog.Logger
metricsBuf []prometheus.Metric
pendingUpdate *prometheus.Desc
queryDurationSeconds *prometheus.Desc
lastScrapeMetric *prometheus.Desc
pendingUpdate *prometheus.Desc
pendingUpdateLastPublished *prometheus.Desc
queryDurationSeconds *prometheus.Desc
lastScrapeMetric *prometheus.Desc
}
func New(config *Config) *Collector {
@@ -146,9 +149,9 @@ func (c *Collector) Close() error {
}
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
c.logger = logger.With(slog.String("collector", Name))
logger.Info("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues.")
c.logger.Info("update collector is in an experimental state! The configuration and metrics may change in future. Please report any issues.")
ctx, cancel := context.WithCancel(context.Background())
@@ -164,7 +167,14 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
c.pendingUpdate = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "pending_info"),
"Expose information for a single pending update item",
[]string{"category", "severity", "title"},
[]string{"id", "revision", "category", "severity", "title"},
nil,
)
c.pendingUpdateLastPublished = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "pending_published_timestamp"),
"Expose last published timestamp for a single pending update item",
[]string{"id", "revision"},
nil,
)
@@ -241,9 +251,16 @@ func (c *Collector) scheduleUpdateStatus(ctx context.Context, logger *slog.Logge
defer musQueryInterface.Release()
_, err = oleutil.PutProperty(musQueryInterface, "UserLocale", 1033)
if err != nil {
initErrCh <- fmt.Errorf("failed to set ClientApplicationID: %w", err)
return
}
_, err = oleutil.PutProperty(musQueryInterface, "ClientApplicationID", "windows_exporter")
if err != nil {
initErrCh <- fmt.Errorf("put ClientApplicationID: %w", err)
initErrCh <- fmt.Errorf("failed to set ClientApplicationID: %w", err)
return
}
@@ -320,7 +337,7 @@ func (c *Collector) scheduleUpdateStatus(ctx context.Context, logger *slog.Logge
}
func (c *Collector) fetchUpdates(logger *slog.Logger, usd *ole.IDispatch) ([]prometheus.Metric, error) {
metricsBuf := make([]prometheus.Metric, 0, len(c.metricsBuf))
metricsBuf := make([]prometheus.Metric, 0, len(c.metricsBuf)*2+1)
timeStart := time.Now()
@@ -367,10 +384,22 @@ func (c *Collector) fetchUpdates(logger *slog.Logger, usd *ole.IDispatch) ([]pro
c.pendingUpdate,
prometheus.GaugeValue,
1,
update.identity,
update.revision,
update.category,
update.severity,
update.title,
))
if update.lastPublished != (time.Time{}) {
metricsBuf = append(metricsBuf, prometheus.MustNewConstMetric(
c.pendingUpdateLastPublished,
prometheus.GaugeValue,
float64(update.lastPublished.Unix()),
update.identity,
update.revision,
))
}
}
metricsBuf = append(metricsBuf, prometheus.MustNewConstMetric(
@@ -383,9 +412,12 @@ func (c *Collector) fetchUpdates(logger *slog.Logger, usd *ole.IDispatch) ([]pro
}
type windowsUpdate struct {
category string
severity string
title string
identity string
revision string
category string
severity string
title string
lastPublished time.Time
}
// getUpdateStatus retrieves the update status of the given item.
@@ -423,10 +455,48 @@ func (c *Collector) getUpdateStatus(updd *ole.IDispatch, item int) (windowsUpdat
return windowsUpdate{}, fmt.Errorf("get Title: %w", err)
}
// Get the Identity object
identityVariant, err := oleutil.GetProperty(updateItem, "Identity")
if err != nil {
return windowsUpdate{}, fmt.Errorf("get Identity: %w", err)
}
identity := identityVariant.ToIDispatch()
defer identity.Release()
// Read the UpdateID
updateIDVariant, err := oleutil.GetProperty(identity, "UpdateID")
if err != nil {
return windowsUpdate{}, fmt.Errorf("get UpdateID: %w", err)
}
revisionVariant, err := oleutil.GetProperty(identity, "RevisionNumber")
if err != nil {
return windowsUpdate{}, fmt.Errorf("get RevisionNumber: %w", err)
}
lastPublished, err := oleutil.GetProperty(updateItem, "LastDeploymentChangeTime")
if err != nil {
return windowsUpdate{}, fmt.Errorf("get LastDeploymentChangeTime: %w", err)
}
lastPublishedDate, err := ole.GetVariantDate(uint64(lastPublished.Val))
if err != nil {
c.logger.Debug("failed to convert LastDeploymentChangeTime",
slog.String("title", title.ToString()),
slog.Any("err", err),
)
lastPublishedDate = time.Time{}
}
return windowsUpdate{
category: categoryName,
severity: severity.ToString(),
title: title.ToString(),
identity: updateIDVariant.ToString(),
revision: strconv.FormatInt(revisionVariant.Val, 10),
category: categoryName,
severity: severity.ToString(),
title: title.ToString(),
lastPublished: lastPublishedDate,
}, nil
}