Skip to content

Commit

Permalink
Add agent info to host metadata (with install script fix)
Browse files Browse the repository at this point in the history
* Update install-script to add install_info

* Add install method to metadata payload

* Add kitchen tests for install info

* Version install script independently from agent

* Use the final names for install_info

* Add missing $sudo_cmd when creating install_info
  • Loading branch information
kbogtob authored May 12, 2020
1 parent 87b1f86 commit dbc3f42
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 9 deletions.
10 changes: 10 additions & 0 deletions cmd/agent/install_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# using the package manager and Datadog repositories.

set -e
install_script_version=1.0.0
logfile="ddagent-install.log"

LEGACY_ETCDIR="/etc/dd-agent"
Expand Down Expand Up @@ -343,6 +344,15 @@ else
$sudo_cmd chmod 640 $CONF
fi

# Creating or overriding the install information
install_info_content="---
install_method:
tool: install_script
tool_version: install_script
installer_version: install_script-$install_script_version
"
$sudo_cmd sh -c "echo '$install_info_content' > $ETCDIR/install_info"

# On SUSE 11, sudo service datadog-agent start fails (because /sbin is not in a base user's path)
# However, sudo /sbin/service datadog-agent does work.
# Use which (from root user) to find the absolute path to service
Expand Down
53 changes: 53 additions & 0 deletions pkg/metadata/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,21 @@ import (
kubelet "github.com/DataDog/datadog-agent/pkg/util/hostname/kubelet"

"github.com/DataDog/datadog-agent/pkg/logs"

yaml "gopkg.in/yaml.v2"
"io/ioutil"
)

const packageCachePrefix = "host"

type installInfo struct {
Method struct {
Tool string `yaml:"tool"`
ToolVersion string `yaml:"tool_version"`
InstallerVersion string `yaml:"installer_version"`
} `yaml:"install_method"`
}

// GetPayload builds a metadata payload every time is called.
// Some data is collected only once, some is cached, some is collected at every call.
func GetPayload(hostnameData util.HostnameData) *Payload {
Expand All @@ -45,6 +56,7 @@ func GetPayload(hostnameData util.HostnameData) *Payload {
ContainerMeta: getContainerMeta(1 * time.Second),
NetworkMeta: getNetworkMeta(),
LogsMeta: getLogsMeta(),
InstallMethod: getInstallMethod(getInstallInfoPath()),
}

// Cache the metadata for use in other payloads
Expand Down Expand Up @@ -217,3 +229,44 @@ func getLogsMeta() *LogsMeta {
func buildKey(key string) string {
return path.Join(common.CachePrefix, packageCachePrefix, key)
}

func getInstallInfoPath() string {
return path.Join(config.FileUsedDir(), "install_info")
}

func getInstallInfo(infoPath string) (*installInfo, error) {
yamlContent, err := ioutil.ReadFile(infoPath)

if err != nil {
return nil, err
}

var install installInfo

if err := yaml.UnmarshalStrict(yamlContent, &install); err != nil {
// file was manipulated and is not relevant to format
return nil, err
}

return &install, nil
}

func getInstallMethod(infoPath string) *InstallMethod {
install, err := getInstallInfo(infoPath)

// if we could not get install info
if err != nil {
// consider install info is kept "undefined"
return &InstallMethod{
ToolVersion: "undefined",
Tool: nil,
InstallerVersion: nil,
}
}

return &InstallMethod{
ToolVersion: install.Method.ToolVersion,
Tool: &install.Method.Tool,
InstallerVersion: &install.Method.InstallerVersion,
}
}
51 changes: 51 additions & 0 deletions pkg/metadata/host/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
package host

import (
"io/ioutil"
"os"
"path"
"testing"
"time"

Expand All @@ -27,6 +30,7 @@ func TestGetPayload(t *testing.T) {
assert.NotNil(t, p.SystemStats)
assert.NotNil(t, p.Meta)
assert.NotNil(t, p.HostTags)
assert.NotNil(t, p.InstallMethod)
}

func TestGetSystemStats(t *testing.T) {
Expand Down Expand Up @@ -113,3 +117,50 @@ func TestGetLogsMeta(t *testing.T) {
meta = getLogsMeta()
assert.Equal(t, &LogsMeta{Transport: "HTTP"}, meta)
}

func TestGetInstallMethod(t *testing.T) {
dir, err := ioutil.TempDir("", "test_install_method")
assert.Nil(t, err)
defer os.RemoveAll(dir)

installInfoPath := path.Join(dir, "install_info")

// ------------- Without file, the install is considered private
installMethod := getInstallMethod(installInfoPath)
require.Equal(t, "undefined", installMethod.ToolVersion)
assert.Nil(t, installMethod.Tool)
assert.Nil(t, installMethod.InstallerVersion)

// ------------- with a correct file
var installInfoContent = `
---
install_method:
tool_version: chef-15
tool: chef
installer_version: datadog-cookbook-4.2.1
`
assert.Nil(t, ioutil.WriteFile(installInfoPath, []byte(installInfoContent), 0666))

// the install is considered coming from chef (example)
installMethod = getInstallMethod(installInfoPath)
require.Equal(t, "chef-15", installMethod.ToolVersion)
assert.NotNil(t, installMethod.Tool)
require.Equal(t, "chef", *installMethod.Tool)
assert.NotNil(t, installMethod.InstallerVersion)
require.Equal(t, "datadog-cookbook-4.2.1", *installMethod.InstallerVersion)

// ------------- with an incorrect file
installInfoContent = `
---
install_methodlol:
name: chef-15
version: datadog-cookbook-4.2.1
`
assert.Nil(t, ioutil.WriteFile(installInfoPath, []byte(installInfoContent), 0666))

// the parsing does not occur and the install is kept undefined
installMethod = getInstallMethod(installInfoPath)
require.Equal(t, "undefined", installMethod.ToolVersion)
assert.Nil(t, installMethod.Tool)
assert.Nil(t, installMethod.InstallerVersion)
}
8 changes: 8 additions & 0 deletions pkg/metadata/host/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ type tags struct {
GoogleCloudPlatform []string `json:"google cloud platform,omitempty"`
}

// InstallMethod is metadata about the agent's installation
type InstallMethod struct {
Tool *string `json:"tool"`
ToolVersion string `json:"tool_version"`
InstallerVersion *string `json:"installer_version"`
}

// Payload handles the JSON unmarshalling of the metadata payload
type Payload struct {
Os string `json:"os"`
Expand All @@ -54,4 +61,5 @@ type Payload struct {
ContainerMeta map[string]string `json:"container-meta,omitempty"`
NetworkMeta *NetworkMeta `json:"network"`
LogsMeta *LogsMeta `json:"logs"`
InstallMethod *InstallMethod `json:"install-method"`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Each section from every releasenote are combined when the
# CHANGELOG.rst is rendered. So the text needs to be worded so that
# it does not depend on any information only available in another
# section. This may mean repeating some details, but each section
# must be readable independently of the other.
#
# Each section note must be formatted as reStructuredText.
---
features:
- Install script creates `install_info` report
- Agent detects `install_info` report and sends it through Host metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@
include_examples 'Agent'

context 'when testing DD_SITE' do
let(:config_file_path) do
if os == :windows
"#{ENV['ProgramData']}\\Datadog\\datadog.yaml"
else
'/etc/datadog-agent/datadog.yaml'
end
end

let(:config) do
YAML.load_file(config_file_path)
YAML.load_file('/etc/datadog-agent/datadog.yaml')
end

it 'uses DD_SITE to set the site' do
expect(config['site']).to eq 'datadoghq.eu'
end
end

context 'when testing the install infos' do
let(:install_info_path) do
'/etc/datadog-agent/install_info'
end

let(:install_info) do
YAML.load_file(install_info_path)
end

it 'adds an install_info' do
expect(install_info['install_method']).to match(
'tool' => 'install_script',
'tool_version' => 'install_script',
'installer_version' => /^install_script-\d+\.\d+\.\d+$/
)
end
end
end

0 comments on commit dbc3f42

Please sign in to comment.