-
Notifications
You must be signed in to change notification settings - Fork 519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pubsys: write SSM parameters and AMI permissions to output files #2987
pubsys: write SSM parameters and AMI permissions to output files #2987
Conversation
Why not always write the file, the way that
I am also not convinced that everything should go under an AMI key like A flattened map like this seems more intuitive:
|
The goal of this code was to generate files that were easily consumed by the SSM validation and EC2 validation code (#2969 and #2983). I should've probably made that more clear in the description. Doing it this way allows us to create a file for version 1.14.0. and a separate file for latest, which is what we use for the validation. Having the AMI id as a key lets the EC2 validation use the same files to check all of the images in there, without having to parse each parameter to see if it contains an image id. |
I don't think it makes sense for the SSM validation code to be organizing parameters under an AMI ID - the AMI ID is irrelevant there. All the SSM validation should care about is whether the current parameter values match the expected values. For EC2 validation, it kind of does seem like the validator logic should be going through and finding the subset of parameters that refer to an AMI ID. But if you wanted something more robust than a suffix match, you could organize it slightly differently:
For pubsys / makefile integration, having the ability to easily work with all published artifacts for a specific build ID (like For other use cases, like an account-wide |
283a660
to
42a0ec8
Compare
^ Addressed @bcressey's suggestions and added the writing of AMI publicity info and launch permissions to the |
It would be a lot easier to review this if it were split into two commits, one for each feature. Would you mind resubmitting it? |
42a0ec8
to
a6cacad
Compare
^ Split the one commit into two. |
tools/pubsys/src/aws/ami/mod.rs
Outdated
@@ -372,20 +418,63 @@ async fn _run(args: &Args, ami_args: &AmiArgs) -> Result<HashMap<String, Image>> | |||
Ok(amis) | |||
} | |||
|
|||
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] | |||
pub(crate) struct LaunchPermissionDef { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it should be an enum, something like:
#[derive(Serialize, Deserialize, etc)]
#[serde(rename_all = "lowercase")]
enum LaunchPermissionDef {
Group(String),
UserId(String),
OrganizationArn(String),
OrganizationalUnitArn(String),
}
I'm pretty sure this still serializes to JSON the way you want.
tools/pubsys/src/aws/ami/mod.rs
Outdated
fn from(launch_permission: LaunchPermission) -> Self { | ||
Self { | ||
group: launch_permission | ||
.group() | ||
.map(|group| group.as_str().to_owned()), | ||
user_id: launch_permission | ||
.user_id() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Supposing you do make this an enum, you could rewrite this pretty succinctly with a match:
let LaunchPermission { group, user_id, organization_arn, organizational_unit_arn } = launch_permission;
match (group, user_id, organization_arn, organizational_unit_arn) {
(Some(group), None, None, None) => LaunchPermissionDef::Group(group),
(None, Some(user_id), None, None) => LaunchPermissionDef::UserId(user_id),
(None, None, Some(organization_arn), None) => LaunchPermissionDef::OrganizationArn(organization_arn),
(None, None, None, Some(organizational_unit_arn)) => LaunchPermissionDef::OrganizationalUnitArn(organizational_unit_arn),
_ => // error!
};
You would have to change this from impl From
to impl TryFrom
to handle the error case of all of the elements being None
though.
|
||
fn write_amis(ami_output: &PathBuf, amis: &HashMap<Region, Image>) -> Result<()> { | ||
let file = File::create(ami_output).context(error::FileSnafu { | ||
op: "write amis to file", | ||
path: ami_output, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should probably also be used in pubsys ami
command, right?
@@ -216,8 +231,86 @@ pub(crate) async fn run(args: &Args, promote_args: &PromoteArgs) -> Result<()> { | |||
Ok(()) | |||
} | |||
|
|||
/// Read parameters in given file, add newly promoted parameters, and write combined parameters to | |||
/// the given file | |||
async fn write_rendered_parameters( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think append_rendered_parameters
may be more telling, since write
often carries a connotation of overwrite
"Writing promoted SSM parameters to {}", | ||
ssm_parameters_output.display() | ||
); | ||
let parsed_parameters = parse_expected_parameters(&ssm_parameters_output.to_owned()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be parse_existing_parameters
or just parse_parameters
? Seems like the name is a holdover from parsing it in the validation code, but we need the method more generically.
let parsed_parameters = parse_expected_parameters(&ssm_parameters_output.to_owned()) | |
let parsed_parameters = parse_parameter_file(&ssm_parameters_output.to_owned()) |
let combined_parameters: HashMap<Region, HashMap<SsmKey, String>> = | ||
combine_parameters(parsed_parameters, set_parameters, source_target_map); | ||
|
||
serde_json::to_writer_pretty( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a function somewhere? We write out this parameter data structure in more than one place now.
/// Return a HashMap of Region mapped to a HashMap of SsmKey, String pairs, representing the newly | ||
/// promoted parameters as well as the original parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the behavior is correct, but it's worth calling out that any parameter collisions take the promoted parameter value.
That case probably deserves a test too.
tools/pubsys/src/aws/ssm/mod.rs
Outdated
@@ -213,6 +223,31 @@ pub(crate) async fn run(args: &Args, ssm_args: &SsmArgs) -> Result<()> { | |||
Ok(()) | |||
} | |||
|
|||
/// Write rendered parameters to the file at `ssm_parameters_output` | |||
async fn write_rendered_parameters( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you do have one! Just need to share it.
.context(error::ParseExistingSsmParametersSnafu { | ||
path: ssm_parameters_output, | ||
})?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I correct that this will fail if the file is empty or doesn't exist? Is it better to just write the new parameters in that case, rather than refusing to proceed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct, but I don't know if we just want to write the new parameters. One reason it could fail is that the syntax in the file is incorrect, in which case I think it's better to alert the user and halt the program than completely overwrite the content of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We chatted offline and concluded that we could specially handle Error::ReadExpectedParameterFile
.
d6b95a2
to
d29cefa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me other than the casing.
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] | ||
pub(crate) enum LaunchPermissionDef { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more coherent with our other serialization code if you lowercased these:
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] | |
pub(crate) enum LaunchPermissionDef { | |
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] | |
#[serde(rename_all = "lowercase")] | |
pub(crate) enum LaunchPermissionDef { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make more sense to use snake_case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I git grep
for rename_all
in Bottlerocket, it seems like we typically use kebab-case
or lowercase
organization_arn, | ||
organizational_unit_arn, | ||
.. | ||
} = launch_permission.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am surprised that you had to clone this.
.context(error::ParseExistingSsmParametersSnafu { | ||
path: ssm_parameters_output, | ||
})?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We chatted offline and concluded that we could specially handle Error::ReadExpectedParameterFile
.
d29cefa
to
ec72838
Compare
Description of changes:
Modified the
pubsys ssm
andpubsys promote-ssm
commands to allow the parameters to be written to a file. Modified thepubsys ami
andpubsys publish-ami
commands to have them write AMI launch permissions and publicity info to the output file.pubsys ssm
has one new argument:ssm_parameter_output
. If set, the rendered parameters will be written to the file at this path with the following structure:pubsys promote-ssm
has one new argument:ssm_parameter_output
. The newly promoted parameters will be combined with the parameters already present in the file and written to it.pubsys ami
now writespublic
andlaunch_permissions
to the output file along withid
andname
. Thepublic
field is set tofalse
by default when registering the AMI, but if the AMI ID has already been registered before, then the field is set to the publicity of that AMI. Thelaunch_permissions
field gets an empty vec by default, but if the AMI is already registered, then the launch permissions for that AMI are retrieved and stored in that field.pubsys publish-ami
updates thepublic
andlaunch_permissions
fields in the input file if anything has changed after the publishing has finished.Testing done:
cargo make
,cargo make ami
,cargo make ssm
with the new argument, and got a file like this:The AMI file looked like this:
cargo make promote-ssm
with the above file as input andkeep_original_parameters
false, and got a file like this:cargo make ami-public
and theamis.json
file looked like this afterwards:Terms of contribution:
By submitting this pull request, I agree that this contribution is dual-licensed under the terms of both the Apache License, version 2.0, and the MIT license.