README.md in convox_installer-1.0.1 vs README.md in convox_installer-1.0.2

- old
+ new

@@ -1,23 +1,290 @@ # Convox Installer +A Ruby gem that makes it easier to build a Convox installation script. This is like Chef/Ansible/Terraform for your initial Convox setup. + +## NOTE: This software is an alpha version + +Please note that the code quality is not very good, and the test coverage needs to be improved. However, I've successfully set up a number of test and production deployments using this gem, and everything seems to work very well. The library also facilitates idempotency and crash-resistance, so you can easily re-run your installation script if something goes wrong. + +## Features + +* Idempotent. If this script crashes, you can restart it and it will pick up + where it left off. Every step looks up the existing state, and only makes a change + if things are not yet set up (or out of sync). +* Ensures that the `convox` and `aws` CLI tools are installed +* Wraps the `convox` CLI and parses JSON output from API calls +* Add n Docker Repository (e.g. ECR registry) +* Set up an S3 bucket with an optional CORS policy + +## Introduction + [Convox](https://convox.com/) is an awesome open source PaaS, which is like Heroku for your own AWS account. [`convox/rack`](https://github.com/convox/rack) is completely open source and free to use, but you can also sign up for a free or paid account to use the hosted service on convox.com. `convox_installer` is a Ruby gem that makes it much easier to build an installation script for `convox/rack` (the open source PaaS). The Convox CLI is awesome, but it's missing a nice way to script a full deployment. I originally wrote a bash script that made API calls and used [`jq`](https://stedolan.github.io/jq/) and `sed`, but this was very error-prone and it did not have good cross-platform support. I've rewritten this installation script in Ruby, which provides very good cross-platform support, and also allows me to write tests. -# Usage +## Usage -You should create a new git repo for your own installation script, and then use the provided classes and methods to build your own installation workflow. You must also include a `convox.yml`. +Create a new Ruby file (e.g. `install.rb`), and use `bundler/inline` to install and require the `convox_installer` gem. Your install script should start like this: -You can see an example in [`examples/full_installation.rb`](./examples/full_installation.rb). -(This Ruby file uses `bundler/inline`, so it will download and install the `convox_installer` gem before running the script.) +```ruby +#!/usr/bin/env ruby +require 'bundler/inline' -# Config +gemfile do + source 'https://rubygems.org' + gem 'convox_installer' +end +require "convox_installer" +include ConvoxInstaller +``` + +Including the `include ConvoxInstaller` gives you some Ruby methods that you can call to construct an installation workflow. See the "`ConvoxInstaller` DSL" section below. + +You should create a new git repo for your own installation script, and then use the provided classes and methods to build your own installation workflow. You must also include a `convox.yml` (or a `convox.example.yml`). + +You can see a complete example in [`examples/full_installation.rb`](./examples/full_installation.rb). + + +## Config + Config is loaded from ENV vars, or from saved JSON data at -`~/.convox/installer_config`. +`~/.convox/installer_config`. The script will save all of the user's responses into `~/.convox/installer_config`. -### License +## Customize the Config Prompts + +You can set your own config prompts in your own installation script, by setting a `@prompts` instance variable. You can extend the default config prompts like this: + +```ruby +@prompts = ConvoxInstaller::Config::DEFAULT_PROMPTS + [ + { + section: "Docker Authentication", + info: "You should have received authentication details for the Docker Registry\n" \ + "via email. If not, please contact support@example.com", + }, + { + key: :docker_registry_url, + title: "Docker Registry URL", + value: "1234567890.dkr.ecr.us-east-1.amazonaws.com", + }, + { + key: :docker_registry_username, + title: "Docker Registry Username", + }, + { + key: :docker_registry_password, + title: "Docker Registry Password", + } +] +``` + +## Prompt API: + +The `@prompts` variable must be an array of hashes. There are two kinds of hashes: + +#### Section Heading + +Shows a heading and optional details. + +```ruby +{ + section: "The heading for this config section", + info: "Description about this config section" +} +``` + +#### Config Prompt + +* A config prompt with a default value: + +```ruby +{ + key: :config_key_name, + title: "Title to show in the user prompt / config summary", + prompt: "Question to show the user", + default: "default value", +} +``` + +* Set a value from a `Proc`, and don't prompt the user: + +```ruby + { + key: :config_key_name, + title: "Title to show in the config summary", + value: -> () { "string-with-random-suffix-#{SecureRandom.hex(4)}" }, + } +``` + +* Set a value, and hide this setting from the user (even in the summary): + +```ruby + { + key: :config_key_name, + value: "Config Value", + hidden: true, + }, +``` + + +## `ConvoxInstaller` DSL + +#### `ensure_requirements!` + +Makes sure that the `convox` and `aws` CLI tools are installed on this system. If not, shows installation instructions and exits. + +#### `prompt_for_config` + +Loads config from ENV vars, or from saved config at `~/.convox/installer_config`. +If any config settings are missing, it prompts the user for input. Finally, it shows a summary of the config, and asks the user if they want to proceed with the installation. If the user enters `y` (or `yes`), the `prompt_for_config` method completes. If they enter `n` (or `no`), we loop over every setting and let them press "enter" to keep the current value, or provide a new value to correct any mistakes. + +#### `backup_convox_host_and_rack` + +If there are any existing files at `~/.convox/host` or `~/.convox/rack`, this method moves these to `~/.convox/host.bak` and `~/.convox/rack.bak`. + +#### `install_convox` + +* **Required Config:** `aws_region`, `aws_access_key_id`, `aws_secret_access_key`, + `stack_name`, `instance_type` + +Runs `convox rack install ...`. Has some validations to ensure that all required settings are present. + +#### `validate_convox_auth_and_set_host!` + +After running `install_convox`, call this method to ensure that the the `~/.convox/auth` file has been updated with the correct details (checks the rack name and AWS region.) Then it sets the rack host in `~/.convox/host` (if not already set.) + +#### `validate_convox_rack!` + +Calls `convox api get /system` to get the Rack details, then makes sure that everything is correct. + +#### `convox_rack_data` + +Returns a Ruby hash with all convox rack data. + +#### `create_convox_app!` + +* **Required Config:** `convox_app_name` + +Checks if the app already exists. If not, calls `convox apps create ... --wait` to create a new app. Then waits for the app to be ready. (Avoids an occasional race condition.) + + +#### `set_default_app_for_directory!` + +Writes the app name into `./.convox/app` (in the current directory.) The `convox` CLI reads this file, so you don't need to specify the `--app` flag for future commands. + + +#### `create_s3_bucket!` + +* **Required Config:** `s3_bucket_name` + +Creates an S3 bucket from the `:s3_bucket_name` config setting. This is not a default setting, so you can add something like this to your custom `@prompts`: + +```ruby + { + key: :s3_bucket_name, + title: "S3 Bucket for uploads", + value: -> () { "yourapp-uploads-#{SecureRandom.hex(4)}" }, + } +``` + +The `:value` `Proc` will generate a bucket name with a random suffix. (Avoids conflicts when you are setting up multiple deployments for your app.) + +`create_s3_bucket!` will also call `set_s3_bucket_cors_policy` automatically, so you don't need to call this manually. + +#### `set_s3_bucket_cors_policy` + +* **Required Config:** `s3_bucket_name` + +Set up a CORS policy for your S3 bucket. (`:s3_bucket_name`) + +*Note: If the `:s3_bucket_cors_policy` setting is not provided, then this method does nothing.* + +You should set `:s3_bucket_cors_policy` to a JSON string. Here's how I set this up in my own `install.rb` script: + +```ruby +S3_BUCKET_CORS_POLICY = <<-JSON +{ + "CORSRules": [ + { + "AllowedOrigins": ["*"], + "AllowedHeaders": ["Authorization", "cache-control", "x-requested-with"], + "AllowedMethods": ["PUT", "POST", "GET"], + "MaxAgeSeconds": 3000 + } + ] +} +JSON + +@prompts = [ + { + key: :s3_bucket_cors_policy, + value: S3_BUCKET_CORS_POLICY, + hidden: true, + } +] +``` + + +#### `s3_bucket_details` + +* **Required Config:** `s3_bucket_name` + +Get the S3 bucket details for `s3_bucket_name`. Parses the URL and returns a hash: + +```ruby +{ + access_key_id: "AWS Access Key ID", + secret_access_key: "AWS Secret Access Key", + name: "Full S3 Bucket Name (includes the rack/app)", +} +``` + +I use these S3 bucket details to set env variables for my app. (`convox env set ...`) + + +#### `add_docker_registry!` + +* **Required Config:** `docker_registry_url`, `docker_registry_username`, `docker_registry_password` + +Checks the list of registries to see if `docker_registry_url` has already been added. If not, runs `convox registries add ...` to add a new Docker registry (e.g. Docker Hub, ECR). + +#### `default_service_domain_name` + +* **Required Config:** `convox_app_name`, `default_service` + +Parses the rack router ELB name and region, and returns the default `convox.site` domain for your default service. (You can visit this URL in the browser to access your app.) + +Example: `myapp-web.rackname-Route-ABCDFE123456-123456789.us-west-2.convox.site` + +Set a default service in your config prompts (e.g. `web`): + +```ruby +@prompts = [ + # ... + { + key: :default_service, + title: "Default Convox Service (for domain)", + value: "web", + hidden: true, + } +] +``` + +> (This hidden setting isn't visible to the user.) + +#### `run_convox_command!(cmd)` + +Runs a `convox` CLI command, and shows all output in the terminal. Crashes the script with an error if the `convox` command has a non-zero exit code. + +If you want to run `convox env set MYVAR=value`, then you would call: + +```ruby +run_convox_command! 'env set MYVAR=value' +``` + + +## License [MIT](./LICENSE)