Skip to content

Commit 8225861

Browse files
committed
Support MacOS VM and macos agent installation
1 parent 20a0a0b commit 8225861

20 files changed

Lines changed: 287 additions & 31 deletions

components/datadog/agent/host.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ func NewHostAgent(e config.Env, host *remoteComp.Host, options ...agentparams.Op
7070
}
7171

7272
func (h *HostAgent) installScriptInstallation(env config.Env, params *agentparams.Params, baseOpts ...pulumi.ResourceOption) (command.Command, error) {
73-
installCmdStr, err := h.manager.getInstallCommand(params.Version, params.AdditionalInstallParameters)
73+
installCmdStr, err := h.manager.getInstallCommand(params.Version, env.AgentAPIKey(), params.AdditionalInstallParameters)
7474
if err != nil {
7575
return nil, err
7676
}
7777

7878
installCmd, err := h.Host.OS.Runner().Command(
7979
h.namer.ResourceName("install-agent"),
8080
&command.Args{
81-
Create: pulumi.Sprintf(installCmdStr, env.AgentAPIKey()),
81+
Create: installCmdStr,
8282
}, baseOpts...)
8383
if err != nil {
8484
return nil, err

components/datadog/agent/host_linuxos.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (am *agentLinuxManager) directInstallCommand(_ config.Env, packagePath stri
2525
return am.targetOS.PackageManager().Ensure("./"+packagePath, nil, "", os.AllowUnsignedPackages(true), os.WithPulumiResourceOptions(opts...))
2626
}
2727

28-
func (am *agentLinuxManager) getInstallCommand(version agentparams.PackageVersion, _ []string) (string, error) {
28+
func (am *agentLinuxManager) getInstallCommand(version agentparams.PackageVersion, apiKey pulumi.StringInput, _ []string) (pulumi.StringOutput, error) {
2929
var commandLine string
3030
testEnvVars := []string{}
3131

@@ -59,10 +59,11 @@ func (am *agentLinuxManager) getInstallCommand(version agentparams.PackageVersio
5959

6060
commandLine = strings.Join(testEnvVars, " ")
6161

62-
return fmt.Sprintf(
62+
commandLine = fmt.Sprintf(
6363
`for i in 1 2 3 4 5; do curl -fsSL https://s3.amazonaws.com/dd-agent/scripts/%v -o install-script.sh && break || sleep $((2**$i)); done && for i in 1 2 3; do DD_API_KEY=%%s %v DD_INSTALL_ONLY=true bash install-script.sh && exit 0 || sleep $((2**$i)); done; exit 1`,
6464
fmt.Sprintf("install_script_agent%s.sh", version.Major),
65-
commandLine), nil
65+
commandLine)
66+
return pulumi.Sprintf(commandLine, apiKey), nil
6667
}
6768

6869
func (am *agentLinuxManager) getAgentConfigFolder() string {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package agent
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/DataDog/test-infra-definitions/common/config"
8+
"github.com/DataDog/test-infra-definitions/components/command"
9+
"github.com/DataDog/test-infra-definitions/components/datadog/agentparams"
10+
remoteComp "github.com/DataDog/test-infra-definitions/components/remote"
11+
12+
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
13+
)
14+
15+
type agentMacOSManager struct {
16+
host *remoteComp.Host
17+
}
18+
19+
func newMacOSManager(host *remoteComp.Host) agentOSManager {
20+
return &agentMacOSManager{host: host}
21+
}
22+
23+
// directInstallCommand expects a locally provided .dmg or .pkg uploaded to the host; it will install it with installer
24+
func (am *agentMacOSManager) directInstallCommand(env config.Env, packagePath string, _ agentparams.PackageVersion, additionalInstallParameters []string, opts ...pulumi.ResourceOption) (command.Command, error) {
25+
// Unsupported for now, installing directly from a dmg without the install script requires way too many step that would imply duplicating the install script code in there.
26+
return nil, fmt.Errorf("installing directly from a dmg without the install script requires way too many step that would imply duplicating the install script code in there")
27+
}
28+
29+
// getInstallCommand downloads appropriate pkg and installs it
30+
func (am *agentMacOSManager) getInstallCommand(version agentparams.PackageVersion, apiKey pulumi.StringInput, _ []string) (pulumi.StringOutput, error) {
31+
// For macOS, use the official install script which supports DD_API_KEY and version envs,
32+
// mirroring Linux flow but using the macOS path. The script detects OS and uses pkg.
33+
// If pipeline is specified, we cannot use public script; we assume local package will be provided in that case.
34+
35+
exports := []string{}
36+
if version.Major != "" {
37+
exports = append(exports, fmt.Sprintf("DD_AGENT_MAJOR_VERSION=%s", version.Major))
38+
}
39+
if version.Minor != "" {
40+
exports = append(exports, fmt.Sprintf("DD_AGENT_MINOR_VERSION=%s", version.Minor))
41+
}
42+
43+
if version.PipelineID != "" {
44+
exports = append(exports, fmt.Sprintf("DD_REPO_URL=https://dd-agent-macostesting.s3.amazonaws.com/ci/datadog-agent/pipeline-%s", version.PipelineID))
45+
}
46+
47+
env := strings.Join(exports, " ")
48+
// Retry curl few times
49+
cmd := fmt.Sprintf(`for i in 1 2 3 4 5; do curl -fsSL https://install.datadoghq.com/scripts/install_mac_os.sh -o install-script.sh && break || sleep $((2**$i)); done && for i in 1 2 3; do DD_API_KEY=%%s %%s %[1]s DD_INSTALL_ONLY=true bash install-script.sh && exit 0 || sleep $((2**$i)); done; exit 1`, env)
50+
// Only the systemdaemon install is supported on macOS, because single user requires to interact with the pop-up.
51+
pulumiCmdStr := pulumi.Sprintf(cmd, apiKey, pulumi.Sprintf("DD_SYSTEMDAEMON_INSTALL=true DD_SYSTEMDAEMON_USER_GROUP=%s:staff", am.host.Username))
52+
return pulumiCmdStr, nil
53+
}
54+
55+
func (am *agentMacOSManager) getAgentConfigFolder() string {
56+
// macOS Agent config default
57+
return "/opt/datadog-agent/etc"
58+
}
59+
60+
func (am *agentMacOSManager) restartAgentServices(transform command.Transformer, opts ...pulumi.ResourceOption) (command.Command, error) {
61+
// On macOS, the launchd service is "com.datadoghq.agent"
62+
cmdName := am.host.Name() + "-restart-agent"
63+
var cmdArgs command.RunnerCommandArgs = &command.Args{
64+
Sudo: true,
65+
Create: pulumi.String("launchctl kickstart -k system/com.datadoghq.agent"),
66+
}
67+
if transform != nil {
68+
cmdName, cmdArgs = transform(cmdName, cmdArgs)
69+
}
70+
return am.host.OS.Runner().Command(cmdName, cmdArgs, opts...)
71+
}

components/datadog/agent/host_os.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
// internal interface to be able to provide the different OS-specific commands
1717
type agentOSManager interface {
1818
directInstallCommand(env config.Env, packagePath string, version agentparams.PackageVersion, additionalInstallParameters []string, opts ...pulumi.ResourceOption) (command.Command, error)
19-
getInstallCommand(version agentparams.PackageVersion, additionalInstallParameters []string) (string, error)
19+
getInstallCommand(version agentparams.PackageVersion, apiKey pulumi.StringInput, additionalInstallParameters []string) (pulumi.StringOutput, error)
2020
getAgentConfigFolder() string
2121
restartAgentServices(transform command.Transformer, opts ...pulumi.ResourceOption) (command.Command, error)
2222
}
@@ -27,7 +27,9 @@ func getOSManager(host *remoteComp.Host) agentOSManager {
2727
return newLinuxManager(host)
2828
case os.WindowsFamily:
2929
return newWindowsManager(host)
30-
case os.MacOSFamily, os.UnknownFamily:
30+
case os.MacOSFamily:
31+
return newMacOSManager(host)
32+
case os.UnknownFamily:
3133
fallthrough
3234
default:
3335
panic(fmt.Sprintf("unsupported OS: %v", host.OS.Descriptor().Family()))

components/datadog/agent/host_windowsos.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ $ErrorActionPreference = 'Stop';
4545
return am.host.OS.Runner().Command("install-agent", &command.Args{Create: pulumi.Sprintf(cmd, env.AgentAPIKey())}, opts...)
4646
}
4747

48-
func (am *agentWindowsManager) getInstallCommand(version agentparams.PackageVersion, additionalInstallParameters []string) (string, error) {
48+
func (am *agentWindowsManager) getInstallCommand(version agentparams.PackageVersion, apiKey pulumi.StringInput, additionalInstallParameters []string) (pulumi.StringOutput, error) {
4949
url, err := getAgentURL(version)
5050
if err != nil {
51-
return "", err
51+
return pulumi.Sprintf(""), err
5252
}
5353

5454
cmd := ""
@@ -73,11 +73,11 @@ for ($i=0; $i -lt 3; $i++) {
7373
`, url, localFilename)
7474
installPackageCommandStr, err := am.getInstallPackageCommand(localFilename, version, additionalInstallParameters)
7575
if err != nil {
76-
return "", err
76+
return pulumi.Sprintf(""), err
7777
}
7878
cmd += installPackageCommandStr
7979

80-
return cmd, nil
80+
return pulumi.Sprintf(cmd, apiKey), nil
8181
}
8282

8383
func (am *agentWindowsManager) getInstallPackageCommand(filePath string, version agentparams.PackageVersion, additionalInstallParameters []string) (string, error) {

components/datadog/agent/package.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func GetPackagePath(localPath string, flavor tifos.Flavor, agentFlavor string, a
4444
wantedExt = ".deb"
4545
case tifos.WindowsServer:
4646
wantedExt = ".msi"
47-
case tifos.MacosOS, tifos.Unknown:
48-
fallthrough
47+
case tifos.MacosOS:
48+
wantedExt = ".dmg"
4949
default:
5050
return "", fmt.Errorf("unsupported flavor for local packages installation: %s", flavor)
5151
}

components/os/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const (
1515
func ArchitectureFromString(archStr string) Architecture {
1616
archStr = strings.ToLower(archStr)
1717
switch archStr {
18-
case "x86_64", "amd64", "": // Default architecture is AMD64
18+
case "x86_64", "amd64", "", "x86_64_mac": // Default architecture is AMD64
1919
return AMD64Arch
2020
case "arm64", "aarch64":
2121
return ARM64Arch

components/os/macos_descriptors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ package os
33
// Implements commonly used descriptors for easier usage
44
var (
55
MacOSDefault = MacOSSonoma
6-
MacOSSonoma = NewDescriptorWithArch(MacosOS, "sonoma", ARM64Arch)
6+
MacOSSonoma = NewDescriptor(MacosOS, "sonoma")
77
)

components/os/macos_servicemanagers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (s *macOSServiceManager) EnsureRestarted(serviceName string, transform comm
2121
cmdName := s.e.CommonNamer().ResourceName("running", serviceName)
2222
var cmdArgs command.RunnerCommandArgs = &command.Args{
2323
Sudo: true,
24-
Create: pulumi.String(fmt.Sprintf("launchctl stop %s && launchctl start %s", serviceName, serviceName)),
24+
Create: pulumi.String(fmt.Sprintf("launchctl kickstart -k %s'", serviceName, serviceName)),
2525
}
2626

2727
// If a transform is provided, use it to modify the command name and args
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package ec2
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/DataDog/test-infra-definitions/common/config"
7+
"github.com/DataDog/test-infra-definitions/common/utils"
8+
"github.com/DataDog/test-infra-definitions/resources/aws"
9+
10+
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2"
11+
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
12+
)
13+
14+
type DedicatedHostArgs struct {
15+
// Mandatory
16+
InstanceType string // e.g., "mac1.metal", "mac2.metal"
17+
18+
// Optional
19+
AvailabilityZone string // If not specified, will use first available zone
20+
HostRecovery string // "on" or "off", defaults to "off"
21+
Tags pulumi.StringMap
22+
}
23+
24+
// NewDedicatedHost creates an EC2 Dedicated Host for Mac instances
25+
func NewDedicatedHost(e aws.Environment, name string, args DedicatedHostArgs, opts ...pulumi.ResourceOption) (*ec2.DedicatedHost, error) {
26+
if args.InstanceType == "" {
27+
return nil, fmt.Errorf("InstanceType is required for dedicated host")
28+
}
29+
30+
// Default values
31+
if args.HostRecovery == "" {
32+
args.HostRecovery = "off"
33+
}
34+
35+
var availabilityZone pulumi.StringInput
36+
if args.AvailabilityZone == "" {
37+
// Use the same AZ as the first subnet
38+
availabilityZone = e.RandomSubnets().Index(pulumi.Int(0)).ApplyT(func(subnetId string) (string, error) {
39+
// Get subnet info to determine AZ
40+
subnet, err := ec2.LookupSubnet(e.Ctx(), &ec2.LookupSubnetArgs{
41+
Id: &subnetId,
42+
}, e.WithProvider(config.ProviderAWS))
43+
if err != nil {
44+
return "", err
45+
}
46+
return subnet.AvailabilityZone, nil
47+
}).(pulumi.StringOutput)
48+
} else {
49+
availabilityZone = pulumi.String(args.AvailabilityZone)
50+
}
51+
52+
// Set default tags if none provided
53+
if args.Tags == nil {
54+
args.Tags = pulumi.StringMap{
55+
"Name": e.Namer.DisplayName(255, pulumi.String(name)),
56+
}
57+
} else if _, exists := args.Tags["Name"]; !exists {
58+
args.Tags["Name"] = e.Namer.DisplayName(255, pulumi.String(name))
59+
}
60+
61+
dedicatedHostArgs := &ec2.DedicatedHostArgs{
62+
InstanceType: pulumi.String(args.InstanceType),
63+
AvailabilityZone: availabilityZone,
64+
HostRecovery: pulumi.String(args.HostRecovery),
65+
Tags: args.Tags,
66+
}
67+
68+
return ec2.NewDedicatedHost(e.Ctx(),
69+
e.Namer.ResourceName(name),
70+
dedicatedHostArgs,
71+
utils.MergeOptions(opts, e.WithProviders(config.ProviderAWS), pulumi.RetainOnDelete(true))..., // Retain on delete because deleting a dedicated host is not possible unless it lived for at least 24 hours, the cleanup will be done by test-infra-cleaner
72+
)
73+
}

0 commit comments

Comments
 (0)