From 4005d5e3ca2a1d7daee84d036ef800c3e812cee6 Mon Sep 17 00:00:00 2001 From: Dimi Kot Date: Wed, 20 Mar 2024 17:33:45 -0700 Subject: [PATCH] Add TZ support; simplify scaling --- docs/classes/CiStorage.md | 28 +++---- docs/interfaces/CiStorageProps.md | 34 +++++--- src/CiStorage.ts | 75 ++++++++++++------ src/__tests__/CiStorage.test.ts | 19 ++++- .../__snapshots__/CiStorage.test.ts.snap | 78 +++++++++++++------ src/internal/cloudConfigBuild.ts | 24 +++++- 6 files changed, 179 insertions(+), 79 deletions(-) diff --git a/docs/classes/CiStorage.md b/docs/classes/CiStorage.md index 6a77aae..2c3b3ce 100644 --- a/docs/classes/CiStorage.md +++ b/docs/classes/CiStorage.md @@ -53,7 +53,7 @@ Construct.constructor #### Defined in -[src/CiStorage.ts:161](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L161) +[src/CiStorage.ts:184](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L184) ## Properties @@ -63,7 +63,7 @@ Construct.constructor #### Defined in -[src/CiStorage.ts:150](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L150) +[src/CiStorage.ts:173](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L173) ___ @@ -73,7 +73,7 @@ ___ #### Defined in -[src/CiStorage.ts:151](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L151) +[src/CiStorage.ts:174](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L174) ___ @@ -83,7 +83,7 @@ ___ #### Defined in -[src/CiStorage.ts:152](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L152) +[src/CiStorage.ts:175](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L175) ___ @@ -93,7 +93,7 @@ ___ #### Defined in -[src/CiStorage.ts:153](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L153) +[src/CiStorage.ts:176](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L176) ___ @@ -110,7 +110,7 @@ ___ #### Defined in -[src/CiStorage.ts:154](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L154) +[src/CiStorage.ts:177](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L177) ___ @@ -120,7 +120,7 @@ ___ #### Defined in -[src/CiStorage.ts:155](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L155) +[src/CiStorage.ts:178](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L178) ___ @@ -130,7 +130,7 @@ ___ #### Defined in -[src/CiStorage.ts:156](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L156) +[src/CiStorage.ts:179](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L179) ___ @@ -140,7 +140,7 @@ ___ #### Defined in -[src/CiStorage.ts:157](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L157) +[src/CiStorage.ts:180](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L180) ___ @@ -150,7 +150,7 @@ ___ #### Defined in -[src/CiStorage.ts:158](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L158) +[src/CiStorage.ts:181](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L181) ___ @@ -160,7 +160,7 @@ ___ #### Defined in -[src/CiStorage.ts:159](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L159) +[src/CiStorage.ts:182](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L182) ___ @@ -170,7 +170,7 @@ ___ #### Defined in -[src/CiStorage.ts:162](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L162) +[src/CiStorage.ts:185](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L185) ___ @@ -180,7 +180,7 @@ ___ #### Defined in -[src/CiStorage.ts:163](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L163) +[src/CiStorage.ts:186](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L186) ___ @@ -190,4 +190,4 @@ ___ #### Defined in -[src/CiStorage.ts:164](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L164) +[src/CiStorage.ts:187](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L187) diff --git a/docs/interfaces/CiStorageProps.md b/docs/interfaces/CiStorageProps.md index 4cd1f1f..16a5e3e 100644 --- a/docs/interfaces/CiStorageProps.md +++ b/docs/interfaces/CiStorageProps.md @@ -17,7 +17,7 @@ VPC to use by this construct. #### Defined in -[src/CiStorage.ts:52](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L52) +[src/CiStorage.ts:56](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L56) ___ @@ -30,7 +30,7 @@ instances. #### Defined in -[src/CiStorage.ts:55](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L55) +[src/CiStorage.ts:59](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L59) ___ @@ -42,7 +42,7 @@ Id of the Security Group to set for the created instances. #### Defined in -[src/CiStorage.ts:57](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L57) +[src/CiStorage.ts:61](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L61) ___ @@ -61,7 +61,7 @@ A Hosted Zone to register the host instances in. #### Defined in -[src/CiStorage.ts:59](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L59) +[src/CiStorage.ts:63](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L63) ___ @@ -74,7 +74,19 @@ must pre-exist. #### Defined in -[src/CiStorage.ts:67](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L67) +[src/CiStorage.ts:71](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L71) + +___ + +### timeZone + +• `Optional` **timeZone**: `string` + +Time zone for instances, example: America/Los_Angeles. + +#### Defined in + +[src/CiStorage.ts:73](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L73) ___ @@ -93,16 +105,18 @@ Configuration for self-hosted runner instances in the pool. | `imageSsmName` | `string` | SSM parameter name which holds the reference to an instance image. | | `volumeGb` | `number` | Size of the root volume. | | `instanceRequirements` | [`InstanceRequirementsProperty`, ...InstanceRequirementsProperty[]] | The list of requirements to choose Spot Instances. | -| `scale` | \{ `onDemandPercentageAboveBaseCapacity`: `number` ; `maxActiveRunnersPercent`: `number` ; `minIdleRunnersCount`: `number` ; `maxCapacity`: `number` ; `maxInstanceLifetime`: `Duration` } | Scaling options. | +| `scale` | \{ `onDemandPercentageAboveBaseCapacity`: `number` ; `maxActiveRunnersPercent`: \{ `periodSec`: `number` ; `value`: `number` } ; `minCapacity`: \{ `id`: `string` ; `value`: `number` ; `cron`: \{ `timeZone?`: `string` } & `CronOptions` }[] ; `maxCapacity`: `number` ; `maxInstanceLifetime`: `Duration` } | Scaling options. | | `scale.onDemandPercentageAboveBaseCapacity` | `number` | The percentages of On-Demand Instances and Spot Instances for your additional capacity. | -| `scale.maxActiveRunnersPercent` | `number` | Maximum percentage of active runners. If the number of active runners grows beyond this threshold, the autoscaling group will launch new instances until the percentage drops. | -| `scale.minIdleRunnersCount` | `number` | Minimal number of idle runners to keep. If the auto scaling group has less than this number of idle runners, the new instances will be created. | +| `scale.maxActiveRunnersPercent` | \{ `periodSec`: `number` ; `value`: `number` } | Maximum percentage of active runners. If the MAX metric of number of active runners within the recent periodSec interval grows beyond this threshold, the autoscaling group will launch new instances until the percentage drops, or maxCapacity is reached. | +| `scale.maxActiveRunnersPercent.periodSec` | `number` | Calculate MAX metric within that period. The higher is the value, the slower will the capacity lower (but it doesn't affect how fast will it increase). | +| `scale.maxActiveRunnersPercent.value` | `number` | Value to use for the target percentage of active (busy) runners. | +| `scale.minCapacity` | \{ `id`: `string` ; `value`: `number` ; `cron`: \{ `timeZone?`: `string` } & `CronOptions` }[] | Minimal number of idle runners to keep, depending on the daytime. If the auto scaling group has less than this number of instances, the new instances will be created. | | `scale.maxCapacity` | `number` | Maximum total number of instances. | | `scale.maxInstanceLifetime` | `Duration` | Re-create instances time to time. | #### Defined in -[src/CiStorage.ts:69](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L69) +[src/CiStorage.ts:75](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L75) ___ @@ -128,4 +142,4 @@ runner has its localhost ports redirected to that instance. #### Defined in -[src/CiStorage.ts:107](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L107) +[src/CiStorage.ts:130](https://github.com/clickup/ci-storage-cdk/blob/master/src/CiStorage.ts#L130) diff --git a/src/CiStorage.ts b/src/CiStorage.ts index 633dd01..393ea3e 100644 --- a/src/CiStorage.ts +++ b/src/CiStorage.ts @@ -1,9 +1,13 @@ import { ArnFormat, Duration, Stack, Tags } from "aws-cdk-lib"; -import type { CfnAutoScalingGroup } from "aws-cdk-lib/aws-autoscaling"; +import type { + CfnAutoScalingGroup, + CronOptions, +} from "aws-cdk-lib/aws-autoscaling"; import { AutoScalingGroup, GroupMetrics, OnDemandAllocationStrategy, + Schedule, SpotAllocationStrategy, UpdatePolicy, } from "aws-cdk-lib/aws-autoscaling"; @@ -65,6 +69,8 @@ export interface CiStorageProps { /** A name of secret in Secrets Manager which holds GitHub PAT. This secret * must pre-exist. */ ghTokenSecretName: string; + /** Time zone for instances, example: America/Los_Angeles. */ + timeZone?: string; /** Configuration for self-hosted runner instances in the pool. */ runner: { /** "{owner}/{repository}" which this self-hosted runners pool serves. */ @@ -87,14 +93,31 @@ export interface CiStorageProps { /** The percentages of On-Demand Instances and Spot Instances for your * additional capacity. */ onDemandPercentageAboveBaseCapacity: number; - /** Maximum percentage of active runners. If the number of active runners - * grows beyond this threshold, the autoscaling group will launch new - * instances until the percentage drops. */ - maxActiveRunnersPercent: number; - /** Minimal number of idle runners to keep. If the auto scaling group has - * less than this number of idle runners, the new instances will be - * created. */ - minIdleRunnersCount: number; + /** Maximum percentage of active runners. If the MAX metric of number of + * active runners within the recent periodSec interval grows beyond this + * threshold, the autoscaling group will launch new instances until the + * percentage drops, or maxCapacity is reached. */ + maxActiveRunnersPercent: { + /** Calculate MAX metric within that period. The higher is the value, + * the slower will the capacity lower (but it doesn't affect how fast + * will it increase). */ + periodSec: number; + /** Value to use for the target percentage of active (busy) runners. */ + value: number; + }; + /** Minimal number of idle runners to keep, depending on the daytime. If + * the auto scaling group has less than this number of instances, the new + * instances will be created. */ + minCapacity: Array<{ + /** Alpha-numeric id of this schedule. */ + id: string; + /** Value to assign to minCapacity when reaching the schedule time. Note + * that it doesn't apply retrospectively, i.e. there is no processing of + * past-due schedules in AWS. */ + value: number; + /** Schedule info. Time zone example: America/Los_Angeles. */ + cron: { timeZone?: string } & CronOptions; + }>; /** Maximum total number of instances. */ maxCapacity: number; /** Re-create instances time to time. */ @@ -257,6 +280,8 @@ export class CiStorage extends Construct { ghDockerComposeDirectoryUrl: props.runner.ghDockerComposeDirectoryUrl, keyPairPrivateKeySecretName: this.keyPairPrivateKeySecretName, + timeZone: props.timeZone, + mount: undefined, }), ), ); @@ -291,7 +316,6 @@ export class CiStorage extends Construct { this.autoScalingGroup = new AutoScalingGroup(this, id.pascal, { autoScalingGroupName: keyNamer.pathKebabFrom(scope), vpc: this.vpc, - minCapacity: 1, // props.runner.scale.minIdleRunnersCount, maxCapacity: props.runner.scale.maxCapacity, maxInstanceLifetime: props.runner.scale.maxInstanceLifetime, mixedInstancesPolicy: { @@ -314,27 +338,29 @@ export class CiStorage extends Construct { groupMetrics: [GroupMetrics.all()], updatePolicy: UpdatePolicy.rollingUpdate(), }); - const namespace = "ci-storage/metrics"; + Tags.of(this.autoScalingGroup).add( + "Name", + namer(keyNamer, "runner").kebab, + ); this.autoScalingGroup.scaleToTrackMetric("ActiveRunnersPercent", { metric: new Metric({ - namespace, + namespace: "ci-storage/metrics", metricName: "ActiveRunnersPercent", dimensionsMap: { GH_REPOSITORY: props.runner.ghRepository }, - period: Duration.seconds(10), - statistic: "max", - }), - targetValue: props.runner.scale.maxActiveRunnersPercent, - }); - this.autoScalingGroup.scaleToTrackMetric("IdleRunnersCountInverse", { - metric: new Metric({ - namespace, - metricName: "IdleRunnersCountInverse", - dimensionsMap: { GH_REPOSITORY: props.runner.ghRepository }, - period: Duration.seconds(10), + period: Duration.seconds( + props.runner.scale.maxActiveRunnersPercent.periodSec, + ), statistic: "max", }), - targetValue: 1000000 - props.runner.scale.minIdleRunnersCount, + targetValue: props.runner.scale.maxActiveRunnersPercent.value, }); + for (const { id, value, cron } of props.runner.scale.minCapacity) { + this.autoScalingGroup.scaleOnSchedule(id, { + minCapacity: value, + timeZone: cron.timeZone ?? props.timeZone, + schedule: Schedule.cron(cron), + }); + } } { @@ -394,6 +420,7 @@ export class CiStorage extends Construct { ghDockerComposeDirectoryUrl: props.host.ghDockerComposeDirectoryUrl, keyPairPrivateKeySecretName: this.keyPairPrivateKeySecretName, + timeZone: props.timeZone, mount: { volumeId: volume.attrVolumeId, path: "/mnt" }, }), ), diff --git a/src/__tests__/CiStorage.test.ts b/src/__tests__/CiStorage.test.ts index 79edda7..9b83038 100644 --- a/src/__tests__/CiStorage.test.ts +++ b/src/__tests__/CiStorage.test.ts @@ -29,6 +29,7 @@ class CiStorageStack extends Stack { zoneName: "test-zoneName", }, ghTokenSecretName: "ci-storage/gh-token", + timeZone: "America/Los_Angeles", runner: { ghRepository: "time-loop/slapdash", ghDockerComposeDirectoryUrl: @@ -43,8 +44,22 @@ class CiStorageStack extends Stack { ], scale: { onDemandPercentageAboveBaseCapacity: 10, - maxActiveRunnersPercent: 80, - minIdleRunnersCount: 5, + maxActiveRunnersPercent: { + periodSec: 600, + value: 70, + }, + minCapacity: [ + { + id: "CaWorkDayStarts", + value: 10, + cron: { hour: "8" }, + }, + { + id: "CaWorkDayEnds", + value: 5, + cron: { hour: "18" }, + }, + ], maxCapacity: 20, maxInstanceLifetime: Duration.days(1), }, diff --git a/src/__tests__/__snapshots__/CiStorage.test.ts.snap b/src/__tests__/__snapshots__/CiStorage.test.ts.snap index 47a734f..b7060dd 100644 --- a/src/__tests__/__snapshots__/CiStorage.test.ts.snap +++ b/src/__tests__/__snapshots__/CiStorage.test.ts.snap @@ -62,6 +62,13 @@ exports[`CiStorage 1`] = ` ], }, }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "ci-storage-runner", + }, + ], "VPCZoneIdentifier": [ { "Ref": "VpcPrivateSubnet1Subnet536B997A", @@ -106,33 +113,32 @@ exports[`CiStorage 1`] = ` "Namespace": "ci-storage/metrics", "Statistic": "Maximum", }, - "TargetValue": 80, + "TargetValue": 70, }, }, "Type": "AWS::AutoScaling::ScalingPolicy", }, - "CiStorageAutoScalingGroupScalingPolicyIdleRunnersCountInverseC0CB6D01": { + "CiStorageAutoScalingGroupScheduledActionCaWorkDayEnds58BA31B2": { "Properties": { "AutoScalingGroupName": { "Ref": "CiStorageAutoScalingGroupASGFCC25A25", }, - "PolicyType": "TargetTrackingScaling", - "TargetTrackingConfiguration": { - "CustomizedMetricSpecification": { - "Dimensions": [ - { - "Name": "GH_REPOSITORY", - "Value": "time-loop/slapdash", - }, - ], - "MetricName": "IdleRunnersCountInverse", - "Namespace": "ci-storage/metrics", - "Statistic": "Maximum", - }, - "TargetValue": 999995, + "MinSize": 5, + "Recurrence": "* 18 * * *", + "TimeZone": "America/Los_Angeles", + }, + "Type": "AWS::AutoScaling::ScheduledAction", + }, + "CiStorageAutoScalingGroupScheduledActionCaWorkDayStartsAAC7A1B8": { + "Properties": { + "AutoScalingGroupName": { + "Ref": "CiStorageAutoScalingGroupASGFCC25A25", }, + "MinSize": 10, + "Recurrence": "* 8 * * *", + "TimeZone": "America/Los_Angeles", }, - "Type": "AWS::AutoScaling::ScalingPolicy", + "Type": "AWS::AutoScaling::ScheduledAction", }, "CiStorageHost001A90FC20D0": { "Properties": { @@ -141,7 +147,7 @@ exports[`CiStorage 1`] = ` "ResourceRecords": [ { "Fn::GetAtt": [ - "CiStorageHost001Instance74416F6Bf26988f5da1e697f", + "CiStorageHost001Instance74416F6B4260e81d2d555257", "PrivateIp", ], }, @@ -151,7 +157,7 @@ exports[`CiStorage 1`] = ` }, "Type": "AWS::Route53::RecordSet", }, - "CiStorageHost001Instance74416F6Bf26988f5da1e697f": { + "CiStorageHost001Instance74416F6B4260e81d2d555257": { "DependsOn": [ "CiStorageHostRole9FF47CEB", ], @@ -215,6 +221,7 @@ exports[`CiStorage 1`] = ` "", [ "#cloud-config +timezone: America/Los_Angeles fqdn: ci-storage-host-001.test-zoneName apt_sources: - source: deb https://cli.github.com/packages stable main @@ -236,6 +243,7 @@ packages: - curl - apt-transport-https - ca-certificates + - tzdata write_files: - path: /etc/sysctl.d/enable-ipv4-forwarding.conf content: | @@ -244,6 +252,13 @@ write_files: content: | vm.vfs_cache_pressure=0 vm.swappiness=10 + - path: /var/lib/cloud/scripts/per-once/define-tz-env.sh + permissions: "0755" + content: | + #!/bin/bash + set -e -o pipefail && echo --- && echo "Running $BASH_SOURCE as $(whoami)" && set -o xtrace + + echo 'TZ="America/Los_Angeles"' >> /etc/environment - path: /var/lib/cloud/scripts/per-once/increase-docker-shutdown-timeout.sh permissions: "0755" content: | @@ -258,10 +273,12 @@ write_files: #!/bin/bash set -e -o pipefail && echo --- && echo "Running $BASH_SOURCE as $(whoami)" && set -o xtrace - sed -i -E \\ - '/ExecStart=/i Environment="ENV=/etc/profile.ssm-user"' \\ - /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service echo '[ "$0$@" = "sh" ] && ENV= sudo -u ubuntu -i' > /etc/profile.ssm-user + mkdir -p /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/ + ( + echo '[Service]' + echo 'Environment="ENV=/etc/profile.ssm-user"' + ) > /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/sh-env.conf systemctl daemon-reload systemctl restart snap.amazon-ssm-agent.amazon-ssm-agent.service || true - path: /var/lib/cloud/scripts/per-once/detach-volume-from-old-instance-and-mount.sh @@ -750,6 +767,7 @@ write_files: "", [ "#cloud-config +timezone: America/Los_Angeles apt_sources: - source: deb https://cli.github.com/packages stable main keyid: 23F3D4EA75716059 @@ -770,6 +788,7 @@ packages: - curl - apt-transport-https - ca-certificates + - tzdata write_files: - path: /etc/sysctl.d/enable-ipv4-forwarding.conf content: | @@ -778,6 +797,13 @@ write_files: content: | vm.vfs_cache_pressure=0 vm.swappiness=10 + - path: /var/lib/cloud/scripts/per-once/define-tz-env.sh + permissions: "0755" + content: | + #!/bin/bash + set -e -o pipefail && echo --- && echo "Running $BASH_SOURCE as $(whoami)" && set -o xtrace + + echo 'TZ="America/Los_Angeles"' >> /etc/environment - path: /var/lib/cloud/scripts/per-once/increase-docker-shutdown-timeout.sh permissions: "0755" content: | @@ -792,10 +818,12 @@ write_files: #!/bin/bash set -e -o pipefail && echo --- && echo "Running $BASH_SOURCE as $(whoami)" && set -o xtrace - sed -i -E \\ - '/ExecStart=/i Environment="ENV=/etc/profile.ssm-user"' \\ - /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service echo '[ "$0$@" = "sh" ] && ENV= sudo -u ubuntu -i' > /etc/profile.ssm-user + mkdir -p /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/ + ( + echo '[Service]' + echo 'Environment="ENV=/etc/profile.ssm-user"' + ) > /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/sh-env.conf systemctl daemon-reload systemctl restart snap.amazon-ssm-agent.amazon-ssm-agent.service || true - path: /var/lib/cloud/scripts/per-boot/run-docker-compose-on-boot.sh diff --git a/src/internal/cloudConfigBuild.ts b/src/internal/cloudConfigBuild.ts index 57345c8..a0012a5 100644 --- a/src/internal/cloudConfigBuild.ts +++ b/src/internal/cloudConfigBuild.ts @@ -10,13 +10,15 @@ export function cloudConfigBuild({ ghTokenSecretName, ghDockerComposeDirectoryUrl, keyPairPrivateKeySecretName, + timeZone, mount, }: { fqdn: string; ghTokenSecretName: string; ghDockerComposeDirectoryUrl: string; keyPairPrivateKeySecretName: string; - mount?: { volumeId: string; path: string }; + timeZone: string | undefined; + mount: { volumeId: string; path: string } | undefined; }) { if (!ghDockerComposeDirectoryUrl.match(/^([^#]+)(?:#([^:]*):(.*))?$/s)) { throw ( @@ -32,6 +34,7 @@ export function cloudConfigBuild({ 'set -e -o pipefail && echo --- && echo "Running $BASH_SOURCE as $(whoami)" && set -o xtrace'; return { + timezone: timeZone, fqdn: fqdn || undefined, apt_sources: [ { @@ -58,6 +61,7 @@ export function cloudConfigBuild({ "curl", "apt-transport-https", "ca-certificates", + "tzdata", ], write_files: compact([ { @@ -73,6 +77,16 @@ export function cloudConfigBuild({ vm.swappiness=10 `), }, + timeZone && { + path: "/var/lib/cloud/scripts/per-once/define-tz-env.sh", + permissions: "0755", + content: dedent(` + #!/bin/bash + ${preamble} + + echo 'TZ="${timeZone}"' >> /etc/environment + `), + }, { path: "/var/lib/cloud/scripts/per-once/increase-docker-shutdown-timeout.sh", permissions: "0755", @@ -91,10 +105,12 @@ export function cloudConfigBuild({ #!/bin/bash ${preamble} - sed -i -E \\ - '/ExecStart=/i Environment="ENV=/etc/profile.ssm-user"' \\ - /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service echo '[ "$0$@" = "sh" ] && ENV= sudo -u ubuntu -i' > /etc/profile.ssm-user + mkdir -p /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/ + ( + echo '[Service]' + echo 'Environment="ENV=/etc/profile.ssm-user"' + ) > /etc/systemd/system/snap.amazon-ssm-agent.amazon-ssm-agent.service.d/sh-env.conf systemctl daemon-reload systemctl restart snap.amazon-ssm-agent.amazon-ssm-agent.service || true `),