diff --git a/cmd/osbuild-worker/config.go b/cmd/osbuild-worker/config.go index 075f7309e..9834436e9 100644 --- a/cmd/osbuild-worker/config.go +++ b/cmd/osbuild-worker/config.go @@ -72,9 +72,10 @@ type pulpConfig struct { } type executorConfig struct { - Type string `toml:"type"` - IAMProfile string `toml:"iam_profile"` - KeyName string `toml:"key_name"` + Type string `toml:"type"` + IAMProfile string `toml:"iam_profile"` + KeyName string `toml:"key_name"` + CloudWatchGroup string `toml:"cloudwatch_group"` } type workerConfig struct { diff --git a/cmd/osbuild-worker/config_test.go b/cmd/osbuild-worker/config_test.go index 2f726a1f9..46aa7f899 100644 --- a/cmd/osbuild-worker/config_test.go +++ b/cmd/osbuild-worker/config_test.go @@ -69,12 +69,21 @@ offline_token = "/etc/osbuild-worker/offline_token" [pulp] credentials = "/etc/osbuild-worker/pulp-creds" server_address = "https://example.com/pulp" + +[osbuild_executor] +type = "aws.ec2" +iam_profile = "osbuild-worker" +key_name = "osbuild-worker" +cloudwatch_group = "osbuild-worker" `, want: &workerConfig{ BasePath: "/api/image-builder-worker/v1", DNFJson: "/usr/libexec/osbuild-depsolve-dnf", OSBuildExecutor: &executorConfig{ - Type: "host", + Type: "aws.ec2", + IAMProfile: "osbuild-worker", + KeyName: "osbuild-worker", + CloudWatchGroup: "osbuild-worker", }, Composer: &composerConfig{ Proxy: "http://proxy.example.com", diff --git a/cmd/osbuild-worker/jobimpl-osbuild.go b/cmd/osbuild-worker/jobimpl-osbuild.go index f993afc32..b8207dd74 100644 --- a/cmd/osbuild-worker/jobimpl-osbuild.go +++ b/cmd/osbuild-worker/jobimpl-osbuild.go @@ -77,9 +77,10 @@ type PulpConfiguration struct { } type ExecutorConfiguration struct { - Type string - IAMProfile string - KeyName string + Type string + IAMProfile string + KeyName string + CloudWatchGroup string } type OSBuildJobImpl struct { @@ -489,7 +490,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error { case "host": executor = osbuildexecutor.NewHostExecutor() case "aws.ec2": - executor = osbuildexecutor.NewAWSEC2Executor(impl.OSBuildExecutor.IAMProfile, impl.OSBuildExecutor.KeyName) + executor = osbuildexecutor.NewAWSEC2Executor(impl.OSBuildExecutor.IAMProfile, impl.OSBuildExecutor.KeyName, impl.OSBuildExecutor.CloudWatchGroup) default: osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, "No osbuild executor defined", nil) return err diff --git a/cmd/osbuild-worker/main.go b/cmd/osbuild-worker/main.go index 4ebcfcd80..27dd700f3 100644 --- a/cmd/osbuild-worker/main.go +++ b/cmd/osbuild-worker/main.go @@ -474,9 +474,10 @@ func main() { Store: store, Output: output, OSBuildExecutor: ExecutorConfiguration{ - Type: config.OSBuildExecutor.Type, - IAMProfile: config.OSBuildExecutor.IAMProfile, - KeyName: config.OSBuildExecutor.KeyName, + Type: config.OSBuildExecutor.Type, + IAMProfile: config.OSBuildExecutor.IAMProfile, + KeyName: config.OSBuildExecutor.KeyName, + CloudWatchGroup: config.OSBuildExecutor.CloudWatchGroup, }, KojiServers: kojiServers, GCPConfig: gcpConfig, diff --git a/internal/cloud/awscloud/secure-instance.go b/internal/cloud/awscloud/secure-instance.go index d0a61fc3b..c7365f101 100644 --- a/internal/cloud/awscloud/secure-instance.go +++ b/internal/cloud/awscloud/secure-instance.go @@ -18,15 +18,27 @@ type SecureInstance struct { Instance *ec2.Instance } -const UserData = `#cloud-config +// SecureInstanceUserData returns the cloud-init user data for a secure instance. +func SecureInstanceUserData(CloudWatchGroup string) string { + additionalFiles := "" + + if CloudWatchGroup != "" { + additionalFiles += fmt.Sprintf(` - path: /tmp/cloud_init_vars + content: | + OSBUILD_EXECUTOR_CLOUDWATCH_GROUP='%s' +`, CloudWatchGroup) + } + + return fmt.Sprintf(`#cloud-config write_files: - path: /tmp/worker-run-executor-service content: '' -` +%s`, additionalFiles) +} // Runs an instance with a security group that only allows traffic to // the host. Will replace resources if they already exists. -func (a *AWS) RunSecureInstance(iamProfile, keyName string) (*SecureInstance, error) { +func (a *AWS) RunSecureInstance(iamProfile, keyName, CloudWatchGroup string) (*SecureInstance, error) { identity, err := a.ec2metadata.GetInstanceIdentityDocument() if err != nil { logrus.Errorf("Error getting the identity document, %s", err) @@ -67,7 +79,7 @@ func (a *AWS) RunSecureInstance(iamProfile, keyName string) (*SecureInstance, er return nil, err } - ltID, err := a.createOrReplaceLT(identity.InstanceID, imageID, sgID, instanceType, iamProfile, keyName) + ltID, err := a.createOrReplaceLT(identity.InstanceID, imageID, sgID, instanceType, iamProfile, keyName, CloudWatchGroup) if ltID != "" { secureInstance.LTID = ltID } @@ -284,7 +296,7 @@ func isLaunchTemplateNotFoundError(err error) bool { } -func (a *AWS) createOrReplaceLT(hostInstanceID, imageID, sgID, instanceType, iamProfile, keyName string) (string, error) { +func (a *AWS) createOrReplaceLT(hostInstanceID, imageID, sgID, instanceType, iamProfile, keyName, CloudWatchGroup string) (string, error) { ltName := fmt.Sprintf("launch-template-for-%s-runner-instance", hostInstanceID) descrLTOutput, err := a.ec2.DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{ LaunchTemplateNames: []*string{ @@ -322,7 +334,7 @@ func (a *AWS) createOrReplaceLT(hostInstanceID, imageID, sgID, instanceType, iam SecurityGroupIds: []*string{ aws.String(sgID), }, - UserData: aws.String(base64.StdEncoding.EncodeToString([]byte(UserData))), + UserData: aws.String(base64.StdEncoding.EncodeToString([]byte(SecureInstanceUserData(CloudWatchGroup)))), }, TagSpecifications: []*ec2.TagSpecification{ &ec2.TagSpecification{ diff --git a/internal/cloud/awscloud/secure-instance_test.go b/internal/cloud/awscloud/secure-instance_test.go new file mode 100644 index 000000000..0a3c9548b --- /dev/null +++ b/internal/cloud/awscloud/secure-instance_test.go @@ -0,0 +1,43 @@ +package awscloud + +import ( + "fmt" + "testing" +) + +func TestSecureInstanceUserData(t *testing.T) { + type testCase struct { + CloudWatchGroup string + ExpectedUserData string + } + + testCases := []testCase{ + { + ExpectedUserData: `#cloud-config +write_files: + - path: /tmp/worker-run-executor-service + content: '' +`, + }, + { + CloudWatchGroup: "test-group", + ExpectedUserData: `#cloud-config +write_files: + - path: /tmp/worker-run-executor-service + content: '' + - path: /tmp/cloud_init_vars + content: | + OSBUILD_EXECUTOR_CLOUDWATCH_GROUP='test-group' +`, + }, + } + + for idx, tc := range testCases { + t.Run(fmt.Sprintf("Test case %d", idx), func(t *testing.T) { + userData := SecureInstanceUserData(tc.CloudWatchGroup) + if userData != tc.ExpectedUserData { + t.Errorf("Expected: %s, got: %s", tc.ExpectedUserData, userData) + } + }) + } +} diff --git a/internal/osbuildexecutor/runner-impl-aws-ec2.go b/internal/osbuildexecutor/runner-impl-aws-ec2.go index d38efaa98..026a64c00 100644 --- a/internal/osbuildexecutor/runner-impl-aws-ec2.go +++ b/internal/osbuildexecutor/runner-impl-aws-ec2.go @@ -13,8 +13,9 @@ import ( ) type awsEC2Executor struct { - iamProfile string - keyName string + iamProfile string + keyName string + cloudWatchGroup string } func (ec2e *awsEC2Executor) RunOSBuild(manifest []byte, store, outputDirectory string, exports, exportPaths, checkpoints, @@ -29,7 +30,7 @@ func (ec2e *awsEC2Executor) RunOSBuild(manifest []byte, store, outputDirectory s return nil, err } - si, err := aws.RunSecureInstance(ec2e.iamProfile, ec2e.keyName) + si, err := aws.RunSecureInstance(ec2e.iamProfile, ec2e.keyName, ec2e.cloudWatchGroup) if err != nil { return nil, err } @@ -92,9 +93,10 @@ func (ec2e *awsEC2Executor) RunOSBuild(manifest []byte, store, outputDirectory s return &osbuildResult, nil } -func NewAWSEC2Executor(iamProfile, keyName string) Executor { +func NewAWSEC2Executor(iamProfile, keyName, cloudWatchGroup string) Executor { return &awsEC2Executor{ iamProfile, keyName, + cloudWatchGroup, } }