<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jackson Bowe</title>
    <description>The latest articles on Forem by Jackson Bowe (@jacksonb).</description>
    <link>https://forem.com/jacksonb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1024063%2F0479ac49-4016-43db-8584-6c75bd573959.png</url>
      <title>Forem: Jackson Bowe</title>
      <link>https://forem.com/jacksonb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jacksonb"/>
    <language>en</language>
    <item>
      <title>Mafia 1: User Authentication</title>
      <dc:creator>Jackson Bowe</dc:creator>
      <pubDate>Fri, 21 Apr 2023 05:02:31 +0000</pubDate>
      <link>https://forem.com/jacksonb/mafia-1-user-authentication-2m5d</link>
      <guid>https://forem.com/jacksonb/mafia-1-user-authentication-2m5d</guid>
      <description>&lt;p&gt;This is the second post in a series of articles documenting my process creating a serverless multiplayer game - Mafia.&lt;/p&gt;

&lt;p&gt;This article covers adding User Authentication and Authorization to the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Setup AWS&lt;/li&gt;
&lt;li&gt;Infrastructure overview&lt;/li&gt;
&lt;li&gt;User authentication &lt;/li&gt;
&lt;li&gt;User authorization&lt;/li&gt;
&lt;li&gt;Quasar frontend&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setting up AWS for a new project
&lt;/h2&gt;

&lt;p&gt;If you search online for AWS project best practices you will find a dozen different guidelines all espousing their method to be the "best". I'd like to avoid pushing my methodology on you here, but this is a quick rundown of the process that I used.&lt;/p&gt;

&lt;p&gt;My main AWS account (the one that I first created) is an empty shell containing no resources. In this account I created an AWS Organization. Under an organization, you can create groups - I've made a group for Mafia. Inside the Mafia group I make two more groups; &lt;strong&gt;dev&lt;/strong&gt; and &lt;strong&gt;prod&lt;/strong&gt;. These two groups each have an AWS account in them. &lt;/p&gt;

&lt;p&gt;In my root AWS account I then configured IAM Identity Center for Single-Sign-On (SSO) and created an SSO profile for myself with Admin Access to the two Mafia accounts. This new SSO account can now generate AWS CLI access keys for each of the deployments (&lt;strong&gt;prod&lt;/strong&gt;/&lt;strong&gt;dev&lt;/strong&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure Overview
&lt;/h2&gt;

&lt;p&gt;In this game users will have individual profiles. Logging in retrieves the associated profile from the database and tags all future API calls with the users' ID. The API will then use the provided ID to determine if the caller has permissions to perform whichever action they're attempting.&lt;/p&gt;

&lt;p&gt;AWS has all the tools to create a robust serverless user authentication system and SST makes it super easy to get started. For this project I'm going to be using the following AWS services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Cognito - to handle user authentication (validate passwords)&lt;/li&gt;
&lt;li&gt;AWS ApiGateway - to route frontend actions to backend functions&lt;/li&gt;
&lt;li&gt;AWS Lambda - the logic&lt;/li&gt;
&lt;li&gt;AWS DynamoDB - to store user profiles&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are two main 'routes' to the API for authentication; &lt;strong&gt;/noauth&lt;/strong&gt; and &lt;strong&gt;/{other}&lt;/strong&gt;. The &lt;strong&gt;/noauth&lt;/strong&gt; route is exclusively used for signup/signin/recovery/etc actions (may be expanded in future). &lt;br&gt;
The &lt;strong&gt;/{other}&lt;/strong&gt; route is simple a placeholder for all other routes. Each of these calls are processed through a custom Lambda Authorizer which validates the users access token and then forwards to the AuthController Lambda.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F-cjBIgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6t3mfhyyjuou5lldvmz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-cjBIgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6t3mfhyyjuou5lldvmz.png" alt="Resource flow for User Authentication" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  DynamoDB setup
&lt;/h3&gt;

&lt;p&gt;DynamoDB is a NoSQL database which offers huge horizontal scaling for a relatively low price point, following a pay-per-request model. There's plenty of ways to use DynamoDB but for this project I'm attempting to follow Single Table Design (STD??) principles as best I can. &lt;/p&gt;

&lt;p&gt;The goal of STD is one big boy DynamoDB table that holds all entity data in one place. The benefits of this approach is speed (apparently?) and increasing the complexity of your business logic by a few magnitudes. I'll try until it gets to hard I guess.&lt;/p&gt;

&lt;p&gt;With the single table approach it's important to have a good grasp on what the access patterns are going to be. That is, in what ways do I intend to query data. Theoretically, by knowing how you intend to &lt;em&gt;query&lt;/em&gt; the data you can infer the optimal way to &lt;em&gt;store&lt;/em&gt; it. If you are unfamiliar with DynamoDB this next part might be a bit confusing.&lt;/p&gt;

&lt;p&gt;Currently my access patterns are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[GET] Item by exact ID (PK + SK) - To retrieve a single record: User/Lobby/Game/etc&lt;/li&gt;
&lt;li&gt;[QUERY] Items by partition ID - To retrieve all records under a partition key: eg. A Lobby and all Users in it&lt;/li&gt;
&lt;li&gt;[QUERY] Items by type (GSI1) - To retrieve all records of a type Users/Lobby/Game&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GET calls return exactly one record whereas QUERY's return all records that match. &lt;/p&gt;

&lt;p&gt;In NoSQL Workbench I made a mock up of the data structure to visualize my GSI's.  &lt;/p&gt;

&lt;p&gt;Primary projection: The red marking shows that Lobby(PK=2143) contains records for itself (SK=A) and the user UncleGenghi (SK=U#1234). Note that I'm encoding the entity type into the SK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--35ee_BwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r07fgb723hu1mkmvg0fo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--35ee_BwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r07fgb723hu1mkmvg0fo.png" alt="DDB Primary Projection" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GSI1 (itemsByType): Projects all records into their TYPE grouping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtMoHXOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5pw96elk8v88y3dhmcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtMoHXOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5pw96elk8v88y3dhmcz.png" alt="GSI1: itemsByType" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  User Authentication
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, I am using AWS Cognito to handle user registration and validation. The Cognito functions are exposed to the frontend via HTTPS API using ApiGateway. Calls such as &lt;strong&gt;/noauth/auth&lt;/strong&gt; pass directly to the AuthController, in this example returning a JWT access token (providing supplied email and password are valid). The JWT token must then be used in the headers of all future requests. &lt;/p&gt;

&lt;h2&gt;
  
  
  User Authorization
&lt;/h2&gt;

&lt;p&gt;I decided to use a custom Lambda Authorizer for this project with these two resources for reference: &lt;a href="https://aws.amazon.com/blogs/security/building-fine-grained-authorization-using-amazon-cognito-api-gateway-and-iam/"&gt;AWS Article&lt;/a&gt;, &lt;a href="https://github.com/aws-samples/amazon-cognito-api-gateway/blob/main/custom-auth/lambda.py"&gt;Code&lt;/a&gt;. &lt;br&gt;
Custom Lambda Authorizers are great for adding fine grain authorization but come with some noticeable performance reductions and cost increases. &lt;/p&gt;

&lt;p&gt;For starters, in terms of AWS resources a Lambda Authorizer is identical to a standard lambda function. This means that all API Gateway routes that flow through a Lambda Authorizer now count as 2 lambda invocations. Two invocations means an increase in time and cost. The more complex the authorizer, the slower every API route that uses it gets.&lt;/p&gt;

&lt;p&gt;Below is an example of an API call that returns a list of all open lobbies. As we can see, before the GET /lobby/list call we must first be authorized. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSqFfpux--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hk6o0mv6f1zwonavf0k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZSqFfpux--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hk6o0mv6f1zwonavf0k3.png" alt="Example of Custom Lambda Authorizer" width="732" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, while the authorizer definitely adds overhead to the API I believe that the ability to have granular access is crucial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quasar frontend
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://quasar.dev/"&gt;Quasar framework&lt;/a&gt; is my go-to when building complex web apps. It uses VueJS under the hood but comes with so many useful utilities that it's hard for me to justify not using it.&lt;/p&gt;

&lt;p&gt;I think that both the best and worst thing about Quasar is how easy it is to create visually appealing and responsive frontends. I say "and worst" because to be honest, using Quasar feels like cheating. Prior to using Quasar I had very little frontend experience with vanilla Javascript and CSS, essentially zero foundational knowledge. Quasar lets me get away with not learning the basics and still succeed. Still not sure how I feel about that, but anyway...&lt;/p&gt;

&lt;p&gt;This is my attempt at a login page. It's probably nothing fancy to any experienced WebDev's reading, but it's pretty up there for me. I linked it up to the backend and it works great. I may remove the self signup feature temporarily in place of an invite-only system so I can do some controlled public testing, but that's unimportant to this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s4w2rKzx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xt11qjx3ru7vi46c885r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s4w2rKzx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xt11qjx3ru7vi46c885r.png" alt="Mafia login page" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To ensure that my design is appropriately responsive I'm using an app called &lt;a href="https://responsively.app/"&gt;ResponsivelyApp&lt;/a&gt;. The app lets me select several target screen sizes and validate my design on all of them in the same screen. Check it out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IDqi3wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36cq27klgjuv4hkbfdod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IDqi3wac--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36cq27klgjuv4hkbfdod.png" alt="Mafia in ResponsivelyApp" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That pretty much wraps up everything I wanted to cover in this article. In my estimation the main educational aspect of this article series is going to be the AWS design and integration. Frontend design/development is very much a necessary chore to me while API and cloud infrastructure design it where the real interesting stuff happens.&lt;/p&gt;

&lt;p&gt;At the time of writing this I've made significant progress towards a lobby hosting/joining system. It's not quite at the stage where I can confidently write about it, but what I can say is that it leverages AWS IoT Core to facilitate real-time push notifications to the frontend.&lt;/p&gt;

&lt;p&gt;If you're interested in this project I've made a discord (&lt;a href="https://discord.gg/EcGx9h2fwy"&gt;https://discord.gg/EcGx9h2fwy&lt;/a&gt;) where I post updates as I go. Happy to answer any questions whether technical or game related.&lt;/p&gt;

&lt;p&gt;Cheers,&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Mafia: A Serverless Multiplayer Game</title>
      <dc:creator>Jackson Bowe</dc:creator>
      <pubDate>Mon, 27 Feb 2023 01:19:56 +0000</pubDate>
      <link>https://forem.com/jacksonb/mafia-a-serverless-multiplayer-game-5f24</link>
      <guid>https://forem.com/jacksonb/mafia-a-serverless-multiplayer-game-5f24</guid>
      <description>&lt;p&gt;This is the first post (of hopefully many) where I will be documenting my journey creating a serverless multiplayer game; Mafia (name not final... unless???).&lt;/p&gt;

&lt;p&gt;I'm going to use this first post to cover the tools I intend to use and give a general idea of what this game should look like at the end. So here we go...&lt;/p&gt;

&lt;h3&gt;
  
  
  What is this game?
&lt;/h3&gt;

&lt;p&gt;I'm basing this game heavily on the popular table top game by the same name. It's a &lt;a href="https://en.wikipedia.org/wiki/Social_deduction_game#:~:text=A%20social%20deduction%20game%20is,and%20another%20being%20%22bad%22."&gt;social deduction game&lt;/a&gt; in which a majority team, the Townies, try to root out the opposing minority team, the Mafia. &lt;/p&gt;

&lt;p&gt;Members of the Mafia are aware of their allies and must work together to mislead the Town and avoid suspicion. Town players are not aware of anyone else's role and must use their deductive reasoning to locate evildoers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mechanics
&lt;/h3&gt;

&lt;p&gt;The game isn't strictly turn based however it follows a similar principal. There are four states; Morning, Day, Evening, and Night. &lt;/p&gt;

&lt;p&gt;During the Day all members of the town (inclusive of the mafia members) gather to discuss what leads they have found on the Mafia. At this time they may also decide to remove a player under suspicion of being a member of the Mafia.&lt;/p&gt;

&lt;p&gt;At Evening, the public forum closes and players are given time to process they day's conversation, and if possible with their role they can chose a player to target that night. For example, a Detective may chose to follow another player and see who they visit that night. Mafia members are able to converse amongst themselves during this time.&lt;/p&gt;

&lt;p&gt;During the Night the game processes all the player actions.&lt;/p&gt;

&lt;p&gt;In the Morning, the events of the previous Night are revealed to the Town.&lt;/p&gt;

&lt;p&gt;The cycle repeats until there is a winner.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enough with the nerd stuff, tell me about the tech stack!
&lt;/h2&gt;

&lt;p&gt;Heavy sarcasm :)&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools I'm using to make this
&lt;/h3&gt;

&lt;p&gt;I want the game to be playable in the web browser at this stage, may change in future. Also primarily using AWS for the backend.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Backend&lt;/em&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/api-gateway/"&gt;AWS ApiGateway&lt;/a&gt; for player to backend communication&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/dynamodb/"&gt;AWS DynamoDB&lt;/a&gt; as a database following &lt;a href="https://aws.amazon.com/blogs/compute/creating-a-single-table-design-with-amazon-dynamodb/"&gt;Single Table Design&lt;/a&gt; principles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/cognito/"&gt;AWS Cognito&lt;/a&gt; for user authentication&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/s3/"&gt;AWS S3&lt;/a&gt; for medium/large file storage (images etc.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/iot/"&gt;AWS IoT Core&lt;/a&gt; for the pub/sub functionality (live updates)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt; to test and document the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All application logic will be written in Python.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Frontend&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The frontend will be made with &lt;a href="https://quasar.dev/"&gt;Quasar Framework&lt;/a&gt; - a Vue.js wrapper. Quasar comes bundles with a wide array of prebuilt components and very thorough documentation. An additional benefit is that Quasar streamlines the process of packaging a single codebase into multiple deployment formats; web, mobile, and desktop. This sparks joy.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;The glue&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;As I previously stated my goal with this project is 100% serverless, however there is another requirement that is just as important; 100% Infrastructure as Code. IaC is exactly as it reads, all infrastructure covered in the &lt;em&gt;Backend&lt;/em&gt; section should be provisioned in code, with nothing needing to be created in AWS manually.&lt;/p&gt;

&lt;p&gt;There are several benefits to this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Changes to Infrastructure can be tracked with Git. Manual editing via the AWS console is un-trackable and generates no history.&lt;/li&gt;
&lt;li&gt;The project can be deployed with a single command. If I deploy in Australia, and then have reason to deploy in North America, I must be able to do so with confidence and speed.&lt;/li&gt;
&lt;li&gt;The project can be destroyed with a single command. This is extremely important. If I determine that the cost becomes unsustainable I must be able to completely eradicate all traces of it with no remnants remaining. Scorched earth.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The IaC framework I will be using for this project is &lt;a href="https://serverless-stack.com/"&gt;Serverless Stack (SST)&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;SST is a cloud architecture development kit that lets you provision Infrastructure as Code by leveraging the base &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html"&gt;AWS CDK&lt;/a&gt; with improvements geared towards serverless applications. If you've used any IaC providers in the past like Terraform, AWS CDK, Serverless, SAM etc. - I seriously recommend checking out SST. It's next level.&lt;/p&gt;




&lt;h2&gt;
  
  
  Process, MVP, and Timeline
&lt;/h2&gt;

&lt;p&gt;This project is my way of diving head first into serverless cloud development. I'm using something I'm passionate about to motivate and upskill myself into a field that I think is the future. For this reason I will be attempting to follow as many best practices as I can, and sharing them here as I adopt them.&lt;/p&gt;

&lt;p&gt;Below is a flow chart with my rough architecture plan. This will no doubt change as the project matures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gIlEDkvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/420qylj26hfojjmyzgtv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gIlEDkvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/420qylj26hfojjmyzgtv.png" alt="Architecture Diagram" width="880" height="704"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;p&gt;User actions are sent via REST API to an AWS Api Gateway. This distributes requests to the appropriate lambda microservice, called &lt;em&gt;Controllers&lt;/em&gt;. The Controllers handle all application logic between frontend and backend services such as the database. &lt;/p&gt;

&lt;p&gt;As there is no server I am not able to maintain continuous state. All user actions must be written to the database. During the Night sequence, all user actions will be read from the database and combined with data about the game (settings etc.).&lt;/p&gt;

&lt;p&gt;The combined user actions and game settings will be resolved using my custom &lt;em&gt;MafiaEngine&lt;/em&gt;. This will return the resolved game state to be communicated back to the users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimum Viable Product
&lt;/h3&gt;

&lt;p&gt;The maximum number of players in a game is 15. A typical game will have 9 Town players, 3 Mafia players, and 3 Neutrals. A fully featured game will have large role diversity between the players, however as players can share the same role I will be aiming for the following.&lt;/p&gt;

&lt;p&gt;Town: Citizen, Doctor, Bodyguard, Detective, Escort&lt;br&gt;&lt;br&gt;
Mafia: Mafioso, Liaison (evil Escort)&lt;br&gt;&lt;br&gt;
Neutral: Serial Killer, Survivor, Jester (of course)&lt;/p&gt;

&lt;p&gt;The game will need a fully functioning lobby system, stage transitioning (morning/night/etc.), and state resolution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline
&lt;/h3&gt;

&lt;p&gt;I began routine work in early January 2023 and have maintained decent weekly progress. I hope to have a playable version of this game out within 2 months (end April). Between now and then I will aim for regular updates and cover technical roadblocks and solutions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I don't particularly want this series to be a generic game "devlog" thing. The real motivation behind this project was to get familiar with serverless cloud development and I think it's going to be a massive learning opportunity. &lt;/p&gt;

&lt;p&gt;What I'm more interested in writing about it showing how I've implemented some serverless feature and provide a legitimate use case that it is fulfilling. &lt;/p&gt;

&lt;p&gt;If this project sounds interesting I'd love to hear from you, especially if you see me running headlong into a classic pitfall. &lt;/p&gt;

&lt;p&gt;Note: I'm keeping somewhat-up-to-date technical documentation for the project at &lt;a href="https://mafia-sdg.netlify.app/"&gt;https://mafia-sdg.netlify.app/&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Cheers,&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Provisioning Lambda Docker Images with AWS CDK (Python)</title>
      <dc:creator>Jackson Bowe</dc:creator>
      <pubDate>Fri, 10 Feb 2023 04:43:51 +0000</pubDate>
      <link>https://forem.com/jacksonb/provisioning-lambda-docker-images-with-aws-cdk-python-4dap</link>
      <guid>https://forem.com/jacksonb/provisioning-lambda-docker-images-with-aws-cdk-python-4dap</guid>
      <description>&lt;p&gt;This is Part 1 of a two-part post. In this part, I will cover provisioning the lambda docker image, and in the next part, I will cover interacting with it.&lt;/p&gt;

&lt;p&gt;Covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Declare Lambda Docker Image in CDK&lt;/li&gt;
&lt;li&gt;  Importing local modules to main docker script&lt;/li&gt;
&lt;li&gt;  Writing the Dockerfile&lt;/li&gt;
&lt;li&gt;  Reading/Writing to Docker Image filesystem&lt;/li&gt;
&lt;li&gt;  Testing locally&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;CDK installed and set up correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prior experience with Docker would be helpful, although I managed to figure this out and it was my first time dealing with Docker so you should be fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;cdk-project&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;lambdas/&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;docker_func/&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;app/&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;__init__.py&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;components/&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;__init__.py&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;com&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;.py&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;com&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;.py&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;utils/&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;__init__.py&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ut&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;.py&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;main.py&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;output.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Optional&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Dockerfile&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;README.md&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;requirements.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;project/&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="err"&gt;|--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;project_stack.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Process
&lt;/h3&gt;

&lt;p&gt;Step by step process based on the above project structure. The file names I’ll be using in these steps directly relate to the files in the above Directory Structure.&lt;/p&gt;

&lt;p&gt;Note: Starting in the root directory of your CDK project &lt;code&gt;../#/cdk-project&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;1 - Declare the docker using CDK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ../project/project_stack.py
&lt;/span&gt;&lt;span class="n"&gt;docker_lambda&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DockerImageFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DockerLambda&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DockerImageCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_image_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
             &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambdas/docker_func&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;  
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# Default is only 3 seconds  
&lt;/span&gt;    &lt;span class="n"&gt;memory_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# If your docker code is pretty complex  
&lt;/span&gt;    &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="c1"&gt;# Optional  
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 - In a new terminal navigate to top-level of docker function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;lambdas/docker_func
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 - Provision some basic components/util classes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/com1.pyclass Com1():  
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
  &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;span class="c1"&gt;###
&lt;/span&gt;
&lt;span class="c1"&gt;# app/components/com2.pyclass Com2():  
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
  &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;span class="c1"&gt;###
&lt;/span&gt;
&lt;span class="c1"&gt;# app/utils/ut1.pyclass Ut1():  
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
  &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 - Set &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;utils&lt;/code&gt; to be treated as modules by adding &lt;code&gt;__init__.py&lt;/code&gt; to each folder and importing the classes specified in #3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/components/__init__.py  
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;components.com1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;componented.com2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;###
# app/utils/__init__.py  
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;utils.ut1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;br&gt;
If you skip this step you will not be able to import these modules in the way that you think. When docker packages up your files it moves them all around which breaks relative imports (even though it may work locally)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;5 -  Write the dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3.8&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Copy&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;  
&lt;span class="n"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;  
&lt;span class="n"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;  
&lt;span class="n"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;  
&lt;span class="n"&gt;ADD&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docx&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docx&lt;/span&gt;  

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Install&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="n"&gt;using&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;  
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;  &lt;span class="o"&gt;.&lt;/span&gt;  
&lt;span class="n"&gt;RUN&lt;/span&gt;  &lt;span class="n"&gt;pip3&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="s"&gt;"${LAMBDA_TASK_ROOT}"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;CMD&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;could&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt; &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;outside&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Dockerfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;CMD&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"main.handler"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Info&lt;/strong&gt;&lt;br&gt;
I'm using &lt;code&gt;main.py&lt;/code&gt; in this example, note all occurances and change if needed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;em&gt;Add local dependencies&lt;/em&gt; section of the Dockerfile there’s some important details.&lt;br&gt;&lt;br&gt;
These &lt;code&gt;ADD&lt;/code&gt; commands tell docker that when it is packaging up your files it needs to grab the contents of &lt;code&gt;source&lt;/code&gt; and place it in &lt;code&gt;target&lt;/code&gt; when done.&lt;/p&gt;

&lt;p&gt;eg. &lt;code&gt;ADD source target&lt;/code&gt; &amp;gt; &lt;code&gt;ADD app/components components&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This means that once our app is built, we can access the files inside &lt;code&gt;components&lt;/code&gt; via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;components.com1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Com1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last line &lt;code&gt;CMD ["main.handler"]&lt;/code&gt; sets the docker entry point to the function &lt;code&gt;handler&lt;/code&gt; in &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t want to call your file &lt;code&gt;main.py&lt;/code&gt; then just change all instances where I use it to your preferred name.&lt;/p&gt;

&lt;p&gt;6 - Deploy your changes. It should take a while to upload initially. I mean a long while. Like 7–10 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing to the local file system (optional)
&lt;/h3&gt;

&lt;p&gt;If you try to write a file you will get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] OSError: [Errno 30] Read-only file ststel: '/path'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the only writable directory with any AWS Lambda function is the &lt;code&gt;/tmp&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;If you need to write a document, do it like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tmp/report.docx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# example using python-docx
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing locally
&lt;/h2&gt;

&lt;p&gt;Uploading the docker image every time you make a change is possibly the fastest way to transform your brain from goo to soup. Don’t do that. Do this instead.&lt;/p&gt;

&lt;p&gt;Say you’ve got a Docker Image that uses the &lt;code&gt;python-docx&lt;/code&gt; library (not important, it’s just a good example). Your script loads a &lt;code&gt;template.docx&lt;/code&gt; file and saves the resulting &lt;code&gt;output.docx&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To test locally we use python’s &lt;code&gt;if __name__ == "__main__":&lt;/code&gt; magic to provide dummy data to our handler function. This method will &lt;strong&gt;&lt;em&gt;ONLY&lt;/em&gt;&lt;/strong&gt; run when we manually run the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tmp/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
   &lt;span class="c1"&gt;# ... code  
&lt;/span&gt;   &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
   &lt;span class="c1"&gt;# ... more code  
&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt;  

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# input.json sample event input  
&lt;/span&gt;        &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note in the above code I’m using a &lt;code&gt;prefix&lt;/code&gt; argument to my &lt;code&gt;handler()&lt;/code&gt; function. This is to control where the output file is written. For my local testing, I don’t want the file to be written to the &lt;code&gt;/tmp&lt;/code&gt; directory because that will be a reserved folder once I deploy.&lt;/p&gt;

&lt;p&gt;To run the file locally you will need to be in the &lt;code&gt;app&lt;/code&gt; folder (referencing the directory structure from the start).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;\cdk-project\lambdas\docker_func\app&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;main.py&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;That should pretty much cover it. The above steps are all I needed to do to get my lambda working. For reference what it does is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; One lambda function preps a json object and sends it to the Docker Image.&lt;/li&gt;
&lt;li&gt; Docker Image parses the json data, imports 3rd party libraries, reads and populates template documents, saves the output to docker filesystem, uploads the result to S3, and finally returns the S3 object URI to the Lambda that invoked it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hopefully this is helpful to anyone who reads. Thanks.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
  </channel>
</rss>
