booster

Going deeper with Booster

Contributing

If you want to start making contributions to Booster, we strongly recommend that you read our contributing guide.

Framework Core

The framework-core package includes the most important components of the framework abstraction. It can be seen as skeleton or the main architecture of the framework.

The package defines the specification of how should a Booster application work without taking into account the specific providers that could be used. Every Booster provider package is based on the components that the framework core needs in order to work on the platform.

Framework Types

The framework-types packages includes the types that define the domain of the Booster framework. It defines domain concepts like an Event, a Command or a Role.

Framework integration tests

Booster framework integration tests package is used to test the Booster project itself, but it is also an example of how a Booster application could be tested. We encourage developers to have a look at our Booster project repository.

Some integration tests highly depend on the provider chosen for the project, and the infrastructure is normally deployed in the cloud right before the tests run. Once tests are completed, the application is teared down.

There are several types of integration tests in this package:

If you are curious about the framework providers, you will be able to read more about them in the following section.

Providers

The providers are different implementations of the Booster runtime to allow Booster applications run on different cloud providers or services. They all implement the same interface, and the main idea behind the providers is that no matter what the developer chooses as backend, they won’t need to know anything about the underlying infrastructure.

Currently, the Booster framework provides a fully working provider package:

Other providers packages are currently under experimental support. Some of the features might be missing:

Configuration and environments

Booster uses sensible defaults, convention over configuration, and code inference to reduce dramatically the amount of configuration needed. However, there are some aspects that can’t be inferred (like the application name) or the provider library used for each environment.

Booster configuration

You configure your application by calling the Booster.configure() method. There are no restrictions about where you should do this call, but the convention is to do it in your configuration files located in the src/config folder. This folder will get automatically generated for you after running the boost new:project <project-name> CLI command.

This is an example of a possible configuration:

import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'

Booster.configure('pre-production', (config: BoosterConfig): void => {
  config.appName = 'my-app-name'
  config.providerPackage = '@boostercloud/framework-provider-aws'
})

The following is the list of the fields you can configure:

Providers configuration

AWS Provider

To configure AWS as a provider you need to meet certain prerequisites:

Now go to your config.ts file, import the aws provider library and set up your app environment.

import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'
import { Provider as AWSProvider } from

Booster.configure('production', (config: BoosterConfig): void => {
  config.appName = 'my-app-name'
  config.providePackage = '@boostercloud/framework-provider-aws'
})

Open your terminal and run the deployment command

boost deploy -e production

Now just let the magic happen, Booster will create everything for you and give you back your app ready to use URL. 🚀

Azure Provider

To configure Azure as a provider you need to meet certain prerequisites:

At this moment you have to log in you Azure account using the Azure CLI with the following command.

az login

Then create a service pricipal running the following command.

az ad sp create-for-rbac --name <service-principal-name>

[!NOTE] Remember to change <service-principal-name> for a custom one.

After the service principal is created, create a bash script with the following content. It will set up the necessary environment variables required by the provider in order to work:

#!/usr/bin/env bash

SP_DISPLAY_NAME="<service-principal-name>" #replace <service-principal-name> with the name of your own SP
REGION="East US" #replace with a region of your choice, see full list here: https://azure.microsoft.com/en-us/global-infrastructure/locations/

export AZURE_APP_ID=$(az ad sp list --display-name ${SP_DISPLAY_NAME} | jq -r '.[].appId')
export AZURE_TENANT_ID=$(az ad sp list --display-name ${SP_DISPLAY_NAME} | jq -r '.[].appOwnerTenantId')
export AZURE_SECRET=$(az ad sp credential reset --name ${AZURE_APP_ID} | jq -r '.password')
export AZURE_SUBSCRIPTION_ID=$(az account show | jq -r '.id')
export REGION

[!NOTE] remember to have jq installed in your system.

Now go to your config.ts file, import the aws provider library and set up your app environment.

import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'

Booster.configure('production', (config: BoosterConfig): void => {
  config.appName = 'my-app-name'
  config.providerPackage = '@boostercloud/framework-provider-azure'
})

Open your terminal and run the bash file to export you env variables and the deploy command

source <path-to-your-bash-file> && boost deploy -e production

Now just let the magic happen, Booster will create everything for you and give you back your app ready to use URL. 🚀

Azure synth command

Azure provider implement the experimental Booster synth command. This command will generate Terrafom templates from your code. It will also generate needed files to deploy your Booster application using cdktf.

Running synth command, for example boost synth -e production will generate following files:

Booster deploy command for Azure will deploy your application using the generated templates. You don’t need to run the synth command for deploy your application, the deploy command will generate the templates before deploy for you.

