-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
install_info.go
138 lines (115 loc) · 4.55 KB
/
install_info.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
// Package installinfo offers helpers to interact with the 'install_info' file.
//
// The install_info files is present next to the agent configuration and contains information about how the agent was//
// installed and its version history. The file is automatically updated by installation tools (MSI installer, Chef,
// Ansible, DPKG, ...).
package installinfo
import (
"encoding/json"
"os"
"path/filepath"
"time"
"gopkg.in/yaml.v2"
"github.com/DataDog/datadog-agent/pkg/config"
configUtils "github.com/DataDog/datadog-agent/pkg/config/utils"
"github.com/DataDog/datadog-agent/pkg/util/log"
"github.com/DataDog/datadog-agent/pkg/version"
)
// InstallInfo contains metadata on how the Agent was installed
type InstallInfo struct {
Tool string `json:"tool" yaml:"tool"`
ToolVersion string `json:"tool_version" yaml:"tool_version"`
InstallerVersion string `json:"installer_version" yaml:"installer_version"`
}
// installInfoMethod contains install info
type installInfoMethod struct {
Method InstallInfo `json:"install_method" yaml:"install_method"`
}
type versionHistoryEntry struct {
Version string `json:"version"`
Timestamp time.Time `json:"timestamp"`
InstallMethod InstallInfo `json:"install_method" yaml:"install_method"`
}
type versionHistoryEntries struct {
Entries []versionHistoryEntry `json:"entries"`
}
const maxVersionHistoryEntries = 60
// GetFilePath returns the path of the 'install_info' directory relative to the loaded coinfiguration file. The
// 'install_info' directory contains information about how the agent was installed.
func GetFilePath(conf config.Reader) string {
return filepath.Join(configUtils.ConfFileDirectory(conf), "install_info")
}
// Get returns information about how the Agent was installed.
func Get(conf config.Reader) (*InstallInfo, error) {
return getFromPath(GetFilePath(conf))
}
func getFromPath(path string) (*InstallInfo, error) {
yamlContent, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var install installInfoMethod
if err := yaml.UnmarshalStrict(yamlContent, &install); err != nil {
// file was manipulated and is not relevant to format
return nil, err
}
return &install.Method, nil
}
// LogVersionHistory loads version history file, append new entry if agent version is different than the last entry in the
// JSON file, trim the file if too many entries then save the file.
func LogVersionHistory() {
versionHistoryFilePath := filepath.Join(config.Datadog.GetString("run_path"), "version-history.json")
installInfoFilePath := GetFilePath(config.Datadog)
logVersionHistoryToFile(versionHistoryFilePath, installInfoFilePath, version.AgentVersion, time.Now().UTC())
}
func logVersionHistoryToFile(versionHistoryFilePath, installInfoFilePath, agentVersion string, timestamp time.Time) {
if agentVersion == "" || timestamp.IsZero() {
return
}
history := versionHistoryEntries{}
file, err := os.ReadFile(versionHistoryFilePath)
if err != nil {
log.Infof("Cannot read file: %s, will create a new one. %v", versionHistoryFilePath, err)
} else {
err = json.Unmarshal(file, &history)
if err != nil {
// If file is in illegal format, ignore the error and regenerate the file.
log.Errorf("Cannot deserialize json file: %s. %v", versionHistoryFilePath, err)
}
}
// Only append the version info if no entry or this is different than the last entry.
if len(history.Entries) != 0 && history.Entries[len(history.Entries)-1].Version == agentVersion {
return
}
newEntry := versionHistoryEntry{
Version: agentVersion,
Timestamp: timestamp,
}
info, err := getFromPath(installInfoFilePath)
if err == nil {
newEntry.InstallMethod = *info
} else {
log.Infof("Cannot read %s: %s", installInfoFilePath, err)
}
history.Entries = append(history.Entries, newEntry)
// Trim entries if they grow beyond the max capacity.
itemsToTrim := len(history.Entries) - maxVersionHistoryEntries
if itemsToTrim > 0 {
copy(history.Entries[0:], history.Entries[itemsToTrim:])
history.Entries = history.Entries[:maxVersionHistoryEntries]
}
file, err = json.Marshal(history)
if err != nil {
log.Errorf("Cannot serialize json file: %s %v", versionHistoryFilePath, err)
return
}
err = os.WriteFile(versionHistoryFilePath, file, 0644)
if err != nil {
log.Errorf("Cannot write json file: %s %v", versionHistoryFilePath, err)
return
}
}