Cloud Deployment Anomalies that send us down rabbit holes
Tags: cloudcomputing, webdev, programming, productivity
Published: 2024-01-24 19:28:30

Building for the cloud is often perceived as a straightforward journey, but as many developers discover, it can be a path filled with unexpected twists and turns. This blog delves into the nuanced reality of cloud development, where seemingly simple tasks can lead us down intricate rabbit holes of deployment and configuration.

## The Core Issue

Our journey began with the use of an OpenAPI specification to define our AWS API. The OpenAPI spec's info section, which typically includes a descriptive name for the API, presented an unexpected behavior.

During the initial deployment to AWS, the API name was derived directly from the info section of the OpenAPI spec.

Pretty straightforward? Except, in subsequent deployments, when the OpenAPI spec remained unchanged, AWS seemed to bypass it, defaulting to the name assigned by Pulumi, which was a generic name concatenated with a random string.

## Technical Examination

Here’s a snippet written which the Pulumi APIs to reproduce the problem:

```typescript
import * as pulumi from '@pulumi/pulumi'
import * as aws from '@pulumi/aws'
import * as awsx from '@pulumi/awsx'

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket('my-bucket')
const body = `
{
   "components":{},
   "info":{
       "title":"my-api",
       "version":"v1"
   },
   "tags": [{
       "name":"testing",
       "x-amazon-apigateway-tag-value": "abc"
   }],
   "openapi":"3.0.1",
   "paths":{
       "/hello":{
           "get":{
               "operationId":"helloget",
               "responses":{
                   "default":{
                       "description":""
                   }
               }
           }
       }
   }
}
`
const apigateway = new aws.apigatewayv2.Api('my-api', {
  protocolType: 'HTTP',
  body,
})

// Export the name of the bucket
export const gatewayName = apigateway.id
```

If we switch to using CDK (cloud formation under the covers), we don’t get an error. This is because CloudFormation will not let you provide conflicting parameters - when you provide an OpenAPI spec, you aren’t allowed to provide meta information such as the name, method etc.

```typescript
import {
 Stack,
 App,
 aws_s3 as s3,
 aws_apigatewayv2 as apigateway,
 StackProps,
 CfnOutput,
} from "aws-cdk-lib";
import { Construct } from "constructs";


export class MyStack extends Stack {
 constructor(scope: Construct, id: string, props?: StackProps) {
   super(scope, id, props);

   // Create an S3 bucket
   const bucket = new s3.Bucket(this, "MyBucket", {
     bucketName: "m4jik-bucket-9643",
   });

   const openApiSpec = {
     components: {},
     info: {
       title: "my-api-m4jik",
       version: "v1",
     },
     tags: [
       {
         name: "testing",
         "x-amazon-apigateway-tag-value": "abc",
       },
     ],
     openapi: "3.0.1",
     paths: {
       "/hello": {
         get: {
           operationId: "helloget",
           responses: {
             default: {
               description: "",
             },
           },
         },
       },
     },
   };

   // Create an API Gateway (HTTP API) using CfnApi
   const api = new apigateway.CfnApi(this, "MyApi", {
     body: openApiSpec,
   });

   // Export the API Gateway ID
   new CfnOutput(this, "ApiGatewayId", {
     value: api.ref,
   });
 }


const app = new App();
new MyStack(app, "MyStack");
```

The Pulumi implementation offers the same flexibility as the AWS CDK implementation, which means that the likely culprit is the AWS Terraform Provider under the covers which does not enforce the rules around API naming as stringent as CloudFormation.
Solution
To address this, we implemented a two-fold strategy:

1. **AWS Extensions for Proper Tagging**: We utilized AWS-specific extensions to ensure tags were formatted as key-value pairs, aligning with AWS's standard tagging practices.
2. **Harmonizing OpenAPI and Pulumi Specs**: We aligned the OpenAPI info section with Pulumi’s naming conventions, ensuring consistency across all deployments.
   Of course, we’ll also be raising a support ticket to address the underlying issue in Terraform.

## Final Remarks

A lot of time can be lost when trying to implement something extremely simply, and being pulled deep into the rabbit holes of cloud deployment and configuration.

By sharing this experience, we hope to assist others navigating similar challenges, and improve our own framework which is now jam-packed with lessons learnt from continually building and experimenting with cloud deployments.

Thanks for reading, if you’ve enjoyed this blog or would like to share your support please give us a star on [GitHub](https://github.com/nitrictech)!