Once you have the new files and folders generates you could use cdktf to deploy your application if you want to.

Azure and CI/CD environments

It is possible to deploy your Azure infrastructure using the Terrafom templates generated by the synth command using Terraform executable.

To deploy a Booster application using the Terrafom templates generated by the Booster synth command:

  1. Navigate to
    > cd cdktf.out/stacks/<application name><environment name>
    
  2. Run (only the first time)
    > terraform init
    
  3. Run
    > terraform plan --out plan
    
  4. Run
    > terrafom apply "plan"
    

You could follow similar steps to integrate the Azure Booster deploys in your CI/CD environment.

  1. Navigate to
    > cd cdktf.out/stacks/<application name><environment name>
    
  2. Copy functionApp.zip to the destination folder
    > cp functionApp.zip <destination>
    

After copying the files you should have the following structure:

<application>
├── cdktf.out
│   └── stacks
│       └── <application name><environment name>
│           └── cdk.tf.json

Now deploy the template:

  1. Run (only the first time)
    > terraform init
    
  2. Run
    > terraform plan --out plan
    
  3. Run
    > terrafom apply "plan"
    

Finally, you need to upload the source code. The main options are (more info):

  1. Using az-cli. Run
    > az functionapp deployment source config-zip -g <resource_group> -n \
    <app_name> --src ./functionApp.json
    
  2. Using REST APIs. Send a POST request to https://<app_name>.scm.azurewebsites.net/api/zipdeploy. Example:
    >  curl -X POST -u <deployment_user> --data-binary @"<zip_file_path>" https://<app_name>.scm.azurewebsites.net/api/zipdeploy
    

[!NOTE] Remember to follow the Azure Provider steps in this page to set up your credentials correctly

Kubernetes provider

To configure Kubernetes as a provider you need to meet certain prerequisites:

Now go to your config.ts file, import the kubernetes provider library and set up your app environment.

Working with minikube

Minikube allows you to use a Kubeneter cluster on your local environment.

Once you have it installed, you just have to make sure that you configure the namespace for the current context. By default, the namespace has the following format: booster-<app_name>-<environment_name>. For example, if our app name were “myApp” and the environment “kubernetes-dev”, the namespace config command would be like:

kubectl config set-context --current --namespace=booster-myApp-kubernetes-dev

Ready! We can now boost deploy -e kubernetes-dev to deploy our application locally.

import { Booster } from '@boostercloud/framework-core'
import { BoosterK8sConfiguration } from '@boostercloud/framework-provider-kubernetes-infrastructure'

Booster.configure('production', (config: BoosterK8sConfiguration): void => {
  config.appName = 'my-app-name'
  config.providerPackage = '@boostercloud/framework-provider-kubernetes'
})

Environments

You can create multiple environments calling the Booster.configure function several times using different environment names as the first argument. You can create one file for each environment, but it is not required. In this example we set all environments in a single file:

// Here we use a single file called src/config.ts, but you can use separate files for each environment too.
import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'
// A provider that deploys your app to AWS:

Booster.configure('stage', (config: BoosterConfig): void => {
  config.appName = 'fruit-store-stage'
  config.providerPackage = '@boostercloud/framework-provider-aws'
})

Booster.configure('prod', (config: BoosterConfig): void => {
  config.appName = 'fruit-store-prod'
  config.providerPackage = '@boostercloud/framework-provider-aws'
})

It is also possible to place an environment configuration in a separated file. Let’s say that a developer called “John” created its own configuration file src/config/john.ts. The content would be the following:

import { Booster } from '@boostercloud/framework-core'
import { BoosterConfig } from '@boostercloud/framework-types'
import * as AWS from

Booster.configure('john', (config: BoosterConfig): void => {
  config.appName = 'john-fruit-store'
  config.providerPackage = '@boostercloud/framework-provider-aws'
})

The environment name will be required by any command from the Booster CLI that depends on the provider. For instance, when you deploy your application, you’ll need to specify on which environment you want to deploy it:

boost deploy -e prod

This way, you can have different configurations depending on your needs.

Booster environments are extremely flexible. As shown in the first example, your ‘fruit-store’ app can have three team-wide environments: ‘dev’, ‘stage’, and ‘prod’, each of them with different app names or providers, that are deployed by your CI/CD processes. Developers, like “John” in the second example, can create their own private environments in separate config files to test their changes in realistic environments before committing them. Likewise, CI/CD processes could generate separate production-like environments to test different branches to perform QA in separate environments without interferences from other features under test.

