Skip to content

Commit

Permalink
Add a VM provisionner for GCP (#29214)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorentClarret authored Sep 11, 2024
1 parent 61bc84e commit f3313b8
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 0 deletions.
32 changes: 32 additions & 0 deletions test/new-e2e/examples/gcp_vm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 examples

import (
gcphost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/gcp/host/linux"
"testing"

"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments"
)

type gcpVMSuite struct {
e2e.BaseSuite[environments.Host]
}

// TestGCPVMSuite runs tests for the VM interface to ensure its implementation is correct.
func TestGCPVMSuite(t *testing.T) {
suiteParams := []e2e.SuiteOption{e2e.WithProvisioner(gcphost.ProvisionerNoAgentNoFakeIntake())}
e2e.Run(t, &gcpVMSuite{}, suiteParams...)
}

func (v *gcpVMSuite) TestExecute() {
vm := v.Env().RemoteHost

out, err := vm.Execute("whoami")
v.Require().NoError(err)
v.Require().NotEmpty(out)
}
1 change: 1 addition & 0 deletions test/new-e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,5 @@ require (
github.com/pulumi/pulumi-azure-native-sdk/network/v2 v2.59.0 // indirect
github.com/pulumi/pulumi-azure-native-sdk/v2 v2.60.0 // indirect
github.com/pulumi/pulumi-gcp/sdk/v6 v6.67.1 // indirect
github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0 // indirect
)
2 changes: 2 additions & 0 deletions test/new-e2e/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 124 additions & 0 deletions test/new-e2e/pkg/environments/gcp/host/linux/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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 gcphost contains the definition of the GCP Host environment.
package gcphost

import (
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
"github.com/DataDog/test-infra-definitions/resources/gcp"
"github.com/DataDog/test-infra-definitions/scenarios/gcp/compute"
"github.com/DataDog/test-infra-definitions/scenarios/gcp/fakeintake"

"github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments"

"github.com/DataDog/test-infra-definitions/components/datadog/agent"
"github.com/DataDog/test-infra-definitions/components/datadog/agentparams"
"github.com/DataDog/test-infra-definitions/components/datadog/updater"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

const (
provisionerBaseID = "gcp-vm-"
defaultVMName = "vm"
)

// Provisioner creates a VM environment with an VM, a FakeIntake and a Host Agent configured to talk to each other.
// FakeIntake and Agent creation can be deactivated by using [WithoutFakeIntake] and [WithoutAgent] options.
func Provisioner(opts ...ProvisionerOption) e2e.TypedProvisioner[environments.Host] {
// We need to build params here to be able to use params.name in the provisioner name
params := GetProvisionerParams(opts...)

provisioner := e2e.NewTypedPulumiProvisioner(provisionerBaseID+params.name, func(ctx *pulumi.Context, env *environments.Host) error {
// We ALWAYS need to make a deep copy of `params`, as the provisioner can be called multiple times.
// and it's easy to forget about it, leading to hard-to-debug issues.
params := GetProvisionerParams(opts...)
return Run(ctx, env, RunParams{ProvisionerParams: params})
}, params.extraConfigParams)

return provisioner
}

// Run deploys an environment given a pulumi.Context
func Run(ctx *pulumi.Context, env *environments.Host, runParams RunParams) error {
var gcpEnv gcp.Environment
if runParams.Environment == nil {
var err error
gcpEnv, err = gcp.NewEnvironment(ctx)
if err != nil {
return err
}
} else {
gcpEnv = *runParams.Environment
}
params := runParams.ProvisionerParams

host, err := compute.NewVM(gcpEnv, params.name, params.instanceOptions...)
if err != nil {
return err
}
err = host.Export(ctx, &env.RemoteHost.HostOutput)
if err != nil {
return err
}

// Create FakeIntake if required
if params.fakeintakeOptions != nil {
fakeIntake, err := fakeintake.NewVMInstance(gcpEnv, params.fakeintakeOptions...)
if err != nil {
return err
}
err = fakeIntake.Export(ctx, &env.FakeIntake.FakeintakeOutput)
if err != nil {
return err
}

// Normally if FakeIntake is enabled, Agent is enabled, but just in case
if params.agentOptions != nil {
// Prepend in case it's overridden by the user
newOpts := []agentparams.Option{agentparams.WithFakeintake(fakeIntake)}
params.agentOptions = append(newOpts, params.agentOptions...)
}
} else {
// Suite inits all fields by default, so we need to explicitly set it to nil
env.FakeIntake = nil
}
if !params.installUpdater {
// Suite inits all fields by default, so we need to explicitly set it to nil
env.Updater = nil
}

// Create Agent if required
if params.installUpdater && params.agentOptions != nil {
updater, err := updater.NewHostUpdater(&gcpEnv, host, params.agentOptions...)
if err != nil {
return err
}

err = updater.Export(ctx, &env.Updater.HostUpdaterOutput)
if err != nil {
return err
}
// todo: add agent once updater installs agent on bootstrap
env.Agent = nil
} else if params.agentOptions != nil {
agent, err := agent.NewHostAgent(&gcpEnv, host, params.agentOptions...)
if err != nil {
return err
}

err = agent.Export(ctx, &env.Agent.HostAgentOutput)
if err != nil {
return err
}

env.Agent.ClientOptions = params.agentClientOptions
} else {
// Suite inits all fields by default, so we need to explicitly set it to nil
env.Agent = nil
}

return nil
}
152 changes: 152 additions & 0 deletions test/new-e2e/pkg/environments/gcp/host/linux/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// 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 gcphost

import (
"fmt"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/runner"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client/agentclientparams"
"github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/optional"
"github.com/DataDog/test-infra-definitions/components/datadog/agentparams"
"github.com/DataDog/test-infra-definitions/resources/gcp"
"github.com/DataDog/test-infra-definitions/scenarios/gcp/compute"
"github.com/DataDog/test-infra-definitions/scenarios/gcp/fakeintake"
)

// ProvisionerParams is a set of parameters for the Provisioner.
type ProvisionerParams struct {
name string

instanceOptions []compute.VMOption
agentOptions []agentparams.Option
agentClientOptions []agentclientparams.Option
fakeintakeOptions []fakeintake.Option
extraConfigParams runner.ConfigMap
installUpdater bool
}

func newProvisionerParams() *ProvisionerParams {
// We use nil arrays to decide if we should create or not
return &ProvisionerParams{
name: defaultVMName,
instanceOptions: []compute.VMOption{},
agentOptions: []agentparams.Option{},
agentClientOptions: []agentclientparams.Option{},
fakeintakeOptions: []fakeintake.Option{},
extraConfigParams: runner.ConfigMap{},
}
}

// GetProvisionerParams return ProvisionerParams from options opts setup
func GetProvisionerParams(opts ...ProvisionerOption) *ProvisionerParams {
params := newProvisionerParams()
err := optional.ApplyOptions(params, opts)
if err != nil {
panic(fmt.Errorf("unable to apply ProvisionerOption, err: %w", err))
}
return params
}

// ProvisionerOption is a provisioner option.
type ProvisionerOption func(*ProvisionerParams) error

// WithName sets the name of the provisioner.
func WithName(name string) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.name = name
return nil
}
}

// WithInstanceOptions adds options to the EC2 VM.
func WithInstanceOptions(opts ...compute.VMOption) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.instanceOptions = append(params.instanceOptions, opts...)
return nil
}
}

// WithAgentOptions adds options to the Agent.
func WithAgentOptions(opts ...agentparams.Option) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.agentOptions = append(params.agentOptions, opts...)
return nil
}
}

// WithAgentClientOptions adds options to the Agent client.
func WithAgentClientOptions(opts ...agentclientparams.Option) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.agentClientOptions = append(params.agentClientOptions, opts...)
return nil
}
}

// WithFakeIntakeOptions adds options to the FakeIntake.
func WithFakeIntakeOptions(opts ...fakeintake.Option) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.fakeintakeOptions = append(params.fakeintakeOptions, opts...)
return nil
}
}

// WithExtraConfigParams adds extra config parameters to the ConfigMap.
func WithExtraConfigParams(configMap runner.ConfigMap) ProvisionerOption {
return func(params *ProvisionerParams) error {
params.extraConfigParams = configMap
return nil
}
}

// WithoutFakeIntake disables the creation of the FakeIntake.
func WithoutFakeIntake() ProvisionerOption {
return func(params *ProvisionerParams) error {
params.fakeintakeOptions = nil
return nil
}
}

// WithoutAgent disables the creation of the Agent.
func WithoutAgent() ProvisionerOption {
return func(params *ProvisionerParams) error {
params.agentOptions = nil
return nil
}
}

// WithUpdater installs the agent through the updater.
func WithUpdater() ProvisionerOption {
return func(params *ProvisionerParams) error {
params.installUpdater = true
return nil
}
}

// ProvisionerNoAgentNoFakeIntake wraps Provisioner with hardcoded WithoutAgent and WithoutFakeIntake options.
func ProvisionerNoAgentNoFakeIntake(opts ...ProvisionerOption) e2e.TypedProvisioner[environments.Host] {
mergedOpts := make([]ProvisionerOption, 0, len(opts)+2)
mergedOpts = append(mergedOpts, opts...)
mergedOpts = append(mergedOpts, WithoutAgent(), WithoutFakeIntake())

return Provisioner(mergedOpts...)
}

// ProvisionerNoFakeIntake wraps Provisioner with hardcoded WithoutFakeIntake option.
func ProvisionerNoFakeIntake(opts ...ProvisionerOption) e2e.TypedProvisioner[environments.Host] {
mergedOpts := make([]ProvisionerOption, 0, len(opts)+1)
mergedOpts = append(mergedOpts, opts...)
mergedOpts = append(mergedOpts, WithoutFakeIntake())

return Provisioner(mergedOpts...)
}

// RunParams is a set of parameters for the Run function.
type RunParams struct {
Environment *gcp.Environment
ProvisionerParams *ProvisionerParams
}
1 change: 1 addition & 0 deletions test/new-e2e/pkg/runner/local_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
var defaultLocalEnvironments = map[string]string{
"aws": "agent-sandbox",
"az": "agent-sandbox",
"gcp": "agent-sandbox",
}

// NewLocalProfile creates a new local profile
Expand Down

0 comments on commit f3313b8

Please sign in to comment.