diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..c1ba363 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = E265,E266,E402,E501 \ No newline at end of file diff --git a/.gitignore b/.gitignore index ff26dbe..fd1027e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -**/*~ -**/*.bak -**/*.swp -/build/ +*~ +*.bak +*.swp +.vscode/ +/work/ diff --git a/LICENSE.txt b/LICENSE.txt index bb205ac..ab6f63d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017-2019 Michael Crute, Jake Buchholz +Copyright (c) 2017-2021 Jake Buchholz Göktürk, Michael Crute Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 3ca129a..a2ac172 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,228 @@ -# Alpine Linux EC2 AMI Builder +## _**NOTE: This is a Work-in-Progress**_ -These are the official Alpine AWS AMIs. For an index of images see the -[Alpine Website](https://alpinelinux.org/cloud/). +_It is intended that this will eventually replace +https://gitlab.alpinelinux.org/alpine/cloud/alpine-ec2-ami +as the offical multi-cloud image builder for Alpine Linux._ -## Pre-Built AMIs +---- -***To get started with a pre-built minimalist AMIs, visit -https://alpinelinux.org/cloud, or the [README](releases/README.md) in the -[releases](releases) subdirectory of this repo.*** +# Alpine Linux Cloud Image Builder -Alternately, with the right filters, you can query the EC2 API to programmatically -find our most recent AMIs. For example, using the `aws` command line tool... -``` -aws ec2 describe-images \ - --output text \ - --filters \ - Name=owner-id,Values=538276064493 \ - Name=name,Values='alpine-ami-*' \ - Name=state,Values=available \ - Name=tag:profile_build,Values=v3_10-x86_64 \ - --query 'max_by(Images[], &CreationDate).ImageId' -``` -...will list the latest AMI id from our collection of 'v3_10-x86_64' builds. -Refer to the AWS CLI Command Reference for -[describe-images](https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-images.html) -for more details. +This repository contains the code and and configs for the build system used to +create official Alpine Linux images for various cloud providers, in various +configurations. This build system is flexible, enabling others to build their +own customized images. -## Custom AMIs +---- +## Pre-Built Offical Cloud Images -Using the scripts and configuration in this project, you can build your own -custom Alpine Linux AMIs. If you experience any problems building custom AMIs, -please open an [issue](https://github.com/mcrute/alpine-ec2-ami/issues) and -include as much detailed information as possible. +To get started with offical pre-built Alpine Linux cloud images, visit +https://alpinelinux.org/cloud. Currently, we build official images for the +following providers: +* AWS + +You should also be able to find the most recently published Alpine Linux +images via your cloud provider's web console, or programatically query their +API with a CLI tool or library. + +_(TODO: examples)_ + +---- +## Build System + +The build system consists of a number of components: + +* the primary `build` script and related cloud-specific helpers +* a directory of `configs/` defining the set of images to be built +* a Packer `alpine.pkr.hcl` orchestrating the images' local build, as well as + importing them to cloud providers and publishing them to desitnation regions +* a directory of `scripts/` which set up the images' contents during + provisioning ### Build Requirements +* [Python](https://python.org) (3.9.7 is known to work) +* [Packer](https://packer.io) (1.7.6 is known to work) +* [QEMU](https://www.qemu.org) (6.1.0 is known to work) +* cloud provider account(s) -* [Packer](https://packer.io) >= 1.4.1 -* [Python 3.x](https://python.org) (3.7 is known to work) -* an AWS account with an existing subnet in an AWS Virtual Private Cloud +### Cloud Credentials -### Profile Configuration +This build system relies on the cloud providers' Python API libraries to find +and use the necessary credentials -- via configuration in the user's home +directory (i.e. `~/.aws/...`, `~/.oci/...`, etc.) or with special environment +variables (i.e. `AWS_...`, `OCI_...`, etc.) -Target profile config files reside in the [profiles](profiles) subdirectory, -where you will also find the [config](profiles/alpine.conf) we use for our -pre-built AMIs. Refer to the [README](profiles/README.md) in that subdirectory -for more details and information about how AMI profile configs work. +It is expected that each cloud provider's user/role will have been set up with +sufficient permission in order to accomplish the operations necessary to query, +import, and publish images; _it is highly recommended that no permissions are +granted beyond what is absolutely necessary_. -### AWS Credentials +### The `build` Script -These scripts use the `boto3` library to interact with AWS, enabling you to -provide your AWS account credentials in a number of different ways. see the -offical `boto3` documentation's section on -[configuring credentials](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html#guide-credentials) -for more details. *Please note that these scripts do not implement the first -two methods on the list.* - -### Building AMIs - -To build all build targets in a target profile, simply... ``` -./scripts/builder.py amis +usage: build [-h] [--debug] [--clean] [--revise] {configs,local,import,publish} + [--custom DIR [DIR ...]] [--skip KEY [KEY ...]] [--only KEY [KEY ...]] + [--no-color] [--parallel N] [--vars FILE [FILE ...]] + +build steps: + configs resolve build configuration + local build local images + import import to cloud providers + publish set permissions and publish to cloud regions + +optional arguments: + -h, --help show this help message and exit + --debug enable debug output (False) + --clean start with a clean work environment (False) + --revise bump revisions if images already published (False) + --custom DIR [DIR ...] overlay custom directory in work environment + --skip KEY [KEY ...] skip variants with dimension key(s) + --only KEY [KEY ...] only variants with dimension key(s) + --no-color turn off Packer color output (False) + --parallel N build N images in parallel (1) + --vars FILE [FILE ...] supply Packer with additional -vars-file(s) ``` -You can also build specfic build targets within a profile: -``` -./scripts/builder.py amis ... -``` +A `work/` directory will be created for its Python virtual environment, any +necessary Python libraries will be `pip install`ed, and `build` will execute +itself to ensure that it's running in the work environment. -Before each build, new Alpine Linux *releases* are detected and the version's -core profile is updated. +This directory also contains `configs/` and `scripts/` subdirs (with custom +overlays), UEFI firmware for QEMU, Packer cache, the generated `configs.yaml` +and `actions.yaml` configs, and the `images/` tree for local image builds. -If there's already an AMI with the same name as the profile build's, that build -will be skipped and the process moves on to build the other profile's build -targets (if any). +Use `--clean` if you want to re-overlay, re-download, re-generate, or rebuild +anything in the `work/` directory. To redo the Python virtual environment, +simply remove the `work/` directory and its contents, and it will be recreated +the next time `build` is run. -After each successful build, `releases/.yaml` is updated with the -build's details, including (most importantly) the ids of the AMI artifacts that -were built. +### Build Steps -Additional information about using your custom AMIs can be found in the -[README](releases/README.md) in the [releases](releases) subdirectory. +When executing `build` you also provide the target step you wish to reach. For +example, if you only want to build local images, use `build local`. Any +predecessor steps which haven't been done will also be executed -- that is, +`build local` also implies `build configs` if that step hasn't completed yet. -### Pruning AMIs +The **configs** step determines the latest stable Alpine Linux release, and +ensures that the `configs/` and `scripts/` overlays, UEFI firmware, and +`configs.yaml` exist. This allows you to validate the generated build variant +configuration before attempting to build any images locally. -Every now and then, you may want to clean up old AMIs from your EC2 account and -your profile's `releases/.yaml`. There are three different levels of -pruning: -* `revision` - keep only the latest revision for each release -* `release` - keep only the latest release for each version -* `end-of-life` - remove any end-of-life versions +If `build` is moving on past **configs** to other steps, it will determine which +image variants to work on (based on `--skip` and `--only` values) and what +actions will be taken, based on existence of local/imported/published images, and +generate the `actions.yaml` file. Providing the `--revise` flag allows you to +rebuild local images that were previously built, reimport unpublished images to +cloud providers, and bump the "revision" value of previously published images -- +this is useful if published images require fixes but the Alpine release itself +isn't changing; published images are not removed (though they may be pruned once +their "end-of-life" date has passed). -To prune a profile (or optionally one build target of a profile)... -``` -./scripts/builder.py prune-amis [] -``` +At this point, `build` executes Packer, which is responsible for the remaining +**local**, **import**, and **publish** steps -- and also for parallelization, if +the `--parallel` argument is given. Because build hardware varies, it is also +possible to tune a number of QEMU timeouts and memory requirements by providing +an HCL2 Packer Vars file and specifying `--vars ` to override the +defaults in `alpine.pkr.hcl`. -### Updating the Release README +### Packer and `alpine.pkr.hcl` -This make target updates the [releases README](releases/README.md), primarily -for updating the list of our pre-built AMIs. This may-or-may-not be useful for -other target profiles. -``` -./scripts/builder.py gen-release-readme -``` +Packer loads and merges `actions.yaml` and `configs.yaml`, and iterates the +resulting object in order to determine what it should do with each image +variant configuration. -### Cleaning up the Build Environment +`alpine.pkr.hcl` defines two base `source` blocks -- `null` is used when an +image variant is already built locally and/or already imported to the +destination cloud provider; otherwise, the `qemu` source is used. -The build process is careful to place all temporary files in the `build` -subdirectory. Remove the temporary `build` subdirectory, which contains the -resolved profile and Packer configs, the Python virtual environment, and other -temporary build-related artifacts. +The `qemu` builder spins up a QEMU virtual machine with a blank virtual disk +attached, using the latest stable Alpine Linux Virtual ISO, brings up the VM's +network, enables the SSH daemon, and sets a random password for root. -## Caveats +If an image variant is to be **built locally**, the two dynamic provisioners copy +the required data for the setup scripts to the VM's `/tmp/` directory, and then +run those setup scripts. It's these scripts that are ultimately responsible for +installing and configuring the desired image on the attached virtual disk. +When the setup scripts are complete, the virtual machine is shut down, and the +resulting local disk image can be found at +`work/images///image.qcow2`. -* New Alpine Linux *versions* are currently not auto-detected and added as a - core version profile; this process is, at the moment, still a manual task. +The dynamic post-processor uses the `cloud_helper.py` script to **import** a +local image to the cloud provider, and/or **publish** an imported image to the +cloud provider's destination regions, based on what actions are applicable for +that image variant. When the **publish** step is reapplied to an +already-published image, the script ensures that images have been copied to all +destination regions (for example, if the cloud provider recently added a new +region), and that all launch permissions are set as expected. + +### The `cloud_helper.py` Script + +This script is only meant to be imported by `build` and called from Packer, and +provides a normalized cloud-agnostic way of doing common cloud operations -- +getting details about a variant's latest imported image, importing new local +image to the cloud, removing a previouly imported (but unpublished) image so it +can be replaced, or publishing an imported image to destination regions. + +---- +## Build Configuration + +The `build` script generates `work/configs.yaml` based on the contents of the +top-level config file, `work/configs/configs.conf`; normally this is a symlink to +`alpine.conf`, but can be overridden for custom builds. All configs are +declared in [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md) +format, which allows importing from other files, simple variable interpolation, +and easy merging of objects. This flexibility helps keep configuration +[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). + +The top-level `build.conf` has three main blocks, `Default` (default/starting +values), `Dimensions` (with configs that apply in different circumstances), and +`Mandatory` (required/final values). The configuration for these blocks are +merged in this exact order. + +### Dimensions and Build Variants + +Build variants _(I was watching Loki™ at the time...)_ are the sets of +dimensional "features" and related configuration details produced from a +Cartesian product across each of the dimensional keys. Dimensional configs are +merged together in the order they appear in `build.conf`. + +If two dimensional keys are incompatible (for example, **version/3.11** did not +yet support **arch/aarch64**), an `EXCLUDE` directive indicates that such a +variant is non-viable, and will be skipped. + +Likewise, if one dimension's configuration depends on the value of a different +dimensional key, the `WHEN` directive will supply the conditional config +details when that other dimensional key is part of the variant. + +Currently the base set of dimensions (and dimension keys) are... + +**version** - current "release" value for each is autodetected, and always a + component of an image's name +* **edge** ("release" value is the current UTC date) +* all *non-EOL* Alpine Linux versions + + **arch** - machine architecture + * **x86_64** (aka "amd64") + * **aarch64** (aka "arm64") + +**firmware** - machine boot firmware +* **bios** (legacy BIOS) +* **uefi** + +**bootstrap** - image instantiation bootstrap is provided by... +* **tiny** (tiny-cloud-boostrap) +* **cloudinit** (cloud-init) + +**cloud** - cloud provider or platform +* **aws** - Amazone Web Services / EC2 +* **oci** - Oracle Cloud Infrastructure _(WiP)_ +* **gcp** - Google Cloud Platform _(WiP)_ +* **azure** - Microsoft Azure _(WiP)_ + +...each dimension may (or may not) contribute to the image name or description, +if the dimensional key's config contributes to the `name` or `description` +array values. + +### Customized Builds + +_(TODO)_ \ No newline at end of file diff --git a/README_BROKER.md b/README_BROKER.md deleted file mode 100644 index 13e1af3..0000000 --- a/README_BROKER.md +++ /dev/null @@ -1,175 +0,0 @@ -# AWS Identity Broker - -The identity broker is used to obtain short-lived and per-region credentials -for an account. Opt-in regions require the use of a long-lived credential (e.g. -IAM user), enabling global STS tokens, or an STS token sourced in that region. -The identity broker holds long-term credentials and uses them to acquire -short-term credentials in a given region. The broker also provides a list of -opt-in regions and should be used to enumerate regions. - -For human-interactive users the identity broker performs OAUTH against GitHub -to chain the user's GitHub identity to the tokens they are given from the -broker. The broker also provides the user an API key to use when interacting -with the broker programmatically. - -As of May 2020 the identity broker is not open sourced. If you want to provide -your own identity broker, the rest of this document specifies the URLs -endpoints and response formats to do so. - -# The API - -The identity broker API is a REST-ful service with an entry-point of -`/api/account`. All further navigation through the API follows links within the -hypertext. - -**Note:** Outside of the account entry-point, URI formats should be considered -implementation details of the broker API and should never be templated. Beyond -the account entry-point, nothing in this specification is normative with -respect to URI paths and locations. - -## Authentication - -All requests to the API must be authenticated with a broker-specific key. That -key is provided to the broker in the `X-API-Key` header. When the broker -determines that the key is either expired or invalid it must redirect the user -to `/logout` to indicate that the user is logged out and must log-in again. - -API keys are bearer tokens and thus must only be exchanged over HTTPS. - -## Status Codes - -`200 OK`: indicates that the request to the broker was successful. - -`302 Found`: indicates that the broker is providing a redirect. Users should -check the redirect, if it is to the location `/logout` the user should consider -themselves logged out and proceed to login. This condition should not be -followed. Otherwise the user should follow all redirects. - -`400 Bad Request`: indicates that some part of the request is invalid as -submitted. The hypertext MAY provide a description of this error and how to -remedy it. - -`429 Rate Limit Exceeded`: indicates that the broker has rate-limited the user. -A user should discontinue requests to the broker immediately and wait for at -least 30 seconds before continuing their requests. The rate limit parameters -are specific to the broker and not controlled by this spec. - -`500 Server Error`: indicates a server error condition that is not under the -user's control. - -## Account End-point - -The account end-point acts as a index of the rest of the API. It presents a -list of accounts to which the user has access as well as links to navigate -further into the API. The format of this document is: - -`short_name` (string): a url-safe name for the account, used as the primary -account identifier within the broker. - -`account_number` (integer): the AWS account number - -`name` (string): a user-friendly name for the account - -`console_redirect_url` (uri): a URI that, when followed, leads to a resource -that redirects the user to an authenticated console session. - -`get_console_url` (uri): a URI that, when followed, leads to a console URL -resource. - -`credentials_url` (uri): a URI that, when followed, leads to a region list -resource. - -`global_credential_url` (uri): a URI that, when followed, leads to a credential -resource which provides a credential usable by all non-opt-in regions. The -contents of this resource are a STS global credential which is not usable in -opt-in regions. - -``` -[ - { - "short_name": "primary-account", - "account_number": 123456789012, - "name": "Primary AWS Account", - "console_redirect_url": "https://broker/api/account/primary-account/console?redirect=1", - "get_console_url": "https://broker/api/account/primary-account/console", - "credentials_url": "https://broker/api/account/primary-account/credentials", - "global_credential_url": "https://broker/api/account/primary-account/credentials/global" - } -] -``` - -## Console URL Resource - -**Note:** This resource is not used by the build scripts. - -The console URL resource provides a URL to the AWS console. This resource is -designed for interactive use. - -When provided the query parameter `redirect` with a value of `1` this resource -will not generate a body and will instead redirect to the URL that would have -been returned for `console_url`. - -`console_url` (uri): a link to the AWS console with authentication credentials -embedded. - -``` -{ - "console_url": "https://signin.aws.amazon.com/federation?..." -} -``` - -## Credential Resource - -The credential resource provides a set of credentials that can be used to -configure AWS tools to access an account. - -`access_key` (string): the AWS access key ID - -`secret_key` (string): the AWS secret access key - -`session_token` (string): the AWS session token - -`expiration` (iso-formatted date): the date and time when the credential will -expire - -``` -{ - "access_key": "ASIA123ABC456DEF567G", - "secret_key": "r7KcIuGdPwoUG2YOLISX2XDrVts55IFGTGaY5Tqa", - "session_token": "7C7FyvzyneaS/eRCVDcjHOSTTIHQyvhGqW...", - "expiration": "2020-01-01T00:00:00Z" -} -``` - -## Region List Resource - -The region list resource provides a list of regions associated with the account -both opted-in and not. For opted-in regions the resource includes a link to a -credential resource for that region. - -`name` (string): AWS name for the region - -`enabled` (boolean): indicates if the region is enabled and opted-in for this -account. - -`credentials_url` (uri): a URI that, when followed, leads to a credential -resource containing a credential for access to that region. The credential -provided will be usable against the region-local STS endpoint for the specified -region. This also applies for classic regions, which typically use a global -endpoint and credential. The returned credential is scoped to the acquiring -region and may not be usable against the global endpoints or a different -regional endpoint. - -``` -[ - { - "name": "af-south-1", - "enabled": false - }, - { - "name": "us-west-2", - "enabled": true, - "credentials_url": "https://broker/api/account/primary-account/credentials/us-west-2" - } -] -``` diff --git a/alpine.pkr.hcl b/alpine.pkr.hcl new file mode 100644 index 0000000..3c93c5e --- /dev/null +++ b/alpine.pkr.hcl @@ -0,0 +1,191 @@ +# Enable script debug output, set via 'packer build -var DEBUG=1' +variable "DEBUG" { + default = 0 +} +variable "USE_BROKER" { + default = 0 +} + +# Tuneable based on perfomance of whatever Packer's running on, +# override with './build --vars ' +variable "qemu" { + default = { + boot_wait = { + aarch64 = "1m" + x86_64 = "1m" + } + cmd_wait = "5s" + ssh_timeout = "1m" + memory = 1024 # MiB + } +} + + +locals { + debug_arg = var.DEBUG == 0 ? "" : "--debug" + broker_arg = var.USE_BROKER == 0 ? "" : "--use-broker" + + # randomly generated password + password = uuidv4() + + # all build configs + all_configs = yamldecode(file("work/configs.yaml")) + + # load the build actions to be taken + actions = yamldecode(file("work/actions.yaml")) + + # resolve actionable build configs + configs = { for b, acfg in local.actions: + b => merge(local.all_configs[b], acfg) if length(acfg.actions) > 0 + } +} + +### Build Sources + +# Don't build +source null alpine { + communicator = "none" +} + +# Common to all QEMU builds +source qemu alpine { + # qemu machine + headless = true + memory = var.qemu.memory + net_device = "virtio-net" + disk_interface = "virtio" + + # build environment + boot_command = [ + "root", + "setup-interfaces", + "ifup eth0", + "setup-sshd -c openssh", + "echo PermitRootLogin yes >> /etc/ssh/sshd_config", + "service sshd restart", + "echo 'root:${local.password}' | chpasswd", + ] + ssh_username = "root" + ssh_password = local.password + ssh_timeout = var.qemu.ssh_timeout + shutdown_command = "poweroff" +} + +build { + name = "alpine" + + ## Builders + + # QEMU builder + dynamic "source" { + for_each = { for b, c in local.configs: + b => c if contains(c.actions, "build") && c.builder == "qemu" + } + iterator = B + labels = ["qemu.alpine"] # links us to the base source + + content { + name = B.key + + # qemu machine + qemu_binary = "qemu-system-${B.value.arch}" + qemuargs = B.value.qemu.args + machine_type = B.value.qemu.machine_type + firmware = B.value.qemu.firmware + + # build environment + iso_url = B.value.qemu.iso_url + iso_checksum = "file:${B.value.qemu.iso_url}.sha512" + boot_wait = var.qemu.boot_wait[B.value.arch] + + # results + output_directory = B.value.image.dir + disk_size = B.value.image.size + format = B.value.image.format + vm_name = B.value.image.file + } + } + + # Null builder (don't build, but we might import and/or publish) + dynamic "source" { + for_each = { for b, c in local.configs: + b => c if !contains(c.actions, "build") + } + iterator = B + labels = ["null.alpine"] + content { + name = B.key + } + } + + ## build provisioners + + # install setup files + dynamic "provisioner" { + for_each = { for b, c in local.configs: + b => c if contains(c.actions, "build") + } + iterator = B + labels = ["file"] + content { + only = [ "${B.value.builder}.${B.key}" ] # configs specific to one build + + sources = [ for d in B.value.script_dirs: "work/scripts/${d}" ] + destination = "/tmp/" + } + } + + # run setup scripts + dynamic "provisioner" { + for_each = { for b, c in local.configs: + b => c if contains(c.actions, "build") + } + iterator = B + labels = ["shell"] + content { + only = [ "${B.value.builder}.${B.key}" ] # configs specific to one build + + scripts = [ for s in B.value.scripts: "work/scripts/${s}" ] + use_env_var_file = true + environment_vars = [ + "DEBUG=${var.DEBUG}", + "ARCH=${B.value.arch}", + "BOOTSTRAP=${B.value.bootstrap}", + "BUILD_NAME=${B.value.name}", + "BUILD_REVISION=${B.value.revision}", + "CLOUD=${B.value.cloud}", + "END_OF_LIFE=${B.value.end_of_life}", + "FIRMWARE=${B.value.firmware}", + "IMAGE_LOGIN=${B.value.image.login}", + "INITFS_FEATURES=${B.value.initfs_features}", + "KERNEL_MODULES=${B.value.kernel_modules}", + "KERNEL_OPTIONS=${B.value.kernel_options}", + "PACKAGES_ADD=${B.value.packages.add}", + "PACKAGES_DEL=${B.value.packages.del}", + "PACKAGES_NOSCRIPTS=${B.value.packages.noscripts}", + "RELEASE=${B.value.release}", + "REPOS=${B.value.repos}", + "SERVICES_ENABLE=${B.value.services.enable}", + "SERVICES_DISABLE=${B.value.services.disable}", + "VERSION=${B.value.version}", + ] + } + } + + ## build post-processor + + # import and/or publish cloud images + dynamic "post-processor" { + for_each = { for b, c in local.configs: + b => c if contains(c.actions, "import") || contains(c.actions, "publish") + } + iterator = B + labels = ["shell-local"] + content { + only = [ "${B.value.builder}.${B.key}", "null.${B.key}" ] + inline = [ for action in ["import", "publish"]: + "./cloud_helper.py ${action} ${local.debug_arg} ${local.broker_arg} ${B.key}" if contains(B.value.actions, action) + ] + } + } +} diff --git a/configs/alpine.conf b/configs/alpine.conf new file mode 100644 index 0000000..9bde530 --- /dev/null +++ b/configs/alpine.conf @@ -0,0 +1,66 @@ +# vim: ts=2 et: + +# all build configs start with these +Default { + # image name/description components + name = [ alpine ] + description = [ "Alpine Linux {release}-r{revision}" ] + + # initial provisioning script and data directory + scripts = [ setup ] + script_dirs = [ setup.d ] + + # image settings + image.format = qcow2 + image.size = 1G + image.login = alpine +} + +# profile build matrix +Dimensions { + version { + "3.14" { include required("version/3.14.conf") } + "3.13" { include required("version/3.13.conf") } + "3.12" { include required("version/3.12.conf") } + "3.11" { include required("version/3.11.conf") } + edge { include required("version/edge.conf") } + } + arch { + x86_64 { include required("arch/x86_64.conf") } + aarch64 { include required("arch/aarch64.conf") } + } + firmware { + bios { include required("firmware/bios.conf") } + uefi { include required("firmware/uefi.conf") } + } + bootstrap { + tiny { include required("bootstrap/tiny.conf") } +# cloudinit { include required("bootstrap/cloudinit.conf") } + } + cloud { + aws { include required("cloud/aws.conf") } +# oci { include required("cloud/oci.conf") } +# gcp { include required("cloud/gcp.conf") } +# azure { include required("cloud/azure.conf") } + } +} + +# all build configs merge these at the very end +Mandatory { + description = [ - https://alpinelinux.org/cloud ] + + # final provisioning script + scripts = [ cleanup ] + + # override while developing/testing... + aws.publish { + access { + PUBLIC = false + } + regions { + ALL = false + us-west-2 = true # where i'm building + us-east-1 = true # test publishing + } + } +} diff --git a/configs/arch/aarch64.conf b/configs/arch/aarch64.conf new file mode 100644 index 0000000..41f1161 --- /dev/null +++ b/configs/arch/aarch64.conf @@ -0,0 +1,14 @@ +# vim: ts=2 et: +name = [aarch64] + +# aarch64 is UEFI only +EXCLUDE = [bios] + +qemu.machine_type = virt +qemu.args = [ + [-cpu, cortex-a57], + [-boot, d], + [-device, virtio-gpu-pci], + [-device, usb-ehci], + [-device, usb-kbd], +] diff --git a/configs/arch/x86_64.conf b/configs/arch/x86_64.conf new file mode 100644 index 0000000..4bf382a --- /dev/null +++ b/configs/arch/x86_64.conf @@ -0,0 +1,5 @@ +# vim: ts=2 et: +name = [x86_64] + +qemu.machine_type = null +qemu.args = null diff --git a/configs/bootstrap/cloudinit.conf b/configs/bootstrap/cloudinit.conf new file mode 100644 index 0000000..1285a29 --- /dev/null +++ b/configs/bootstrap/cloudinit.conf @@ -0,0 +1,2 @@ +# vim: ts=2 et: +name = [cloudinit] diff --git a/configs/bootstrap/tiny.conf b/configs/bootstrap/tiny.conf new file mode 100644 index 0000000..fa88cf4 --- /dev/null +++ b/configs/bootstrap/tiny.conf @@ -0,0 +1,10 @@ +# vim: ts=2 et: +name = [tiny] +WHEN { + aws { + packages.tiny-ec2-bootstrap = true + services.default.tiny-ec2-bootstrap = true + scripts = [ setup-tiny ] + script_dirs = [ setup-tiny.d ] + } +} diff --git a/configs/cloud/aws.conf b/configs/cloud/aws.conf new file mode 100644 index 0000000..d197a91 --- /dev/null +++ b/configs/cloud/aws.conf @@ -0,0 +1,40 @@ +# vim: ts=2 et: +builder = qemu + +aws { + s3_bucket = alpine-cloud-images + publish { + access { + PUBLIC = true + # alternately... + #PUBLIC = false + # and/or + # = true + # ... + } + regions { + ALL = true + # alternately... + # = true + # ... + } + } +} + +WHEN { + # Arch + aarch64 { + aws.arch = arm64 + } + x86_64 { + aws.arch = x86_64 + } + + # Firmware + bios { + aws.boot_mode = legacy-bios + } + uefi { + aws.boot_mode = uefi + } +} diff --git a/configs/cloud/oci.conf b/configs/cloud/oci.conf new file mode 100644 index 0000000..17a83c2 --- /dev/null +++ b/configs/cloud/oci.conf @@ -0,0 +1,2 @@ +# vim: ts=2 et: +builder = qemu \ No newline at end of file diff --git a/configs/configs.conf b/configs/configs.conf new file mode 120000 index 0000000..99f2529 --- /dev/null +++ b/configs/configs.conf @@ -0,0 +1 @@ +alpine.conf \ No newline at end of file diff --git a/configs/firmware/bios.conf b/configs/firmware/bios.conf new file mode 100644 index 0000000..ca37692 --- /dev/null +++ b/configs/firmware/bios.conf @@ -0,0 +1,8 @@ +# vim: ts=2 et: +name = [bios] + +packages { + syslinux = --no-scripts +} + +qemu.firmware = null \ No newline at end of file diff --git a/configs/firmware/uefi.conf b/configs/firmware/uefi.conf new file mode 100644 index 0000000..b37c25b --- /dev/null +++ b/configs/firmware/uefi.conf @@ -0,0 +1,15 @@ +# vim: ts=2 et: +name = [uefi] + +packages { + grub-efi = --no-scripts +} + +WHEN { + aarch64 { + qemu.firmware = work/firmware/uefi-aarch64.bin + } + x86_64 { + qemu.firmware = work/firmware/uefi-x86_64.bin + } +} \ No newline at end of file diff --git a/configs/version/3.11.conf b/configs/version/3.11.conf new file mode 100644 index 0000000..eb5359d --- /dev/null +++ b/configs/version/3.11.conf @@ -0,0 +1,8 @@ +# vim: ts=2 et: + +include required("base/1.conf") + +end_of_life = "2021-11-01" + +# Alpine 3.11 doesn't support aarch64 +EXCLUDE = [ aarch64 ] diff --git a/configs/version/3.12.conf b/configs/version/3.12.conf new file mode 100644 index 0000000..7c3d71d --- /dev/null +++ b/configs/version/3.12.conf @@ -0,0 +1,5 @@ +# vim: ts=2 et: + +include required("base/1.conf") + +end_of_life = "2022-05-01" \ No newline at end of file diff --git a/configs/version/3.13.conf b/configs/version/3.13.conf new file mode 100644 index 0000000..26d7cd6 --- /dev/null +++ b/configs/version/3.13.conf @@ -0,0 +1,5 @@ +# vim: ts=2 et: + +include required("base/2.conf") + +end_of_life = "2022-11-01" \ No newline at end of file diff --git a/configs/version/3.14.conf b/configs/version/3.14.conf new file mode 100644 index 0000000..98acad1 --- /dev/null +++ b/configs/version/3.14.conf @@ -0,0 +1,5 @@ +# vim: ts=2 et: + +include required("base/2.conf") + +end_of_life = "2023-05-01" \ No newline at end of file diff --git a/configs/version/base/1.conf b/configs/version/base/1.conf new file mode 100644 index 0000000..628fe85 --- /dev/null +++ b/configs/version/base/1.conf @@ -0,0 +1,66 @@ +# vim: ts=2 et: + +repos { + "https://dl-cdn.alpinelinux.org/alpine/v{version}/main" = true + "https://dl-cdn.alpinelinux.org/alpine/v{version}/community" = true + "https://dl-cdn.alpinelinux.org/alpine/v{version}/testing" = false +} + +packages { + alpine-base = true + linux-virt = true + alpine-mirrors = true + chrony = true + iproute2 = true + nvme-cli = true + openssh = true + sudo = true + tzdata = true +} + +services { + sysinit { + devfs = true + dmesg = true + hwdrivers = true + mdev = true + } + boot { + acpid = true + bootmisc = true + hostname = true + hwclock = true + modules = true + swap = true + sysctl = true + syslog = true + } + default { + chronyd = true + networking = true + sshd = true + } + shutdown { + killprocs = true + mount-ro = true + savecache = true + } +} + +kernel_modules { + sd-mod = true + usb-storage = true + ext4 = true + nvme = true + ena = true +} + +kernel_options { + "console=ttyS0,115200n8" = true + "nvme_core.io_timeout=4294967295" = true +} + +initfs_features { + nvme = true + ena = true +} diff --git a/configs/version/base/2.conf b/configs/version/base/2.conf new file mode 100644 index 0000000..644485b --- /dev/null +++ b/configs/version/base/2.conf @@ -0,0 +1,11 @@ +# vim: ts=2 et: + +include required("1.conf") + +packages { + # drop old alpine-mirrors + alpine-mirrors = null + # use iproute2-minimal instead of full iproute2 + iproute2 = null + iproute2-minimal = true +} diff --git a/configs/version/base/3.conf b/configs/version/base/3.conf new file mode 100644 index 0000000..3aba9b8 --- /dev/null +++ b/configs/version/base/3.conf @@ -0,0 +1,9 @@ +# vim: ts=2 et: + +include required("2.conf") + +packages { + # doas replaces sudo + sudo = null + doas = true +} diff --git a/configs/version/edge.conf b/configs/version/edge.conf new file mode 100644 index 0000000..4818ada --- /dev/null +++ b/configs/version/edge.conf @@ -0,0 +1,11 @@ +# vim: ts=2 et: + +include required("base/3.conf") + +# clear out inherited repos +repos = null +repos { + "https://dl-cdn.alpinelinux.org/alpine/edge/main" = true + "https://dl-cdn.alpinelinux.org/alpine/edge/community" = true + "https://dl-cdn.alpinelinux.org/alpine/edge/testing" = true +} diff --git a/packer.conf b/packer.conf deleted file mode 100644 index b7ab850..0000000 --- a/packer.conf +++ /dev/null @@ -1,114 +0,0 @@ -# This Packer config file is in HOCON, and is converted to JSON at build time. -# https://github.com/lightbend/config/blob/master/HOCON.md -# vim: ts=2 et: - -builders = [ - { - type = "amazon-ebssurrogate" - profile = "{{user `aws_profile`}}" - - ### Builder Instance Details - - region = "{{user `build_region`}}" - subnet_id = "{{user `build_subnet`}}" - instance_type = "{{user `build_instance_type`}}" - associate_public_ip_address = "{{user `build_public_ip`}}" - source_ami_filter { - # use the latest Amazon Linux AMI - owners = [ "{{user `build_ami_owner`}}" ] - most_recent = "{{user `build_ami_latest`}}" - filters { - virtualization-type = "hvm" - root-device-type = "ebs" - architecture = "{{user `build_arch`}}" - name = "{{user `build_ami_name`}}" - } - } - launch_block_device_mappings = [ - { - volume_type = "gp2" - device_name = "/dev/xvdf" - encrypted = "{{user `ami_encrypt`}}" - delete_on_termination = "true" - volume_size = "{{user `ami_volume_size`}}" - } - ] - shutdown_behavior = "terminate" - ssh_username = "{{user `build_user`}}" - - ### AMI Build Details - - ami_name = "{{user `ami_name`}}" - ami_description = "{{user `ami_desc`}}" - tags { - Name = "{{user `ami_name`}}" - profile = "{{user `profile`}}" - profile_build = "{{user `profile_build`}}" - version = "{{user `version`}}" - release = "{{user `release`}}" - arch = "{{user `arch`}}" - revision = "{{user `revision`}}" - end_of_life = "{{user `end_of_life`}}" - } - ami_virtualization_type = "hvm" - ami_architecture = "{{user `build_arch`}}" # need packer 1.4.1 - ami_root_device { - volume_type = "gp2" - source_device_name = "/dev/xvdf" - device_name = "/dev/xvda" - delete_on_termination = "true" - volume_size = "{{user `ami_volume_size`}}" - } - ena_support = "true" - sriov_support = "true" - ami_users = "{{user `aws_users`}}" - } -] - - -provisioners = [ - { - type = "file" - source = "./profile/{{user `profile`}}/{{user `profile_build`}}/setup-ami.d" - destination = "/tmp/setup-ami.d" - } - { - type = "shell" - script = "../scripts/setup-ami" - environment_vars = [ - "VERSION={{user `version`}}" - "RELEASE={{user `release`}}" - "REVISION={{user `revision`}}" - "ARCH={{user `arch`}}" - "BOOTLOADER={{user `bootloader`}}" - "REPOS={{user `repos`}}" - "PKGS={{user `pkgs`}}" - "SVCS={{user `svcs`}}" - "KERNEL_MODS={{user `kernel_modules`}}" - "KERNEL_OPTS={{user `kernel_options`}}" - "INITFS_FEATURES={{user `initfs_features`}}" - "EC2_USER={{user `ami_user`}}" - ] - use_env_var_file = "true" - execute_command = "sudo sh -c '. {{.EnvVarFile}} && {{.Path}}'" - } -] - - -post-processors = [ - { - type = "manifest" - output = "profile/{{user `profile`}}/{{user `profile_build`}}/manifest.json" - custom_data { - ami_name = "{{user `ami_name`}}" - ami_desc = "{{user `ami_desc`}}" - profile = "{{user `profile`}}" - profile_build = "{{user `profile_build`}}" - version = "{{user `version`}}" - release = "{{user `release`}}" - arch = "{{user `arch`}}" - revision = "{{user `revision`}}" - end_of_life = "{{user `end_of_life`}}" - } - } -] diff --git a/profiles/README.md b/profiles/README.md deleted file mode 100644 index 568b2fc..0000000 --- a/profiles/README.md +++ /dev/null @@ -1,174 +0,0 @@ -# Profiles - -Profiles are collections of related build definitions, which are used to -generate the `vars.json` files that [Packer](https://packer.io) consumes -when building AMIs. - -Profiles use [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md) -(Human-Optimized Config Object Notation) which allows importing common configs -from other files, simple variable interpolation, and easy merging of objects. -This flexibility helps keep configuration for related build targets -[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). - -## Core Profiles - -Core profile configurations are found in the `base`, `version`, and `arch` -subdirectories. Core profiles do not have a `.conf` suffix because they're not -meant to be directly used like target profiles with the `builder.py` script. - -Base core profiles define all build vars with default values -- those left -empty or null are usually set in version, arch, or target profile configs. -Base profiles are included in version profiles, and do not need to be included -in target profiles. - -Version core profiles expand on the base profile they include, and set the -`version`, `release`, `end_of_life` (if known), and the associated Alpine Linux -`repos`. - -Arch core profiles further define architecture-specific variables, such as -which `apk-tools` and `alpine-keys` to use (and their SHA256 checksums). - -## Target Profiles - -Target profiles, defined in this directory, are the top-level configuration -used with `./scripts/builder.py`; they must have a `.conf` suffix. Several -configuration objects are defined and later merged within the `BUILDS` object, -ultimately defining each individual build. - -Simple profiles have an object that loads a "version" core profile and -another that loads an "arch" core profile. A more complicated version-arch -matrix profile would have an object for each version and arch. - -Additionally, there are one or more objects that define profile-specific -settings. - -The `BUILDS` object's elements merge core and profile configs (with optional -inline build settings) into named build definitions; these build names can be -used to specify a subset of a profile's builds, for example: -`./scripts/builder.py amis ...` - -**Please note that merge order matters!** The merge sequence is version --> -architecture --> profile --> build. - -## Customization - -If the AWS configuration you're using does not specify a default region, your -custom profile will need to specify `build_region`. If the build region does -not have a default VPC, you'll need to specify `build_subnet`. - -`version` and `release` are meant to match Alpine; however, `revision` is used -used to track changes to the profile, additions of new -[alpine-ec2-ami](https://github.com/mcrute/alpine-ec2-ami) features, -or other situations where the AMIs needs to be rebuilt. The "edge" core -version profile sets `revision` to null, which translates into the current -datetime. Otherwise, the default set in the base profile is `r0`. - -You will probably want to personalize the name and description of your AMI. -Set `ami_name_prefix` and `ami_name_suffix`; setting `ami_desc_suffix` and -`ami_desc_suffix` is optional. - -Set `build_instance_type` if you want/need to use a different instance type to -build the image; the default is `t3.nano`. - -If 1 GiB is not enough to install the packages in your base AMI, you can set -the `ami_volume_size` to the number of GiB you need. Note, however, that the -[tiny-ec2-bootstrap](https://github.com/mcrute/tiny-ec2-bootstrap) init script -will expand the root partition to use the instance's entire EBS root volume -during the first boot, so you shouldn't need to make space for anything other -than installed packages. - -Set `ami_encrypt` to "true" to create an encrypted AMI image. Launching images -from an encrypted AMI results in an encrypted EBS root volume. Please note -that if your AMI is encrypted, only the owning account will be able to use it. - -_*NOTE*: The following funcitonality that is currently not operational -- it -is pending completion and integration of a new release tool. In the meantime, -you will have to manually copy AMIs from the build region to other regions._ - -To copy newly built AMIs to regions other than the `build_region` region, set -`ami_regions`. This variable is a *hash*, which allows for finer control over -inherited values when merging configs. Region identifiers are the keys, a -value of `true` means the AMI should be copied to that region; `null` or -`false` indicate that it shouldn't be copied to that region. If you want to -ensure that the `ami_regions` hash does not inherit any values, set it to -`null` before configuring your regions. For example: -``` -ami_regions = null # don't inherit any previous values -ami_regions { - us-west-2 = true - eu-north-1 = true -} -``` - -By default, the AMIs built are accessible only by the owning account. To -make your AMIs publicly available, set the `ami_access` hash variable: -``` -ami_access { - all = true -} -``` - -Controlling what packages are installed and enabled in the AMI is the number -one reason for creating custom profile. The `repos`, `pkgs`, and `svcs` hash -variables serve precisely that purpose. With some exceptions (noted below), -they work the same as the `ami_regions` hash: `true` values enable, `false` -and `null` values disable, and inherited values can be cleared by first setting -the variable itself to `null`. - -With `repos`, the keys are double-quoted URLs to the `apk` repos that you want -set up; these are initially set in the "version" core profiles. In addition -to the `true`, `false`, and `null` values, you can also use a "repo alias" -string value, allowing you to pin packages to be sourced from that particular -repo. For example, with a profile based from a non-edge core profile, you may -want to be able to pull packages from the edge testing repo: -``` -repos { - "http://dl-cdn.alpinelinux.org/alpine/edge/testing" = "edge-testing" -} -``` - -The `pkgs` hash's default is set in the base core profile; its keys are -simply the Alpine package to install (or not install, if the value is `false` -or `null`). A `true` value installs the package from the default repos; if the -value is a repo alias string, the package will be pinned to explicitly install -from that repo. For example: -``` -pkgs { - # install docker-compose from edge-testing repo - docker-compose = "edge-testing" -} -``` - -To control when (or whether) a system service starts, use the `svcs` hash -variable. Its first-level keys are names of runlevels (`sysinit`, `boot`, -`default`, and `shutown`), and the second-level keys are the services, as they -appear in `/etc/init.d`. Like the other profile hash variables, setting -`false` or `null` disable the service in the runlevel, `true` will enable the -service. - -Further customization can be done by specifying your own setup script with the -`setup_script` profile variable. This will be copied to the build instance at -`/tmp/setup-ami.d/setup_script`, and executed by the `setup-ami` script just -before the final cleanup phase. - -If there are additional data or scripts that your setup script uses, use the -`setup_copy` hash variable -- the key is the destination path under the build -instance's `/tmp/setup-ami.d` directory, and the value is the local path to -the source file or directory. No data is automatically installed in the AMI, -and no additional scripts are executed -- you must explicitly install/execute -via the `setup_script` script. - -The AMI's default login user is `alpine`. If you want to specify a alternate -login, set it with the `ami_user` profile variable. This setting is saved in -`/etc/conf.d/tiny-ec2-bootstrap` as `EC2_USER` and -[tiny-ec2-bootstrap](https://github.com/mcrute/tiny-ec2-bootstrap) -will use that valie instead of `alpine`. - -## Limitations and Caveats - -* Hash variables that are reset to clear inherited values *must* be - re-defined as a hash, even if it is to remain empty: - ``` - hash_var = null # drops inherited values - hash_var {} # re-defines as an empty hash - ``` diff --git a/profiles/alpine.conf b/profiles/alpine.conf deleted file mode 100644 index 62b44dd..0000000 --- a/profiles/alpine.conf +++ /dev/null @@ -1,33 +0,0 @@ -### Profile for Building the Publically-Available Alpine Linux AMIs -# vim: ts=2 et: - -version-3_14 { include required("version/3.14") } -version-3_13 { include required("version/3.13") } -version-3_12 { include required("version/3.12") } -version-3_11 { include required("version/3.11") } -version-edge { include required("version/edge") } -arch-x86_64 { include required("arch/x86_64") } -arch-aarch64 { include required("arch/aarch64") } - -# profile vars -community { - ami_desc_suffix = " - https://github.com/mcrute/alpine-ec2-ami" -} -official { - ami_desc_suffix = " - https://alpinelinux.org/cloud" -} - -# Build definitions -BUILDS { - # merge version, arch, profile; add per-build { revision = "r1" } if needed - edge-x86_64 = ${version-edge} ${arch-x86_64} ${official} - v3_14-x86_64 = ${version-3_14} ${arch-x86_64} ${official} - v3_13-x86_64 = ${version-3_13} ${arch-x86_64} ${official} - v3_12-x86_64 = ${version-3_12} ${arch-x86_64} ${community} - v3_11-x86_64 = ${version-3_11} ${arch-x86_64} ${community} - - edge-aarch64 = ${version-edge} ${arch-aarch64} ${official} - v3_14-aarch64 = ${version-3_14} ${arch-aarch64} ${official} - v3_13-aarch64 = ${version-3_13} ${arch-aarch64} ${official} { revision = "r1" } - v3_12-aarch64 = ${version-3_12} ${arch-aarch64} ${community} { revision = "r1" } -} diff --git a/profiles/arch/aarch64 b/profiles/arch/aarch64 deleted file mode 120000 index f1b77e5..0000000 --- a/profiles/arch/aarch64 +++ /dev/null @@ -1 +0,0 @@ -aarch64-1 \ No newline at end of file diff --git a/profiles/arch/aarch64-1 b/profiles/arch/aarch64-1 deleted file mode 100644 index 34eb665..0000000 --- a/profiles/arch/aarch64-1 +++ /dev/null @@ -1,7 +0,0 @@ -### aarch64 vars, revision 1 -# vim: ts=2 et: - -arch = "aarch64" -bootloader = "grub-efi" -build_arch = "arm64" -build_instance_type = "t4g.nano" diff --git a/profiles/arch/x86_64 b/profiles/arch/x86_64 deleted file mode 120000 index f3c4f51..0000000 --- a/profiles/arch/x86_64 +++ /dev/null @@ -1 +0,0 @@ -x86_64-1 \ No newline at end of file diff --git a/profiles/arch/x86_64-1 b/profiles/arch/x86_64-1 deleted file mode 100644 index 3deceff..0000000 --- a/profiles/arch/x86_64-1 +++ /dev/null @@ -1,7 +0,0 @@ -### x86_64 vars, revision 1 -# vim: ts=2 et: - -arch = "x86_64" -bootloader = "syslinux" -build_arch = "x86_64" -build_instance_type = "t3a.nano" diff --git a/profiles/base/1 b/profiles/base/1 deleted file mode 100644 index 0ab69be..0000000 --- a/profiles/base/1 +++ /dev/null @@ -1,103 +0,0 @@ -### base vars, revision 1 -# vim: ts=2 et: - -# Profile/Build -profile = null -profile_build = null -revision = "r0" - -# Versioning -version = null -release = null # defaults to autodetect -end_of_life = null - -# Architecture -arch = null -build_arch = null -build_instance_type = null - -# Builder-instance -build_region = "us-west-2" -build_subnet = null -build_public_ip = null -build_user = "ec2-user" -build_ami_name = "amzn2-ami-hvm-2.0.*-gp2" -build_ami_owner = "137112412989" -build_ami_latest = "true" - -# AMI build/deploy -aws_profile = null # AWS profile to build AMI -aws_accounts = null # comma-separated AWS accounts allowed to launch AMI -ami_name_prefix = "alpine-ami-" -ami_name_suffix = "" -ami_desc_prefix = "Alpine Linux " -ami_desc_suffix = "" -ami_volume_size = "1" -ami_encrypt = "false" -ami_user = "alpine" - -# NOTE: the following are python format strings, resolved in builder.py -ami_name = "{var.ami_name_prefix}{var.release}-{var.arch}-{var.revision}{var.ami_name_suffix}" -ami_desc = "{var.ami_desc_prefix}{var.release} {var.arch} {var.revision}{var.ami_desc_suffix}" - -# AMI configuration -bootloader = null -repos {} -pkgs { - linux-virt = true - alpine-mirrors = true - chrony = true - iproute2 = true - nvme-cli = true - openssh = true - sudo = true - tiny-ec2-bootstrap = true - tzdata = true -} -svcs { - sysinit { - devfs = true - dmesg = true - hwdrivers = true - mdev = true - } - boot { - acpid = true - bootmisc = true - hostname = true - hwclock = true - modules = true - swap = true - sysctl = true - syslog = true - } - default { - chronyd = true - eth-eni-setup = true - networking = true - sshd = true - tiny-ec2-bootstrap = true - } - shutdown { - killprocs = true - mount-ro = true - savecache = true - } -} -kernel_modules { - sd-mod = true - usb-storage = true - ext4 = true - nvme = true - ena = true -} -kernel_options { - "console=ttyS0,115200n8" = true - "nvme_core.io_timeout=4294967295" = true -} -# NOTE: nvme and ena are critical for i3, a1, m6g, and anything in the 5 series -# forward. Without them these instances will not boot. -initfs_features { - nvme = true - ena = true -} diff --git a/profiles/base/2 b/profiles/base/2 deleted file mode 100644 index 7e39ebf..0000000 --- a/profiles/base/2 +++ /dev/null @@ -1,111 +0,0 @@ -### base vars, revision 2 -# vim: ts=2 et: - -# Profile/Build -profile = null -profile_build = null -revision = "r0" - -# Versioning -version = null -release = null # defaults to autodetect -end_of_life = null - -# Architecture -arch = null -build_arch = null -build_instance_type = null - -# Builder-instance -build_region = "us-west-2" -build_subnet = null -build_public_ip = null -build_user = "ec2-user" -build_ami_name = "amzn2-ami-hvm-2.0.*-gp2" -build_ami_owner = "137112412989" -build_ami_latest = "true" - -# AMI build/deploy -aws_profile = null # AWS profile to build AMI -aws_accounts = null # comma-separated AWS accounts allowed to launch AMI -ami_name_prefix = "alpine-ami-" -ami_name_suffix = "" -ami_desc_prefix = "Alpine Linux " -ami_desc_suffix = "" -ami_volume_size = "1" -ami_encrypt = "false" -ami_user = "alpine" - -# NOTE: the following are python format strings, resolved in builder.py -ami_name = "{var.ami_name_prefix}{var.release}-{var.arch}-{var.revision}{var.ami_name_suffix}" -ami_desc = "{var.ami_desc_prefix}{var.release} {var.arch} {var.revision}{var.ami_desc_suffix}" - -# AMI configuration -bootloader = null -repos {} -pkgs { - linux-virt = true - chrony = true - iproute2-minimal = true - nvme-cli = true - openssh = true - sudo = true - tiny-ec2-bootstrap = true - tzdata = true -} -svcs { - sysinit { - devfs = true - dmesg = true - hwdrivers = true - mdev = true - } - boot { - acpid = true - bootmisc = true - hostname = true - hwclock = true - modules = true - swap = true - sysctl = true - syslog = true - } - default { - chronyd = true - eth-eni-setup = true - networking = true - sshd = true - tiny-ec2-bootstrap = true - } - shutdown { - killprocs = true - mount-ro = true - savecache = true - } -} -kernel_modules { - sd-mod = true - usb-storage = true - ext4 = true - nvme = true - ena = true -} -kernel_options { - "console=ttyS0,115200n8" = true - "nvme_core.io_timeout=4294967295" = true -} -# NOTE: nvme and ena are critical for i3, a1, m6g, and anything in the 5 -# series forward. Without them these instances will not boot. -initfs_features { - nvme = true - ena = true -} - -# Local path to additional setup script, runs before setup-ami cleanup. -setup_script = null - -# Files/directories to copy to /tmp/setup-ami.d/ on build instance for -# setup-script to use. Map key is the copy target in the build instance -# /tmp/setup-ami.d/ directory, map value is local file/directory path. -# Nothing copied ends up in the AMI unless `setup_script` does it. -setup_copy = null diff --git a/profiles/base/current b/profiles/base/current deleted file mode 120000 index d8263ee..0000000 --- a/profiles/base/current +++ /dev/null @@ -1 +0,0 @@ -2 \ No newline at end of file diff --git a/profiles/test.conf b/profiles/test.conf deleted file mode 100644 index e7c8e53..0000000 --- a/profiles/test.conf +++ /dev/null @@ -1,38 +0,0 @@ -### Profile for Testing Builds -# vim: ts=2 et: - -version-3_13 { include required("version/3.13") } -version-3_12 { include required("version/3.12") } -version-3_11 { include required("version/3.11") } -version-3_10 { include required("version/3.10") } -version-edge { include required("version/edge") } - -arch-x86_64 { include required("arch/x86_64") } -arch-aarch64 { include required("arch/aarch64") } - -# specific to this profile's builds -test { - #bootloader = "EFI_STUB" # currently does not work - ami_name_prefix = "test-" - ami_desc_prefix = "Alpine Test " - ami_user = "test" - setup_script = scripts/test-setup_script.sh - setup_copy { - base = profiles/base - aarch64 = profiles/arch/aarch64 - } -} - -# Build definitions -BUILDS { - # merge version, arch, profile, and build vars - v3_13-x86_64 = ${version-3_13} ${arch-x86_64} ${test} - v3_12-x86_64 = ${version-3_12} ${arch-x86_64} ${test} - v3_11-x86_64 = ${version-3_11} ${arch-x86_64} ${test} { revision = "r1" } - v3_10-x86_64 = ${version-3_10} ${arch-x86_64} ${test} { revision = "r2" } - edge-x86_64 = ${version-edge} ${arch-x86_64} ${test} - - v3_13-aarch64 = ${version-3_13} ${arch-aarch64} ${test} - v3_12-aarch64 = ${version-3_12} ${arch-aarch64} ${test} - edge-aarch64 = ${version-edge} ${arch-aarch64} ${test} -} diff --git a/profiles/version/3.10 b/profiles/version/3.10 deleted file mode 100644 index fe5248c..0000000 --- a/profiles/version/3.10 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.10 vars -# vim: ts=2 et: - -# start with base vars -include required("../base/1") - -# set version-specific vars -version = "3.10" -end_of_life = "2021-05-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.10/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.10/community" = true -} diff --git a/profiles/version/3.11 b/profiles/version/3.11 deleted file mode 100644 index e1d3b02..0000000 --- a/profiles/version/3.11 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.11 vars -# vim: ts=2 et: - -# start with base vars -include required("../base/1") - -# set version-specific vars -version = "3.11" -end_of_life = "2021-11-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.11/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.11/community" = true -} diff --git a/profiles/version/3.12 b/profiles/version/3.12 deleted file mode 100644 index 1b15751..0000000 --- a/profiles/version/3.12 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.12 vars -# vim: ts=2 et: - -# start with base vars -include required("../base/1") - -# set version-specific vars -version = "3.12" -end_of_life = "2022-05-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.12/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.12/community" = true -} diff --git a/profiles/version/3.13 b/profiles/version/3.13 deleted file mode 100644 index 233a9ae..0000000 --- a/profiles/version/3.13 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.13 vars -# vim: ts=2 et: - -# based on current -include required("../base/current") - -# set version-specific vars -version = "3.13" -end_of_life = "2022-11-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.13/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.13/community" = true -} diff --git a/profiles/version/3.14 b/profiles/version/3.14 deleted file mode 100644 index 52d3e53..0000000 --- a/profiles/version/3.14 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.14 vars -# vim: ts=2 et: - -# based on current -include required("../base/current") - -# set version-specific vars -version = "3.14" -end_of_life = "2023-05-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.14/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.14/community" = true -} diff --git a/profiles/version/3.9 b/profiles/version/3.9 deleted file mode 100644 index a908489..0000000 --- a/profiles/version/3.9 +++ /dev/null @@ -1,13 +0,0 @@ -### version 3.9 vars -# vim: ts=2 et: - -# start with base vars -include required("../base/1") - -# set version-specific vars -version = "3.9" -end_of_life = "2021-01-01" -repos { - "http://dl-cdn.alpinelinux.org/alpine/v3.9/main" = true - "http://dl-cdn.alpinelinux.org/alpine/v3.9/community" = true -} diff --git a/profiles/version/current b/profiles/version/current deleted file mode 120000 index 3a4f41e..0000000 --- a/profiles/version/current +++ /dev/null @@ -1 +0,0 @@ -3.13 \ No newline at end of file diff --git a/profiles/version/edge b/profiles/version/edge deleted file mode 100644 index 7e5a3a8..0000000 --- a/profiles/version/edge +++ /dev/null @@ -1,16 +0,0 @@ -### edge vars -# vim: ts=2 et: - -# based on current -include required("../base/current") - -# add edge-specific tweaks... -version = "edge" -end_of_life = null # defaults to tomorrow -revision = null # defaults to datetime - -repos { - "http://dl-cdn.alpinelinux.org/alpine/edge/main" = true - "http://dl-cdn.alpinelinux.org/alpine/edge/community" = true - "http://dl-cdn.alpinelinux.org/alpine/edge/testing" = true -} diff --git a/releases/README.md b/releases/README.md deleted file mode 100644 index 28f676b..0000000 --- a/releases/README.md +++ /dev/null @@ -1,184 +0,0 @@ -# Alpine Linux EC2 AMIs - -These are the official Alpine AWS AMIs. For an index of images see the -[Alpine Website](https://alpinelinux.org/cloud/). - -These AMIs should work with most EC2 features -- such as ENIs (Elastic Network -Interfaces) and NVMe EBS (Elastic Block Storage) volumes. If you find any -problems launching these AMIs on current generation instances, please open an -[issue](https://github.com/mcrute/alpine-ec2-ami/issues) and include as much -detailed information as possible. - -All AMIs built after 2020-09-15 include support for hot-pluggable ENIs, and will -sync all associated IPv6 and secondary IPv4 addresses during `udhcpc` post-bound -and post-renew events. - -Starting with Alpine release 3.12.1, IMDSv2 (Instance MetaData Service v2) is -fully supported, and `aarch64` AMIs are provided for EC2 ARM-based instances. - -During the *first boot* of instances created with these AMIs, the lightweight -[tiny-ec2-bootstrap](https://github.com/mcrute/tiny-ec2-bootstrap) init -script... -- sets the instance's hostname, -- installs the SSH authorized_keys for the AMI user (default 'alpine'), -- disables 'root' and AMI user (default 'alpine') passwords, -- expands the root partition to use all available EBS volume space, -- and executes a "user data" script (must be a shell script that starts with `#!`) - -If you launch these AMIs to build other images (via [Packer](https://packer.io), -etc.), don't forget to remove `/var/lib/cloud/.bootstrap-complete` -- otherwise -instances launched from those second-generation AMIs will not run -`tiny-ec2-bootstrap` on their first boot. - -The more popular [cloud-init](https://cloudinit.readthedocs.io/en/latest/) is -currently not supported on Alpine Linux. If `cloud-init` support is important -to you, please open an [issue](https://github.com/mcrute/alpine-ec2-ami/issues). - -***These AMIs are also available by visiting https://alpinelinux.org/cloud*** - -## AMIs - -### Alpine Linux 3.14.1 (2021-08-07) -
click to show/hide

- -| Region | alpine-ami-3.14.1-aarch64-r0 | alpine-ami-3.14.1-x86_64-r0 | -| ------ | --- | --- | -| af-south-1 | [ami-0bcf48f55b9949a84](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0bcf48f55b9949a84) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0bcf48f55b9949a84)) | [ami-08aea7443b5d08f79](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08aea7443b5d08f79) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08aea7443b5d08f79)) | -| ap-east-1 | [ami-092aa3a38d204b2b5](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-092aa3a38d204b2b5) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-092aa3a38d204b2b5)) | [ami-0d4ba085b9c1557b8](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d4ba085b9c1557b8) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0d4ba085b9c1557b8)) | -| ap-northeast-1 | [ami-0b559d40e76da2119](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b559d40e76da2119) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0b559d40e76da2119)) | [ami-033c6bceffad8df75](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-033c6bceffad8df75) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-033c6bceffad8df75)) | -| ap-northeast-2 | [ami-07667213840caca26](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07667213840caca26) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-07667213840caca26)) | [ami-0d7a7fdf22367713c](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d7a7fdf22367713c) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0d7a7fdf22367713c)) | -| ap-northeast-3 | [ami-0956b7bafd7a6720d](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0956b7bafd7a6720d) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0956b7bafd7a6720d)) | [ami-08b52d5efd9abef56](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08b52d5efd9abef56) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-08b52d5efd9abef56)) | -| ap-south-1 | [ami-07ed5174d6da65d84](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07ed5174d6da65d84) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-07ed5174d6da65d84)) | [ami-012f6469429d4ad42](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-012f6469429d4ad42) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-012f6469429d4ad42)) | -| ap-southeast-1 | [ami-0aa966cf6ca931b11](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0aa966cf6ca931b11) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0aa966cf6ca931b11)) | [ami-01a542451033fd940](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01a542451033fd940) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-01a542451033fd940)) | -| ap-southeast-2 | [ami-069d767cedbd4d7a5](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-069d767cedbd4d7a5) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-069d767cedbd4d7a5)) | [ami-014e86c5062e164af](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-014e86c5062e164af) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-014e86c5062e164af)) | -| ca-central-1 | [ami-08bc6f39436e47c30](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08bc6f39436e47c30) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08bc6f39436e47c30)) | [ami-0bd936dc3de6b791d](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0bd936dc3de6b791d) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0bd936dc3de6b791d)) | -| eu-central-1 | [ami-06f364adb4a297706](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06f364adb4a297706) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-06f364adb4a297706)) | [ami-0bb1a41a1c0eafc95](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0bb1a41a1c0eafc95) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0bb1a41a1c0eafc95)) | -| eu-north-1 | [ami-05f1271ab15bf4557](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-05f1271ab15bf4557) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-05f1271ab15bf4557)) | [ami-0e2b6818c61b0b113](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e2b6818c61b0b113) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e2b6818c61b0b113)) | -| eu-south-1 | [ami-06fb368b1c23356ca](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06fb368b1c23356ca) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-06fb368b1c23356ca)) | [ami-0e69ca71abc28babe](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e69ca71abc28babe) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e69ca71abc28babe)) | -| eu-west-1 | [ami-0887b1a806a73417a](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0887b1a806a73417a) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0887b1a806a73417a)) | [ami-0274c314a1272d2fd](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0274c314a1272d2fd) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0274c314a1272d2fd)) | -| eu-west-2 | [ami-0d0ae9eb51f79e338](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d0ae9eb51f79e338) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0d0ae9eb51f79e338)) | [ami-0e4e3a358f49875dc](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e4e3a358f49875dc) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0e4e3a358f49875dc)) | -| eu-west-3 | [ami-08af7eab2ea99f7e5](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08af7eab2ea99f7e5) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-08af7eab2ea99f7e5)) | [ami-031817718bbaf781d](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-031817718bbaf781d) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-031817718bbaf781d)) | -| me-south-1 | [ami-02b98efa2cd0b2ab0](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02b98efa2cd0b2ab0) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02b98efa2cd0b2ab0)) | [ami-0a665daaaf042257a](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a665daaaf042257a) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a665daaaf042257a)) | -| sa-east-1 | [ami-07f46bbd5674b79a6](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07f46bbd5674b79a6) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-07f46bbd5674b79a6)) | [ami-0101f8d6e37b57173](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0101f8d6e37b57173) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0101f8d6e37b57173)) | -| us-east-1 | [ami-03175ec34a7192e70](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03175ec34a7192e70) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03175ec34a7192e70)) | [ami-0f1919ed094505be3](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f1919ed094505be3) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0f1919ed094505be3)) | -| us-east-2 | [ami-082d34382e0289102](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-082d34382e0289102) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-082d34382e0289102)) | [ami-01f0c5ccdeab2a3d8](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01f0c5ccdeab2a3d8) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-01f0c5ccdeab2a3d8)) | -| us-west-1 | [ami-0081057f803624455](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0081057f803624455) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0081057f803624455)) | [ami-0000605ad5571b4c0](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0000605ad5571b4c0) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0000605ad5571b4c0)) | -| us-west-2 | [ami-03e685d5fddaf46e7](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03e685d5fddaf46e7) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-03e685d5fddaf46e7)) | [ami-05e856cad09314ed0](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-05e856cad09314ed0) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-05e856cad09314ed0)) | - -

- -### Alpine Linux 3.13.5 (2021-06-15) -
click to show/hide

- -| Region | alpine-ami-3.13.5-aarch64-r1 | alpine-ami-3.13.5-x86_64-r0 | -| ------ | --- | --- | -| af-south-1 | [ami-077c41d657f21a319](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-077c41d657f21a319) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-077c41d657f21a319)) | [ami-0441b82fd3fb48433](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0441b82fd3fb48433) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0441b82fd3fb48433)) | -| ap-east-1 | [ami-0650eebfb386ffb89](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0650eebfb386ffb89) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0650eebfb386ffb89)) | [ami-0e2d8dfaf125a7631](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e2d8dfaf125a7631) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e2d8dfaf125a7631)) | -| ap-northeast-1 | [ami-034682c4a51e88cf7](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-034682c4a51e88cf7) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-034682c4a51e88cf7)) | [ami-02fb62943caac3c71](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02fb62943caac3c71) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02fb62943caac3c71)) | -| ap-northeast-2 | [ami-070b973637320a52c](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-070b973637320a52c) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-070b973637320a52c)) | [ami-038fc3b2d886b83ce](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-038fc3b2d886b83ce) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-038fc3b2d886b83ce)) | -| ap-northeast-3 | [ami-0df1727e6d714cc2d](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0df1727e6d714cc2d) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0df1727e6d714cc2d)) | [ami-0678e9272dbb276a1](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0678e9272dbb276a1) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0678e9272dbb276a1)) | -| ap-south-1 | [ami-073046a717b1930dc](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-073046a717b1930dc) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-073046a717b1930dc)) | [ami-0c0073e10ee3a5b66](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0c0073e10ee3a5b66) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0c0073e10ee3a5b66)) | -| ap-southeast-1 | [ami-03ac48af40b667111](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03ac48af40b667111) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03ac48af40b667111)) | [ami-07c71c29e8cfef967](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07c71c29e8cfef967) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-07c71c29e8cfef967)) | -| ap-southeast-2 | [ami-04d61dc48ced711e3](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04d61dc48ced711e3) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-04d61dc48ced711e3)) | [ami-039c2e2595e9da7fc](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-039c2e2595e9da7fc) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-039c2e2595e9da7fc)) | -| ca-central-1 | [ami-0f40fb960a6bac29a](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f40fb960a6bac29a) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0f40fb960a6bac29a)) | [ami-0a464816128999904](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a464816128999904) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a464816128999904)) | -| eu-central-1 | [ami-0dcacb8c85ee2349e](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0dcacb8c85ee2349e) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0dcacb8c85ee2349e)) | [ami-0d6ad9b3597da40c4](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d6ad9b3597da40c4) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0d6ad9b3597da40c4)) | -| eu-north-1 | [ami-0469d8b4b0f1b6493](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0469d8b4b0f1b6493) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0469d8b4b0f1b6493)) | [ami-03cb5b21303291be4](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03cb5b21303291be4) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03cb5b21303291be4)) | -| eu-south-1 | [ami-0417a5f0b9249cb6e](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0417a5f0b9249cb6e) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0417a5f0b9249cb6e)) | [ami-02bd65e399a33d2c0](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02bd65e399a33d2c0) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02bd65e399a33d2c0)) | -| eu-west-1 | [ami-0ff5ce856793c0471](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ff5ce856793c0471) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0ff5ce856793c0471)) | [ami-097ec49079a5d612f](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-097ec49079a5d612f) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-097ec49079a5d612f)) | -| eu-west-2 | [ami-07cd282c1d6887959](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07cd282c1d6887959) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-07cd282c1d6887959)) | [ami-0360a9a8fec665753](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0360a9a8fec665753) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0360a9a8fec665753)) | -| eu-west-3 | [ami-0b5cffef9ef83241c](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b5cffef9ef83241c) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0b5cffef9ef83241c)) | [ami-0adda5d20f341ae82](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0adda5d20f341ae82) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0adda5d20f341ae82)) | -| me-south-1 | [ami-00cd03fd11106a4cf](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-00cd03fd11106a4cf) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-00cd03fd11106a4cf)) | [ami-0cd56b96bf4e83dcf](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0cd56b96bf4e83dcf) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0cd56b96bf4e83dcf)) | -| sa-east-1 | [ami-0b92ae87d6e141bbb](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b92ae87d6e141bbb) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0b92ae87d6e141bbb)) | [ami-08370ac3635a06147](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08370ac3635a06147) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08370ac3635a06147)) | -| us-east-1 | [ami-08500c947a8870478](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08500c947a8870478) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08500c947a8870478)) | [ami-068216a0f0800db09](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-068216a0f0800db09) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-068216a0f0800db09)) | -| us-east-2 | [ami-0acde1f635f6811c4](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0acde1f635f6811c4) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0acde1f635f6811c4)) | [ami-06c24203121c5baa9](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06c24203121c5baa9) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-06c24203121c5baa9)) | -| us-west-1 | [ami-09e1594112290a416](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09e1594112290a416) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-09e1594112290a416)) | [ami-03815baf7396cc308](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03815baf7396cc308) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03815baf7396cc308)) | -| us-west-2 | [ami-034dc333a09683077](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-034dc333a09683077) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-034dc333a09683077)) | [ami-0ccbd58d6e42a3f64](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ccbd58d6e42a3f64) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0ccbd58d6e42a3f64)) | - -

- -### Alpine Linux 3.12.7 (2021-06-15) -
click to show/hide

- -| Region | alpine-ami-3.12.7-aarch64-r1 | alpine-ami-3.12.7-x86_64-r0 | -| ------ | --- | --- | -| af-south-1 | [ami-02a463c487f299306](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02a463c487f299306) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02a463c487f299306)) | [ami-0ef71998139e39742](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ef71998139e39742) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0ef71998139e39742)) | -| ap-east-1 | [ami-07ef9272fba7965e3](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07ef9272fba7965e3) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-07ef9272fba7965e3)) | [ami-0628f9a686336d658](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0628f9a686336d658) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0628f9a686336d658)) | -| ap-northeast-1 | [ami-035fb63f9b293417d](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-035fb63f9b293417d) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-035fb63f9b293417d)) | [ami-0b34d9cbb63106e61](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b34d9cbb63106e61) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0b34d9cbb63106e61)) | -| ap-northeast-2 | [ami-07b5a03e1bb389253](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07b5a03e1bb389253) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-07b5a03e1bb389253)) | [ami-080b50f812df717ea](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-080b50f812df717ea) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-080b50f812df717ea)) | -| ap-northeast-3 | [ami-0181e54576075b5cc](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0181e54576075b5cc) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0181e54576075b5cc)) | [ami-01ddf8610921c1f5b](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01ddf8610921c1f5b) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-01ddf8610921c1f5b)) | -| ap-south-1 | [ami-01044c4b5478c4d1d](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01044c4b5478c4d1d) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-01044c4b5478c4d1d)) | [ami-0f8f786ca89d0968c](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f8f786ca89d0968c) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0f8f786ca89d0968c)) | -| ap-southeast-1 | [ami-01b96ea14b3a15a77](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01b96ea14b3a15a77) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-01b96ea14b3a15a77)) | [ami-079d7c6b97cac3237](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-079d7c6b97cac3237) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-079d7c6b97cac3237)) | -| ap-southeast-2 | [ami-06855d79571a6e4ac](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06855d79571a6e4ac) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-06855d79571a6e4ac)) | [ami-0d2e1217ececc880d](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d2e1217ececc880d) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0d2e1217ececc880d)) | -| ca-central-1 | [ami-042c2cb60c794554c](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-042c2cb60c794554c) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-042c2cb60c794554c)) | [ami-094e7437e91419deb](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-094e7437e91419deb) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-094e7437e91419deb)) | -| eu-central-1 | [ami-00eda68b21e026a70](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-00eda68b21e026a70) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-00eda68b21e026a70)) | [ami-09eec438ca839ca58](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09eec438ca839ca58) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-09eec438ca839ca58)) | -| eu-north-1 | [ami-005856c2c0649fd53](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-005856c2c0649fd53) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-005856c2c0649fd53)) | [ami-09447e487e10b7655](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09447e487e10b7655) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-09447e487e10b7655)) | -| eu-south-1 | [ami-0a4b952da5d2e6d51](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a4b952da5d2e6d51) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a4b952da5d2e6d51)) | [ami-04d393060a61a1eca](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04d393060a61a1eca) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-04d393060a61a1eca)) | -| eu-west-1 | [ami-0463f3ec0e6f3ae1c](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0463f3ec0e6f3ae1c) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0463f3ec0e6f3ae1c)) | [ami-095c3d5967fd9c885](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-095c3d5967fd9c885) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-095c3d5967fd9c885)) | -| eu-west-2 | [ami-0fb6524693687cca0](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0fb6524693687cca0) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0fb6524693687cca0)) | [ami-005a2f89daaf77547](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-005a2f89daaf77547) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-005a2f89daaf77547)) | -| eu-west-3 | [ami-0d53043aa280e8b70](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d53043aa280e8b70) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0d53043aa280e8b70)) | [ami-0700c55763af6a1bb](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0700c55763af6a1bb) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0700c55763af6a1bb)) | -| me-south-1 | [ami-0a1d69b39aea74ece](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a1d69b39aea74ece) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a1d69b39aea74ece)) | [ami-039b1ff0cfd4c9e1c](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-039b1ff0cfd4c9e1c) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-039b1ff0cfd4c9e1c)) | -| sa-east-1 | [ami-0274fe1f8294b34fb](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0274fe1f8294b34fb) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0274fe1f8294b34fb)) | [ami-0d364d95ef0e3ed08](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d364d95ef0e3ed08) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0d364d95ef0e3ed08)) | -| us-east-1 | [ami-0f2f4e0001bf5b275](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f2f4e0001bf5b275) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0f2f4e0001bf5b275)) | [ami-08a8e1cd5cfe6fc40](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08a8e1cd5cfe6fc40) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08a8e1cd5cfe6fc40)) | -| us-east-2 | [ami-01dd480af3ec8e7b5](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01dd480af3ec8e7b5) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-01dd480af3ec8e7b5)) | [ami-0f213093a04cde948](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f213093a04cde948) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0f213093a04cde948)) | -| us-west-1 | [ami-006f02a62ee4c57a0](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-006f02a62ee4c57a0) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-006f02a62ee4c57a0)) | [ami-0a5fc9a6a5351641e](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a5fc9a6a5351641e) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a5fc9a6a5351641e)) | -| us-west-2 | [ami-04485971d90cee562](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04485971d90cee562) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-04485971d90cee562)) | [ami-06b1887a7bc6eb122](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06b1887a7bc6eb122) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-06b1887a7bc6eb122)) | - -

- -### Alpine Linux 3.11.11 (2021-04-15) -
click to show/hide

- -| Region | alpine-ami-3.11.11-x86_64-r0 | -| ------ | --- | -| af-south-1 | [ami-0a5bccfa2bc184bb1](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a5bccfa2bc184bb1) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a5bccfa2bc184bb1)) | -| ap-east-1 | [ami-057fe5fb6dbaeaabe](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-057fe5fb6dbaeaabe) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-057fe5fb6dbaeaabe)) | -| ap-northeast-1 | [ami-0ce72c30606d58e2a](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ce72c30606d58e2a) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0ce72c30606d58e2a)) | -| ap-northeast-2 | [ami-075d81ae6bd805c9a](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-075d81ae6bd805c9a) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-075d81ae6bd805c9a)) | -| ap-northeast-3 | [ami-048b8b812a37a6e9d](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-048b8b812a37a6e9d) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-048b8b812a37a6e9d)) | -| ap-south-1 | [ami-0a09ced4a0968561a](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a09ced4a0968561a) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a09ced4a0968561a)) | -| ap-southeast-1 | [ami-0c3268d224a7be79f](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0c3268d224a7be79f) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0c3268d224a7be79f)) | -| ap-southeast-2 | [ami-0fe81386105b2c320](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0fe81386105b2c320) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0fe81386105b2c320)) | -| ca-central-1 | [ami-02351d48ae19b5153](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02351d48ae19b5153) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02351d48ae19b5153)) | -| eu-central-1 | [ami-082d5ae92b013cf0c](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-082d5ae92b013cf0c) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-082d5ae92b013cf0c)) | -| eu-north-1 | [ami-0a67df0ddb737bca0](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a67df0ddb737bca0) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a67df0ddb737bca0)) | -| eu-south-1 | [ami-0b9e1df4e1ea3f09a](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b9e1df4e1ea3f09a) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0b9e1df4e1ea3f09a)) | -| eu-west-1 | [ami-06318efc075e9c192](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06318efc075e9c192) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-06318efc075e9c192)) | -| eu-west-2 | [ami-0a68f325601d8968f](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a68f325601d8968f) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0a68f325601d8968f)) | -| eu-west-3 | [ami-06d6b6f3eb35b4988](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06d6b6f3eb35b4988) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-06d6b6f3eb35b4988)) | -| me-south-1 | [ami-0fee76fd32b0acb09](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0fee76fd32b0acb09) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0fee76fd32b0acb09)) | -| sa-east-1 | [ami-0e3921332d634a1bc](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e3921332d634a1bc) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e3921332d634a1bc)) | -| us-east-1 | [ami-09a785c1dd12aa464](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09a785c1dd12aa464) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-09a785c1dd12aa464)) | -| us-east-2 | [ami-0f27d5f504d774c42](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f27d5f504d774c42) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0f27d5f504d774c42)) | -| us-west-1 | [ami-0e1759b0e8c3b8fde](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e1759b0e8c3b8fde) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e1759b0e8c3b8fde)) | -| us-west-2 | [ami-038d4cc13841ec79f](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-038d4cc13841ec79f) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-038d4cc13841ec79f)) | - -

- -### Alpine Linux Edge (2021-08-07) -
click to show/hide

- -| Region | alpine-ami-edge-aarch64-20210807035653 | alpine-ami-edge-x86_64-20210807035653 | -| ------ | --- | --- | -| af-south-1 | [ami-0d119db2dbae0d1f2](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d119db2dbae0d1f2) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0d119db2dbae0d1f2)) | [ami-043fabf28ec27dd48](https://af-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-043fabf28ec27dd48) ([launch](https://af-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-043fabf28ec27dd48)) | -| ap-east-1 | [ami-06720fd442e84af52](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06720fd442e84af52) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-06720fd442e84af52)) | [ami-0cc5ca3f207d56fe4](https://ap-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0cc5ca3f207d56fe4) ([launch](https://ap-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0cc5ca3f207d56fe4)) | -| ap-northeast-1 | [ami-03bb3e6b43c7e5b3a](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03bb3e6b43c7e5b3a) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03bb3e6b43c7e5b3a)) | [ami-03c77a5b6ef2ce5cf](https://ap-northeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03c77a5b6ef2ce5cf) ([launch](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03c77a5b6ef2ce5cf)) | -| ap-northeast-2 | [ami-05f6a1444b2dba1be](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-05f6a1444b2dba1be) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-05f6a1444b2dba1be)) | [ami-04efc6ef9b126d1b7](https://ap-northeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04efc6ef9b126d1b7) ([launch](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-04efc6ef9b126d1b7)) | -| ap-northeast-3 | [ami-06553907a3296d356](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06553907a3296d356) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-06553907a3296d356)) | [ami-09de309f09de8634d](https://ap-northeast-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09de309f09de8634d) ([launch](https://ap-northeast-3.console.aws.amazon.com/ec2/home#launchAmi=ami-09de309f09de8634d)) | -| ap-south-1 | [ami-02276ceadfba3d8dc](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-02276ceadfba3d8dc) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-02276ceadfba3d8dc)) | [ami-04e3beb414e5c63a0](https://ap-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04e3beb414e5c63a0) ([launch](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-04e3beb414e5c63a0)) | -| ap-southeast-1 | [ami-009e681f7123d7507](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-009e681f7123d7507) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-009e681f7123d7507)) | [ami-091c8e0712eb73cfc](https://ap-southeast-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-091c8e0712eb73cfc) ([launch](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-091c8e0712eb73cfc)) | -| ap-southeast-2 | [ami-0f609e1d8e752a7d5](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f609e1d8e752a7d5) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0f609e1d8e752a7d5)) | [ami-0e0e696ef294e3c54](https://ap-southeast-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e0e696ef294e3c54) ([launch](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0e0e696ef294e3c54)) | -| ca-central-1 | [ami-0e79a284afab67660](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e79a284afab67660) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e79a284afab67660)) | [ami-0ddc8f4f4adfb9432](https://ca-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ddc8f4f4adfb9432) ([launch](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0ddc8f4f4adfb9432)) | -| eu-central-1 | [ami-00d213f492389362f](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-00d213f492389362f) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-00d213f492389362f)) | [ami-0b8f1eb4317be682b](https://eu-central-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b8f1eb4317be682b) ([launch](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0b8f1eb4317be682b)) | -| eu-north-1 | [ami-09efda8acd6cd23a3](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-09efda8acd6cd23a3) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-09efda8acd6cd23a3)) | [ami-013ec2fcd6a337f5c](https://eu-north-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-013ec2fcd6a337f5c) ([launch](https://eu-north-1.console.aws.amazon.com/ec2/home#launchAmi=ami-013ec2fcd6a337f5c)) | -| eu-south-1 | [ami-08b0d59e27d5834cd](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-08b0d59e27d5834cd) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-08b0d59e27d5834cd)) | [ami-04d0311c3a559280e](https://eu-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-04d0311c3a559280e) ([launch](https://eu-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-04d0311c3a559280e)) | -| eu-west-1 | [ami-083fce6ab0babc52b](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-083fce6ab0babc52b) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-083fce6ab0babc52b)) | [ami-092803064e707b52d](https://eu-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-092803064e707b52d) ([launch](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-092803064e707b52d)) | -| eu-west-2 | [ami-0d5e165b15b45d294](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0d5e165b15b45d294) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0d5e165b15b45d294)) | [ami-06a1e1353977132a1](https://eu-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-06a1e1353977132a1) ([launch](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-06a1e1353977132a1)) | -| eu-west-3 | [ami-0b9e58d8fb5a5851d](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b9e58d8fb5a5851d) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0b9e58d8fb5a5851d)) | [ami-0403f988d8f677db2](https://eu-west-3.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0403f988d8f677db2) ([launch](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-0403f988d8f677db2)) | -| me-south-1 | [ami-0a6ce44e9cae12563](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0a6ce44e9cae12563) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0a6ce44e9cae12563)) | [ami-0759c23186662c037](https://me-south-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0759c23186662c037) ([launch](https://me-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0759c23186662c037)) | -| sa-east-1 | [ami-0ed048bcd64ffdac7](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0ed048bcd64ffdac7) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0ed048bcd64ffdac7)) | [ami-03502f7175066565d](https://sa-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-03502f7175066565d) ([launch](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-03502f7175066565d)) | -| us-east-1 | [ami-0e71191f5db7e9a53](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0e71191f5db7e9a53) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0e71191f5db7e9a53)) | [ami-0320caad46860385f](https://us-east-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0320caad46860385f) ([launch](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-0320caad46860385f)) | -| us-east-2 | [ami-01ae9b5526a5530b9](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-01ae9b5526a5530b9) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-01ae9b5526a5530b9)) | [ami-0b6d988e51817d6cb](https://us-east-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0b6d988e51817d6cb) ([launch](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0b6d988e51817d6cb)) | -| us-west-1 | [ami-07236d99bffc73676](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-07236d99bffc73676) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-07236d99bffc73676)) | [ami-05516c86a2533df33](https://us-west-1.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-05516c86a2533df33) ([launch](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-05516c86a2533df33)) | -| us-west-2 | [ami-0361c483837e42be5](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0361c483837e42be5) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0361c483837e42be5)) | [ami-0f9252184eaaf469b](https://us-west-2.console.aws.amazon.com/ec2/home#Images:visibility=public-images;imageId=ami-0f9252184eaaf469b) ([launch](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-0f9252184eaaf469b)) | - -

diff --git a/releases/README_alpine.md b/releases/README_alpine.md deleted file mode 120000 index 42061c0..0000000 --- a/releases/README_alpine.md +++ /dev/null @@ -1 +0,0 @@ -README.md \ No newline at end of file diff --git a/releases/alpine.yaml b/releases/alpine.yaml deleted file mode 100644 index d13acf5..0000000 --- a/releases/alpine.yaml +++ /dev/null @@ -1,311 +0,0 @@ -3.13.5: - v3_13-aarch64: - alpine-ami-3.13.5-aarch64-r1: - description: Alpine Linux 3.13.5 aarch64 r1 - https://alpinelinux.org/cloud - profile: alpine - profile_build: v3_13-aarch64 - version: '3.13' - release: 3.13.5 - arch: aarch64 - revision: r1 - creation_date: '2021-06-15T15:13:26.000Z' - end_of_life: '2022-11-01T00:00:00' - build_time: 1623798806 - artifacts: - af-south-1: ami-077c41d657f21a319 - eu-north-1: ami-0469d8b4b0f1b6493 - ap-south-1: ami-073046a717b1930dc - eu-west-3: ami-0b5cffef9ef83241c - eu-west-2: ami-07cd282c1d6887959 - eu-south-1: ami-0417a5f0b9249cb6e - eu-west-1: ami-0ff5ce856793c0471 - ap-northeast-3: ami-0df1727e6d714cc2d - ap-northeast-2: ami-070b973637320a52c - me-south-1: ami-00cd03fd11106a4cf - ap-northeast-1: ami-034682c4a51e88cf7 - sa-east-1: ami-0b92ae87d6e141bbb - ca-central-1: ami-0f40fb960a6bac29a - ap-east-1: ami-0650eebfb386ffb89 - ap-southeast-1: ami-03ac48af40b667111 - ap-southeast-2: ami-04d61dc48ced711e3 - eu-central-1: ami-0dcacb8c85ee2349e - us-east-1: ami-08500c947a8870478 - us-east-2: ami-0acde1f635f6811c4 - us-west-1: ami-09e1594112290a416 - us-west-2: ami-034dc333a09683077 - v3_13-x86_64: - alpine-ami-3.13.5-x86_64-r0: - description: Alpine Linux 3.13.5 x86_64 r0 - https://alpinelinux.org/cloud - profile: alpine - profile_build: v3_13-x86_64 - version: '3.13' - release: 3.13.5 - arch: x86_64 - revision: r0 - creation_date: '2021-04-15T01:03:30.000Z' - end_of_life: '2022-11-01T00:00:00' - build_time: 1618477410 - artifacts: - af-south-1: ami-0441b82fd3fb48433 - eu-north-1: ami-03cb5b21303291be4 - ap-south-1: ami-0c0073e10ee3a5b66 - eu-west-3: ami-0adda5d20f341ae82 - eu-west-2: ami-0360a9a8fec665753 - eu-south-1: ami-02bd65e399a33d2c0 - eu-west-1: ami-097ec49079a5d612f - ap-northeast-3: ami-0678e9272dbb276a1 - ap-northeast-2: ami-038fc3b2d886b83ce - me-south-1: ami-0cd56b96bf4e83dcf - ap-northeast-1: ami-02fb62943caac3c71 - sa-east-1: ami-08370ac3635a06147 - ca-central-1: ami-0a464816128999904 - ap-east-1: ami-0e2d8dfaf125a7631 - ap-southeast-1: ami-07c71c29e8cfef967 - ap-southeast-2: ami-039c2e2595e9da7fc - eu-central-1: ami-0d6ad9b3597da40c4 - us-east-1: ami-068216a0f0800db09 - us-east-2: ami-06c24203121c5baa9 - us-west-1: ami-03815baf7396cc308 - us-west-2: ami-0ccbd58d6e42a3f64 -3.12.7: - v3_12-aarch64: - alpine-ami-3.12.7-aarch64-r1: - description: Alpine Linux 3.12.7 aarch64 r1 - https://github.com/mcrute/alpine-ec2-ami - profile: alpine - profile_build: v3_12-aarch64 - version: '3.12' - release: 3.12.7 - arch: aarch64 - revision: r1 - creation_date: '2021-06-15T15:16:00.000Z' - end_of_life: '2022-05-01T00:00:00' - build_time: 1623798960 - artifacts: - af-south-1: ami-02a463c487f299306 - eu-north-1: ami-005856c2c0649fd53 - ap-south-1: ami-01044c4b5478c4d1d - eu-west-3: ami-0d53043aa280e8b70 - eu-west-2: ami-0fb6524693687cca0 - eu-south-1: ami-0a4b952da5d2e6d51 - eu-west-1: ami-0463f3ec0e6f3ae1c - ap-northeast-3: ami-0181e54576075b5cc - ap-northeast-2: ami-07b5a03e1bb389253 - me-south-1: ami-0a1d69b39aea74ece - ap-northeast-1: ami-035fb63f9b293417d - sa-east-1: ami-0274fe1f8294b34fb - ca-central-1: ami-042c2cb60c794554c - ap-east-1: ami-07ef9272fba7965e3 - ap-southeast-1: ami-01b96ea14b3a15a77 - ap-southeast-2: ami-06855d79571a6e4ac - eu-central-1: ami-00eda68b21e026a70 - us-east-1: ami-0f2f4e0001bf5b275 - us-east-2: ami-01dd480af3ec8e7b5 - us-west-1: ami-006f02a62ee4c57a0 - us-west-2: ami-04485971d90cee562 - v3_12-x86_64: - alpine-ami-3.12.7-x86_64-r0: - description: Alpine Linux 3.12.7 x86_64 r0 - https://github.com/mcrute/alpine-ec2-ami - profile: alpine - profile_build: v3_12-x86_64 - version: '3.12' - release: 3.12.7 - arch: x86_64 - revision: r0 - creation_date: '2021-04-15T00:51:54.000Z' - end_of_life: '2022-05-01T00:00:00' - build_time: 1618476714 - artifacts: - af-south-1: ami-0ef71998139e39742 - eu-north-1: ami-09447e487e10b7655 - ap-south-1: ami-0f8f786ca89d0968c - eu-west-3: ami-0700c55763af6a1bb - eu-west-2: ami-005a2f89daaf77547 - eu-south-1: ami-04d393060a61a1eca - eu-west-1: ami-095c3d5967fd9c885 - ap-northeast-3: ami-01ddf8610921c1f5b - ap-northeast-2: ami-080b50f812df717ea - me-south-1: ami-039b1ff0cfd4c9e1c - ap-northeast-1: ami-0b34d9cbb63106e61 - sa-east-1: ami-0d364d95ef0e3ed08 - ca-central-1: ami-094e7437e91419deb - ap-east-1: ami-0628f9a686336d658 - ap-southeast-1: ami-079d7c6b97cac3237 - ap-southeast-2: ami-0d2e1217ececc880d - eu-central-1: ami-09eec438ca839ca58 - us-east-1: ami-08a8e1cd5cfe6fc40 - us-east-2: ami-0f213093a04cde948 - us-west-1: ami-0a5fc9a6a5351641e - us-west-2: ami-06b1887a7bc6eb122 -edge: - edge-x86_64: - alpine-ami-edge-x86_64-20210807035653: - description: Alpine Linux edge x86_64 20210807035653 - https://alpinelinux.org/cloud - profile: alpine - profile_build: edge-x86_64 - version: edge - release: edge - arch: x86_64 - revision: '20210807035653' - creation_date: '2021-08-07T04:07:41.000Z' - end_of_life: '2021-08-08T03:56:53' - build_time: 1628338061 - artifacts: - af-south-1: ami-043fabf28ec27dd48 - eu-north-1: ami-013ec2fcd6a337f5c - ap-south-1: ami-04e3beb414e5c63a0 - eu-west-3: ami-0403f988d8f677db2 - eu-west-2: ami-06a1e1353977132a1 - eu-south-1: ami-04d0311c3a559280e - eu-west-1: ami-092803064e707b52d - ap-northeast-3: ami-09de309f09de8634d - ap-northeast-2: ami-04efc6ef9b126d1b7 - me-south-1: ami-0759c23186662c037 - ap-northeast-1: ami-03c77a5b6ef2ce5cf - sa-east-1: ami-03502f7175066565d - ca-central-1: ami-0ddc8f4f4adfb9432 - ap-east-1: ami-0cc5ca3f207d56fe4 - ap-southeast-1: ami-091c8e0712eb73cfc - ap-southeast-2: ami-0e0e696ef294e3c54 - eu-central-1: ami-0b8f1eb4317be682b - us-east-1: ami-0320caad46860385f - us-east-2: ami-0b6d988e51817d6cb - us-west-1: ami-05516c86a2533df33 - us-west-2: ami-0f9252184eaaf469b - edge-aarch64: - alpine-ami-edge-aarch64-20210807035653: - description: Alpine Linux edge aarch64 20210807035653 - https://alpinelinux.org/cloud - profile: alpine - profile_build: edge-aarch64 - version: edge - release: edge - arch: aarch64 - revision: '20210807035653' - creation_date: '2021-08-07T04:02:09.000Z' - end_of_life: '2021-08-08T03:56:53' - build_time: 1628337729 - artifacts: - af-south-1: ami-0d119db2dbae0d1f2 - eu-north-1: ami-09efda8acd6cd23a3 - ap-south-1: ami-02276ceadfba3d8dc - eu-west-3: ami-0b9e58d8fb5a5851d - eu-west-2: ami-0d5e165b15b45d294 - eu-south-1: ami-08b0d59e27d5834cd - eu-west-1: ami-083fce6ab0babc52b - ap-northeast-3: ami-06553907a3296d356 - ap-northeast-2: ami-05f6a1444b2dba1be - me-south-1: ami-0a6ce44e9cae12563 - ap-northeast-1: ami-03bb3e6b43c7e5b3a - sa-east-1: ami-0ed048bcd64ffdac7 - ca-central-1: ami-0e79a284afab67660 - ap-east-1: ami-06720fd442e84af52 - ap-southeast-1: ami-009e681f7123d7507 - ap-southeast-2: ami-0f609e1d8e752a7d5 - eu-central-1: ami-00d213f492389362f - us-east-1: ami-0e71191f5db7e9a53 - us-east-2: ami-01ae9b5526a5530b9 - us-west-1: ami-07236d99bffc73676 - us-west-2: ami-0361c483837e42be5 -3.11.11: - v3_11-x86_64: - alpine-ami-3.11.11-x86_64-r0: - description: Alpine Linux 3.11.11 x86_64 r0 - https://github.com/mcrute/alpine-ec2-ami - profile: alpine - profile_build: v3_11-x86_64 - version: '3.11' - release: 3.11.11 - arch: x86_64 - revision: r0 - creation_date: '2021-04-15T01:12:43.000Z' - end_of_life: '2021-11-01T00:00:00' - build_time: 1618477963 - artifacts: - af-south-1: ami-0a5bccfa2bc184bb1 - eu-north-1: ami-0a67df0ddb737bca0 - ap-south-1: ami-0a09ced4a0968561a - eu-west-3: ami-06d6b6f3eb35b4988 - eu-west-2: ami-0a68f325601d8968f - eu-south-1: ami-0b9e1df4e1ea3f09a - eu-west-1: ami-06318efc075e9c192 - ap-northeast-3: ami-048b8b812a37a6e9d - ap-northeast-2: ami-075d81ae6bd805c9a - me-south-1: ami-0fee76fd32b0acb09 - ap-northeast-1: ami-0ce72c30606d58e2a - sa-east-1: ami-0e3921332d634a1bc - ca-central-1: ami-02351d48ae19b5153 - ap-east-1: ami-057fe5fb6dbaeaabe - ap-southeast-1: ami-0c3268d224a7be79f - ap-southeast-2: ami-0fe81386105b2c320 - eu-central-1: ami-082d5ae92b013cf0c - us-east-1: ami-09a785c1dd12aa464 - us-east-2: ami-0f27d5f504d774c42 - us-west-1: ami-0e1759b0e8c3b8fde - us-west-2: ami-038d4cc13841ec79f -3.14.1: - v3_14-x86_64: - alpine-ami-3.14.1-x86_64-r0: - description: Alpine Linux 3.14.1 x86_64 r0 - https://alpinelinux.org/cloud - profile: alpine - profile_build: v3_14-x86_64 - version: '3.14' - release: 3.14.1 - arch: x86_64 - revision: r0 - creation_date: '2021-08-07T04:04:47.000Z' - end_of_life: '2023-05-01T00:00:00' - build_time: 1628337887 - artifacts: - af-south-1: ami-08aea7443b5d08f79 - eu-north-1: ami-0e2b6818c61b0b113 - ap-south-1: ami-012f6469429d4ad42 - eu-west-3: ami-031817718bbaf781d - eu-west-2: ami-0e4e3a358f49875dc - eu-south-1: ami-0e69ca71abc28babe - eu-west-1: ami-0274c314a1272d2fd - ap-northeast-3: ami-08b52d5efd9abef56 - ap-northeast-2: ami-0d7a7fdf22367713c - me-south-1: ami-0a665daaaf042257a - ap-northeast-1: ami-033c6bceffad8df75 - sa-east-1: ami-0101f8d6e37b57173 - ca-central-1: ami-0bd936dc3de6b791d - ap-east-1: ami-0d4ba085b9c1557b8 - ap-southeast-1: ami-01a542451033fd940 - ap-southeast-2: ami-014e86c5062e164af - eu-central-1: ami-0bb1a41a1c0eafc95 - us-east-1: ami-0f1919ed094505be3 - us-east-2: ami-01f0c5ccdeab2a3d8 - us-west-1: ami-0000605ad5571b4c0 - us-west-2: ami-05e856cad09314ed0 - v3_14-aarch64: - alpine-ami-3.14.1-aarch64-r0: - description: Alpine Linux 3.14.1 aarch64 r0 - https://alpinelinux.org/cloud - profile: alpine - profile_build: v3_14-aarch64 - version: '3.14' - release: 3.14.1 - arch: aarch64 - revision: r0 - creation_date: '2021-08-07T03:59:14.000Z' - end_of_life: '2023-05-01T00:00:00' - build_time: 1628337554 - artifacts: - af-south-1: ami-0bcf48f55b9949a84 - eu-north-1: ami-05f1271ab15bf4557 - ap-south-1: ami-07ed5174d6da65d84 - eu-west-3: ami-08af7eab2ea99f7e5 - eu-west-2: ami-0d0ae9eb51f79e338 - eu-south-1: ami-06fb368b1c23356ca - eu-west-1: ami-0887b1a806a73417a - ap-northeast-3: ami-0956b7bafd7a6720d - ap-northeast-2: ami-07667213840caca26 - me-south-1: ami-02b98efa2cd0b2ab0 - ap-northeast-1: ami-0b559d40e76da2119 - sa-east-1: ami-07f46bbd5674b79a6 - ca-central-1: ami-08bc6f39436e47c30 - ap-east-1: ami-092aa3a38d204b2b5 - ap-southeast-1: ami-0aa966cf6ca931b11 - ap-southeast-2: ami-069d767cedbd4d7a5 - eu-central-1: ami-06f364adb4a297706 - us-east-1: ami-03175ec34a7192e70 - us-east-2: ami-082d34382e0289102 - us-west-1: ami-0081057f803624455 - us-west-2: ami-03e685d5fddaf46e7 diff --git a/scripts/builder.py b/scripts/builder.py deleted file mode 100755 index f16885e..0000000 --- a/scripts/builder.py +++ /dev/null @@ -1,1366 +0,0 @@ -#!/usr/bin/env python3 - -# This bit has to stay at the very top of the script. It exists to ensure that -# running this script all by itself uses the python virtual environment with -# our dependencies installed. If will create that environment if it doesn't -# exist. -import os -import sys -import subprocess - -args = [os.path.join("build", "bin", "python3")] + sys.argv - -# Create the build root if it doesn't exist -if not os.path.exists("build"): - import venv - - print("Build environment does not exist, creating...", file=sys.stderr) - venv.create("build", with_pip=True) - subprocess.run(["build/bin/pip", "install", "-U", "pip", - "pyhocon", "boto3", "python-dateutil", "PyYAML"]) - - print("Re-executing with builder python...", file=sys.stderr) - os.execv(args[0], args) -else: - # If the build root python is not running this script re-execute it with - # that python instead to ensure all of our dependencies exist. - if os.path.join(os.getcwd(), args[0]) != sys.executable: - print("Re-executing with builder python...", file=sys.stderr) - os.execv(args[0], args) - -# Below here is the real script -import io -import os -import re -import sys -import glob -import json -import time -import shutil -import logging -import argparse -import textwrap -import subprocess -import urllib.error -import dateutil.parser - -from enum import Enum -from collections import defaultdict -from datetime import datetime, timedelta -from distutils.version import StrictVersion -from urllib.request import Request, urlopen - -import yaml -import boto3 -import pyhocon - - -# allows us to set values deep within an object that might not be fully defined -def dictfactory(): - return defaultdict(dictfactory) - -# undo dictfactory() objects to normal objects -def undictfactory(o): - if isinstance(o, defaultdict): - o = {k: undictfactory(v) for k, v in o.items()} - return o - -# This is an ugly hack. We occasionally need the region name but it's not -# attached to anything publicly exposed on the client objects. Hide this here. -def region_from_client(client): - return client._client_config.region_name - -# version sorting -def sortable_version(x): - v = x.split('_rc')[0] - return StrictVersion("0.0" if v == "edge" else v) - - -class EC2Architecture(Enum): - - I386 = "i386" - X86_64 = "x86_64" - ARM64 = "arm64" - - -class AMIState(Enum): - - PENDING = "pending" - AVAILABLE = "available" - INVALID = "invalid" - DEREGISTERED = "deregistered" - TRANSIENT = "transient" - FAILED = "failed" - ERROR = "error" - - -class EC2SnapshotState(Enum): - - PENDING = "pending" - COMPLETED = "completed" - ERROR = "error" - - -class TaggedAWSObject: - """Base class for AWS API models that support tagging - """ - - EDGE = StrictVersion("0.0") - - missing_known_tags = None - - _identity = lambda x: x - _known_tags = { - "Name": _identity, - "profile": _identity, - "revision": _identity, - "profile_build": _identity, - "source_ami": _identity, - "source_region": _identity, - "arch": lambda x: EC2Architecture(x), - "end_of_life": lambda x: datetime.fromisoformat(x), - "release": lambda v: EDGE if v == "edge" else StrictVersion(v), - "version": lambda v: EDGE if v == "edge" else StrictVersion(v), - } - - def __repr__(self): - attrs = [] - for k, v in self.__dict__.items(): - if isinstance(v, TaggedAWSObject): - attrs.append(f"{k}=" + object.__repr__(v)) - elif not k.startswith("_"): - attrs.append(f"{k}={v!r}") - attrs = ", ".join(attrs) - - return f"{self.__class__.__name__}({attrs})" - - __str__ = __repr__ - - @property - def aws_tags(self): - """Convert python tags to AWS API tags - - See AMI.aws_permissions for rationale. - """ - for key, values in self.tags.items(): - for value in values: - yield { "Key": key, "Value": value } - - @aws_tags.setter - def aws_tags(self, values): - """Convert AWS API tags to python tags - - See AMI.aws_permissions for rationale. - """ - if not getattr(self, "tags", None): - self.tags = {} - - tags = defaultdict(list) - - for tag in values: - tags[tag["Key"]].append(tag["Value"]) - - self.tags.update(tags) - self._transform_known_tags() - - # XXX(mcrute): The second paragraph might be considered a bug and worth - # fixing at some point. For now those are all read-only attributes though. - def _transform_known_tags(self): - """Convert well known tags into python attributes - - Some tags have special meanings for the model objects that they're - attached to. This copies those tags, transforms them, then sets them in - the model attributes. - - It doesn't touch the tag itself so if that - attribute needs updated and re-saved the tag must be updated in - addition to the model. - """ - self.missing_known_tags = [] - - for k, tf in self._known_tags.items(): - v = self.tags.get(k, []) - if not v: - self.missing_known_tags.append(k) - continue - - if len(v) > 1: - raise Exception(f"multiple instances of tag {k}") - - setattr(self, k, v[0]) - - -class AMI(TaggedAWSObject): - - @property - def aws_permissions(self): - """Convert python permissions to AWS API permissions - - The permissions model for the API makes more sense for a web service - but is overly verbose for working with in Python. This and the setter - allow transforming to/from the API syntax. The python code should - consume the allowed_groups and allowed_users lists directly. - """ - perms = [] - for g in self.allowed_groups: - perms.append({"Group": g}) - - for i in self.allowed_users: - perms.append({"UserId": i}) - - return perms - - @aws_permissions.setter - def aws_permissions(self, perms): - """Convert AWS API permissions to python permissions - """ - for perm in perms: - group = perm.get("Group") - if group: - self.allowed_groups.append(group) - - user = perm.get("UserId") - if user: - self.allowed_users.append(user) - - @classmethod - def from_aws_model(cls, ob, region): - self = cls() - - self.linked_snapshot = None - self.allowed_groups = [] - self.allowed_users = [] - self.region = region - self.architecture = EC2Architecture(ob["Architecture"]) - self.creation_date = ob["CreationDate"] - self.description = ob.get("Description", None) - self.image_id = ob["ImageId"] - self.name = ob.get("Name") - self.owner_id = int(ob["OwnerId"]) - self.public = ob["Public"] - self.state = AMIState(ob["State"]) - self.virtualization_type = ob["VirtualizationType"] - self.state_reason = ob.get("StateReason", {}).get("Message", None) - self.aws_tags = ob.get("Tags", []) - - # XXX(mcrute): Assumes we only ever have one device mapping, which is - # valid for Alpine AMIs but not a good general assumption. - # - # This should always resolve for AVAILABLE images but any part of the - # data structure may not yet exist for images that are still in the - # process of copying. - if ob.get("BlockDeviceMappings"): - self.snapshot_id = \ - ob["BlockDeviceMappings"][0]["Ebs"].get("SnapshotId") - - return self - - -class EC2Snapshot(TaggedAWSObject): - - @classmethod - def from_aws_model(cls, ob, region): - self = cls() - - self.linked_ami = None - self.region = region - self.snapshot_id = ob["SnapshotId"] - self.description = ob.get("Description", None) - self.owner_id = int(ob["OwnerId"]) - self.progress = int(ob["Progress"].rstrip("%")) / 100 - self.start_time = ob["StartTime"] - self.state = EC2SnapshotState(ob["State"]) - self.volume_size = ob["VolumeSize"] - self.aws_tags = ob.get("Tags", []) - - return self - - -class ColoredFormatter(logging.Formatter): - """Log formatter that colors output based on level - """ - - _colors = { - "red": "31", - "green": "32", - "yellow": "33", - "blue": "34", - "magenta": "35", - "cyan": "36", - "white": "37", - } - - def _color_wrap(self, text, color, bold=False): - code = self._colors[color] - if bold: - code = "1;{}".format(code) - return "\033[{}m{}\033[0m".format(code, text) - - def format(self, record): - msg = super().format(record) - # Levels: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET - if record.levelno in {logging.ERROR, logging.CRITICAL}: - return self._color_wrap(msg, "red") - elif record.levelno == logging.WARNING: - return self._color_wrap(msg, "yellow") - else: - return self._color_wrap(msg, "green") - - -class IdentityBrokerClient: - """Client for identity broker - - Export IDENTITY_BROKER_ENDPOINT to override the default broker endpoint. - Export IDENTITY_BROKER_API_KEY to specify an API key for the broker. - - See README_BROKER.md for more information and a spec. - """ - - _DEFAULT_ENDPOINT = "https://aws-access.crute.us/api/account" - _DEFAULT_ACCOUNT = "alpine-amis-user" - - def __init__(self, endpoint=None, key=None, account=None): - self.endpoint = endpoint or self._DEFAULT_ENDPOINT - self.account = account or self._DEFAULT_ACCOUNT - self.key = key - self._logger = logging.getLogger() - - override_endpoint = os.environ.get("IDENTITY_BROKER_ENDPOINT") - if override_endpoint: - self.endpoint = override_endpoint - - if not self.key: - self.key = os.environ.get("IDENTITY_BROKER_API_KEY") - - if not self.key: - raise Exception("No identity broker key found") - - def _get(self, path): - while True: # to handle rate limits - try: - res = urlopen(Request(path, headers={"X-API-Key": self.key})) - except urllib.error.HTTPError as ex: - if ex.headers.get("Location") == "/logout": - raise Exception("Identity broker token is expired") - - if ex.status == 429: - self._logger.warning( - "Rate-limited by identity broker, sleeping 30 seconds") - time.sleep(30) - continue - - raise ex - - if res.status not in {200, 429}: - raise Exception(res.reason) - - return json.load(res) - - def get_credentials_url(self): - for account in self._get(self.endpoint): - if account["short_name"] == self.account: - return account["credentials_url"] - - raise Exception("No account found") - - def get_regions(self): - out = {} - - for region in self._get(self.get_credentials_url()): - if region["enabled"]: - out[region["name"]] = region["credentials_url"] - - return out - - def get_credentials(self, region): - return self._get(self.get_regions()[region]) - - def _boto3_session_from_creds(self, creds, region): - return boto3.session.Session( - aws_access_key_id=creds["access_key"], - aws_secret_access_key=creds["secret_key"], - aws_session_token=creds["session_token"], - region_name=region) - - def boto3_session_for_region(self, region): - return self._boto3_session_from_creds( - self.get_credentials(region), region) - - def iter_regions(self): - for region, cred_url in self.get_regions().items(): - yield self._boto3_session_from_creds(self._get(cred_url), region) - - -class ConfigBuilder: - - now = datetime.utcnow() - tomorrow = now + timedelta(days=1) - - @staticmethod - def unquote(x): - return x.strip('"') - - @staticmethod - def force_iso_date(input): - return datetime.fromisoformat(input).isoformat(timespec="seconds") - - @classmethod - def resolve_release(cls, input, cfg): - if input: - # release is explicitly defined, run with it - return input - if cfg['version'] == 'edge': - return 'edge' - # hack to determine release value from version's alpine-base APK - pkgs_url = f"https://dl-cdn.alpinelinux.org/alpine/v{cfg['version']}/main/{cfg['arch']}/" - try: - res = urlopen(pkgs_url) - except urllib.error.HTTPError as ex: - print(f"Unable to urlopen {pkgs_url} - {ex}", file=sys.stderr) - exit(1) - - line = res.readline().decode('utf-8') - while line: - if line.startswith(' 1: - raise Exception(f"Too many images for query {tags!r}") - elif len(images) == 0: - return None - else: - return images[0] - -def get_all_images(client): - return get_images_with_tags(client) - - -class ReleaseAMIs: - """Copy one or more AMIs to other regions and/or set AMI permissions. - - By default, source AMI permissions are applied to their copies, unless - --public, --private, or --allow-account options are specified. - - If the source AMI's permissions are different than the options provided, - its permissions will be updated to match. - """ - command_name = "release" - - @staticmethod - def add_args(parser): - parser.add_argument("--source-region", default="us-west-2", - metavar="REGION", help="region of source AMI(s)") - rgroup = parser.add_mutually_exclusive_group(required=True) - rgroup.add_argument("--use-broker", action="store_true", - help="identity broker provides destination regions and credentials") - rgroup.add_argument("--region", "-r", action="append", dest="regions", - metavar="REGION", help="destination region (multiple OK)") - pgroup = parser.add_mutually_exclusive_group() - pgroup.add_argument("--public", action="store_true", default=None, - help="make source and copied AMIs public") - pgroup.add_argument("--private", dest="public", action="store_false", - help="make source and copied AMIs private") - pgroup.add_argument("--allow-account", dest="allow_accounts", - action="append", metavar="ID", help="make source and copied AMIs " - "accessible by AWS account id (multiple OK)") - parser.add_argument("amis", metavar="AMI", nargs="+", - help="AMI id(s) to copy") - - def get_source_region_client(self, use_broker, source_region): - if use_broker: - return IdentityBrokerClient().boto3_session_for_region( - source_region).client("ec2") - else: - return boto3.session.Session(region_name=source_region).client( - "ec2") - - def copy_image(self, from_client, to_client, image_id): - source = get_image(from_client, image_id) - - res = to_client.copy_image( - Name=source.name, Description=source.description, - SourceImageId=source.image_id, SourceRegion=source.region) - - tags = [ - { "Key": "source_ami", "Value": source.image_id, }, - { "Key": "source_region", "Value": source.region, } - ] - tags.extend(source.aws_tags) - - to_client.create_tags(Resources=[res["ImageId"]], Tags=tags) - - return get_image(to_client, res["ImageId"]) - - def has_incorrect_perms(self, image, perms): - if (set(image.allowed_groups) != set(perms['groups']) - or set(image.allowed_users) != set(perms['users'])): - return True - - def update_image_permissions(self, client, image): - client.reset_image_attribute( - Attribute="launchPermission", ImageId=image.image_id) - client.modify_image_attribute( - Attribute="launchPermission", ImageId=image.image_id, - LaunchPermission={"Add": image.aws_permissions}) - - def run(self, args, root, log): - source_perms = {} - pending_copy = [] - pending_perms = [] - source_region = args.source_region - - log.info(f"Source region {source_region}") - source_client = self.get_source_region_client( - args.use_broker, source_region) - - # resolve source ami perms, queue for fixing if necessary - for ami in args.amis: - image = get_image(source_client, ami) - - if args.public is True: - source_perms[ami] = { 'groups': ['all'], 'users': [] } - elif args.public is False: - source_perms[ami] = { 'groups': [], 'users': [] } - elif args.allow_accounts: - source_perms[ami] = { 'groups': [], 'users': args.allow_accounts } - else: - log.warning(f"Will apply {source_region} {ami} permissions to its copies") - source_perms[ami] = { - 'groups': image.allowed_groups, - 'users': image.allowed_users - } - - if self.has_incorrect_perms(image, source_perms[ami]): - log.warning(f"Will update source {source_region} {ami} permissions") - pending_perms.append((source_client, ami, source_perms[ami])) - - # Copy image to regions where it's missing, queue images that need - # permission fixes - log.info('') - for client in iter_regions(args.use_broker, args.regions): - region_name = region_from_client(client) # For logging - - # Don't bother copying to source region - if region_name == args.source_region: - continue - - log.info(f"Destination region {region_name}...") - for ami in args.amis: - src_log = f"* source {ami}" - image = get_image_with_tags(client, - source_ami=ami, source_region=args.source_region) - if not image: - log.info(f"{src_log} - copying to {region_name}") - ami_copy = self.copy_image(source_client, client, ami) - pending_copy.append( - (client, ami_copy.image_id, source_perms[ami])) - elif self.has_incorrect_perms(image, source_perms[ami]): - log.info(f"{src_log} - will update {image.image_id} perms") - pending_perms.append( - (client, image.image_id, source_perms[ami])) - else: - log.info(f"{src_log} - verified {image.image_id}") - log.info('') - - if pending_copy: - # seems to take at least 3m - pending_copy.append(('sleep', 180, '')) - - # Wait for images to copy - while pending_copy: - client, id, perms = pending_copy.pop(0) # emulate a FIFO queue - if client == 'sleep': - if not pending_copy: - continue - log.info(f"Sleeping {id}s...") - time.sleep(id) - # recheck every 30s - pending_copy.append(('sleep', 30, '')) - continue - - region_name = region_from_client(client) # For logging - image = get_image(client, id) - if image.state != AMIState.AVAILABLE: - log.info(f"- copying: {id} ({region_name})") - pending_copy.append((client, id, perms)) - else: - done_log = f"+ completed: {id} ({region_name})" - if self.has_incorrect_perms(image, perms): - log.info(f"{done_log} - will update perms") - pending_perms.append((client, id, perms)) - else: - log.info(f"{done_log} - verified perms") - - # Update all permissions - for client, id, perms in pending_perms: - region_name = region_from_client(client) # For logging - - log.info(f"% updating perms: {id} ({region_name})") - image = get_image(client, id) - image.allowed_groups = perms['groups'] - image.allowed_users = perms['users'] - self.update_image_permissions(client, image) - - if pending_perms: - log.info('') - - log.info('Release Completed') - - -class Releases: - - RELEASE_FIELDS = [ - 'description', 'profile', 'profile_build', 'version', 'release', - 'arch', 'revision', 'creation_date', 'end_of_life' - ] - - def __init__(self, profile=None, use_broker=None, regions=None): - self.profile = profile - self.tags = { 'profile': profile } - self.use_broker = use_broker - self.regions = regions - self.clients = {} - self.reset_images() - self.reset_releases() - - def reset_images(self): - self.images = defaultdict(list) - - def reset_releases(self): - self.releases = dictfactory() - - # TODO: separate Clients class? - def iter_clients(self): - if not self.clients: - for client in iter_regions(self.use_broker, self.regions): - region = region_from_client(client) - self.clients[region] = client - yield (region, client) - else: - for region, client in self.clients.items(): - yield (region, client) - - # when we're just interested in the profile's images - def load_profile_images(self, log=None): - for region, client in self.iter_clients(): - if log: log.info(f"Loading '{self.profile}' profile images from {region}...") - self.images[region] = get_images_with_tags(client, **self.tags) - - # not belonging to any profile - def load_unknown_images(self, log=None): - for region, client in self.iter_clients(): - if log: log.info(f"Loading unknown images from {region}...") - for image in get_all_images(client): - if 'profile' not in image.tags: - self.images[region].append(image) - - # build profile releases object based on loaded self.images - def build_releases(self, log=None, trim=None): - now = datetime.utcnow() - versions = dictfactory() - - for region, amis in self.images.items(): - if log: log.info(f"{region}") - for ami in amis: - eol = datetime.fromisoformat(ami.end_of_life) - # if we're trimming, we're not interested in EOL images - if trim and eol < now: - continue - - version = ami.version - release = ami.release - build = ami.profile_build - name = ami.name - id = ami.image_id - build_time = int(dateutil.parser.parse(ami.creation_date).strftime('%s')) - - if log: log.info(f" * {ami.image_id} {ami.name}") - version_obj = versions[version][release][build][name] - - for field in self.RELEASE_FIELDS: - if field not in version_obj: - version_obj[field] = getattr(ami, field) - - # ensure earliest build_time is used - if ('build_time' not in version_obj or - build_time < version_obj['build_time']): - version_obj['build_time'] = build_time - version_obj['creation_date'] = ami.creation_date - - version_obj['artifacts'][region] = id - - for version, releases in versions.items(): - for release, builds in sorted(releases.items(), reverse=True, - key=lambda x: sortable_version(x[0])): - for build, revisions in builds.items(): - for revision, info in sorted(revisions.items(), reverse=True, - key=lambda x: x[1]['build_time']): - self.releases[release][build][revision] = info - # if we are trimming, we want only the most recent revisions - if trim: break - # if we are trimming releases, we want only the most recent release - if trim == 'release': break - - -class ReleasesYAML: - """Update releases/.yaml with profile's currently existing AMIs - """ - command_name = "release-yaml" - - @staticmethod - def add_args(parser): - TRIM_HELP=""" - revision = keep last x.y.z-r# of non-EOL releases, - release = keep last x.y.# of non-EOL versions - """ - - rgroup = parser.add_mutually_exclusive_group(required=True) - rgroup.add_argument("--use-broker", action="store_true", - help="identity broker provides destination regions and credentials") - rgroup.add_argument("--region", "-r", action="append", dest="regions", - metavar="REGION", help="destination region (multiple OK)") - parser.add_argument("--trim", "-t", - choices=['revision','release'], help=TRIM_HELP) - parser.add_argument("profile", metavar="PROFILE", help="profile name") - - def run(self, args, root, log): - release_dir = os.path.join(root, 'releases') - if not os.path.exists(release_dir): - os.makedirs(release_dir) - release_yaml = os.path.join(release_dir, f"{args.profile}.yaml") - - r = Releases( - profile = args.profile, - use_broker = args.use_broker, - regions = args.regions) - r.load_profile_images(log) - r.build_releases(trim=args.trim) - - log.info(f"Writing new {release_yaml}") - with open(release_yaml, 'w') as data: - yaml.dump(undictfactory(r.releases), data, sort_keys=False) - - -class ReleasesReadme: - """Build releases/README_.md from releases/.yaml - """ - command_name = "release-readme" - - SECTION_TPL = textwrap.dedent(""" - ### Alpine Linux {release} ({date}) -
click to show/hide

- - {rows} - -

- """) - - AMI_TPL = ( - " [{id}](https://{r}.console.aws.amazon.com/ec2/home" - "#Images:visibility=public-images;imageId={id}) " - "([launch](https://{r}.console.aws.amazon.com/ec2/home" - "#launchAmi={id})) |" - ) - - @staticmethod - def add_args(parser): - parser.add_argument("profile", metavar="PROFILE", help="profile name") - - @staticmethod - def extract_ver(x): - return sortable_version(x['release']) - - def resolve_sections(self, release_data, log): - sects = dictfactory() - for release, builds in sorted(release_data.items(), reverse=True): - version = '.'.join(release.split('.')[0:2]) - if version in sects: - continue - for build, revisions in builds.items(): - ver = sects[version] - ver['release'] = release - name, info = sorted( - revisions.items(), - key=lambda x: x[1]['build_time'], - reverse=True)[0] - if name in ver['builds']: - log.warning( - f"Duplicate AMI '{name}' in builds " - f"'{info['profile_build']} and " - f"'{ver['builds'][name]['build']}") - ver['builds'][name] = { - 'build': info['profile_build'], - 'built': int(info['build_time']), - 'amis': info['artifacts'] - } - self.sections = sorted( - undictfactory(sects).values(), - key=self.extract_ver, - reverse=True) - - def get_ami_markdown(self): - ami_list = "## AMIs\n" - - for section in self.sections: - built = 0 - regions = [] - rows = ["| Region |", "| ------ |"] - - for name, info in sorted(section['builds'].items()): - rows[0] += f" {name} |" - rows[1] += " --- |" - regions = set(regions) | set(info['amis'].keys()) - built = max(built, info['built']) - - for region in sorted(regions): - row = f"| {region} |" - for name, info in sorted(section['builds'].items()): - amis = info['amis'] - if region in amis: - row += self.AMI_TPL.format(r=region, id=amis[region]) - else: - row += ' |' - rows.append(row) - - ami_list += self.SECTION_TPL.format( - release=section['release'].capitalize(), - date=datetime.utcfromtimestamp(built).date(), - rows="\n".join(rows)) - - return ami_list - - def run(self, args, root, log): - profile = args.profile - release_dir = os.path.join(root, "releases") - profile_file = os.path.join(release_dir, f"{profile}.yaml") - with open(profile_file, "r") as data: - self.resolve_sections(yaml.safe_load(data), log) - - ami_markdown = self.get_ami_markdown() - - readme = "" - readme_md = os.path.join(release_dir, f"README_{profile}.md") - action = "Updated" - if os.path.exists(readme_md): - with open(readme_md, "r") as file: - readme = file.read() - else: - action = "Created" - - re_images = re.compile(r"## AMIs.*\Z", flags=re.DOTALL) - if re_images.search(readme): - readme = re_images.sub(ami_markdown, readme) - else: - log.warning("appending") - readme += "\n" + ami_markdown - - with open(readme_md, "w") as file: - file.write(readme) - - log.info(f"{action} {readme_md}") - - -class PruneAMIs: - """Prune Released AMIs - """ - command_name = "prune" - - @staticmethod - def add_args(parser): - LEVEL_HELP = """ - revision = x.y.z-r#, - release = x.y.#, - end-of-life = EOL versions (#.#), - UNKNOWN = AMIs with no profile tag - """ - - parser.add_argument("level", - choices=["revision", "release", "end-of-life", "UNKNOWN"], - help=LEVEL_HELP) - rgroup = parser.add_mutually_exclusive_group(required=True) - rgroup.add_argument("--use-broker", action="store_true", - help="use identity broker to obtain per-region credentials") - rgroup.add_argument("--region", "-r", metavar='REGION', dest='regions', - action="append", help="regions to prune, may be specified multiple times") - parser.add_argument("profile", metavar='PROFILE', - help="profile to prune") - parser.add_argument("builds", metavar='BUILD', - nargs="*", help="build(s) within profile to prune") - agroup = parser.add_mutually_exclusive_group() - agroup.add_argument( - '--keep', metavar='NUM', type=int, default=0, - help='keep NUM most-recent additional otherwise-pruneable per LEVEL') - agroup.add_argument('--defer-eol', metavar='DAYS', type=int, default=0, - help='defer end-of-life pruning for additional days') - parser.add_argument( - '--no-pretend', action='store_true', help='actually prune images') - - @staticmethod - def check_args(args): - if args.level != 'end-of-life' and args.defer_eol != 0: - return ["--defer-eol may only be used with 'end-of-life' pruning."] - if args.keep < 0: - return ["Only non-negative integers are valid for --keep."] - - def __init__(self): - self.pruneable = dictfactory() - - def find_pruneable(self, r, args, log): - level = args.level - builds = args.builds - keep = args.keep - defer_eol = args.defer_eol - - now = datetime.utcnow() - timedelta(days=args.defer_eol) - - # build releases from profile images - r.load_profile_images(log) - r.build_releases() - - # scan for pruning criteria - criteria = dictfactory() - for release, rdata in r.releases.items(): - for build, bdata in rdata.items(): - if builds and build not in builds: - continue - for ami_name, info in bdata.items(): - version = info['version'] - built = info['build_time'] - eol = datetime.fromisoformat(info['end_of_life']) - # default: level == 'release' - basis = version - if level == 'revision': - basis = release - elif level == 'end-of-life': - # not enough in common with revision/release - if build not in criteria[version]: - criteria[version][build] = [now] - if eol < now and eol not in criteria[version][build]: - criteria[version][build].append(eol) - criteria[version][build].sort(reverse=True) - continue - # revsion/release have enough commonality - if build not in criteria[basis]: - criteria[basis][build] = [built] - elif built not in criteria[basis][build]: - criteria[basis][build].append(built) - criteria[basis][build].sort(reverse=True) - - # scan again to determine what doesn't make the cut - for release, rdata in r.releases.items(): - for build, bdata in rdata.items(): - if builds and build not in builds: - continue - for ami_name, info in bdata.items(): - version = info['version'] - built = info['build_time'] - eol = datetime.fromisoformat(info['end_of_life']) - # default: level == 'release' - basis = version - value = built - if level == 'revision': - basis = release - elif level == 'end-of-life': - value = eol - c = criteria[basis][build] - if keep < len(c) and value < c[keep]: - for region, ami in info['artifacts'].items(): - self.pruneable[region][ami] = { - 'name': ami_name, - } - - # populate AMI creation_date and snapshot_id from Release().images - for region, images in r.images.items(): - for image in images: - if image.image_id in self.pruneable[region]: - p = self.pruneable[region][image.image_id] - p['name'] = image.name - p['creation_date'] = dateutil.parser.parse( - image.creation_date).strftime('%Y-%m-%d') - p['snapshot_id'] = image.snapshot_id - - def all_unknown_pruneable(self, r, log): - r.load_unknown_images(log) - for region, images in r.images.items(): - for image in images: - self.pruneable[region][image.image_id] = { - 'name': image.name, - 'creation_date': dateutil.parser.parse( - image.creation_date).strftime('%Y-%m-%d'), - 'snapshot_id': image.snapshot_id - } - - def run(self, args, root, log): - # instantiate Releases object - r = Releases( - profile=args.profile, - use_broker=args.use_broker, - regions=args.regions) - - if args.level == 'UNKNOWN': - self.all_unknown_pruneable(r, log) - else: - self.find_pruneable(r, args, log) - - for region, amis in sorted(self.pruneable.items()): - r_str = f"{args.level} AMIs in {region}" - if not amis: - log.info(f"No pruneable {r_str}.") - continue - if args.no_pretend: - log.error(f"REMOVING {r_str}:") - else: - log.warning(f"Removable {r_str}:") - for ami, info in sorted(amis.items(), key=lambda x: x[1]['creation_date']): - a_str = f" * {ami} ({info['creation_date']}) {info['name']}" - if args.no_pretend: - log.warning(a_str) - r.clients[region].deregister_image(ImageId=ami) - r.clients[region].delete_snapshot(SnapshotId=info['snapshot_id']) - else: - log.info(a_str) - - -def find_repo_root(): - """Find the root of the repo, which contains a .git folder - """ - path = os.getcwd() - - while ".git" not in set(os.listdir(path)) and path != "/": - path = os.path.dirname(path) - - if path == "/": - raise Exception("No repo found, stopping at /") - - return path - - -def main(): - """An introspective main method - - Just some silly metaprogramming to make commands really easy to write and - to avoid needing to hand register them. Commands have a specific interface, - per below, but should be really easy to create and will be auto discovered. - - Commands are objects that have the following attributes: - - __doc__ (python docstring) - used as help text in the CLI - - command_name (string) - name of the command as invoked by the cli - - add_args(parser) (class or static method) - passed an argparse subparser at setup time that will ultimately - handle the arguments for the command at runtime. Should add any - configuration necessary for the command to use later. Must not - rely on object state as it is not invoked with an instance of the - object. - - check_args(self, args) (class method, optional) - passed the set of arguments that were parsed before the command was - invoked. This function should return a list of errors or None. - Non-empty lists will cause the command to not be executed and help - being printed. - - run(self, args, root, log) (instance method) - passed the arguments object as parsed by argparse as well as a - string indicating the root of the repository (the folder containing - the .git folder) and an instance of a stanard libary logger for - output. Should throw exceptions on error and return when completed. - Should *not* execute sys.exit - """ - dispatch = {} - - parser = argparse.ArgumentParser() - subs = parser.add_subparsers(dest="command_name", required=True) - - # Configure logger - logger = logging.getLogger() - handler = logging.StreamHandler() - handler.setFormatter(ColoredFormatter(fmt="%(message)s")) - logger.addHandler(handler) - logger.setLevel(logging.INFO) - - for command in sys.modules[__name__].__dict__.values(): - if not hasattr(command, "command_name"): - continue - - dispatch[command.command_name] = command() - - doc = getattr(command, "__doc__", "") - subparser = subs.add_parser( - command.command_name, help=doc, description=doc) - - add_args = getattr(command, "add_args", None) - if add_args: - command.add_args(subparser) - - args = parser.parse_args() - - command = dispatch[args.command_name] - errors = getattr(command, "check_args", lambda x: [])(args) - if errors: - logger.error("\n".join(errors)) - # Ugly hack, gets the help for the subcommand, no public API for this - parser._actions[1]._name_parser_map[args.command_name].print_help() - else: - command.run(args, find_repo_root(), logger) - - -if __name__ == "__main__": - main() diff --git a/scripts/cleanup b/scripts/cleanup new file mode 100644 index 0000000..2c97cba --- /dev/null +++ b/scripts/cleanup @@ -0,0 +1,42 @@ +#!/bin/sh -eu +# vim: ts=4 et: + +[ -z "$DEBUG" ] || set +x + +export \ + TARGET=/mnt + + +die() { + printf '\033[1;7;31m FATAL: %s \033[0m\n' "$@" >&2 # bold reversed red + exit 1 +} +einfo() { + printf '\n\033[1;7;36m> %s <\033[0m\n' "$@" >&2 # bold reversed cyan +} + +cleanup() { + # Sweep cruft out of the image that doesn't need to ship or will be + # re-generated when the image boots + rm -f \ + "$TARGET/var/cache/apk/"* \ + "$TARGET/etc/resolv.conf" \ + "$TARGET/root/.ash_history" \ + "$TARGET/etc/"*- + + # unmount extra EFI mount + if [ "$FIRMWARE" = uefi ]; then + umount "$TARGET/boot/efi" + fi + + umount \ + "$TARGET/dev" \ + "$TARGET/proc" \ + "$TARGET/sys" + + umount "$TARGET" +} + +einfo "Cleaning up and unmounting image volume..." +cleanup +einfo "Done!" \ No newline at end of file diff --git a/scripts/setup b/scripts/setup new file mode 100755 index 0000000..15e1d2f --- /dev/null +++ b/scripts/setup @@ -0,0 +1,222 @@ +#!/bin/sh -eu +# vim: ts=4 et: + +[ -z "$DEBUG" -o "$DEBUG" = 0 ] || set -x + +export \ + DEVICE=/dev/vda \ + TARGET=/mnt \ + SETUP=/tmp/setup.d + + +die() { + printf '\033[1;7;31m FATAL: %s \033[0m\n' "$@" >&2 # bold reversed red + exit 1 +} +einfo() { + printf '\n\033[1;7;36m> %s <\033[0m\n' "$@" >&2 # bold reversed cyan +} + +# set up the builder's environment +setup_builder() { + einfo "Setting up Builder Instance" + setup-apkrepos -1 # main repo via dl-cdn + # ODO? also uncomment community repo? + apk --no-cache add \ + e2fsprogs \ + dosfstools \ + gettext \ + lsblk \ + parted +} + +make_filesystem() { + einfo "Making the Filesystem" + root_dev=$DEVICE + + # make sure we're using a blank block device + lsblk -P --fs "$DEVICE" >/dev/null 2>&1 || \ + die "'$DEVICE' is not a valid block device" + if lsblk -P --fs "$DEVICE" | grep -vq 'FSTYPE=""'; then + die "Block device '$DEVICE' is not blank" + fi + + if [ "$FIRMWARE" = uefi ]; then + # EFI partition isn't optimally aligned, but is rarely used after boot + parted "$DEVICE" -- \ + mklabel gpt \ + mkpart EFI fat32 512KiB 1MiB \ + mkpart / ext4 1MiB 100% \ + set 1 esp on \ + unit MiB print + + root_dev="${DEVICE}2" + /usr/sbin/mkfs.fat -n EFI "${DEVICE}1" + fi + + mkfs.ext4 -O ^64bit -L / "$root_dev" + mkdir -p "$TARGET" + mount -t ext4 "$root_dev" "$TARGET" + + if [ "$FIRMWARE" = uefi ]; then + mkdir -p "$TARGET/boot/efi" + mount -t vfat "${DEVICE}1" "$TARGET/boot/efi" + fi +} + +install_base() { + einfo "Installing Alpine Base" + mkdir -p "$TARGET/etc/apk" + echo "$REPOS" > "$TARGET/etc/apk/repositories" + cp -a /etc/apk/keys "$TARGET/etc/apk" + apk --root "$TARGET" --initdb --no-cache add $PACKAGES_ADD + [ -z "$PACKAGES_NOSCRIPTS" ] || \ + apk --root "$TARGET" --no-cache --no-scripts add $PACKAGES_NOSCRIPTS + [ -z "$PACKAGES_DEL" ] || \ + apk --root "$TARGET" --no-cache del $PACKAGES_DEL +} + +setup_chroot() { + mount -t proc none "$TARGET/proc" + mount --bind /dev "$TARGET/dev" + mount --bind /sys "$TARGET/sys" + + # Needed for bootstrap, will be removed in the cleanup stage. + install -Dm644 /etc/resolv.conf "$TARGET/etc/resolv.conf" +} + +install_bootloader() { + einfo "Installing Bootloader" + + # create initfs + sed -Ei "s/^features=\"([^\"]+)\"/features=\"\1 $INITFS_FEATURES\"/" \ + "$TARGET/etc/mkinitfs/mkinitfs.conf" + # shellcheck disable=SC2046 + chroot "$TARGET" /sbin/mkinitfs $(basename $(find "$TARGET/lib/modules/"* -maxdepth 0)) + + if [ "$FIRMWARE" = uefi ]; then + install_grub_efi + else + install_extlinux + fi +} + +install_extlinux() { + # Use disk labels instead of UUID or devices paths so that this works across + # instance familes. UUID works for many instances but breaks on the NVME + # ones because EBS volumes are hidden behind NVME devices. + # + # Shorten timeout (1/10s), eliminating delays for instance launches. + # + # ttyS0 is for EC2 Console "Get system log" and "EC2 Serial Console" + # features, whereas tty0 is for "Get Instance screenshot" feature. Enabling + # the port early in extlinux gives the most complete output in the log. + # + # TODO: review for other clouds -- this may need to be cloud-specific. + sed -Ei -e "s|^[# ]*(root)=.*|\1=LABEL=/|" \ + -e "s|^[# ]*(default_kernel_opts)=.*|\1=\"$KERNEL_OPTIONS\"|" \ + -e "s|^[# ]*(serial_port)=.*|\1=ttyS0|" \ + -e "s|^[# ]*(modules)=.*|\1=$KERNEL_MODULES|" \ + -e "s|^[# ]*(default)=.*|\1=virt|" \ + -e "s|^[# ]*(timeout)=.*|\1=1|" \ + "$TARGET/etc/update-extlinux.conf" + + chroot "$TARGET" /sbin/extlinux --install /boot + # TODO: is this really necessary? can we set all this stuff during --install? + chroot "$TARGET" /sbin/update-extlinux --warn-only +} + +install_grub_efi() { + [ -d "/sys/firmware/efi" ] || die "/sys/firmware/efi does not exist" + + case "$ARCH" in + x86_64) grub_target=x86_64-efi ; fwa=x64 ;; + aarch64) grub_target=arm64-efi ; fwa=aa64 ;; + *) die "ARCH=$ARCH is currently unsupported" ;; + esac + + # disable nvram so grub doesn't call efibootmgr + chroot "$TARGET" /usr/sbin/grub-install --target="$grub_target" --efi-directory=/boot/efi \ + --bootloader-id=alpine --boot-directory=/boot --no-nvram + + # fallback mode + install -D "$TARGET/boot/efi/EFI/alpine/grub$fwa.efi" "$TARGET/boot/efi/EFI/boot/boot$fwa.efi" + + # install default grub config + envsubst < "$SETUP/grub.template" > "$SETUP/grub" + install -o root -g root -Dm644 -t "$TARGET/etc/default" \ + "$SETUP/grub" + + # generate/install new config + chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg +} + +configure_system() { + einfo "Configuring System" + + # setup fstab + install -o root -g root -Dm644 -t "$TARGET/etc" \ + "$SETUP/fstab" + # if we're using an EFI bootloader, add extra line for EFI partition + if [ "$FIRMWARE" = uefi ]; then + cat "$SETUP/fstab.grub-efi" >> "$TARGET/etc/fstab" + fi + + # Disable getty for physical ttys, enable getty for serial ttyS0. + sed -Ei -e '/^tty[0-9]/s/^/#/' -e '/^#ttyS0:/s/^#//' "$TARGET/etc/inittab" + + # setup sudo and/or doas + if grep -q '^sudo$' "$TARGET/etc/apk/world"; then + echo '%wheel ALL=(ALL) NOPASSWD: ALL' > "$TARGET/etc/sudoers.d/wheel" + fi + if grep -q '^doas$' "$TARGET/etc/apk/world"; then + echo 'permit nopass :wheel' > "$TARGET/etc/doas.d/wheel.conf" + fi + + # explicitly lock the root account + chroot "$TARGET" /usr/bin/passwd -l root + + # set up image user + user="${IMAGE_LOGIN:-alpine}" + chroot "$TARGET" /usr/sbin/addgroup "$user" + chroot "$TARGET" /usr/sbin/adduser -h "/home/$user" -s /bin/sh -G "$user" -D "$user" + chroot "$TARGET" /usr/sbin/addgroup "$user" wheel + chroot "$TARGET" /usr/bin/passwd -u "$user" + + # modify PS1s in /etc/profile to add user + sed -Ei \ + -e "s/(^PS1=')(\\$\\{HOSTNAME%)/\\1\\$\\USER@\\2/" \ + -e "s/( PS1=')(\\\\h:)/\\1\\\\u@\\2/" \ + -e "s/( PS1=')(%m:)/\\1%n@\\2/" \ + "$TARGET"/etc/profile + + setup_services +} + +# shellcheck disable=SC2046 +setup_services() { + for lvl_svcs in $SERVICES_ENABLE; do + rc add $(echo "$lvl_svcs" | tr '=,' ' ') + done + for lvl_svcs in $SERVICES_DISABLE; do + rc del $(echo "$lvl_svcs" | tr '=,' ' ') + done +} + +rc() { + op="$1" # add or del + runlevel="$2" # runlevel name + shift 2 + services="$*" # names of services + + for svc in $services; do + chroot "$TARGET" rc-update "$op" "$svc" "$runlevel" + done +} + +setup_builder +make_filesystem +install_base +setup_chroot +install_bootloader +configure_system diff --git a/scripts/setup-ami b/scripts/setup-ami deleted file mode 100755 index 47955fc..0000000 --- a/scripts/setup-ami +++ /dev/null @@ -1,414 +0,0 @@ -#!/bin/sh -# vim: set ts=4 et: - -set -eu - -DEVICE=/dev/xvdf -TARGET=/mnt/target -SETUP=/tmp/setup-ami.d - -[ "$VERSION" = 'edge' ] && V= || V=v -MAIN_REPO="https://dl-cdn.alpinelinux.org/alpine/$V$VERSION/main/$ARCH" - -die() { - printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2 # bold red - exit 1 -} - -einfo() { - printf '\n\033[1;36m> %s\033[0m\n' "$@" >&2 # bold cyan -} - -rc_add() { - runlevel="$1"; shift # runlevel name - services="$*" # names of services - - for svc in $services; do - mkdir -p "$TARGET/etc/runlevels/$runlevel" - ln -s "/etc/init.d/$svc" "$TARGET/etc/runlevels/$runlevel/$svc" - echo " * service $svc added to runlevel $runlevel" - done -} - -validate_block_device() { - lsblk -P --fs "$DEVICE" >/dev/null 2>&1 || \ - die "'$DEVICE' is not a valid block device" - - if lsblk -P --fs "$DEVICE" | grep -vq 'FSTYPE=""'; then - die "Block device '$DEVICE' is not blank" - fi -} - -main_repo_pkgs() { - wget -T 10 -q -O - "$MAIN_REPO/" | grep '^
"$TARGET/etc/apk/repositories" -} - -fetch_keys() { - tmp="$(mktemp -d)" - tarball="$(main_repo_pkgs | grep ^alpine-keys- | sort -V | tail -n 1)" - - wget -T 10 -q -O "$tmp/$tarball" "$MAIN_REPO/$tarball" - tar -C "$TARGET" --warning=no-unknown-keyword -xvf "$tmp/$tarball" etc/apk/keys - rm -rf "$tmp" -} - -install_base() { - $apk add --root "$TARGET" --no-cache --initdb alpine-base - # verify release matches - if [ "$VERSION" != edge ]; then - ALPINE_RELEASE=$(cat "$TARGET/etc/alpine-release") - [ "$RELEASE" = "$ALPINE_RELEASE" ] || \ - die "Release '$RELEASE' does not match /etc/alpine-release: $ALPINE_RELEASE" - fi -} - -setup_chroot() { - mount -t proc none "$TARGET/proc" - mount --bind /dev "$TARGET/dev" - mount --bind /sys "$TARGET/sys" - - # Needed for bootstrap, will be removed in the cleanup stage. - install -Dm644 /etc/resolv.conf "$TARGET/etc/resolv.conf" -} - -install_core_packages() { - chroot "$TARGET" apk --no-cache add $PKGS - - # EFI_STUB requires no bootloader - [ "$BOOTLOADER" = EFI_STUB ] || \ - chroot "$TARGET" apk --no-cache add --no-scripts "$BOOTLOADER" - - # Disable starting getty for physical ttys because they're all inaccessible - # anyhow. With this configuration boot messages will still display in the - # EC2 console. - sed -Ei '/^tty[0-9]/s/^/#/' "$TARGET/etc/inittab" - - # Enable the getty for the serial terminal. This will show the login prompt - # in the get-console-output API that's accessible by the CLI and the web - # console. - sed -Ei '/^#ttyS0:/s/^#//' "$TARGET/etc/inittab" - - # Make it a little more obvious who is logged in by adding username to the - # prompt - sed -i "s/^export PS1='/&\\\\u@/" "$TARGET/etc/profile" - -} - -setup_mdev() { - install -o root -g root -Dm755 -t "$TARGET/lib/mdev" \ - "$SETUP/nvme-ebs-links" \ - "$SETUP/eth-eni-hotplug" - - # insert nvme ebs mdev configs just above "# fallback" comment - sed -n -i \ - -e "/# fallback/r $SETUP/etc/mdev-ec2.conf" \ - -e 1x -e '2,${x;p}' -e '${x;p}' \ - "$TARGET/etc/mdev.conf" -} - -create_initfs() { - sed -Ei "s/^features=\"([^\"]+)\"/features=\"\1 $INITFS_FEATURES\"/" \ - "$TARGET/etc/mkinitfs/mkinitfs.conf" - - chroot "$TARGET" /sbin/mkinitfs $(basename $(find "$TARGET/lib/modules/"* -maxdepth 0)) -} - -install_bootloader() { - case "$BOOTLOADER" in - syslinux) install_extlinux ;; - grub-efi) install_grub_efi ;; - EFI_STUB) install_EFI_STUB ;; - *) die "unknown bootloader '$BOOTLOADER'" ;; - esac -} - -install_extlinux() { - # Must use disk labels instead of UUID or devices paths so that this works - # across instance familes. UUID works for many instances but breaks on the - # NVME ones because EBS volumes are hidden behind NVME devices. - # - # Enable ext4 because the root device is formatted ext4 - # - # Shorten timeout (1/10s) as EC2 has no way to interact with instance console - # - # ttyS0 is the target for EC2s "Get System Log" feature whereas tty0 is the - # target for EC2s "Get Instance Screenshot" feature. Enabling the serial - # port early in extlinux gives the most complete output in the system log. - sed -Ei -e "s|^[# ]*(root)=.*|\1=LABEL=/|" \ - -e "s|^[# ]*(default_kernel_opts)=.*|\1=\"$KERNEL_OPTS\"|" \ - -e "s|^[# ]*(serial_port)=.*|\1=ttyS0|" \ - -e "s|^[# ]*(modules)=.*|\1=$KERNEL_MODS|" \ - -e "s|^[# ]*(default)=.*|\1=virt|" \ - -e "s|^[# ]*(timeout)=.*|\1=1|" \ - "$TARGET/etc/update-extlinux.conf" - - chroot "$TARGET" /sbin/extlinux --install /boot - chroot "$TARGET" /sbin/update-extlinux --warn-only -} - -install_grub_efi() { - [ -d "/sys/firmware/efi" ] || die "/sys/firmware/efi does not exist" - - case "$ARCH" in - x86_64) grub_target=x86_64-efi ; fwa=x64 ;; - aarch64) grub_target=arm64-efi ; fwa=aa64 ;; - *) die "ARCH=$ARCH is currently unsupported" ;; - esac - - # disable nvram so grub doesn't call efibootmgr - chroot "$TARGET" /usr/sbin/grub-install --target="$grub_target" --efi-directory=/boot/efi \ - --bootloader-id=alpine --boot-directory=/boot --no-nvram - - # fallback mode - install -D "$TARGET/boot/efi/EFI/alpine/grub$fwa.efi" "$TARGET/boot/efi/EFI/boot/boot$fwa.efi" - - # install default grub config - envsubst < "$SETUP/etc/grub.template" > "$SETUP/etc/grub" - install -o root -g root -Dm644 -t "$TARGET/etc/default" \ - "$SETUP/etc/grub" - - # generate/install new config - chroot "$TARGET" grub-mkconfig -o /boot/grub/grub.cfg -} - -install_EFI_STUB() { - [ -d "/sys/firmware/efi" ] || die "/sys/firmware/efi does not exist" - - case "$ARCH" in - x86_64) fwa=x64 ;; - aarch64) fwa=aa64 ;; - *) die "ARCH=$ARCH is currently unsupported" ;; - esac - - # TODO: kernel modules/options? - # TODO: will also need initfs in here too - # TODO: make it work - - # install kernel as UEFI fallback - install -o root -g root -Dm644 "$TARGET/boot/vmlinuz-virt" \ - "$TARGET/boot/efi/EFI/boot/boot$fwa.efi" - # replace original with a symlink - rm "$TARGET/boot/vmlinuz-virt" - ln -s "efi/EFI/boot/boot$fwa.efi" "$TARGET/boot/vmlinuz-virt" -} - -setup_fstab() { - install -o root -g root -Dm644 -t "$TARGET/etc" \ - "$SETUP/etc/fstab" - - # if we're using an EFI bootloader, add extra line for EFI partition - if [ "$BOOTLOADER" = grub-efi ] || [ "$BOOTLOADER" = EFI_STUB ]; then - cat "$SETUP/etc/fstab.grub-efi" >> "$TARGET/etc/fstab" - fi -} - -setup_networking() { - # configure standard interfaces - IFACE_CFG="$TARGET/etc/network/interfaces" - install -o root -g root -Dm755 -d "$SETUP/etc/interfaces.d" "$IFACE_CFG.d" - install -o root -g root -Dm644 -t "$IFACE_CFG.d" \ - "$SETUP/etc/interfaces.d/"* - cat "$IFACE_CFG.d/lo" "$IFACE_CFG.d/eth0" > "$IFACE_CFG" - - install -o root -g root -Dm755 -t "$TARGET/etc/network" \ - "$SETUP/assemble-interfaces" - - # install ucdhcp hooks for EC2 ENI IPv6 and secondary IPv4 - install -o root -g root -Dm755 -t "$TARGET/usr/share/udhcpc" \ - "$SETUP/eth-eni-hook" - for i in post-bound post-renew; do - mkdir -p "$TARGET/etc/udhcpc/$i" - ln -sf /usr/share/udhcpc/eth-eni-hook \ - "$TARGET/etc/udhcpc/$i" - done - - # install ENI interface setup init script - install -o root -g root -Dm755 -t "$TARGET/etc/init.d" \ - "$SETUP/eth-eni-setup" -} - -enable_services() { - for lvl_svcs in $SVCS; do - rc_add $(echo "$lvl_svcs" | tr '=,' ' ') - done -} - -create_alpine_user() { - # Allow members of the wheel group to sudo without a password. By default - # this will only be the alpine user. This allows us to ship an AMI that is - # accessible via SSH using the user's configured SSH keys (thanks to - # tiny-ec2-bootstrap) but does not allow remote root access which is the - # best-practice. - sed -i '/%wheel .* NOPASSWD: .*/s/^# //' "$TARGET/etc/sudoers" - - # explicitly lock the root account - chroot "$TARGET" /usr/bin/passwd -l root - - # There is no real standard ec2 username across AMIs, Amazon uses ec2-user - # for their Amazon Linux AMIs but Ubuntu uses ubuntu, Fedora uses fedora, - # etc... (see: https://alestic.com/2014/01/ec2-ssh-username/). So our user - # and group, by default, are alpine because this is Alpine Linux. - user="${EC2_USER:-alpine}" - chroot "$TARGET" /usr/sbin/addgroup "$user" - chroot "$TARGET" /usr/sbin/adduser -h "/home/$user" -s /bin/sh -G "$user" -D "$user" - chroot "$TARGET" /usr/sbin/addgroup "$user" wheel - chroot "$TARGET" /usr/bin/passwd -u "$user" - - # Let tiny-ec2-bootstrap know what the EC2 user of the AMI is - echo "EC2_USER=$user" > "$TARGET/etc/conf.d/tiny-ec2-bootstrap" -} - -configure_ntp() { - # EC2 provides an instance-local NTP service syncronized with GPS and - # atomic clocks in-region. Prefer this over external NTP hosts when running - # in EC2. - # - # See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html - sed -e 's/^pool /server /' \ - -e 's/pool.ntp.org/169.254.169.123/g' \ - -i "$TARGET/etc/chrony/chrony.conf" -} - -setup_script() { - if [ -f "$SETUP/setup_script" ]; then - einfo "Executing additional setup script" - ( - cd "$SETUP" - chmod u+x ./setup_script - TARGET="$TARGET" ./setup_script - ) - else - einfo "No additional setup script" - fi -} - -cleanup() { - # Sweep cruft out of the image that doesn't need to ship or will be - # re-generated when the image boots - rm -f \ - "$TARGET/var/cache/apk/"* \ - "$TARGET/etc/resolv.conf" \ - "$TARGET/root/.ash_history" \ - "$TARGET/etc/"*- - - # unmount extra EFI mount - if [ "$BOOTLOADER" = grub-efi ] || [ "$BOOTLOADER" = EFI_STUB ]; then - umount "$TARGET/boot/efi" - fi - - umount \ - "$TARGET/dev" \ - "$TARGET/proc" \ - "$TARGET/sys" - - umount "$TARGET" -} - -main() { - validate_block_device - - [ -d "$TARGET" ] || mkdir "$TARGET" - - einfo "Fetching static APK tools" - apk="$(fetch_apk_tools)" - - einfo "Creating root filesystem" - make_filesystem - - einfo "Configuring Alpine repositories" - setup_repositories - - einfo "Fetching Alpine signing keys" - fetch_keys - - einfo "Installing base system" - install_base - - setup_chroot - - einfo "Installing core packages" - install_core_packages - - einfo "Configuring and enabling '$BOOTLOADER' boot loader" - create_initfs - install_bootloader - - einfo "Configuring system" - setup_mdev - setup_fstab - setup_networking - enable_services - create_alpine_user - configure_ntp - - setup_script - - einfo "All done, cleaning up" - cleanup -} - -main "$@" diff --git a/scripts/setup-ami.d/eth-eni-setup b/scripts/setup-ami.d/eth-eni-setup deleted file mode 100755 index 7246dbe..0000000 --- a/scripts/setup-ami.d/eth-eni-setup +++ /dev/null @@ -1,16 +0,0 @@ -#!/sbin/openrc-run - -description="Sets up interfaces for attached Elastic Network Interfaces" - -depend() { - before net - need sysfs -} - -start() { - local iface - - ebegin "Setting up interfaces for attached ENIs" - /etc/network/assemble-interfaces - eend "$?" -} diff --git a/scripts/setup-ami.d/nvme-ebs-links b/scripts/setup-ami.d/nvme-ebs-links deleted file mode 100755 index 6ebdb10..0000000 --- a/scripts/setup-ami.d/nvme-ebs-links +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -[ -x /usr/sbin/nvme ] || exit - -PROC="$(basename "$0")[$$]" - -log() { - FACILITY="kern.$1" - shift - logger -s -p "$FACILITY" -t "$PROC" "$@" -} - -raw_ebs_alias() { - /usr/sbin/nvme id-ctrl "/dev/$BASE" -b 2>/dev/null | dd bs=32 skip=96 count=1 2>/dev/null -} - -case $ACTION in - add|"") - BASE=$(echo "$MDEV" | sed -re 's/^(nvme[0-9]+n[0-9]+).*/\1/') - PART=$(echo "$MDEV" | sed -re 's/nvme[0-9]+n[0-9]+p?//g') - MAXTRY=50 - TRY=0 - until [ -n "$EBS" ]; do - EBS=$(raw_ebs_alias | sed -nre '/^(\/dev\/)?(s|xv)d[a-z]{1,2} /p' | tr -d ' ') - [ -n "$EBS" ] && break - TRY=$((TRY + 1)) - if [ $TRY -eq $MAXTRY ]; then - log err "Failed to get EBS volume alias for $MDEV after $MAXTRY attempts ($(raw_ebs_alias))" - exit 1 - fi - sleep 0.1 - done - # remove any leading '/dev/', 'sd', or 'xvd', and append partition - EBS=${EBS#/dev/} - EBS=${EBS#sd} - EBS=${EBS#xvd}$PART - ln -sf "$MDEV" "sd$EBS" && log notice "Added sd$EBS symlink for $MDEV" - ln -sf "$MDEV" "xvd$EBS" && log notice "Added xvd$EBS symlink for $MDEV" - ;; - remove) - for TARGET in sd* xvd* - do - [ "$(readlink "$TARGET" 2>/dev/null)" = "$MDEV" ] && rm -f "$TARGET" && log notice "Removed $TARGET symlink for $MDEV" - done - ;; -esac diff --git a/scripts/setup-tiny b/scripts/setup-tiny new file mode 100755 index 0000000..9d0bc9c --- /dev/null +++ b/scripts/setup-tiny @@ -0,0 +1,67 @@ +#!/bin/sh -eu +# vim: ts=4 et: + +# NOTE: This is lifted almost verbatim from alpine-ec2-ami's setup-ami script +# While refactoring that, it became apparent that these bits really belonged +# to the tiny-ec2-bootstrap package, and in order to also provide a +# "cloud-init" image flavor, we needed separate these parts out. +# +# It is our intention to eventually incorporate this in a tiny-cloud-bootstrap +# package (and per-cloud subpackages). + +[ -z "$DEBUG" -o "$DEBUG" = 0 ] || set -x + +TARGET=/mnt +SETUP=/tmp/setup-tiny.d + +die() { + printf '\033[1;7;31m FATAL: %s \033[0m\n' "$@" >&2 # bold reversed red + exit 1 +} +einfo() { + printf '\n\033[1;7;36m> %s <\033[0m\n' "$@" >&2 # bold reversed cyan +} + +setup_mdev() { + echo " * Setting up mdev" + install -o root -g root -Dm755 -t "$TARGET/lib/mdev" \ + "$SETUP/nvme-ebs-links" \ + "$SETUP/eth-eni-hotplug" + + # insert nvme ebs mdev configs just above "# fallback" comment + sed -n -i \ + -e "/# fallback/r $SETUP/mdev-ec2.conf" \ + -e 1x -e '2,${x;p}' -e '${x;p}' \ + "$TARGET/etc/mdev.conf" +} + +setup_networking() { + echo " * Setting up networking" + # configure standard interfaces + IFACE_CFG="$TARGET/etc/network/interfaces" + install -o root -g root -Dm755 -d "$SETUP/interfaces.d" "$IFACE_CFG.d" + install -o root -g root -Dm644 -t "$IFACE_CFG.d" \ + "$SETUP/interfaces.d/"* + cat "$IFACE_CFG.d/lo" "$IFACE_CFG.d/eth0" > "$IFACE_CFG" + + install -o root -g root -Dm755 -t "$TARGET/etc/network" \ + "$SETUP/assemble-interfaces" + + # install ucdhcp hooks for EC2 ENI IPv6 and secondary IPv4 + install -o root -g root -Dm755 -t "$TARGET/usr/share/udhcpc" \ + "$SETUP/eth-eni-hook" + for i in post-bound post-renew; do + mkdir -p "$TARGET/etc/udhcpc/$i" + ln -sf /usr/share/udhcpc/eth-eni-hook \ + "$TARGET/etc/udhcpc/$i" + done + + # install ENI interface setup init script + install -o root -g root -Dm755 -t "$TARGET/etc/init.d" \ + "$SETUP/eth-eni-setup" +} + +einfo "Installing up tiny bootstrap components..." +setup_mdev +setup_networking +echo "EC2_USER=$IMAGE_LOGIN" > "$TARGET/etc/conf.d/tiny-ec2-bootstrap" diff --git a/scripts/setup-ami.d/assemble-interfaces b/scripts/setup-tiny.d/assemble-interfaces similarity index 100% rename from scripts/setup-ami.d/assemble-interfaces rename to scripts/setup-tiny.d/assemble-interfaces diff --git a/scripts/setup-ami.d/eth-eni-hook b/scripts/setup-tiny.d/eth-eni-hook similarity index 100% rename from scripts/setup-ami.d/eth-eni-hook rename to scripts/setup-tiny.d/eth-eni-hook diff --git a/scripts/setup-ami.d/eth-eni-hotplug b/scripts/setup-tiny.d/eth-eni-hotplug similarity index 100% rename from scripts/setup-ami.d/eth-eni-hotplug rename to scripts/setup-tiny.d/eth-eni-hotplug diff --git a/scripts/setup-tiny.d/eth-eni-setup b/scripts/setup-tiny.d/eth-eni-setup new file mode 100755 index 0000000..aba272a --- /dev/null +++ b/scripts/setup-tiny.d/eth-eni-setup @@ -0,0 +1,19 @@ +#!/sbin/openrc-run + +# vim: set ts=4 et: +# shellcheck shell=sh + +description="Sets up interfaces for attached Elastic Network Interfaces" + +depend() { + before net + need sysfs +} + +start() { + local iface + + ebegin "Setting up interfaces for attached ENIs" + /etc/network/assemble-interfaces + eend "$?" +} diff --git a/scripts/setup-ami.d/etc/interfaces.d/DEFAULT b/scripts/setup-tiny.d/interfaces.d/DEFAULT similarity index 100% rename from scripts/setup-ami.d/etc/interfaces.d/DEFAULT rename to scripts/setup-tiny.d/interfaces.d/DEFAULT diff --git a/scripts/setup-ami.d/etc/interfaces.d/eth0 b/scripts/setup-tiny.d/interfaces.d/eth0 similarity index 100% rename from scripts/setup-ami.d/etc/interfaces.d/eth0 rename to scripts/setup-tiny.d/interfaces.d/eth0 diff --git a/scripts/setup-ami.d/etc/interfaces.d/lo b/scripts/setup-tiny.d/interfaces.d/lo similarity index 100% rename from scripts/setup-ami.d/etc/interfaces.d/lo rename to scripts/setup-tiny.d/interfaces.d/lo diff --git a/scripts/setup-ami.d/etc/mdev-ec2.conf b/scripts/setup-tiny.d/mdev-ec2.conf similarity index 100% rename from scripts/setup-ami.d/etc/mdev-ec2.conf rename to scripts/setup-tiny.d/mdev-ec2.conf diff --git a/scripts/setup-tiny.d/nvme-ebs-links b/scripts/setup-tiny.d/nvme-ebs-links new file mode 100755 index 0000000..e4ba49f --- /dev/null +++ b/scripts/setup-tiny.d/nvme-ebs-links @@ -0,0 +1,47 @@ +#!/bin/sh +# vim: set ts=4 et: + +[ -x /usr/sbin/nvme ] || exit + +PROC="$(basename "$0")[$$]" + +log() { + FACILITY="kern.$1" + shift + logger -s -p "$FACILITY" -t "$PROC" "$@" +} + +raw_ebs_alias() { + /usr/sbin/nvme id-ctrl "/dev/$BASE" -b 2>/dev/null | dd bs=32 skip=96 count=1 2>/dev/null +} + +case $ACTION in + add|"") + BASE=$(echo "$MDEV" | sed -re 's/^(nvme[0-9]+n[0-9]+).*/\1/') + PART=$(echo "$MDEV" | sed -re 's/nvme[0-9]+n[0-9]+p?//g') + MAXTRY=50 + TRY=0 + until [ -n "$EBS" ]; do + EBS=$(raw_ebs_alias | sed -nre '/^(\/dev\/)?(s|xv)d[a-z]{1,2} /p' | tr -d ' ') + [ -n "$EBS" ] && break + TRY=$((TRY + 1)) + if [ $TRY -eq $MAXTRY ]; then + log err "Failed to get EBS volume alias for $MDEV after $MAXTRY attempts ($(raw_ebs_alias))" + exit 1 + fi + sleep 0.1 + done + # remove any leading '/dev/', 'sd', or 'xvd', and append partition + EBS=${EBS#/dev/} + EBS=${EBS#sd} + EBS=${EBS#xvd}$PART + ln -sf "$MDEV" "sd$EBS" && log notice "Added sd$EBS symlink for $MDEV" + ln -sf "$MDEV" "xvd$EBS" && log notice "Added xvd$EBS symlink for $MDEV" + ;; + remove) + for TARGET in sd* xvd* + do + [ "$(readlink "$TARGET" 2>/dev/null)" = "$MDEV" ] && rm -f "$TARGET" && log notice "Removed $TARGET symlink for $MDEV" + done + ;; +esac diff --git a/scripts/setup-ami.d/etc/fstab b/scripts/setup.d/fstab similarity index 100% rename from scripts/setup-ami.d/etc/fstab rename to scripts/setup.d/fstab diff --git a/scripts/setup-ami.d/etc/fstab.grub-efi b/scripts/setup.d/fstab.grub-efi similarity index 100% rename from scripts/setup-ami.d/etc/fstab.grub-efi rename to scripts/setup.d/fstab.grub-efi diff --git a/scripts/setup-ami.d/etc/grub.template b/scripts/setup.d/grub.template similarity index 68% rename from scripts/setup-ami.d/etc/grub.template rename to scripts/setup.d/grub.template index 7af988a..b88f991 100644 --- a/scripts/setup-ami.d/etc/grub.template +++ b/scripts/setup.d/grub.template @@ -1,5 +1,5 @@ -GRUB_TIMEOUT=0 -GRUB_DISABLE_SUBMENU=y +GRUB_CMDLINE_LINUX_DEFAULT="modules=$KERNEL_MODULES $KERNEL_OPTIONS" GRUB_DISABLE_RECOVERY=true +GRUB_DISABLE_SUBMENU=y GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1" -GRUB_CMDLINE_LINUX_DEFAULT="modules=$KERNEL_MODS $KERNEL_OPTS" +GRUB_TIMEOUT=0 diff --git a/scripts/test-setup_script.sh b/scripts/test-setup_script.sh deleted file mode 100755 index 18e3fdb..0000000 --- a/scripts/test-setup_script.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env sh - -set -ex - -# copy a directory into place -cp -a ./base "$TARGET/home/$EC2_USER/test" - -# process a file and put it into place -tac ./aarch64 | rev > "$TARGET/home/$EC2_USER/test/46hcraa" - -# set ownership of installed things -chroot "$TARGET" chown -R "$EC2_USER:$EC2_USER" "/home/$EC2_USER/test"