The only thing you need to do to deploy a whole new completely-independent copy of your application is to use a different name. Also, Booster uses the credentials available in the machine (~/.aws/credentials in AWS) that performs the deployment process, so developers can even work on separate accounts than production or staging environments.

Extending Booster with Rockets!

You can extend Booster by creating rockets. A rocket is just a node package that implements the public Booster rocket interfaces. You can use them for many things:

  1. Extend your infrastructure (Currently, only in AWS): You can write a rocket that adds provider resources to your application stack.
  2. Runtime extensions (Not yet implemented): Add new annotations and interfaces, which combined with infrastructure extensions, could implement new abstractions on top of highly requested use cases.
  3. Deploy and init hooks (Not yet implemented): Run custom scripts before or after deployment, or before a Booster application is loaded.

This extension mechanism is very new, but we’re planning to port most of the functionality as rockets. This has two benefits:

Create your own Rocket

[!NOTE] Currently, Rockets work in AWS, we are working on porting them to other providers.

A rocket is nothing more than an npm package that extends your current Booster architecture. The structure is simple, and it mainly has 2 methods: mountStack and unmountStack. We’ll explain what they are in shortly.

Infrastructure Rocket interfaces are provider-dependant, so Infrastructure Rockets must import the corresponding booster infrastructure package for their chosen provider. For AWS, that’s @boostercloud/framework-provider-aws-infrastructure. Notice that, as the only thing we use of that package is the InfrastructureRocket interface, you can import it as a dev dependency to avoid including that big package in your deployed lambdas.

So let’s start by creating a new package and adding this dependency:`

mkdir rocket-your-rocket-name-aws-infrastructure
cd rocket-your-rocket-name-aws-infrastructure
npm init
...
npm install --save @boostercloud/framework-provider-aws-infrastructure

The basic structure of an Infrastructure Rocket project is quite simple as you can see here:

rocket-your-rocket-name-aws-infrastructure
├── package.json
├── src
    ├── index.ts
    └── your-main-class.ts

<your-main-class>.ts can be named as you want and this is where we define the mountStack and unmount methods.

export class YourMainClass {
  public static mountStack(params: YourRocketParams, stack: Stack, config: BoosterConfig): void {
    /* CDK code to expand your Booster infrastructure */
  }
  public static unmountStack?(params: YourRocketParams, utils: RocketUtils): void {
    /* Optional code that runs before removing the stack */
  }
}

Let’s look in more detail these two special functions:

We also have an index.ts file to export these two functions:

export interface InfrastructureRocket {
  mountStack: (stack: Stack, config: BoosterConfig) => void
  unmountStack?: (utils: RocketUtils) => void
}

You’ll have to implement a default exported function that accepts a parameters object and returns an initialized InfrastructureRocket object:

const YourRocketInitializator = (params: YourRocketParams): InfrastructureRocket => ({
  mountStack: SomePrivateObject.mountStack.bind(null, params),
  unmountStack: SomePrivateObject.unmountStack.bind(null, params),
})

export default YourRocketInitializator

Notice that Infrastructure Rockets should not be included in the Booster application code to avoid including the CDK and other unused dependencies in the lambdas, as there are some strict restrictions on code size on most platforms. That’s why Infrastructure Rockets are dynamically loaded by Booster passing the package names as strings in the application config file:

src/config/production.ts:

Booster.configure('development', (config: BoosterConfig): void => {
  config.appName = 'my-store'
  config.providerPackage = '@boostercloud/framework-provider-aws'
  config.rockets = [
    {
      packageName: 'rocket-your-rocket-name-aws-infrastructure', // The name of your infrastructure rocket package
      parameters: {
        // An arbitrary object with the parameters required by your infrastructure rocket initializator
        hello: 'world',
      },
    },
  ]
})

Naming recommendations

There are no restrictions on how you name your rocket packages, but we propose the following naming convention to make it easier to find your extensions in the vast npm library and find related packages (code and infrastructure extensions cannot be distributed in the same package).

Notice that some functionalities, for instance an S3 uploader, might require both runtime and infrastructure extensions. In these cases, the convention is to use the same name rocket-name and add the suffix -infrastructure to the infrastructure rocket. It’s recommended, but not required, to manage these dependent packages in a monorepo and ensure that the versions match on each release.

If you want to support the same functionality in several providers, it could be handy to also have a package named rocket-{rocket-name}-{provider}-core where you can have cross-provider code that you can use from all the provider-specific implementations. For instance, a file uploader rocket that supports both AWS and Azure could have an structure like this:

Booster Rockets list

Here you can check out the official Booster Rockets developed at this time: