diff --git a/test/new-e2e/examples/gcp_vm_test.go b/test/new-e2e/examples/gcp_vm_test.go new file mode 100644 index 0000000000000..1f897a87bbfe3 --- /dev/null +++ b/test/new-e2e/examples/gcp_vm_test.go @@ -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) +} diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index b237c08f61928..529c6d10ec489 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -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 ) diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index 4103fed9d0cb4..10fe8e4f663e7 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -419,6 +419,8 @@ github.com/pulumi/pulumi-eks/sdk/v2 v2.7.8 h1:NeCKFxyOLpAaG4pJDk7+ewnCuV2IbXR7Pg github.com/pulumi/pulumi-eks/sdk/v2 v2.7.8/go.mod h1:ARGNnIZENIpDUVSX21JEQJKrESj/0u0r0iT61rpb86I= github.com/pulumi/pulumi-gcp/sdk/v6 v6.67.1 h1:PUH/sUbJmBmHjNFNthJ/dW2+riFuJV0FhrGAwuUuRIg= github.com/pulumi/pulumi-gcp/sdk/v6 v6.67.1/go.mod h1:OmZeji3dNMwB1qldAlaQfcfJPc2BaZyweVGH7Ej4SJg= +github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0 h1:21oSj+TKlKTzQcxN9Hik7iSNNHPUQXN4s3itOnahy/w= +github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0/go.mod h1:YaEZms1NgXFqGhObKVofcAeWXu2V+3t/BAXdHQZq7fU= github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.17.1 h1:VDX+hu+qK3fbf2FodgG5kfh2h1bHK0FKirW1YqKWkRc= github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.17.1/go.mod h1:e69ohZtUePLLYNLXYgiOWp0FvRGg6ya/3fsq3o00nN0= github.com/pulumi/pulumi-libvirt/sdk v0.4.7 h1:/BBnqqx/Gbg2vINvJxXIVb58THXzw2lSqFqxlRSXH9M= diff --git a/test/new-e2e/pkg/environments/gcp/host/linux/host.go b/test/new-e2e/pkg/environments/gcp/host/linux/host.go new file mode 100644 index 0000000000000..0e479d8a51bdf --- /dev/null +++ b/test/new-e2e/pkg/environments/gcp/host/linux/host.go @@ -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 +} diff --git a/test/new-e2e/pkg/environments/gcp/host/linux/params.go b/test/new-e2e/pkg/environments/gcp/host/linux/params.go new file mode 100644 index 0000000000000..442fd28b889b0 --- /dev/null +++ b/test/new-e2e/pkg/environments/gcp/host/linux/params.go @@ -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 +} diff --git a/test/new-e2e/pkg/runner/local_profile.go b/test/new-e2e/pkg/runner/local_profile.go index 633e0ccf0972c..2cba95a568cd2 100644 --- a/test/new-e2e/pkg/runner/local_profile.go +++ b/test/new-e2e/pkg/runner/local_profile.go @@ -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