Skip to content

feat: aws package, so builder and signet_node are isolated to k8s resources #4

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

Open
wants to merge 1 commit into
base: swanny/utils-are-dry
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions pkg/aws/iam.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package aws

import (
"encoding/json"
"fmt"

"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/iam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

// IAMResources contains AWS IAM resources created for a component
type IAMResources struct {
Role *iam.Role
Policy *iam.Policy
PolicyAttachment *iam.RolePolicyAttachment
}

// CreateIAMResources creates IAM resources (role, policy, and policy attachment) for a component
Copy link
Preview

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function CreateIAMResources lacks a top-level doc comment. Please add a comment describing its purpose, parameters, and return values to improve API discoverability.

Suggested change
// CreateIAMResources creates IAM resources (role, policy, and policy attachment) for a component
// CreateIAMResources creates AWS IAM resources (role, policy, and policy attachment) for a component.
//
// Parameters:
// - ctx: The Pulumi context used for resource creation.
// - name: A unique name for the IAM resources.
// - serviceName: The name of the service that will use the IAM role.
// - keyArn: The ARN of the KMS key to include in the policy.
// - parent: The parent Pulumi resource for organizational purposes.
//
// Returns:
// - *IAMResources: A struct containing the created IAM role, policy, and policy attachment.
// - error: An error if any of the resource creation steps fail.

Copilot uses AI. Check for mistakes.

func CreateIAMResources(
ctx *pulumi.Context,
name string,
serviceName string,
keyArn pulumi.StringInput,
parent pulumi.Resource,
) (*IAMResources, error) {
// Create IAM role
assumeRolePolicy := IAMPolicy{
Version: "2012-10-17",
Statement: []IAMStatement{
{
Sid: "AllowEksAuthToAssumeRoleForPodIdentity",
Effect: "Allow",
Principal: struct {
Service []string `json:"Service"`
}{
Service: []string{
"pods.eks.amazonaws.com",
"ec2.amazonaws.com",
},
},
Action: []string{
"sts:AssumeRole",
"sts:TagSession",
},
},
},
}

assumeRolePolicyJSON, err := json.Marshal(assumeRolePolicy)
if err != nil {
return nil, fmt.Errorf("failed to marshal assume role policy: %w", err)
}

role, err := iam.NewRole(ctx, fmt.Sprintf("%s-role", name), &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(assumeRolePolicyJSON),
Description: pulumi.String(fmt.Sprintf("Role for %s pod to assume", serviceName)),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-role", name)),
},
}, pulumi.Parent(parent))
if err != nil {
return nil, fmt.Errorf("failed to create IAM role: %w", err)
}

// Create KMS policy
policyJSON := CreateKMSPolicy(keyArn)

policy, err := iam.NewPolicy(ctx, fmt.Sprintf("%s-policy", name), &iam.PolicyArgs{
Policy: policyJSON,
}, pulumi.Parent(parent))
if err != nil {
return nil, fmt.Errorf("failed to create IAM policy: %w", err)
}

// Attach policy to role
policyAttachment, err := iam.NewRolePolicyAttachment(ctx, fmt.Sprintf("%s-role-policy-attachment", name), &iam.RolePolicyAttachmentArgs{
Role: role.Name,
PolicyArn: policy.Arn,
}, pulumi.Parent(parent))
if err != nil {
return nil, fmt.Errorf("failed to attach policy to role: %w", err)
}

return &IAMResources{
Role: role,
Policy: policy,
PolicyAttachment: policyAttachment,
}, nil
}

// CreateKMSPolicy creates a KMS policy for the given key
func CreateKMSPolicy(key pulumi.StringInput) pulumi.StringOutput {
policy := KMSPolicy{
Version: "2012-10-17",
Statement: []KMSStatement{
{
Effect: "Allow",
Action: []string{
"kms:Sign",
"kms:GetPublicKey",
},
Resource: key,
},
},
}

// Convert to JSON string output
return pulumi.All(key).ApplyT(func(_ []interface{}) (string, error) {
jsonBytes, err := json.Marshal(policy)
if err != nil {
return "", err
}
return string(jsonBytes), nil
}).(pulumi.StringOutput)
}
28 changes: 28 additions & 0 deletions pkg/aws/iam_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package aws

import (
"testing"

"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/stretchr/testify/assert"
)

func TestCreateKMSPolicy(t *testing.T) {
// Test with a simple key ARN
keyArn := "arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
key := pulumi.String(keyArn)

// Get the policy string
// Note: We can't directly access the string value from StringOutput in a unit test
// So we'll just verify the function returns a non-nil value and the structure will
// be tested separately when used in the actual builder component.
policy := CreateKMSPolicy(key)

// We can only indirectly test this by asserting the output is not nil
assert.NotNil(t, policy)

// Test with another key to ensure the function uses the provided key
anotherKey := pulumi.String("another-key-arn")
anotherPolicy := CreateKMSPolicy(anotherKey)
assert.NotNil(t, anotherPolicy)
}
34 changes: 34 additions & 0 deletions pkg/aws/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package aws
Copy link
Preview

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Exported types IAMStatement, IAMPolicy, KMSStatement, and KMSPolicy lack documentation. Consider adding type-level comments to clarify their structure and intended usage.

Copilot uses AI. Check for mistakes.


import (
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

// IAMStatement represents a statement in an IAM policy
type IAMStatement struct {
Sid string `json:"sid,omitempty"`
Effect string `json:"effect"`
Principal struct {
Service []string `json:"Service"`
} `json:"Principal"`
Action []string `json:"Action"`
}

// IAMPolicy represents an IAM policy document
type IAMPolicy struct {
Version string `json:"Version"`
Statement []IAMStatement `json:"Statement"`
}

// KMSStatement represents a statement in a KMS policy
type KMSStatement struct {
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource pulumi.StringInput `json:"Resource"`
}

// KMSPolicy represents a KMS policy document
type KMSPolicy struct {
Version string `json:"Version"`
Statement []KMSStatement `json:"Statement"`
}
64 changes: 1 addition & 63 deletions pkg/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
package builder

import (
"encoding/json"
"fmt"

"github.com/init4tech/signet-infra-components/pkg/utils"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/iam"
crd "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apiextensions"
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
Expand Down Expand Up @@ -40,66 +38,6 @@ func NewBuilder(ctx *pulumi.Context, args BuilderComponentArgs, opts ...pulumi.R
}
component.ServiceAccount = sa

// Create IAM role
assumeRolePolicy := IAMPolicy{
Version: "2012-10-17",
Statement: []IAMStatement{
{
Sid: "AllowEksAuthToAssumeRoleForPodIdentity",
Effect: "Allow",
Principal: struct {
Service []string `json:"Service"`
}{
Service: []string{
"pods.eks.amazonaws.com",
"ec2.amazonaws.com",
},
},
Action: []string{
"sts:AssumeRole",
"sts:TagSession",
},
},
},
}

assumeRolePolicyJSON, err := json.Marshal(assumeRolePolicy)
if err != nil {
return nil, fmt.Errorf("failed to marshal assume role policy: %w", err)
}

role, err := iam.NewRole(ctx, fmt.Sprintf("%s-role", args.Name), &iam.RoleArgs{
AssumeRolePolicy: pulumi.String(assumeRolePolicyJSON),
Description: pulumi.String(fmt.Sprintf("Role for %s pod to assume", args.Name)),
Tags: pulumi.StringMap{
"Name": pulumi.String(fmt.Sprintf("%s-role", args.Name)),
},
}, pulumi.Parent(component))
if err != nil {
return nil, fmt.Errorf("failed to create IAM role: %w", err)
}
component.IAMRole = role

// Create KMS policy
policyJSON := CreateKMSPolicy(args.BuilderEnv.BuilderKey)

policy, err := iam.NewPolicy(ctx, fmt.Sprintf("%s-policy", args.Name), &iam.PolicyArgs{
Policy: policyJSON,
}, pulumi.Parent(component))
if err != nil {
return nil, fmt.Errorf("failed to create IAM policy: %w", err)
}
component.IAMPolicy = policy

// Attach policy to role
_, err = iam.NewRolePolicyAttachment(ctx, fmt.Sprintf("%s-role-policy-attachment", args.Name), &iam.RolePolicyAttachmentArgs{
Role: role.Name,
PolicyArn: policy.Arn,
}, pulumi.Parent(component))
if err != nil {
return nil, fmt.Errorf("failed to attach policy to role: %w", err)
}

// Create ConfigMap for environment variables
configMap, err := utils.CreateConfigMap(
ctx,
Expand Down Expand Up @@ -210,7 +148,7 @@ func NewBuilder(ctx *pulumi.Context, args BuilderComponentArgs, opts ...pulumi.R
},
},
},
}, pulumi.DependsOn([]pulumi.Resource{role, policy}), pulumi.DeleteBeforeReplace(true), pulumi.Parent(component))
}, pulumi.DeleteBeforeReplace(true), pulumi.Parent(component))
if err != nil {
return nil, fmt.Errorf("failed to create deployment: %w", err)
}
Expand Down
33 changes: 0 additions & 33 deletions pkg/builder/helpers.go
Original file line number Diff line number Diff line change
@@ -1,34 +1 @@
package builder

import (
"encoding/json"

"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

// CreateKMSPolicy creates a KMS policy for the builder service.
// Exported for testing.
func CreateKMSPolicy(key pulumi.StringInput) pulumi.StringOutput {
policy := KMSPolicy{
Version: "2012-10-17",
Statement: []KMSStatement{
{
Effect: "Allow",
Action: []string{
"kms:Sign",
"kms:GetPublicKey",
},
Resource: key,
},
},
}

// Convert to JSON string output
return pulumi.All(key).ApplyT(func(_ []interface{}) (string, error) {
jsonBytes, err := json.Marshal(policy)
if err != nil {
return "", err
}
return string(jsonBytes), nil
}).(pulumi.StringOutput)
}
20 changes: 0 additions & 20 deletions pkg/builder/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,6 @@ import (
"github.com/stretchr/testify/assert"
)

func TestCreateKMSPolicy(t *testing.T) {
// Test with a simple key ARN
keyArn := "arn:aws:kms:us-west-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
key := pulumi.String(keyArn)

// Get the policy string
// Note: We can't directly access the string value from StringOutput in a unit test
// So we'll just verify the function returns a non-nil value and the structure will
// be tested separately when used in the actual builder component.
policy := CreateKMSPolicy(key)

// We can only indirectly test this by asserting the output is not nil
assert.NotNil(t, policy)

// Test with another key to ensure the function uses the provided key
anotherKey := pulumi.String("another-key-arn")
anotherPolicy := CreateKMSPolicy(anotherKey)
assert.NotNil(t, anotherPolicy)
}

func TestBuilderEnvGetEnvMap(t *testing.T) {
// Create a test BuilderEnv with some values
env := BuilderEnv{
Expand Down
28 changes: 0 additions & 28 deletions pkg/builder/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package builder

import (
"github.com/init4tech/signet-infra-components/pkg/utils"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/iam"
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
Expand All @@ -28,8 +27,6 @@ type BuilderComponent struct {
Deployment *appsv1.Deployment
Service *corev1.Service
ServiceAccount *corev1.ServiceAccount
IAMRole *iam.Role
IAMPolicy *iam.Policy
ConfigMap *corev1.ConfigMap
}

Expand Down Expand Up @@ -89,31 +86,6 @@ func (e BuilderEnv) GetEnvMap() pulumi.StringMap {
return utils.CreateEnvMap(e)
}

type IAMStatement struct {
Sid string `json:"sid,omitempty"`
Effect string `json:"effect"`
Principal struct {
Service []string `json:"Service"`
} `json:"Principal"`
Action []string `json:"Action"`
}

type IAMPolicy struct {
Version string `json:"Version"`
Statement []IAMStatement `json:"Statement"`
}

type KMSStatement struct {
Effect string `json:"Effect"`
Action []string `json:"Action"`
Resource pulumi.StringInput `json:"Resource"`
}

type KMSPolicy struct {
Version string `json:"Version"`
Statement []KMSStatement `json:"Statement"`
}

type Builder interface {
GetServiceURL() pulumi.StringOutput
GetMetricsURL() pulumi.StringOutput
Expand Down