Skip to content

Commit

Permalink
Pkl pattern with Dynamic
Browse files Browse the repository at this point in the history
  • Loading branch information
ericzbeard committed Mar 14, 2024
1 parent 09db5fa commit b5011e7
Show file tree
Hide file tree
Showing 13 changed files with 764 additions and 287 deletions.
18 changes: 18 additions & 0 deletions internal/aws/cfn/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,21 @@ func patchSESContactList(schema *Schema) error {
dss.Enum = convertStrings(valid)
return nil
}

func patchIAMRole(schema *Schema) error {
policy, found := schema.Definitions["Policy"]
if !found {
return errors.New("expected AWS::IAM::Role to have Policy")
}
policyDocument, found := policy.Properties["PolicyDocument"]
if !found {
return errors.New("expected AWS::IAM::Role to have Policy.PolicyDocument")
}
policyDocument.Type = "object"
arpd, found := schema.Properties["AssumeRolePolicyDocument"]
if !found {
return errors.New("expected AWS::IAM::Role to have AssumeRolePolicyDocument")
}
arpd.Type = "object"
return nil
}
4 changes: 3 additions & 1 deletion internal/aws/cfn/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type SchemaLike interface {
type Prop struct {
Description string `json:"description"`
Items *Prop `json:"items"`
Type string `json:"type"`
Type any `json:"type"`
UniqueItems bool `json:"uniqueItems"`
InsertionOrder bool `json:"insertionOrder"`
Ref string `json:"$ref"`
Expand Down Expand Up @@ -92,6 +92,8 @@ func (schema *Schema) Patch() error {
return patchSESConfigurationSetEventDestination(schema)
case "AWS::SES::ContactList":
return patchSESContactList(schema)
case "AWS::IAM::Role":
return patchIAMRole(schema)

}
return nil
Expand Down
4 changes: 4 additions & 0 deletions internal/cmd/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func buildProp(n *yaml.Node, propName string, prop cfn.Prop, schema cfn.Schema,

isCircular := false

if prop.Type == nil {
prop.Type = ""
}

switch prop.Type {
case "string":
if len(prop.Enum) > 0 {
Expand Down
51 changes: 36 additions & 15 deletions internal/cmd/build/pkl.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package build

import (
"errors"
"fmt"
"slices"
"strings"
Expand Down Expand Up @@ -28,9 +29,10 @@ type pklDefProp struct {

// A class that represents a definition in a registry resource schema
type pklDefClass struct {
Name string
Props []*pklDefProp
Aliases []*stringAlias
Name string
Description string
Props []*pklDefProp
Aliases []*stringAlias
}

func printTypeAlias(alias *stringAlias) {
Expand All @@ -54,6 +56,7 @@ func printCls(cls *pklDefClass) {

fmt.Println()

printDescription(cls.Description)
fmt.Printf("open class %s {\n", cls.Name)

for _, prop := range cls.Props {
Expand All @@ -77,6 +80,9 @@ func createTypeAlias(defName string, propName string, cls *pklDefClass, enum []a
func getPropType(defName string, propName string,
prop *cfn.Prop, cls *pklDefClass, required bool) (string, error) {

if prop.Type == nil {
prop.Type = ""
}
var retval string
switch prop.Type {
case "string":
Expand All @@ -91,13 +97,16 @@ func getPropType(defName string, propName string,
retval = "String|Mapping"
}
case "object":
return "", fmt.Errorf("unexpected object type: %s", propName)
retval = "Dynamic"
case "array":
if prop.Items != nil {
if prop.Items.Ref != "" {
clsName := strings.Replace(prop.Items.Ref, "#/definitions/", "", 1)
retval = fmt.Sprintf("Listing<%s>", clsName)
} else {
if prop.Items.Type == nil {
prop.Items.Type = ""
}
switch prop.Items.Type {
case "string":
if len(prop.Items.Enum) > 0 {
Expand Down Expand Up @@ -135,16 +144,24 @@ func getPropType(defName string, propName string,
}

if retval == "" {
//return "", errors.New("unable to determine type for " + propName)
// TODO
retval = "Todo"
return "", errors.New("unable to determine type for " + propName)
}
if !required {
retval = fmt.Sprintf("(%s)?", retval)
}
return retval, nil
}

func printDescription(description string) {
descTokens := strings.Split(description, "\n")
for i, d := range descTokens {
fmt.Println("///", d)
if i == 0 && len(descTokens) > 1 {
fmt.Println("///")
}
}
}

func generatePklClass(typeName string) error {
schema, err := getSchema(typeName)
if err != nil {
Expand All @@ -157,17 +174,17 @@ func generatePklClass(typeName string) error {
fmt.Println("module", strings.ToLower(strings.Replace(typeName, "::", ".", -1)))
fmt.Println()

// TODO: Needs to be on a URI somewhere public
fmt.Println("import \"../../cloudformation.pkl\"")

classes := make([]*pklDefClass, 0)

// Iterate over definitions, creating a class for each one
for name, def := range schema.Definitions {
cls := &pklDefClass{
Name: name,
Props: make([]*pklDefProp, 0),
Aliases: make([]*stringAlias, 0),
Name: name,
Description: def.Description,
Props: make([]*pklDefProp, 0),
Aliases: make([]*stringAlias, 0),
}
classes = append(classes, cls)

Expand All @@ -185,23 +202,24 @@ func generatePklClass(typeName string) error {

// Print out each of the classes
for _, cls := range classes {

printCls(cls)
}

// Create a class for the type itself
shortName := strings.Split(typeName, "::")[2]
fmt.Println()
printDescription(schema.Description)
fmt.Printf("open class %s extends cloudformation.Resource {\n", shortName)
fmt.Println()
fmt.Printf(" Type = \"%s\"\n", typeName)
fmt.Println()

propNames := make([]string, 0)
cls := &pklDefClass{
Name: shortName,
Props: make([]*pklDefProp, 0),
Aliases: make([]*stringAlias, 0),
Name: shortName,
Description: schema.Description,
Props: make([]*pklDefProp, 0),
Aliases: make([]*stringAlias, 0),
}
requiredProps := schema.GetRequired()
for propName, prop := range schema.Properties {
Expand All @@ -215,6 +233,9 @@ func generatePklClass(typeName string) error {
if err != nil {
return err
}
fmt.Println()

printDescription(prop.Description)
fmt.Printf(" hidden %s: %s\n", propName, propType)
propNames = append(propNames, propName)
}
Expand Down
88 changes: 88 additions & 0 deletions pkl/aws/iam/role.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/// AWS::IAM::Role
///
/// Generated by rain build --pkl-class AWS::IAM::Role
module aws.iam.role

import "../../cloudformation.pkl"

/// Contains information about an attached policy.
///
/// An attached policy is a managed policy that has been attached to a user, group, or role.
/// For more information about managed policies, refer to [Managed Policies and Inline Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/policies-managed-vs-inline.html) in the *User Guide*.
open class Policy {
PolicyName: String|Mapping
PolicyDocument: Dynamic
}

/// A structure that represents user-provided metadata that can be associated with an IAM resource. For more information about tagging, see [Tagging IAM resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) in the *IAM User Guide*.
open class Tag {
Key: String|Mapping
Value: String|Mapping
}

/// Creates a new role for your AWS-account.
///
/// For more information about roles, see [IAM roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) in the *IAM User Guide*. For information about quotas for role names and the number of roles you can create, see [IAM and quotas](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html) in the *IAM User Guide*.
open class Role extends cloudformation.Resource {

Type = "AWS::IAM::Role"


/// The path to the role. For more information about paths, see [IAM Identifiers](https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) in the *IAM User Guide*.
///
/// This parameter is optional. If it is not included, it defaults to a slash (/).
/// This parameter allows (through its [regex pattern](https://docs.aws.amazon.com/http://wikipedia.org/wiki/regex)) a string of characters consisting of either a forward slash (/) by itself or a string that must begin and end with forward slashes. In addition, it can contain any ASCII character from the ! (``\u0021``) through the DEL character (``\u007F``), including most punctuation characters, digits, and upper and lowercased letters.
hidden Path: (String|Mapping)?

/// The ARN of the policy used to set the permissions boundary for the role.
///
/// For more information about permissions boundaries, see [Permissions boundaries for IAM identities](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) in the *IAM User Guide*.
hidden PermissionsBoundary: (String|Mapping)?

/// Adds or updates an inline policy document that is embedded in the specified IAM role.
///
/// When you embed an inline policy in a role, the inline policy is used as part of the role's access (permissions) policy. The role's trust policy is created at the same time as the role. You can update a role's trust policy later. For more information about IAM roles, go to [Using Roles to Delegate Permissions and Federate Identities](https://docs.aws.amazon.com/IAM/latest/UserGuide/roles-toplevel.html).
/// A role can also have an attached managed policy. For information about policies, see [Managed Policies and Inline Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/policies-managed-vs-inline.html) in the *User Guide*.
/// For information about limits on the number of inline policies that you can embed with a role, see [Limitations on Entities](https://docs.aws.amazon.com/IAM/latest/UserGuide/LimitationsOnEntities.html) in the *User Guide*.
/// If an external policy (such as ``AWS::IAM::Policy`` or
hidden Policies: (Listing<Policy>)?

/// A name for the IAM role, up to 64 characters in length. For valid values, see the ``RoleName`` parameter for the [CreateRole](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateRole.html) action in the *User Guide*.
///
/// This parameter allows (per its [regex pattern](https://docs.aws.amazon.com/http://wikipedia.org/wiki/regex)) a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@-. The role name must be unique within the account. Role names are not distinguished by case. For example, you cannot create roles named both "Role1" and "role1".
/// If you don't specify a name, CFN generates a unique physical ID and uses that ID for the role name.
/// If you specify a name, you must specify the ``CAPABILITY_NAMED_IAM`` value to acknowledge your template's capabilities. For more information, see [Acknowledging Resources in Templates](https://docs.aws.amazon.com/AWSCloudFormation/latest/Use
hidden RoleName: (String|Mapping)?

/// The trust policy that is associated with this role. Trust policies define which entities can assume the role. You can associate only one trust policy with a role. For an example of a policy that can be used to assume a role, see [Template Examples](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#aws-resource-iam-role--examples). For more information about the elements that you can use in an IAM policy, see [Policy Elements Reference](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html) in the *User Guide*.
hidden AssumeRolePolicyDocument: Dynamic

/// A description of the role that you provide.
hidden Description: (String|Mapping)?

/// A list of tags that are attached to the role. For more information about tagging, see [Tagging IAM resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) in the *IAM User Guide*.
hidden Tags: (Listing<Tag>)?

/// A list of Amazon Resource Names (ARNs) of the IAM managed policies that you want to attach to the role.
///
/// For more information about ARNs, see [Amazon Resource Names (ARNs) and Service Namespaces](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) in the *General Reference*.
hidden ManagedPolicyArns: (Listing<String|Mapping>)?

/// The maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default value of one hour is applied. This setting can have a value from 1 hour to 12 hours.
///
/// Anyone who assumes the role from the CLI or API can use the ``DurationSeconds`` API parameter or the ``duration-seconds`` CLI parameter to request a longer session. The ``MaxSessionDuration`` setting determines the maximum duration that can be requested using the ``DurationSeconds`` parameter. If users don't specify a value for the ``DurationSeconds`` parameter, their security credentials are valid for one hour by default. This applies when you use the ``AssumeRole*`` API operations or the ``assume-role*`` CLI operations but does not apply when you use those operations to create a console URL. For more information, see [Using IAM roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) in the *IAM User Guide*.
hidden MaxSessionDuration: (Int|Mapping)?

Properties {
["Path"] = if (Path == null) null else Path
["PermissionsBoundary"] = if (PermissionsBoundary == null) null else PermissionsBoundary
["Policies"] = if (Policies == null) null else Policies
["RoleName"] = if (RoleName == null) null else RoleName
["AssumeRolePolicyDocument"] = if (AssumeRolePolicyDocument == null) null else AssumeRolePolicyDocument
["Description"] = if (Description == null) null else Description
["Tags"] = if (Tags == null) null else Tags
["ManagedPolicyArns"] = if (ManagedPolicyArns == null) null else ManagedPolicyArns
["MaxSessionDuration"] = if (MaxSessionDuration == null) null else MaxSessionDuration
}

}
43 changes: 43 additions & 0 deletions pkl/aws/iam/rolepolicy.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// AWS::IAM::RolePolicy
///
/// Generated by rain build --pkl-class AWS::IAM::RolePolicy
module aws.iam.rolepolicy

import "../../cloudformation.pkl"

/// Adds or updates an inline policy document that is embedded in the specified IAM role.
///
/// When you embed an inline policy in a role, the inline policy is used as part of the role's access (permissions) policy. The role's trust policy is created at the same time as the role, using [CreateRole](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateRole.html). You can update a role's trust policy using [UpdateAssumeRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAssumeRolePolicy.html). For information about roles, see [roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/roles-toplevel.html) in the *IAM User Guide*.
/// A role can also have a managed policy attached to it. To attach a managed policy to a role, use [AWS::IAM::Role](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html). To create a new managed policy, use [AWS::IAM::ManagedPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-managedpolicy.html). For information about policies, see [Managed policies and inline policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/policies-managed-vs-inline.html) in the *IAM User Guide*.
/// For information about the maximum number of inline policies that you can embed with a role, see [IAM and quotas](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html) in the *IAM User Guide*.
open class RolePolicy extends cloudformation.Resource {

Type = "AWS::IAM::RolePolicy"


/// The policy document.
///
/// You must provide policies in JSON format in IAM. However, for CFN templates formatted in YAML, you can provide the policy in JSON or YAML format. CFN always converts a YAML policy to JSON format before submitting it to IAM.
/// The [regex pattern](https://docs.aws.amazon.com/http://wikipedia.org/wiki/regex) used to validate this parameter is a string of characters consisting of the following:
/// + Any printable ASCII character ranging from the space character (``\u0020``) through the end of the ASCII character range
/// + The printable characters in the Basic Latin and Latin-1 Supplement character set (through ``\u00FF``)
/// + The special characters tab (``\u0009``), line feed (``\u000A``), and carriage return (``\u000D``)
hidden PolicyDocument: (Dynamic)?

/// The name of the policy document.
///
/// This parameter allows (through its [regex pattern](https://docs.aws.amazon.com/http://wikipedia.org/wiki/regex)) a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@-
hidden PolicyName: String|Mapping

/// The name of the role to associate the policy with.
///
/// This parameter allows (through its [regex pattern](https://docs.aws.amazon.com/http://wikipedia.org/wiki/regex)) a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@-
hidden RoleName: String|Mapping

Properties {
["PolicyDocument"] = if (PolicyDocument == null) null else PolicyDocument
["PolicyName"] = if (PolicyName == null) null else PolicyName
["RoleName"] = if (RoleName == null) null else RoleName
}

}
Loading

0 comments on commit b5011e7

Please sign in to comment.