BBC::Cosmos::Tools
- Introduction
- Example commands
- Project directory structure
- File structure explained
- Using YAML or JSON
- Example repositories
Introduction
The BBC Cosmos Tool is a Ruby gem that runs on the command line and helps make generating CloudFormation specifically for the BBC Cosmos infrastructure easier.
The gem supports the following features:
- Pushing component config to the cosmos API
- Create/update and delete stacks for a component in cosmos
- Do release and snapshot deployments to any environment
- List instances running for a component and filter by tag
The gem supports the following formats:
- CFNDSL
- YAML
- JSON
Installation
Add this line to your application's Gemfile:
gem 'bbc-cosmos-tools'
And then execute:
$ bundle
Or install it yourself as:
$ gem install bbc-cosmos-tools
Project directory structure
The cli tool needs to be used inside a project that has a specific directory structure. This structure looks something like the following (if using CFNDSL):
.
├── configs
│ ├── app.yaml
│ └── {component}.yaml.erb
├── resources
│ ├── int
│ │ └── {component}.yaml
│ ├── live
│ │ └── {component}.yaml
│ └── test
│ │ └── {component}.yaml
└── stacks
└── {component}
└── {stack}
├── aws
│ ├── {resource}
│ ├── {type}.rb
│ └── {resource}
│ ├── {type}.rb
└── template.rb
Where by the {x}
items equate to the following:
{component}
The name of your Cosmos component (e.g.responsive-jmeter
){stack}
Typically this will bemain
(as there must always be amain
stack)
But be aware you can have multiple "stacks" for a component (it's just a structural convenience){resource}
This could be (for example) a Scaling Group, IAM policy, SQS, or DynamoDB (any AWS service){type}
This is a subsection of the resource e.g. if the resource wasAWS::SQS::Queue
then this file would be namedqueue.rb
Note: if you're using the YAML or JSON formats then the following structure is used (notice it's almost identical)
.
├── configs
│ ├── app.yaml
│ └── {component}.yaml.erb
├── resources
│ ├── int
│ │ └── {component}.yaml
│ ├── live
│ │ └── {component}.yaml
│ └── test
│ │ └── {component}.yaml
└── stacks
└── {component}
└── {stack}.{yaml|json}
Quick directory creation
A quick way to create the YAML/JSON directory structure is shown below:
function setup() {
local component_name=$1
local type=$2
mkdir {configs,resources,stacks}
mkdir resources/{int,test,live}
mkdir stacks/$component_name
touch configs/$component_name.yaml.erb \
resources/{int,test,live}/$component_name.yaml \
stacks/$component_name/main.$type
}
setup foobar yaml
setup bazqux json
File structure explained
The file structure required by the BBC Cosmos CLI Tool can be confusing so let’s break it down into sections so we can better understand the purpose of each directory:
Config
The files in this folder let you define custom configuration that will be baked into your EC2 instances.
You need to use the bbc-cosmos-tools config push
command to upload the updated configuration data (run the help command bbc-cosmos-tools help config push
to see more options and details).
The content of this folder is usually:
app.yaml
(defaults){component}.yaml.erb
(custom)
So the contents of app.yaml
can look like the following:
bbc:
cosmos:
api: 'https://api.live.bbc.co.uk'
config_endpoint: '/cosmos/env/%s/component/%s/configuration'
stack_create_endpoint: '/cosmos/env/%s/component/%s/stacks/create'
stack_update_endpoint: '/cosmos/env/%s/component/%s/stack/%s/update'
deployments: '/cosmos/deployments/component/%s/env/%s'
release: '/cosmos/component/%s/releases'
deploy: '/cosmos/env/%s/component/%s/deploy_release'
snapshot_deploy: '/cosmos/env/%s/component/%s/deploy_snapshot'
stack_events: '/cosmos/env/%s/component/%s/stack/%s/events'
stacks: '/cosmos/env/%s/component/%s/stacks'
stack_delete: '/cosmos/env/%s/component/%s/stack/%s/delete'
stack_details: '/cosmos/env/%s/component/%s/stack/%s'
stack_template: '/cosmos/env/%s/component/%s/stack/%s/template'
Note:
stack_details
is needed for when a stack update happens. As we don't have all the config values that Cosmos should automatically add in for us when it's created, we need to get them from Cosmos and add them to the parameter list when doing the update
Steven Jack [12:38 PM] otherwise they'll be blank
Where as the contents of {component}.yaml.erb
can look like the following:
components:
'{component}':
s3_bucket_id: "foo"
s3_bucket_path: "bar"
In YAML you can get fancy and make certain properties more re-usable by using &
to create an "anchor" which you can then reference with *
, along with <<:
to inject content. See the following file for an example (although it’s not a very good example as there would be no need to use any of those features for such a small file, but hopefully it gives you an idea on how they are used)…
base: &shared
s3_bucket_id: "foo"
s3_object_path: "bar"
components:
'responsive-jmeter':
<<: *shared
Secure values
The cosmos API allows you to mark certain values as "secure" when adding them to the API. This means that when hitting the config REST endpoint, anything marked as secure won't show. It'll only be available when it's baked onto the instance. This is useful for stuff like API keys etc where you don't want everyone with a dev cert being able to see/use it.
To use this you need to have the following structure for a key in your config:
components:
my_component:
api_key:
secure: true
value: "SUPER_SECRET_KEY"
foo: bar
bar: qux
Using CFNDSL
The project config is an ERB template that is renderer into a YAML file. it gets passed all resources from the resource config that can be used in the template. Below is an example of this:
sequencer: &shared_sequencer
sequencer_table_name: '<%= config['sequencer']['name'] %>'
storage: &shared_storage
storage_path: '<%= config['storage']['name'] %>'
base: &shared
<<: *shared_sequencer
logger_path: 'some.loggingserver.com'
logger_port: '12201'
storage_path: '<% config['storage']['name]' %>
keep_all_messages: 'true'
components:
'test_component':
<<: *shared
some_specific_value: 'foo, bar'
The config has a number of components under a project, and the shared key allows you to have shared config for the components. The config is built up from the shared data with the component config merged in afterwards.
Application config
There's also an application config that stores data used by the app, at the moment this is only the API endpoints.
bbc:
cosmos:
api: "https://www.my.api.com"
create_endpoint: "/some/restful/endpoint"
update_endpoint: "/some/restful/endpoint"
Resources
The resources directory is used to provide custom values to the Parameters
defined within your stacks (see the Stacks directory below). There is usually three folders inside this directory that reflect the Cosmos environments:
int
test
live
Each of those folders will have a {component}.yaml
file where you define the values for your Parameters (if you were using AWS directly, rather than via the Cosmos abstraction layer, then this is typically where you would specify the custom values either using the AWS CLI tool or by entering the values into form fields when using the AWS Console GUI).
The format of this file looks something like the following:
cloudformation:
components:
'{component_folder_name}':
{stack}:
variants:
default:
{parameter_a}: value
{parameter_b}: value
{parameter_c}: value
Using CFNDSL
The project resources yaml file is used to describe the resources used within the project. The reason for having a single file is that you can define resources that are used in the cloudformation templates and config, so when for example an ARN changes this will be picked up across the CF templates and the config.
The file is split up into two section:
- The standard resource list, this is generally the ARNS for all the resources your component uses.
- Cloudformation parameters (These are usually referenced from the aforementioned)
Below is an annotated example of a resource config:
sequencer:
name: 'example_name'
arn: 'example_arn'
roles:
broker:
name: &roles_broker_name 'some_iam_role_name'
cloudformation:
shared: &component_shared #<- This node is ignored, it's used as a base for other config templates
ACFParameter: 'Value'
AnotherCFParameter: 'Another value'
components:
'my-example-component': #<- The name of the component in cosmos
main: <- This is the name of the stack in cosmos
variants: <- This is so you can have a different variant of the same config, i.e for int/test one for during the day and one for evening/weekends.
default: &my-example-component_default #<- We set this anchor so we can use this defaults in other variants
<<: *component_shared #<- We import the shared component params into these so we're not duplicating
ARoleParamForDefault: *roles_broker_name #<- We use the anchor for the roles set above to add them here, again stopping repetition
scheduled:
<<: *my-example-component_default
MinSize: 0
MaxSize: 0
Stacks
The stacks directory is the most important part of the configuration as it defines your infrastructure requirements (i.e. creates all the specified AWS resources as you’ve defined them using CloudFormation).
The folder structure resembles the following:
.
├── stacks
└── {component}
└── {stack}
├── aws
│ ├── {resource}
│ │ ├── {type}
An example of this could be:
.
├── stacks
└── responsive-jmeter
└── main
├── aws
│ ├── sqs
│ │ ├── policy.rb
│ │ ├── queue.rb
│ ├── iam
│ │ ├── instance_profile.rb
Note: when opening up the files
policy.rb
andqueue.rb
we’ll see theType
syntax and that should correspond to the folder structure described above. So aws/sqs/policy.rb will have aType "AWS::SQS::QueuePolicy”
, and aws/iam/instance_profile.rb will have aType "AWS::IAM::InstanceProfile”
Example commands
When you want to push up some new configuration into your instance (for your application to use) then the following command is what you need (this isn't done very often):
bbc-cosmos-tools config push {cosmos_component_name} \
--project={resources yaml filename} \
--key-path=/custom/path/to/your/pem/certificate
To update your stack:
bbc-cosmos-tools stack update {cosmos_component_name} \
--project={resources yaml filename} \
--key-path=/custom/path/to/your/pem/certificate
To generate CloudFormation that is sent to stdout
:
bbc-cosmos-tools stack generate {cosmos_component_name} \
--project={resources yaml filename} \
--key-path=/custom/path/to/your/pem/certificate
Note: in case you're not a CLI wizard, instead of specifying the
--key-path
option you can set a$DEV_CERT_PATH
environment variable that the tool uses by default if it detects it. Inside your shell's configuration file (either~/.bashrc
or~/.zshrc
) you'll want to addexport DEV_CERT_PATH=/custom/path/to/your/pem/certificate
Deploy component to a specific environment:
bbc-cosmos-tools release deploy <component> --project=<project> --from=X --env=Y
Using YAML or JSON
Use the same commands as above but make sure to include a --type={yaml|json}
. If it's not specified then cfndsl
is assumed as the default (almost as if you had written --type=cfndsl
, but it's clearer to just remove the flag in that instance).
.
├── configs
│ ├── app.yaml
│ └── foo.yaml.erb
├── resources
│ ├── int
│ │ └── bar.yaml
│ ├── live
│ │ └── bar.yaml
│ └── test
│ └── bar.yaml
└── stacks
└── foo
├── dns.yaml
├── main.yaml
To update the DNS stack in the live environment, you would execute:
bbc-cosmos-tools stack update foo --project=bar --env=live --type=yaml --stack=dns
Example repositories
There are a few example projects using this tool now:
Note: it's important to realise that you don't need to have your configuration in a separate repository. If anything it probably would be better maintained as part of your application repository (placed inside a
/config/
directory)The reason some of the projects choose to locate their configuration in a separate repo is because they're made up of a number of different components.
Help?
You can use the built-in help
command to see a list of parameters and options that need to be passed into the command you wish to execute. For example, if we were unsure of the options for the stack
command then we would execute bbc-cosmos-tools stack help
and this would display the following output:
Commands:
bbc-cosmos-tools stack help [COMMAND] # Describe subcommands or one specific subco...
bbc-cosmos-tools stack create [COMPONENT, MAIN_STACK = true] # Generates and create the cloudformation te...
bbc-cosmos-tools stack delete [COMPONENT] # Deletes a stack
bbc-cosmos-tools stack events [COMPONENT = nil] # Shows the stack events for a component
bbc-cosmos-tools stack generate [COMPONENT] # Generates a cloudformation template based ...
bbc-cosmos-tools stack list [COMPONENT = nil] # Lists stacks for a component
bbc-cosmos-tools stack update [COMPONENT = nil] # Generates and updates the cloudformation t...
SSH
You can SSH into an instance quickly by calling the component ssh command such as:
bbc-cosmos-tools component ssh news-javelin --env=live
Your IP for i-38c96895 is 10.0.233.46
If there are multiple instances for a component, a list will be provided to let you chose an instance ID:
bbc-cosmos-tools component ssh news-jenkins-agent --env=live
Multiple instances detected, please select one
1. i-67ad08ca
2. i-be8e9814
? 1 ### Selection
Creating Session
Creating Session
Creating Session
Creating Session
Your IP for i-67ad08ca is 10.0.234.217
Other Commands
List Instances
bbc-cosmos-tools component instances news-javelin --env=live
Instance ID
i-38c96895
List Releases
bbc-cosmos-tools release list news-javelin
Component: news-javelin
Version Created at
0.1.6-1.el6 2015-06-30T16:10:54+01:00
List Deployed Releases
bbc-cosmos-tools release deployed news-javelin --env=live
Component: news-javelin
Release Deployed at Deployment id Deployed by Status
0.1.83-1.el6 2015-07-16T15:39:59+01:00 282759 [email protected] done
Redeploy a Component
bbc-cosmos-tools release redeploy news-release --env=int
news-release successfully redeployed
View deployment of news-release here https://admin.live.bbc.co.uk/cosmos/env/int/deployment/286431
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request