Worker: allow configuring executor CloudWatch group
We need the ability to use different CloudWatch group for the osbuild-executor on Fedora workers in staging and production environment. Extend the worker confguration to allow configuring the CloudWatch group name used by the osbuild-executor. Extend the secure instance code to instruct cloud-init via user data to create /tmp/cloud_init_vars file with the CloudWatch group name in the osbuild-executor instance, to make it possible for the executor to configure its logging differently based on the value. Cover new changes by unit tests. Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
parent
ceddabc395
commit
e7743f17ec
7 changed files with 90 additions and 21 deletions
|
|
@ -72,9 +72,10 @@ type pulpConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type executorConfig struct {
|
type executorConfig struct {
|
||||||
Type string `toml:"type"`
|
Type string `toml:"type"`
|
||||||
IAMProfile string `toml:"iam_profile"`
|
IAMProfile string `toml:"iam_profile"`
|
||||||
KeyName string `toml:"key_name"`
|
KeyName string `toml:"key_name"`
|
||||||
|
CloudWatchGroup string `toml:"cloudwatch_group"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type workerConfig struct {
|
type workerConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,21 @@ offline_token = "/etc/osbuild-worker/offline_token"
|
||||||
[pulp]
|
[pulp]
|
||||||
credentials = "/etc/osbuild-worker/pulp-creds"
|
credentials = "/etc/osbuild-worker/pulp-creds"
|
||||||
server_address = "https://example.com/pulp"
|
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{
|
want: &workerConfig{
|
||||||
BasePath: "/api/image-builder-worker/v1",
|
BasePath: "/api/image-builder-worker/v1",
|
||||||
DNFJson: "/usr/libexec/osbuild-depsolve-dnf",
|
DNFJson: "/usr/libexec/osbuild-depsolve-dnf",
|
||||||
OSBuildExecutor: &executorConfig{
|
OSBuildExecutor: &executorConfig{
|
||||||
Type: "host",
|
Type: "aws.ec2",
|
||||||
|
IAMProfile: "osbuild-worker",
|
||||||
|
KeyName: "osbuild-worker",
|
||||||
|
CloudWatchGroup: "osbuild-worker",
|
||||||
},
|
},
|
||||||
Composer: &composerConfig{
|
Composer: &composerConfig{
|
||||||
Proxy: "http://proxy.example.com",
|
Proxy: "http://proxy.example.com",
|
||||||
|
|
|
||||||
|
|
@ -77,9 +77,10 @@ type PulpConfiguration struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecutorConfiguration struct {
|
type ExecutorConfiguration struct {
|
||||||
Type string
|
Type string
|
||||||
IAMProfile string
|
IAMProfile string
|
||||||
KeyName string
|
KeyName string
|
||||||
|
CloudWatchGroup string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OSBuildJobImpl struct {
|
type OSBuildJobImpl struct {
|
||||||
|
|
@ -489,7 +490,7 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
|
||||||
case "host":
|
case "host":
|
||||||
executor = osbuildexecutor.NewHostExecutor()
|
executor = osbuildexecutor.NewHostExecutor()
|
||||||
case "aws.ec2":
|
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:
|
default:
|
||||||
osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, "No osbuild executor defined", nil)
|
osbuildJobResult.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, "No osbuild executor defined", nil)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -474,9 +474,10 @@ func main() {
|
||||||
Store: store,
|
Store: store,
|
||||||
Output: output,
|
Output: output,
|
||||||
OSBuildExecutor: ExecutorConfiguration{
|
OSBuildExecutor: ExecutorConfiguration{
|
||||||
Type: config.OSBuildExecutor.Type,
|
Type: config.OSBuildExecutor.Type,
|
||||||
IAMProfile: config.OSBuildExecutor.IAMProfile,
|
IAMProfile: config.OSBuildExecutor.IAMProfile,
|
||||||
KeyName: config.OSBuildExecutor.KeyName,
|
KeyName: config.OSBuildExecutor.KeyName,
|
||||||
|
CloudWatchGroup: config.OSBuildExecutor.CloudWatchGroup,
|
||||||
},
|
},
|
||||||
KojiServers: kojiServers,
|
KojiServers: kojiServers,
|
||||||
GCPConfig: gcpConfig,
|
GCPConfig: gcpConfig,
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,27 @@ type SecureInstance struct {
|
||||||
Instance *ec2.Instance
|
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:
|
write_files:
|
||||||
- path: /tmp/worker-run-executor-service
|
- path: /tmp/worker-run-executor-service
|
||||||
content: ''
|
content: ''
|
||||||
`
|
%s`, additionalFiles)
|
||||||
|
}
|
||||||
|
|
||||||
// Runs an instance with a security group that only allows traffic to
|
// Runs an instance with a security group that only allows traffic to
|
||||||
// the host. Will replace resources if they already exists.
|
// 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()
|
identity, err := a.ec2metadata.GetInstanceIdentityDocument()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("Error getting the identity document, %s", err)
|
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
|
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 != "" {
|
if ltID != "" {
|
||||||
secureInstance.LTID = 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)
|
ltName := fmt.Sprintf("launch-template-for-%s-runner-instance", hostInstanceID)
|
||||||
descrLTOutput, err := a.ec2.DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{
|
descrLTOutput, err := a.ec2.DescribeLaunchTemplates(&ec2.DescribeLaunchTemplatesInput{
|
||||||
LaunchTemplateNames: []*string{
|
LaunchTemplateNames: []*string{
|
||||||
|
|
@ -322,7 +334,7 @@ func (a *AWS) createOrReplaceLT(hostInstanceID, imageID, sgID, instanceType, iam
|
||||||
SecurityGroupIds: []*string{
|
SecurityGroupIds: []*string{
|
||||||
aws.String(sgID),
|
aws.String(sgID),
|
||||||
},
|
},
|
||||||
UserData: aws.String(base64.StdEncoding.EncodeToString([]byte(UserData))),
|
UserData: aws.String(base64.StdEncoding.EncodeToString([]byte(SecureInstanceUserData(CloudWatchGroup)))),
|
||||||
},
|
},
|
||||||
TagSpecifications: []*ec2.TagSpecification{
|
TagSpecifications: []*ec2.TagSpecification{
|
||||||
&ec2.TagSpecification{
|
&ec2.TagSpecification{
|
||||||
|
|
|
||||||
43
internal/cloud/awscloud/secure-instance_test.go
Normal file
43
internal/cloud/awscloud/secure-instance_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type awsEC2Executor struct {
|
type awsEC2Executor struct {
|
||||||
iamProfile string
|
iamProfile string
|
||||||
keyName string
|
keyName string
|
||||||
|
cloudWatchGroup string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec2e *awsEC2Executor) RunOSBuild(manifest []byte, store, outputDirectory string, exports, exportPaths, checkpoints,
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
si, err := aws.RunSecureInstance(ec2e.iamProfile, ec2e.keyName)
|
si, err := aws.RunSecureInstance(ec2e.iamProfile, ec2e.keyName, ec2e.cloudWatchGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -92,9 +93,10 @@ func (ec2e *awsEC2Executor) RunOSBuild(manifest []byte, store, outputDirectory s
|
||||||
return &osbuildResult, nil
|
return &osbuildResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAWSEC2Executor(iamProfile, keyName string) Executor {
|
func NewAWSEC2Executor(iamProfile, keyName, cloudWatchGroup string) Executor {
|
||||||
return &awsEC2Executor{
|
return &awsEC2Executor{
|
||||||
iamProfile,
|
iamProfile,
|
||||||
keyName,
|
keyName,
|
||||||
|
cloudWatchGroup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue