Skip to content

Commit

Permalink
feat(ec2): Implement UserData methods in MultipartUserData (aws#14347)
Browse files Browse the repository at this point in the history
Implements: aws#14346

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
ddneilson authored and hollanddd committed Aug 26, 2021
1 parent 9202b04 commit 91b1e6f
Show file tree
Hide file tree
Showing 3 changed files with 345 additions and 14 deletions.
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,23 @@ new ec2.LaunchTemplate(stack, '', {
For more information see
[Specifying Multiple User Data Blocks Using a MIME Multi Part Archive](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/bootstrap_container_instance.html#multi-part_user_data)

#### Using add*Command on MultipartUserData

To use the `add*Command` methods, that are inherited from the `UserData` interface, on `MultipartUserData` you must add a part
to the `MultipartUserData` and designate it as the reciever for these methods. This is accomplished by using the `addUserDataPart()`
method on `MultipartUserData` with the `makeDefault` argument set to `true`:

```ts
const multipartUserData = new ec2.MultipartUserData();
const commandsUserData = ec2.UserData.forLinux();
multipartUserData.addUserDataPart(commandsUserData, MultipartBody.SHELL_SCRIPT, true);

// Adding commands to the multipartUserData adds them to commandsUserData, and vice-versa.
multipartUserData.addCommands('touch /root/multi.txt');
commandsUserData.addCommands('touch /root/userdata.txt');
```

When used on an EC2 instance, the above `multipartUserData` will create both `multi.txt` and `userdata.txt` in `/root`.

## Importing existing subnet

Expand Down
62 changes: 48 additions & 14 deletions packages/@aws-cdk/aws-ec2/lib/user-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,13 +436,15 @@ export interface MultipartUserDataOptions {
*
*/
export class MultipartUserData extends UserData {
private static readonly USE_PART_ERROR = 'MultipartUserData does not support this operation. Please add part using addPart.';
private static readonly USE_PART_ERROR = 'MultipartUserData only supports this operation if it has a default UserData. Call addUserDataPart with makeDefault=true.';
private static readonly BOUNDRY_PATTERN = '[^a-zA-Z0-9()+,-./:=?]';

private parts: MultipartBody[] = [];

private opts: MultipartUserDataOptions;

private defaultUserData?: UserData;

constructor(opts?: MultipartUserDataOptions) {
super();

Expand Down Expand Up @@ -472,16 +474,28 @@ export class MultipartUserData extends UserData {
}

/**
* Adds a multipart part based on a UserData object
* Adds a multipart part based on a UserData object.
*
* If `makeDefault` is true, then the UserData added by this method
* will also be the target of calls to the `add*Command` methods on
* this MultipartUserData object.
*
* This is the same as calling:
* If `makeDefault` is false, then this is the same as calling:
*
* ```ts
* multiPart.addPart(MultipartBody.fromUserData(userData, contentType));
* ```
*
* An undefined `makeDefault` defaults to either:
* - `true` if no default UserData has been set yet; or
* - `false` if there is no default UserData set.
*/
public addUserDataPart(userData: UserData, contentType?: string) {
public addUserDataPart(userData: UserData, contentType?: string, makeDefault?: boolean) {
this.addPart(MultipartBody.fromUserData(userData, contentType));
makeDefault = makeDefault ?? (this.defaultUserData === undefined ? true : false);
if (makeDefault) {
this.defaultUserData = userData;
}
}

public render(): string {
Expand Down Expand Up @@ -510,23 +524,43 @@ export class MultipartUserData extends UserData {
return resultArchive.join('\n');
}

public addS3DownloadCommand(_params: S3DownloadOptions): string {
throw new Error(MultipartUserData.USE_PART_ERROR);
public addS3DownloadCommand(params: S3DownloadOptions): string {
if (this.defaultUserData) {
return this.defaultUserData.addS3DownloadCommand(params);
} else {
throw new Error(MultipartUserData.USE_PART_ERROR);
}
}

public addExecuteFileCommand(_params: ExecuteFileOptions): void {
throw new Error(MultipartUserData.USE_PART_ERROR);
public addExecuteFileCommand(params: ExecuteFileOptions): void {
if (this.defaultUserData) {
this.defaultUserData.addExecuteFileCommand(params);
} else {
throw new Error(MultipartUserData.USE_PART_ERROR);
}
}

public addSignalOnExitCommand(_resource: Resource): void {
throw new Error(MultipartUserData.USE_PART_ERROR);
public addSignalOnExitCommand(resource: Resource): void {
if (this.defaultUserData) {
this.defaultUserData.addSignalOnExitCommand(resource);
} else {
throw new Error(MultipartUserData.USE_PART_ERROR);
}
}

public addCommands(..._commands: string[]): void {
throw new Error(MultipartUserData.USE_PART_ERROR);
public addCommands(...commands: string[]): void {
if (this.defaultUserData) {
this.defaultUserData.addCommands(...commands);
} else {
throw new Error(MultipartUserData.USE_PART_ERROR);
}
}

public addOnExitCommands(..._commands: string[]): void {
throw new Error(MultipartUserData.USE_PART_ERROR);
public addOnExitCommands(...commands: string[]): void {
if (this.defaultUserData) {
this.defaultUserData.addOnExitCommands(...commands);
} else {
throw new Error(MultipartUserData.USE_PART_ERROR);
}
}
}
Loading

0 comments on commit 91b1e6f

Please sign in to comment.