Skip to content

Commit

Permalink
[Installer] Check pinned version when installing default packages (#2…
Browse files Browse the repository at this point in the history
  • Loading branch information
paullegranddc authored Jun 14, 2024
1 parent 6f3d8c8 commit 597e5b9
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 36 deletions.
46 changes: 46 additions & 0 deletions pkg/fleet/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package env
import (
"fmt"
"os"
"slices"
"strings"

"github.com/DataDog/datadog-agent/pkg/config"
Expand All @@ -23,6 +24,7 @@ const (
envRegistryAuth = "DD_INSTALLER_REGISTRY_AUTH"
envDefaultPackageVersion = "DD_INSTALLER_DEFAULT_PKG_VERSION"
envDefaultPackageInstall = "DD_INSTALLER_DEFAULT_PKG_INSTALL"
envApmLibraries = "DD_APM_INSTRUMENTATION_LIBRARIES"
)

var defaultEnv = Env{
Expand All @@ -43,6 +45,21 @@ var defaultEnv = Env{
},
}

// ApmLibLanguage is a language defined in DD_APM_INSTRUMENTATION_LIBRARIES env var
type ApmLibLanguage string

// ApmLibVersion is the version of the library defined in DD_APM_INSTRUMENTATION_LIBRARIES env var
type ApmLibVersion string

// AsVersionTag returns the version tag associated with the version of the library defined in DD_APM_INSTRUMENTATION_LIBRARIES
// if the value is empty we return latest
func (v ApmLibVersion) AsVersionTag() string {
if v == "" {
return "latest"
}
return string(v) + "-1"
}

// Env contains the configuration for the installer.
type Env struct {
APIKey string
Expand All @@ -57,6 +74,8 @@ type Env struct {
DefaultPackagesInstallOverride map[string]bool
DefaultPackagesVersionOverride map[string]string

ApmLibraries map[ApmLibLanguage]ApmLibVersion

InstallScript InstallScriptEnv
}

Expand All @@ -75,6 +94,8 @@ func FromEnv() *Env {
DefaultPackagesInstallOverride: overridesByNameFromEnv(envDefaultPackageInstall, func(s string) bool { return s == "true" }),
DefaultPackagesVersionOverride: overridesByNameFromEnv(envDefaultPackageVersion, func(s string) string { return s }),

ApmLibraries: parseApmLibrariesEnv(),

InstallScript: installScriptEnvFromEnv(),
}
}
Expand Down Expand Up @@ -108,13 +129,38 @@ func (e *Env) ToEnv() []string {
if e.RegistryAuthOverride != "" {
env = append(env, envRegistryAuth+"="+e.RegistryAuthOverride)
}
if len(e.ApmLibraries) > 0 {
libraries := []string{}
for l, v := range e.ApmLibraries {
l := string(l)
if v != "" {
l = l + ":" + string(v)
}
libraries = append(libraries, l)
}
slices.Sort(libraries)
env = append(env, envApmLibraries+"="+strings.Join(libraries, ","))
}
env = append(env, overridesByNameToEnv(envRegistryURL, e.RegistryOverrideByImage)...)
env = append(env, overridesByNameToEnv(envRegistryAuth, e.RegistryAuthOverrideByImage)...)
env = append(env, overridesByNameToEnv(envDefaultPackageInstall, e.DefaultPackagesInstallOverride)...)
env = append(env, overridesByNameToEnv(envDefaultPackageVersion, e.DefaultPackagesVersionOverride)...)
return env
}

func parseApmLibrariesEnv() map[ApmLibLanguage]ApmLibVersion {
apmLibraries := os.Getenv(envApmLibraries)
apmLibrariesVersion := map[ApmLibLanguage]ApmLibVersion{}
if apmLibraries == "" {
return apmLibrariesVersion
}
for _, library := range strings.Split(apmLibraries, ",") {
libraryName, libraryVersion, _ := strings.Cut(library, ":")
apmLibrariesVersion[ApmLibLanguage(libraryName)] = ApmLibVersion(libraryVersion)
}
return apmLibrariesVersion
}

func overridesByNameFromEnv[T any](envPrefix string, convert func(string) T) map[string]T {
env := os.Environ()
overridesByPackage := map[string]T{}
Expand Down
15 changes: 14 additions & 1 deletion pkg/fleet/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestFromEnv(t *testing.T) {
RegistryAuthOverrideByImage: map[string]string{},
DefaultPackagesInstallOverride: map[string]bool{},
DefaultPackagesVersionOverride: map[string]string{},
ApmLibraries: map[ApmLibLanguage]ApmLibVersion{},
InstallScript: InstallScriptEnv{
APMInstrumentationEnabled: APMInstrumentationNotSet,
},
Expand All @@ -50,6 +51,7 @@ func TestFromEnv(t *testing.T) {
envDefaultPackageInstall + "_ANOTHER_PACKAGE": "false",
envDefaultPackageVersion + "_PACKAGE": "1.2.3",
envDefaultPackageVersion + "_ANOTHER_PACKAGE": "4.5.6",
envApmLibraries: "java,dotnet:latest,ruby:1.2",
envApmInstrumentationEnabled: "all",
},
expected: &Env{
Expand All @@ -74,6 +76,11 @@ func TestFromEnv(t *testing.T) {
"package": "1.2.3",
"another-package": "4.5.6",
},
ApmLibraries: map[ApmLibLanguage]ApmLibVersion{
"java": "",
"dotnet": "latest",
"ruby": "1.2",
},
InstallScript: InstallScriptEnv{
APMInstrumentationEnabled: APMInstrumentationEnabledAll,
},
Expand All @@ -88,7 +95,7 @@ func TestFromEnv(t *testing.T) {
defer os.Unsetenv(key)
}
result := FromEnv()
assert.Equal(t, tt.expected, result)
assert.Equal(t, tt.expected, result, "failed %v", tt.name)
})
}
}
Expand Down Expand Up @@ -128,13 +135,19 @@ func TestToEnv(t *testing.T) {
"package": "1.2.3",
"another-package": "4.5.6",
},
ApmLibraries: map[ApmLibLanguage]ApmLibVersion{
"java": "",
"dotnet": "latest",
"ruby": "1.2",
},
},
expected: []string{
"DD_API_KEY=123456",
"DD_SITE=datadoghq.eu",
"DD_REMOTE_UPDATES=true",
"DD_INSTALLER_REGISTRY_URL=registry.example.com",
"DD_INSTALLER_REGISTRY_AUTH=auth",
"DD_APM_INSTRUMENTATION_LIBRARIES=dotnet:latest,java,ruby:1.2",
"DD_INSTALLER_REGISTRY_URL_IMAGE=another.registry.example.com",
"DD_INSTALLER_REGISTRY_URL_ANOTHER_IMAGE=yet.another.registry.example.com",
"DD_INSTALLER_REGISTRY_AUTH_IMAGE=another.auth",
Expand Down
78 changes: 55 additions & 23 deletions pkg/fleet/installer/default_packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,94 @@ package installer

import (
"slices"
"strings"

"github.com/DataDog/datadog-agent/pkg/fleet/env"
"github.com/DataDog/datadog-agent/pkg/fleet/internal/oci"
)

type defaultPackage struct {
name string
// Package represents a package known to the installer
type Package struct {
Name string
released bool
releasedBySite []string
releasedWithRemoteUpdates bool
condition func(*env.Env) bool
condition func(Package, *env.Env) bool
}

var defaultPackagesList = []defaultPackage{
{name: "datadog-apm-inject", released: false, condition: apmInjectEnabled},
{name: "datadog-apm-library-java", released: false, condition: apmInjectEnabled},
{name: "datadog-apm-library-ruby", released: false, condition: apmInjectEnabled},
{name: "datadog-apm-library-js", released: false, condition: apmInjectEnabled},
{name: "datadog-apm-library-dotnet", released: false, condition: apmInjectEnabled},
{name: "datadog-apm-library-python", released: false, condition: apmInjectEnabled},
{name: "datadog-agent", released: false, releasedWithRemoteUpdates: true},
// PackagesList lists all known packages. Not all of them are installable
var PackagesList = []Package{
{Name: "datadog-apm-inject", released: false, condition: apmInjectEnabled},
{Name: "datadog-apm-library-java", released: false, condition: apmLanguageEnabled},
{Name: "datadog-apm-library-ruby", released: false, condition: apmLanguageEnabled},
{Name: "datadog-apm-library-js", released: false, condition: apmLanguageEnabled},
{Name: "datadog-apm-library-dotnet", released: false, condition: apmLanguageEnabled},
{Name: "datadog-apm-library-python", released: false, condition: apmLanguageEnabled},
{Name: "datadog-agent", released: false, releasedWithRemoteUpdates: true},
}

// DefaultPackages resolves the default packages URLs to install based on the environment.
func DefaultPackages(env *env.Env) []string {
return defaultPackages(env, defaultPackagesList)
return defaultPackages(env, PackagesList)
}

func defaultPackages(env *env.Env, defaultPackages []defaultPackage) []string {
func defaultPackages(env *env.Env, defaultPackages []Package) []string {
var packages []string
for _, p := range defaultPackages {
released := p.released || slices.Contains(p.releasedBySite, env.Site) || (p.releasedWithRemoteUpdates && env.RemoteUpdates)
installOverride, isOverridden := env.DefaultPackagesInstallOverride[p.name]
condition := p.condition == nil || p.condition(env)
installOverride, isOverridden := env.DefaultPackagesInstallOverride[p.Name]
condition := p.condition == nil || p.condition(p, env)

shouldInstall := released && condition
if isOverridden {
shouldInstall = installOverride
}
if !shouldInstall {
continue
}

version := "latest"

// Respect pinned version of APM packages if we don't define any overwrite
if apmLibVersion, ok := env.ApmLibraries[packageToLanguage(p.Name)]; ok {
version = apmLibVersion.AsVersionTag()
// TODO(paullgdc): Emit a warning here if APM packages are not pinned to at least a major
}

if shouldInstall {
version := "latest"
if v, ok := env.DefaultPackagesVersionOverride[p.name]; ok {
version = v
}
url := oci.PackageURL(env, p.name, version)
packages = append(packages, url)
if v, ok := env.DefaultPackagesVersionOverride[p.Name]; ok {
version = v
}
url := oci.PackageURL(env, p.Name, version)
packages = append(packages, url)
}
return packages
}

func apmInjectEnabled(e *env.Env) bool {
func apmInjectEnabled(_ Package, e *env.Env) bool {
switch e.InstallScript.APMInstrumentationEnabled {
case env.APMInstrumentationEnabledAll, env.APMInstrumentationEnabledDocker, env.APMInstrumentationEnabledHost:
return true
}
return false
}

func apmLanguageEnabled(p Package, e *env.Env) bool {
if !apmInjectEnabled(p, e) {
return false
}
if _, ok := e.ApmLibraries[packageToLanguage(p.Name)]; ok {
return true
}
if _, ok := e.ApmLibraries["all"]; ok {
return true
}
return false
}

func packageToLanguage(packageName string) env.ApmLibLanguage {
lang, found := strings.CutPrefix(packageName, "datadog-apm-library-")
if !found {
return ""
}
return env.ApmLibLanguage(lang)
}
Loading

0 comments on commit 597e5b9

Please sign in to comment.