diff --git a/README.md b/README.md index 23f1948..d4c2e9b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,187 @@ -# rmk -RMK CLI - Reduced Management for Kubernetes. +# RMK CLI - Reduced Management for Kubernetes + +[![Release](https://img.shields.io/github/v/release/edenlabllc/rmk.svg?style=for-the-badge)](https://github.com/edenlabllc/rmk/releases/latest) +[![Software License](https://img.shields.io/github/license/edenlabllc/rmk.svg?style=for-the-badge)](LICENSE) +[![Powered By: GoReleaser](https://img.shields.io/badge/powered%20by-edenlab-8A2BE2.svg?style=for-the-badge)](https://edenlab.io) + +Command line tool for reduced management and provisioning of Kubernetes clusters and environments, Helm secrets and releases. + +* [RMK CLI - Reduced Management for Kubernetes](#rmk-cli---reduced-management-for-kubernetes) + * [Overview](#overview) + * [Advantages](#advantages) + * [Edenlab LLC use cases](#edenlab-llc-use-cases) + * [Requirements](#requirements) + * [Quickstart](docs/quickstart.md) + * [Installation](#installation) + * [Update](#update) + * [General update process](#general-update-process) + * [Update to specific version](#update-to-specific-version) + * Configuration + * [RMK configuration management](docs/configuration/rmk-configuration-management.md) + * Project management + * [Requirement for project repository](docs/configuration/project-management/requirement-for-project-repository.md) + * [Preparation of project repository](docs/configuration/project-management/preparation-of-project-repository.md) + * [Dependencies management and Project inheritance](docs/configuration/project-management/dependencies-management-and-project-inheritance.md) + * [Cluster management](docs/configuration/cluster-management/cluster-management.md) + * [Exported environment variables](docs/configuration/cluster-management/exported-environment-variables.md) + * [Release management](docs/configuration/release-management/release-management.md) + * [Secrets management](docs/configuration/secrets-management/secrets-management.md) + * [Commands](docs/commands.md) + * [Development and release flow](docs/development-and-release-flow.md) + * [Features](#features) + * [Supported Kubernetes providers](#supported-kubernetes-providers) + * [Roadmap](#roadmap) + * [License](#license) + +## Overview + +This tool has been designed and developed initially by [Edenlab LLC](https://edenlab.io/) as the main CLI +for managing [Kodjin FHIR Server](https://kodjin.com) on Kubernetes clusters in different environments. + +It is a wrapper around many popular CI/CD and DevOps CLI tools, including: + +- [Helmfile](https://helmfile.readthedocs.io/en/latest/) +- [Helm](https://helm.sh/) +- [kubectl](https://kubernetes.io/docs/reference/kubectl/) +- [SOPS](https://getsops.io/) +- [Terraform](https://www.terraform.io/) +- [K3D](https://k3d.io/) + +The main goal of the tool is to simplify ("reduce") management of Kubernetes clusters and releases. + +**RMK** is an abbreviation which stands for "**R**educed **M**anagement for **K**ubernetes". + +### Advantages + +RMK simplifies the start of any level of complexity of a project using Kubernetes due to the following advantages: +- **Respects the [GitOPS](https://www.gitops.tech/) approach:** Each Git branch is used as a unique identifier for determining the environment, cluster name, + set of configurations and other attributes required for setting up the wrapped tools for project management in the Kubernetes environment. +- **Respects the [GitLabFlow](https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/) workflow**: Supports the standard _develop_, _staging_, _production_ and different ephemeral (e.g, _feature_, _release_) environments. +- **Provides a transparent project structure with a basic set of configurations**: Allows you to correctly reuse configurations between projects + and inherit project configurations from other repositories, e.g., establish parent-child ("upstream-downstream") project relationships. +- **Allows a diverse team to work in the [DevOPS](https://www.atlassian.com/devops) methodology without blocking each other**: Each team or multiple teams + can develop and release their projects separately, later on the result of their work can be combined in a single project. +- **Supports versioning of projects in a CI/CD pipeline**: Each project can be versioned and referenced by static or dynamic tags (e.g., [SemVer2](https://semver.org/)), + which guarantees stable, well-tested and predictable releases. +- **Integrates with any CI/CD tool easily**: The tool is a self-sufficient binary that strictly follows the [12 factor app](https://12factor.net/) methodology. +- **Calls the CLI tools directly instead of using their libraries/SDKs**: RMK executes the tools directly in a way that a typical person would do it, + passing correct sets of CLI arguments and flags to the commands based on a project configuration structure. + This decouples the updating of RMK itself from the wrapped CLI tools, allowing developers to utilize recent functionality and fixes. + +### Edenlab LLC use cases + +At [Edenlab LLC](https://edenlab.io/), RMK is used for deploying the [Kodjin FHIR Server](https://kodjin.com). + +A classic Kodjin installation uses 3-level inheritance: +- **Dependencies (upstream#1)**: Core components like DBs, search engines, caches, load balancers/proxies, operators + etc. +- **Kodjin (downstream#1)**: Kodjin FHIR API ([REST](https://en.wikipedia.org/wiki/REST)) +- **Target installation (downstream#2)**: Products based on Kodjin, such as UI components, user portals and middleware services. + +The additional components used by Kodjin are: +- **\*.provisioner.infra:** Repositories for Kubernetes cluster provisioning. +- **helmfile.hooks.infra:** Shell scrips used as [Helmfile hooks](https://helmfile.readthedocs.io/en/latest/#hooks) in + deps/Kodjin/any other tenant. +- **core.charts.infra:** Helm charts used by the Kodjin services. + +The examples of Kubernetes providers, to which Kodjin has been installed, are: +- [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/) +- [Open Telekom Cloud - Cloud Container Engine (CCE)](https://www.open-telekom-cloud.com/en/products-services/core-services/cloud-container-engine) +- [Rancher Kubernetes Platform](https://www.rancher.com/) +- [Kubermatic Kubernetes Platform (KKP)](https://www.kubermatic.com/) +- on-premise installations deployed using [Ansible Kubespray](https://github.com/kubernetes-sigs/kubespray) +- single-machine [K3D](https://k3d.io/) clusters + +## Requirements + +Currently, RMK only supports Unix-like operating systems (OS): +* **OS:** + * **MacOS**: amd64, arm64 (M1, M2 require [Rosetta](https://support.apple.com/en-us/HT211861)) + * **Linux**: amd64 +* **Software:** + * **Python** >= 3.9 + * **[AWS CLI](https://aws.amazon.com/cli/)** + * _For managing local clusters using K3D:_ Version _v5.x.x_ requires [Docker](https://www.docker.com/) => v20.10.5 ([runc](https://github.com/opencontainers/runc) >= v1.0.0-rc93) to work + properly. + +> If this is your first project repository managed by RMK, ensure that the above tools are specified in the [project.yaml](docs/configuration/project-management/preparation-of-project-repository.md#projectyaml) file. + +## Installation + +To install RMK, run the self-installer script using the following command: + +```shell +curl -sL "https://edenlabllc-rmk.s3.eu-north-1.amazonaws.com/rmk/s3-installer" | bash +``` + +Alternatively, you can go directly to https://github.com/edenlabllc/rmk/releases and download the binary. + +As another option, the binary can be [built from source](docs/development-and-release-flow.md#building-from-source). + +## Update + +### General update process + +To update RMK to the latest version, run the following command: + +```shell +rmk update +``` + +### Update to specific version + +You can update to a specific RMK version to maintain backward compatibility or when updating to the latest version is not possible. +> This may be necessary due to specific version requirements or when a bug has been detected. + +To update to a specific version, use the following command: + +```shell +rmk update --version vX.X.X +``` + +## Features + +- **[Reduced and simplified management of Kubernetes projects:](#overview)** Deploy to Kubernetes using Helmfile/Helm, use popular DevOps tools together in a single CI/CD pipeline. +- **[Time-proven project structure:](docs/configuration/project-management/preparation-of-project-repository.md)** Define the project structure using the [GitLabFlow](https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/) methodology. +- **[Hierarchies between different projects:](docs/configuration/project-management/dependencies-management-and-project-inheritance.md)** Define upstream-downstream relationships between sibling projects to reuse releases and services across different installations. +- **[Batch secret management:](docs/configuration/secrets-management/secrets-management.md#generating-all-secrets-from-scratch-in-a-batch-manner-using-the-rmk-secrets-manager)** Template, generate, and encode project secrets for all environments in a batch manner. +- **[Clone environments with one click:](docs/configuration/rmk-configuration-management.md#initialization-of-rmk-configuration-for-feature-or-release-clusters)** Use the special `--config-from-environment` (`--cfe`) flag to create an environment based on an existing one. +- **[Automatic detection of Multi-Factor Authentication](docs/configuration/rmk-configuration-management.md#support-for-multi-factor-authentication-mfa) ([MFA](https://en.wikipedia.org/wiki/Multi-factor_authentication)):** Automatically detect and use an MFA device if one is defined by an [IAM](https://aws.amazon.com/iam/) user (must be supported by the cluster provider, e.g., [AWS](https://aws.amazon.com/)). +- **[Push-based release and downstream project updates:](docs/configuration/release-management/release-management.md#release-update-and-integration-into-the-cd-pipeline)** Easily integrate with CI/CD solutions via webhooks or workflow dispatch events + to update release and service version declarations, automatically commit the changes to Git. +- **[Project structure generation:](docs/configuration/project-management/preparation-of-project-repository.md#automatic-generation-of-the-project-structure-from-scratch)** Generate a complete Kubernetes-based project structure from scratch using RMK, following the best practices. +- **[Documentation generation:](docs/commands.md#doc)** Generate the full command documentation in the Markdown format with one click. +- **[Support for different types of code sources:](docs/configuration/rmk-configuration-management.md#use-upstream-artifact-for-the-downstream-projects-repository)** Use Git when the _artifact-mode_ is _none_, S3 when the _artifact-mode_ is _online_, + switch to fully offline installations when the _artifact-mode_ is _offline_. + +## Supported Kubernetes providers + +By design, RMK can work with any Kubernetes provider. + +Among the providers are: + +- [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/) +- [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) +- [Azure Kubernetes Service (AKS)](https://azure.microsoft.com/en-us/products/kubernetes-service/) +- [Red Hat OpenShift](https://redhat.com/en/technologies/cloud-computing/openshift) +- [VMware Tanzu Kubernetes Grid](https://tanzu.vmware.com/kubernetes-grid) +- [Rancher Kubernetes Platform](https://www.rancher.com/) +- [Open Telekom Cloud - Cloud Container Engine (CCE)](https://www.open-telekom-cloud.com/en/products-services/core-services/cloud-container-engine) +- [Kubermatic Kubernetes Platform (KKP)](https://www.kubermatic.com/) +- on-premise installations deployed using [Ansible Kubespray](https://github.com/kubernetes-sigs/kubespray) +- single-machine [K3D](https://k3d.io/) clusters + +## Roadmap + +- **Guidelines for contributors:** Create comprehensive guidelines for contributors, including instructions for creating pull requests (PRs). +- **Integration with Helmfile [vals](https://github.com/helmfile/vals)**: Integrate RMK with the **vals** tool for enhanced values and secret management. +- **Integration with [Packer](https://www.packer.io/):** Implement integration with the **Packer** tool for creating Kodjin instance images, e.g., Amazon Machine Images ([AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)). +- **Major update of the AWS [EKS](https://aws.amazon.com/eks/) cluster provider:** Update the AWS EKS cluster provider to the latest versions to utilize all the supported features of the [Terraform](https://www.terraform.io/) CLI and modules. +- **Implementation of additional RMK cluster providers:** Implement support for additional cluster providers for popular Kubernetes services such as [GKE](https://cloud.google.com/kubernetes-engine), [AKS](https://azure.microsoft.com/en-us/products/kubernetes-service/), etc. +- **Offline artifact mode:** Implement the **offline** artifact mode to install artifacts in fully isolated offline environments. +- **Web documentation generator:** Add an HTML documentation generator based on the **.md** files. +- **Automatic testing of RMK during the CI/CD pipeline:** Ensure that changes to the RMK codebase do not introduce errors or regressions during the CI/CD. + +## License + +RMK is open source software (OSS) licensed under the [Apache 2.0 License](LICENSE). diff --git a/docs/commands.md b/docs/commands.md new file mode 100644 index 0000000..068b1cb --- /dev/null +++ b/docs/commands.md @@ -0,0 +1,407 @@ +# NAME + +rmk - Reduced management for Kubernetes + +# SYNOPSIS + +rmk + +``` +[--help|-h] +[--log-format|--lf]=[value] +[--log-level|--ll]=[value] +[--version|-v] +``` + +# DESCRIPTION + +Command line tool for reduced management of the provision of Kubernetes clusters in different environments and management of service releases. +BuiltBy: goreleaser +Commit: fef9dcc +Date: 2024-05-09T07:30:00Z +Target: darwin_amd64 + +**Usage**: + +``` +rmk [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +``` + +# GLOBAL OPTIONS + +**--help, -h**: show help + +**--log-format, --lf**="": log output format, available: console, json (default: "console") + +**--log-level, --ll**="": log level severity, available: debug, info, error (default: "info") + +**--version, -v**: print the version + + +# COMMANDS + +## cluster + +Cluster management + +### container-registry, c + +Container registry management + +#### login + +Log in to container registry + +**--get-token, -g**: get ECR token for authentication + +#### logout + +Log out from container registry + +### destroy, d + +Destroy AWS cluster using Terraform + +### list, l + +List all Terraform available workspaces + +### k3d, k + +K3D cluster management + +#### create, c + +Create K3D cluster + +**--k3d-volume-host-path, --kv**="": host local directory path for mount into K3D cluster (default: "${PWD}/rmk.tools.infra") + +#### delete, d + +Delete K3D cluster + +#### import, i + +Import images from docker to K3D cluster + +**--k3d-import-image, --ki**="": list images for import into K3D cluster + +#### list, l + +List K3D clusters + +#### start, s + +Start K3D cluster + +#### stop + +Stop K3D cluster + +### provision, p + +Provision AWS cluster using Terraform + +**--plan, -p**: creates an execution Terraform plan + +### state, t + +State cluster management using Terraform + +#### delete, d + +Delete resource from Terraform state + +**--resource-address, --ra**="": resource address for delete from Terraform state + +#### list, l + +List resources from Terraform state + +#### refresh, r + +Update state file for AWS cluster using Terraform + +### switch, s + +Switch Kubernetes context for tenant cluster + +**--force, -f**: force update Kubernetes context from remote cluster + +## completion + +Completion management + +### zsh, z + +View Zsh completion scripts + +## config + +Configuration management + +### init, i + +Initialize configuration for current tenant and selected environment + +**--artifact-mode, --am**="": choice of artifact usage model, available: none, online, offline (default: "none") + +**--aws-ecr-host, --aeh**="": AWS ECR host (default: "288509344804.dkr.ecr.eu-north-1.amazonaws.com") + +**--aws-ecr-region, --aer**="": AWS region for specific ECR host (default: "eu-north-1") + +**--aws-ecr-user-name, --aeun**="": AWS ECR user name (default: "AWS") + +**--aws-reconfigure, -r**: force AWS profile creation + +**--aws-reconfigure-artifact-license, -l**: force AWS profile creation for artifact license, used only if RMK config option artifact-mode has values: online, offline + +**--cloudflare-token, --cft**="": Cloudflare API token for provision NS records + +**--cluster-provider, --cp**="": select cluster provider to provision clusters (default: "aws") + +**--cluster-provisioner-state-locking, -c**: disable or enable cluster provisioner state locking + +**--config-from-environment, --cfe**="": inheritance of RMK config credentials from environments: develop, staging, production + +**--github-token, --ght**="": personal access token for download GitHub artifacts + +**--progress-bar, -p**: globally disable or enable progress bar for download process + +**--root-domain, --rd**="": domain name for external access to app services via ingress controller + +**--s3-charts-repo-region, --scrr**="": location constraint region of S3 charts repo (default: "eu-north-1") + +**--slack-channel, --sc**="": channel name for Slack notification + +**--slack-message-details, --smd**="": additional information for body of Slack message + +**--slack-notifications, -n**: enable Slack notifications + +**--slack-webhook, --sw**="": URL for Slack webhook + +### delete, d + +Delete configuration for selected environment + +### list, l + +List available configurations for current tenant + +**--all, -a**: list all tenant configurations + +### view, v + +View configuration for selected environment + +## doc + +Documentation management + +**--help, -h**: show help + +### generate, g + +Generate documentation by commands and flags in Markdown format + +**--help, -h**: show help + +#### help, h + +Shows a list of commands or help for one command + +### help, h + +Shows a list of commands or help for one command + +## project + +Project management + +### generate, g + +Generate project directories and files structure + +**--create-sops-age-keys, -c**: create SOPS age keys for generated project structure + +### update, u + +Update project file with specific dependencies version + +**--dependency, -d**="": specific dependency name for updating project file + +**--skip-ci, -i**: add [skip ci] to commit message line to skip triggering other CI builds + +**--skip-commit, -c**: only change a version in for project file without committing and pushing it + +**--version, -v**="": specific dependency version for updating project file + +## release + +Release components list from state file (Helmfile) + +### build, b + +Build releases + +**--helmfile-args, --ha**="": Helmfile additional arguments + +**--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") + +**--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### destroy, d + +Destroy releases + +**--helmfile-args, --ha**="": Helmfile additional arguments + +**--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") + +**--output, -o**="": output format, available: short, yaml (default: "short") + +**--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### list, l + +List releases + +**--helmfile-args, --ha**="": Helmfile additional arguments + +**--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") + +**--output, -o**="": output format, available: short, yaml (default: "short") + +**--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### rollback, r + +Rollback specific releases to latest stable state + +**--release-name, --rn**="": list release names for rollback status in Kubernetes + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### sync, s + +Sync releases + +**--helmfile-args, --ha**="": Helmfile additional arguments + +**--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") + +**--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### template, t + +Template releases + +**--helmfile-args, --ha**="": Helmfile additional arguments + +**--helmfile-log-level, --hll**="": Helmfile log level severity, available: debug, info, warn, error (default: "error") + +**--selector, -l**="": only run using releases that match labels. Labels can take form of foo=bar or foo!=bar + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +### update, u + +Update releases file with specific environment values + +**--commit, -c**: only commit and push changes for releases file + +**--deploy, -d**: deploy updated releases after committed and pushed changes + +**--repository, -r**="": specific repository for updating releases file + +**--skip-ci, -i**: add [skip ci] to commit message line to skip triggering other CI builds + +**--skip-context-switch, -s**: skip context switch for not provisioned cluster + +**--tag, -t**="": specific tag for updating releases file + +## secret + +secrets management + +### manager, m + +batch secrets management + +#### decrypt, d + +Decrypt secrets batch for selected scope and environment + +**--environment, -e**="": specific environments for selected secrets + +**--scope, -s**="": specific scopes for selected secrets + +#### encrypt, e + +Encrypt secrets batch for selected scope and environment + +**--environment, -e**="": specific environments for selected secrets + +**--scope, -s**="": specific scopes for selected secrets + +#### generate, g + +Generate secrets batch for selected scope and environment + +**--environment, -e**="": specific environments for selected secrets + +**--force, -f**: force overwriting current secrets after generating new + +**--scope, -s**="": specific scopes for selected secrets + +### keys, k + +SOPS age keys management + +#### create, c + +Create SOPS age keys + +#### download, d + +Download SOPS age keys from S3 bucket + +#### upload, u + +Upload SOPS age keys to S3 bucket + +### encrypt, e + +Encrypt secret file + +### decrypt, d + +Decrypt secret file + +### view, v + +View secret file + +### edit + +Edit secret file + +## update + +Update RMK CLI to a new version + +**--release-candidate, -r**: force update RMK to latest release candidate version + +**--version, -v**="": RMK special version. (default: empty value corresponds latest version) + +## help, h + +Shows a list of commands or help for one command diff --git a/docs/configuration/cluster-management/cluster-management.md b/docs/configuration/cluster-management/cluster-management.md new file mode 100644 index 0000000..765a0e7 --- /dev/null +++ b/docs/configuration/cluster-management/cluster-management.md @@ -0,0 +1,131 @@ +# Cluster management + +RMK uses [Terraform](https://www.terraform.io/) and [K3D](https://k3d.io) for cluster management. + +RMK is suitable for both simple and complex Kubernetes deployments, enabling multi-level project inheritance through native Helmfile functionality. + +The 2 scenarios are: +- **A cluster has already been provisioned:** An existing Kubernetes context will be used by RMK. +- **A cluster will be provisioned from scratch using RMK**: Any of the supported cluster provider like AWS, K3D etc. will be utilized. + +## RMK cluster providers + +Currently, the following cluster providers are supported by RMK: +- [aws.provisioner.infra](https://github.com/edenlabllc/aws.provisioner.infra): Configuration for managing AWS EKS + clusters using Terraform. Kubernetes clusters can be provisioned from scratch and destroyed + via the `rmk cluster provision`, `rmk cluster destroy` commands. +- [k3d.provisioner.infra](https://github.com/edenlabllc/k3d.provisioner.infra): Configuration for managing + single-machine clusters using K3D (suitable for both local development and minimal cloud deployments). + Kubernetes clusters can be created from scratch and deleted via the `rmk cluster k3d create`, `rmk cluster k3d delete` commands. + +Support for other cloud providers such as GCP, Azure will be implemented in the future. +This enhancement will include the introduction of new RMK commands and cluster providers, as well as the addition of _*.provisioner.infra_ repositories. + +### Provision or destroy AWS EKS Kubernetes clusters + +> AWS users must have the `AdministratorAccess` permissions to be able to provision and destroy EKS clusters. + +Before provisioning the K8S cluster, modify the core configurations for the on-demand cluster. +The core configurations are divided into two types: + +- **variables** (common AWS cluster management): \ + _Path:_ `etc/clusters/aws//values/variables.auto.tfvars` \ + _Frequently changed values:_ + ```terraform + # k8s user list + k8s_master_usernames = [] # list of AWS IAM users for K8S cluster management + k8s_cluster_version = "1.27" # current version K8S(EKS) control plane + # ... + ``` + +- **worker-groups** (resources for AWS worker nodes): \ + _Path:_ `etc/clusters/aws//values/worker-groups.auto.tfvars` \ + _Frequently changed values:_ + ```terraform + worker_groups = [ + { + instance_type = "t3.xlarge" + additional_userdata = "t3.xlarge" + asg_desired_capacity = 1 + asg_max_size = 1 + asg_min_size = 1 + ami_id = "ami-0dd8af8522cf16846" + }, + # ... + ] + ``` + + - `instance_type`: [AWS EC2 instance type](https://aws.amazon.com/ec2/instance-types) + - `asg_desired_capacity`: Number of nodes of a specific group. + - `ami_id`: Identifier of AWS AMI image for EKS. + + > Each AWS region requires its own AMI image ID. To determine the appropriate ID for a specific region, run the following command: + > ```shell + > AWS_PROFILE=$(rmk --lf=json config view | jq '.config.Profile' -r) \ + > aws ssm get-parameter \ + > --name /aws/service/eks/optimized-ami//amazon-linux-2/recommended/image_id \ + > --region \ + > --query "Parameter.Value" \ + > --output text + > ``` + +Full list of input Terraform variables: `.PROJECT/inventory/clusters/aws.provisioner.infra-/terraform/variables.tf` + +To start provisioning a Kubernetes cluster, run the commands: + +```shell +# prepare only plan +rmk cluster provision --plan +# prepare plan and launch it +rmk cluster provision +``` + +> When the K8S cluster is ready, RMK automatically switches the kubectl context to the newly created K8S cluster. + +To destroy a Kubernetes cluster, run the command: + +```shell +rmk cluster destroy +``` + +#### Switch the kubectl context to an already created K8S cluster + +Checkout to the branch from which the K8S cluster was previously created, then run the following commands: + +```shell +# required if the RMK configuration for this cluster has not been created before +rmk config init +rmk cluster switch --force +``` + +### Create or delete K3D Kubernetes clusters + +RMK supports managing single-node Kubernetes clusters using [K3D](https://k3d.io). + +The CLI will create a cluster according to the declarative instruction for K3D: +`.PROJECT/inventory/clusters/k3d.provisioner.infra-/k3d.yaml`. + +> Prerequisites: +> 1. Create a separate feature branch: `feature/--`. +> 2. [Initialize configuration](../rmk-configuration-management.md#initialization-of-rmk-configuration) for this branch with the `localhost` root domain name: +> ```shell +> rmk config init --root-domain=localhost +> ``` + +#### Create K3D clusters + +> By default, RMK will use `volume-host-path` as the current directory: + +Run the following command: + +```shell +rmk cluster k3d create +``` + +> When the Kubernetes cluster is ready, RMK automatically switches the kubectl context to the newly created Kubernetes cluster. + +#### Delete K3D clusters + +```shell +rmk cluster k3d delete +``` diff --git a/docs/configuration/cluster-management/exported-environment-variables.md b/docs/configuration/cluster-management/exported-environment-variables.md new file mode 100644 index 0000000..b04b0c2 --- /dev/null +++ b/docs/configuration/cluster-management/exported-environment-variables.md @@ -0,0 +1,105 @@ +## Exported environment variables + +### Terraform output variables + +RMK exports all output Terraform variables with the `rmk_` prefix for further use in the Helmfile releases. +For example, a Terraform `outputs.tf` file might look like: + +```shell +output "rmk_hosted_zone_id" { + description = "Hosted zone ID" + value = aws_route53_zone.hosted_zone.zone_id +} + +output "rmk_hosted_zone_ns_record" { + description = "Nameserver of hosted zone" + value = aws_route53_zone.hosted_zone.name_servers[0] +} +``` + +RMK converts the output variable keys from lowercase to uppercase and removes the `rmk_` prefix. +To view the exported variables, run the `rmk config view` command and check the `exported-vars` section: + +```yaml +exported-vars: + terraform-output: + # ... + rmk_hosted_zone_id: ZXXXXXXXXXXXXXXXXX + rmk_hosted_zone_ns_record: ns-1522.awsdns-62.org + # ... + env: + # ... + HOSTED_ZONE_ID: ZXXXXXXXXXXXXXXXXX + HOSTED_ZONE_NS_RECORD: ns-1522.awsdns-62.org + # ... +``` + +The variables listed in the `env` section will be used when running the `rmk release ...` commands. + +> Only the `string` output variables are supported. + +## Default exported variables + +By default, RMK exports the following environment variables to each launch of the `rmk release` category's commands: + +```shell +# AWS credentials are automatically generated by RMK during the configuration initialization +AWS_PROFILE=- +AWS_CONFIG_FILE=${HOME}/.aws/config_- +AWS_SHARED_CREDENTIALS_FILE=${HOME}/.aws/credentials_- +# For example: +# AWS_PROFILE=kodjin-develop +# AWS_CONFIG_FILE=${HOME}/.aws/config_kodjin-develop +# AWS_SHARED_CREDENTIALS_FILE=${HOME}/.aws/credentials_kodjin-develop + +# GitHub Personal Access Token +GITHUB_TOKEN= + +# The path to the directory of the specific version of hooks required for the inherited upstream project +HELMFILE__HOOKS_DIR=${PWD}/.PROJECT/inventory/hooks/- +# For example: +# HELMFILE_DEPS_HOOKS_DIR=${PWD}/.PROJECT/inventory/hooks/helmfile.hooks.infra-v1.18.0 + +# The path to the directory of the specific version of hooks required for the downstream project +HELMFILE__HOOKS_DIR=${PWD}/.PROJECT/inventory/hooks/- +# For example: +# HELMFILE_KODJIN_HOOKS_DIR=${PWD}/.PROJECT/inventory/hooks/helmfile.hooks.infra-v1.18.0 + +# The paths to the directories of the listed inherited upstream projects in the JSON format. +HELMFILE__PATHS='[{"path":"${PWD}/.PROJECT/dependencies/-/helmfile.yaml.gotmpl"}]' +# For example: +# HELMFILE_KODJIN_PATHS=[{"path":"${PWD}/.PROJECT/dependencies/deps.bootstrap.infra-v2.18.0/helmfile.yaml.gotmpl"}] + +# The version of the inherited upstream project +HELMFILE__VERSION=vN.N.N +# For example: +# HELMFILE_DEPS_BOOTSTRAP_INFRA_VERSION=v2.18.0 + +# The configuration RMK name for the current project and environment +NAME=- +# For example: +# NAME=kodjin-develop + +# The root domain name for target applications in the cluster +ROOT_DOMAIN=-.example.com +# For example: +# ROOT_DOMAIN=kodjin-develop.example.com + +# The path to the file with the merged private Age keys +SOPS_AGE_KEY_FILE=${HOME}/.rmk/sops-age-keys/-sops-age-keys-/.keys.txt +# For example: +# SOPS_AGE_KEY_FILE=${HOME}/.rmk/sops-age-keys/kodjin-sops-age-keys-28804/.keys.txt + +# The tenant name, which is an equivalent to the project name +TENANT= +# For example: +# NAME=kodjin + +# The flag that indicates whether the cluster is currently based on K3D. The variable can be used for overriding values in the releases. +K3D_CLUSTER=true +``` + +> All exported variables and their values of the `rmk release command` category can be viewed by running all these commands in the debug mode: +> ```shell +> rmk --log-level=debug release list +> ``` diff --git a/docs/configuration/project-management/dependencies-management-and-project-inheritance.md b/docs/configuration/project-management/dependencies-management-and-project-inheritance.md new file mode 100644 index 0000000..99aef15 --- /dev/null +++ b/docs/configuration/project-management/dependencies-management-and-project-inheritance.md @@ -0,0 +1,238 @@ +# Dependencies management and project inheritance + +To work with the RMK project's repository, RMK needs to resolve and install additional dependencies that are described in +the [project.yaml](preparation-of-project-repository.md#projectyaml) file. +The inheritance configuration of the upstream project's repository is defined in the `project.dependencies` section of the [project.yaml](preparation-of-project-repository.md#projectyaml) file. +All inherited upstream project repositories will be loaded into the `.PROJECT` directory +in the root directory according to the sections described in the [project.yaml](preparation-of-project-repository.md#projectyaml) file. + +> To override inherited versions of dependencies and add-ons described in the inventory, +> you need to specify the entire block with all the required fields. +> ```yaml +> inventory: +> # ... +> hooks: +> helmfile.hooks.infra: +> version: v1.18.0 +> url: git::https://github.com//{{.Name}}.git?ref={{.Version}} +> # ... +> ``` + +> Dependency resolution occurs when executing almost any RMK command, except for those in the `rmk config` command category. + +## Change dependency versions of the inherited project's repository + +Find the `project` section in the [project.yaml](preparation-of-project-repository.md#projectyaml) file and change the `version` value to the needed stable tag. +For example: + +```yaml +project: + dependencies: + # ... + - name: .bootstrap.infra + version: v2.17.0 # e.g., a different version of the dependency is required by this project + url: git::https://github.com//{{.Name}}.git?ref={{.Version}} + # ... +``` + +Then, in the `helmfiles` section of the `helmfile.yaml.gotmpl` file the `{{ env "HELMFILE__PATHS" }}` environment variable +will be used, this way RMK will manage the dependencies of the nested `Helmfile`s. + +> The variable name is formed according to the following template: `HELMFILE__PATHS`. +> This mechanism is necessary for resolving circular dependencies correctly. + +## Change inherited versions of Helmfile hooks + +RMK allows to avoid controlling the versioning of the `Helmfile` hooks through the [project.yaml](preparation-of-project-repository.md#projectyaml) file of the downstream project's repository, +instead of it, RMK allows inheriting these version hooks from the upstream project's repository. +It also supports multi-versioning of the `Helmfile` hooks as part of the inheritance from several upstream projects by a downstream project. + +> In order for these features to work, you need to use the `HELMFILE__HOOKS_DIR` variable in `helmfile.yaml.gotmpl`. +> For example: +> ```yaml +> commonLabels: +> # ... +> bin: {{ env "HELMFILE_KODJIN_HOOKS_DIR" }}/bin +> # ... +> ``` + +Let's look at the following examples of the inheritance: +1. **Hook version inheritance from the upstream project's repository:** + + The [project.yaml](preparation-of-project-repository.md#projectyaml) file of the downstream project is the following: + + ```yaml + project: + dependencies: + - name: kodjin.bootstrap.infra + version: v4.4.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + In this case, a version of the Helmfile hooks in the `inventory.hooks` section is not specified, however, + it is indicated that the current project of the repository inherits `kodjin.bootstrap.infra` with the `v4.4.0` version. + In turn, `kodjin.bootstrap.infra` inherits the `deps.bootstrap.infra` repository. + The [project.yaml](preparation-of-project-repository.md#projectyaml) file for the `kodjin.bootstrap.infra` repository is also missing the version of the hooks: + + ```yaml + project: + dependencies: + - name: deps.bootstrap.infra + version: v2.19.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + Also, the [project.yaml](preparation-of-project-repository.md#projectyaml) file of the `deps.bootstrap.infra` repository will contain the version of the `Helmfile` hooks, + which will finally be inherited by the downstream project's repository. + + ```yaml + inventory: + # ... + hooks: + helmfile.hooks.infra: + version: v1.18.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + > There is no `project.dependencies` section in the [project.yaml](preparation-of-project-repository.md#projectyaml) file + > of the `deps.bootstrap.infra` repository, since there is no inheritance. + + This configuration scheme is **the most common** and has the following inheritance scheme for the `Helmfile` hooks: + ```textmate + Project repo name: deps.bootstrap.infra ---------> kodjin.bootstrap.infra -------> .bootstrap.infra + Project repo version: v2.19.0 v4.4.0 + Hooks repo name with version: helmfile.hooks.infra-v1.18.0 -> helmfile.hooks.infra-v1.18.0 -> helmfile.hooks.infra-v1.18.0 + ``` + +2. **Hook version inheritance from the upstream project's repository in case the `Kodjin` project has a fixed version + of the `Helmfile` hooks specified in its [project.yaml](preparation-of-project-repository.md#projectyaml) file:** + + The [project.yaml](preparation-of-project-repository.md#projectyaml) file of the downstream project is the following: + + ```yaml + project: + dependencies: + - name: kodjin.bootstrap.infra + version: v4.4.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + In this case, the version of the Helmfile hooks in the `inventory.hooks` section is not specified, + however, it is indicated that the current project of the repository inherits `kodjin.bootstrap.infra` with the `v4.4.0` version. + In turn, `kodjin.bootstrap.infra` inherits the `deps.bootstrap.infra` repository which already + has its own fixed version of `v1.17.0` of the `Helmfile` hooks in the `inventory.hooks` section: + + ```yaml + project: + dependencies: + - name: deps.bootstrap.infra + version: v2.19.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + inventory: + # ... + hooks: + helmfile.hooks.infra: + version: v1.17.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + The [project.yaml](preparation-of-project-repository.md#projectyaml) file of the `deps.bootstrap.infra` repository will contain the version of the `Helmfile` hooks, + which will be inherited by the downstream project's repository: + + ```yaml + inventory: + # ... + hooks: + helmfile.hooks.infra: + version: v1.18.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + This configuration scheme will look like this: + + ```textmate + Project repo name: deps.bootstrap.infra ---------> kodjin.bootstrap.infra -------> .bootstrap.infra + Project repo version: v2.19.0 v4.4.0 + Hooks repo name with version: helmfile.hooks.infra-v1.18.0 -> helmfile.hooks.infra-v1.17.0 -> helmfile.hooks.infra-v1.18.0 + ``` + + > The downstream project's repository will inherit the latest version of `Helmfile` hooks, specifically from the `deps.bootstrap.infra` repository. + > As a result, in the downstream project's repository, we will have the two loaded versions of `Helmfile` hooks: + > - One will be relevant for the `deps.bootstrap.infra` repository and the downstream project's repository. + > - Another will be relevant for the `kodjin.bootstrap.infra` repository. + > This mechanism allows for multi-versioning support of the `Helmfile` hooks at different levels of the inheritance. + +3. **Hook version inheritance from the upstream project's repository in case the downstream project + has a fixed version of `deps.bootstrap.infra` specified in its [project.yaml](preparation-of-project-repository.md#projectyaml) file:** + + The [project.yaml](preparation-of-project-repository.md#projectyaml) file of the downstream project is the following: + + ```yaml + project: + dependencies: + - name: deps.bootstrap.infra + version: v2.20.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + - name: kodjin.bootstrap.infra + version: v4.4.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + > The dependencies should be declared in the correct order of inheritance: the first one is `deps.bootstrap.infra`, + > then `kodjin.bootstrap.infra`, then other repositories (if needed). + + In this case, a version of the `Helmfile` hooks in the `inventory.hooks` section is not specified, + however, it is indicated that the current project of the repository inherits `kodjin.bootstrap.infra` with the `v4.4.0` version. + In turn, `kodjin.bootstrap.infra` inherits the `deps.bootstrap.infra` repository. + The [project.yaml](preparation-of-project-repository.md#projectyaml) file for the `kodjin.bootstrap.infra` repository is also missing the version of the hooks: + + ```yaml + project: + dependencies: + - name: deps.bootstrap.infra + version: v2.19.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + The [project.yaml](preparation-of-project-repository.md#projectyaml) file of the `deps.bootstrap.infra` repository + of the `v2.20.0` version will contain the version of the `Helmfile` hooks, which will be inherited by the downstream projects: + + ```yaml + inventory: + # ... + hooks: + helmfile.hooks.infra: + version: v1.19.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + # ... + ``` + + This configuration scheme will look like this: + + ```textmate + Project repo name: deps.bootstrap.infra ---------> kodjin.bootstrap.infra -------> .bootstrap.infra + Project repo version: v2.20.0 v4.4.0 + Hooks repo name with version: helmfile.hooks.infra-v1.19.0 -> helmfile.hooks.infra-v1.19.0 -> helmfile.hooks.infra-v1.19.0 + ``` + + > Since the downstream project's repositories inherit the `Helmfile` hooks from the `deps.bootstrap.infra` + > repository, and we redefined the `deps.bootstrap.infra` dependency in the downstream project's, + > all repositories will inherit this concrete version, and only it will be downloaded. + +## Change inherited versions of clusters, Helm plugins, tools + +The same inheritance method as for the `Helmfile` hooks is supported for `inventory` sections as `clusters`, `helm-plugins` and `tools`. +If a specific version is not specified, the latest version from the upstream project's repository will always be used, +with one exception only: in this case, multi-versioning is not supported, and only one version will be downloaded. + +> All add-ons versions in the inventory sections must be specified in the `SemVer2` format, +> as the inheritance mechanism relies on this format to distinguish the version order. diff --git a/docs/configuration/project-management/preparation-of-project-repository.md b/docs/configuration/project-management/preparation-of-project-repository.md new file mode 100644 index 0000000..7c4cc8b --- /dev/null +++ b/docs/configuration/project-management/preparation-of-project-repository.md @@ -0,0 +1,218 @@ +# Preparation of the project repository + +> Prerequisite: +> - Create a remote repository in your Version Control System (GitHub) according to the following [requirements](requirement-for-project-repository.md#requirement-for-project-repository). +> - Clone the project repository. For example: **project.bootstrap.infra** OR `git init && git remote add && git commit -m "init commit"` +> - Checkout the needed branch. For example: `develop|staging|production`. +> - Make sure there is a file in the root of the repository named [project.yaml](#projectyaml), which contains the project configuration. +> - [Initialize the configuration](../rmk-configuration-management.md#initialization-of-rmk-configuration). + +## Automatic generation of the project structure from scratch + +RMK supports automatic generation of the project structure from scratch, according to the presented project specification described in [project.yaml](#projectyaml) file. + +Use the following command: + +```shell +rmk project generate +``` + +> Add the `--create-sops-age-keys` flag if you want to create the project structure along with SOPS age private keys. + +This will create a default project structure and prepare an example release based on [Nginx](https://nginx.org/). + +## project.yaml + +The `project.yaml` file is the main configuration file of the repository, the file is used by RMK +and contains the following main sections: + +* `project`: Optional, contains a list of dependencies of the upstream project's repositories and the project specification. + + ```yaml + project: + # Optional, needed if you want to add the dependencies with upstream projects to the downstream project. + dependencies: + # Required, dependencies upstream project's repository name. + - name: .bootstrap.infra + # Required, dependencies upstream project's repository version in `SemVer2` format, also can be a branch name or a commit hash. + version: + # Required, dependencies upstream project's repository URL. + url: git::https://github.com//{{.Name}}.git?ref={{.Version}} + # Optional, if it is expected that the dependency will be downloaded as an artifact from another storage. + artifact-url: https://-{{.HelmfileTenant}}-artifacts-infra.s3..amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz + + # Optional, needed if you want automatic generation of the project structure from scratch. + spec: + # Required, list of available environments of the project (Git branches). + environments: + - develop + - staging + - production + # Optional, list of owners of the project. + owners: + - + - + # Required, list of available scope of the project. + scopes: + - clusters + - + - + # ... + ``` + +* `inventory`: Optional, contains a map of the extra configurations required to launch the project. + + ```yaml + inventory: + # Optional, contains a map of the cluster provider repositories with Terraform manifests or other provisioning configurations. + clusters: + # Optional, cluster provider repository name. + aws.provisioner.infra: + # Required, cluster provider repository version in the `SemVer2` format. + version: + # Required, cluster provider repository URL. + url: git::https://github.com//{{.Name}}.git?ref={{.Version}} + # ... + # Optional, contains a map of the Helm plugins repositories. + helm-plugins: + # Optional, Helm plugin name. + diff: + # Required, Helm plugin version in the `SemVer2` format. + version: + # Required, Helm plugin repository URL. + url: https://github.com//helm-diff + # ... + # Optional, contains a map of the Helmfile hooks repositories with shell scripts. + hooks: + # Optional, Helmfile hooks repository name. + helmfile.hooks.infra: + # Required, Helmfile hooks repository version in the `SemVer2` format. + version: + # Required, Helmfile hooks repository URL. + url: git::https://github.com//{{.Name}}.git?ref={{.Version}} + # Optional, contains a map of the sources of binary file tools. + tools: + # Optional, tool name. + terraform: + # Required, tool version in `SemVer2` format. + version: + # Required, tool source URL. + url: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_{{.Os}}_amd64.zip + # Optional, tool checksum source URL. + checksum: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_SHA256SUMS + # Optional, specific key overrides for the described OS name. + os-linux: linux + os-mac: darwin + # ... + ``` +
+ Example of the full project.yaml file. + +```yaml +project: + dependencies: + - name: deps.bootstrap.infra + version: v2.17.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + artifact-url: https://edenlabllc-{{.HelmfileTenant}}-artifacts-infra.s3.eu-north-1.amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz + spec: + environments: + - develop + - staging + - production + owners: + - owner1 + - owner2 + scopes: + - clusters + - deps + - project1 +inventory: + clusters: + aws.provisioner.infra: + version: v1.5.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + k3d.provisioner.infra: + version: v0.2.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + helm-plugins: + diff: + version: v3.8.1 + url: https://github.com/databus23/helm-diff + secrets: + version: v4.5.0 + url: https://github.com/jkroepke/helm-secrets + hooks: + helmfile.hooks.infra: + version: v1.18.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + tools: + terraform: + version: 1.0.2 + url: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_{{.Os}}_amd64.zip + checksum: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_SHA256SUMS + os-linux: linux + os-mac: darwin + kubectl: + version: 1.27.6 + url: https://dl.k8s.io/release/v{{.Version}}/bin/{{.Os}}/amd64/{{.Name}} + checksum: https://dl.k8s.io/release/v{{.Version}}/bin/{{.Os}}/amd64/{{.Name}}.sha256 + os-linux: linux + os-mac: darwin + helm: + version: 3.10.3 + url: https://get.helm.sh/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz + checksum: https://get.helm.sh/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz.sha256sum + os-linux: linux + os-mac: darwin + helmfile: + version: 0.157.0 + url: https://github.com/{{.Name}}/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}_{{.Version}}_{{.Os}}_amd64.tar.gz + checksum: https://github.com/{{.Name}}/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}_{{.Version}}_checksums.txt + os-linux: linux + os-mac: darwin + jq: + version: 1.7 + url: https://github.com/jqlang/{{.Name}}/releases/download/{{.Name}}-{{.Version}}/{{.Name}}-{{.Os}} + os-linux: linux-amd64 + os-mac: macos-amd64 + rename: true + sops: + version: 3.8.1 + url: https://github.com/getsops/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-v{{.Version}}.{{.Os}} + os-linux: linux.amd64 + os-mac: darwin + rename: true + age: + version: 1.1.1 + url: https://github.com/FiloSottile/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz + os-linux: linux + os-mac: darwin + k3d: + version: 5.6.0 + url: https://github.com/k3d-io/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-{{.Os}}-amd64 + os-linux: linux + os-mac: darwin + rename: true + yq: + version: 4.35.2 + url: https://github.com/mikefarah/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}_{{.Os}}_amd64 + os-linux: linux + os-mac: darwin + rename: true +``` + +
+ +The project file supports placeholders, they are required for correct URL formation. + +* **{{.Name}}:** Replaced with the `name` field. +* **{{.Version}}:** Replaced with the `version` field. +* **{{.HelmfileTenant}}:** Replaced with the tenant name for the Helmfile selected from the list. +* **{{.Os}}:** Replaced with the values from the `os-linux`, `os-mac` fields according to the specific operating system, where RMK is run. + +> The field `rename` of the boolean type is required to correct the name of the binary file of the downloaded tool +> according to the value of the `name` field. This is mainly required for the cases, when the artifact is not the archive. +> For example: +> - The initial file name after the download: `helmfile_darwin_amd64`. +> - After applying the `rename` instruction it gets a value of the `name` field: `helmfile`. diff --git a/docs/configuration/project-management/requirement-for-project-repository.md b/docs/configuration/project-management/requirement-for-project-repository.md new file mode 100644 index 0000000..a529f79 --- /dev/null +++ b/docs/configuration/project-management/requirement-for-project-repository.md @@ -0,0 +1,163 @@ +# Requirement for project repository + +1. The name of the project repository should consist of the following parts: ``.``. + For example: `kodjin.bootstrap.infra` or `kodjin.infra`. +2. The project's repository exists within [GitLabFlow](https://docs.gitlab.co.jp/ee/topics/gitlab_flow.html) only + and therefor supports the following set of static branches: + - `develop` + - `staging` + - `production` + Each branch corresponds to its own environment with a separately deployed K8S cluster. RMK supports these branches + as well as the feature or release branches: + - A feature branch should have the following naming: `feature/--`. + For example: `feature/FFS-1446-example`. RMK will use `` and `` as the feature cluster name. + - A release branch should have the following naming: `release/-rc` or `release/` + For example: `release/v1.0.0`. RMK will use the project name and the `` tag as the release cluster name. + +## Expected repository structure: + +```yaml +etc/clusters///values/ + variables.auto.tfvars # Core variables for the AWS EKS cluster provider. + worker-groups.auto.tfvars # Variables describing the resources of the AWS EKS cluster. +etc///secrets/ + .sops.yaml # The public key for the current set of secrets. + .spec.yaml.gotmpl # The secrets template for generating new or rotating current secrets. + .yaml # Values containing release secrets for a specific environment. +etc///values/ + .yaml # Values containing release configuration for a specific environment. + .yaml.gotmpl # Values containing the release configuration for a specific environment using the Golang templates. +etc/// + releases.yaml # Release specification for installation of the charts. + globals.yaml # Set of global values within a specific scope. + globals.yaml.gotmpl # Set of global values within a specific scope using the Golang templates. +etc///secrets/ + .sops.yaml # -//- + .spec.yaml.gotmpl # -//- + .yaml # -//- +etc///secrets/ + .yaml # -//- + .yaml.gotmpl # -//- +etc/// + releases.yaml # -//- + globals.yaml # - // - + globals.yaml.gotmpl # - // - +helmfile.yaml.gotmpl # Helmfile describing the release process for specific project releases using the Golang templates. +project.yaml # Project specification for the dependencies and inventory installed via RMK. +``` + +## Files for managing releases and their values at the scope level + +### Requirement for `release.yaml` + +```yaml +: # Required, release name from helmfile.yaml.gotmpl. + enabled: true # Required, enable|disable release from helmfile.yaml.gotmpl. + image: # Optional, needed when using a private container image with the automatic release update feature of RMK. + repository: + tag: +: # -//- + enabled: false # -//- +# ... +``` + +> releases.yaml cannot be used as a template, all the values must be defined. + +### Requirement for `globals.yaml.gotmpl` + +```yaml +# configs - enumeration of configurations divided into sets related to the Kubernetes ConfigMaps. +configs: + auditLog: | + {{- readFile (printf "%s/audit-log.json" "values/configs") | nindent 4 }} + # ... + +# envs - enumeration of environment variables divided into sets related to the Kubernetes environment variables for the containers. +envs: + # The global environment variable used by multiple releases + FOO: false + # ... + +# hooks - enumeration of environment variables divided into sets related to the Helmfile hooks arguments. +hooks: + : + common-postuninstall-hook: + events: + - postuninstall + showlogs: true + command: "{{`{{ .Release.Labels.bin }}`}}/common-postuninstall-hook.sh" + args: + - "{{`{{ .Release.Namespace }}`}}" + # ... +``` + +> globals.yaml.gotmpl is used in two cases: +> 1. When values, configurations or environment variables need to be declared globally for multiple releases. +> 2. When the current project is planned to be inherited by a downstream project and the overrides should be supported. + +### Requirement for `helmfile.yaml.gotmpl` + +The list of the `helmfile.yaml.gotmpl` sections that must be defined and remained unchanged for working with RMK correctly is: + +```gotemplate +environments: + local: + develop: + missingFileHandler: Warn + values: + - etc//{{ .Environment.Name }}/globals.yaml + - etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - etc//{{ .Environment.Name }}/releases.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/releases.yaml + staging: + missingFileHandler: Warn + values: + - etc//{{ .Environment.Name }}/globals.yaml + - etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - etc//{{ .Environment.Name }}/releases.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/releases.yaml + production: + missingFileHandler: Warn + values: + - etc//{{ .Environment.Name }}/globals.yaml + - etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - etc//{{ .Environment.Name }}/releases.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/globals.yaml.gotmpl + - {{ requiredEnv "PWD" }}/etc//{{ .Environment.Name }}/releases.yaml +--- + +# The set of paths for the inherited Helmfiles is controlled through the project.yaml file using RMK. +# DO NOT EDIT the "helmfiles" field's values. +helmfiles: {{ env "HELMFILE__PATHS" }} + +missingFileHandler: Warn + +commonLabels: + scope: + bin: {{ env "HELMFILE__HOOKS_DIR" }}/bin + +templates: + release: + createNamespace: true + labels: + app: "{{`{{ .Release.Name }}`}}" + missingFileHandler: Warn + values: + - etc/{{`{{ .Release.Labels.scope }}`}}/{{`{{ .Environment.Name }}`}}/values/{{`{{ .Release.Name }}`}}.yaml.gotmpl + - etc/{{`{{ .Release.Labels.scope }}`}}/{{`{{ .Environment.Name }}`}}/values/{{`{{ .Release.Name }}`}}.yaml + - {{ requiredEnv "PWD" }}/etc/{{`{{ .Release.Labels.scope }}`}}/{{`{{ .Environment.Name }}`}}/values/{{`{{ .Release.Name }}`}}.yaml.gotmpl + - {{ requiredEnv "PWD" }}/etc/{{`{{ .Release.Labels.scope }}`}}/{{`{{ .Environment.Name }}`}}/values/{{`{{ .Release.Name }}`}}.yaml + secrets: + - {{ requiredEnv "PWD" }}/etc/{{`{{ .Release.Labels.scope }}`}}/{{`{{ .Environment.Name }}`}}/secrets/{{`{{ .Release.Name }}`}}.yaml + +releases: + - name: + installed: {{ .Values | get (print " " ".enabled") false }} +``` + +> You can use the `rmk project generate` command to view the full example of the contents of all the project files. diff --git a/docs/configuration/release-management/release-management.md b/docs/configuration/release-management/release-management.md new file mode 100644 index 0000000..773aadd --- /dev/null +++ b/docs/configuration/release-management/release-management.md @@ -0,0 +1,224 @@ +# Release management + +RMK uses [Helmfile](https://github.com/helmfile/helmfile) for the release management. + +RMK uses a reduced set of the `Helmfile` commands without changing their behavior. +The full list of commands can be found in the [release category](../../commands.md#release). +Additionally, flags are provided for the commands, which allow extending capabilities and help during the command execution debug. + +For example: + +```shell +rmk release build --selector app=name +rmk release sync --helmfile-log-level=debug --selector app=name +rmk release destroy +``` + +In a project repository, all the release values files are stored in the `etc///values/` directories. +For example: + +``` +etc/deps/develop/values/ +etc/kodjin/staging/values/ +``` + +> The release values are inherited by the projects, e.g., the upstream project's values are included into the downstream project's values. + +All `releases.yaml` files controlling which releases are enabled/disabled are stored in the `etc///`directories. +For example: + +``` +etc/deps/develop/releases.yaml +``` + +> The `releases.yaml` files are not inherited by the projects in contrast to the values. Each project should have its +> `releases.yaml` files for all deployed scopes and envs. +> Running any of the commands in the release category will trigger the dependency resolution mechanism, +> as well as the check for the Kubernetes context for the current environment to prevent releases +> from being synchronized outside the environment context. + +The release installation order is declared in `helmfile.yaml.gotmpl` file. + +## Examples of Usage + +### List of all available releases + +```shell +rmk release list +``` + +### Viewing a specific release YAML after the Helm values template rendering + +```shell +rmk release template --selector app=traefik +``` + +### Synchronization of a specific scope of the releases + +```shell +rmk release sync --selector scope=deps +``` + +### Synchronization of a specific release with passing the "--set" Helmfile argument + +```shell +rmk release sync --selector app=redis --helmfile-args="--set='values.name=foo'" +``` + +### Destroy all releases + +```shell +rmk release destroy +``` + +Among the `Helmfile` selectors, the following [predefined keys](https://helmfile.readthedocs.io/en/stable/#labels-overview) +are provided out of the box: +- Release name. +- Release namespace. +- Chart name. +For example: + +```shell +rmk release sync --selector namespace=kube-system +``` + +## Overriding release values for inherited projects + +It is possible to override any release value for the [inherited project repository](../project-management/dependencies-management-and-project-inheritance.md#dependencies-management-and-project-inheritance). +You can override any element separately in its YAML file. + +For example, you have the following file in the upstream project by the `.PROJECT/dependencies/deps.bootstrap.infra-/etc/deps/develop/values/metrics-server.yaml` path: + +```yaml +apiService: + create: true +extraArgs: + - --metric-resolution=10s + - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname + - --cert-dir=/tmp +resources: + requests: + cpu: 100m + memory: 200Mi +containerSecurityContext: + readOnlyRootFilesystem: true +extraVolumes: + - name: tmpdir + emptyDir: {} +extraVolumeMounts: + - name: tmpdir + mountPath: /tmp +``` + +Then you want to change the `resources.requests.cpu` value of the `develop` environment in your downstream project. +In this case, you don't need to copy the whole file but only change the concrete value by repeating the YAML path to it. +So, your file with the override will look like this: + +```yaml +resources: + requests: + cpu: 100m +``` + +> You cannot override a part of an element if it is an array. +> If you want to override the name of the extraVolumeMounts field of the example file above, you cannot use the following content: +> ```yaml +> extraVolumeMounts: +> - name: tmp +> ``` +> The correct way to override is to provide the whole item of the array: +> ```yaml +> extraVolumeMounts: +> - name: tmp +> mountPath: /tmp +> ``` + +To check the final result, run the `rmk release template` command and see the final YAML. + +## Release update and integration into the CD pipeline + +The `rmk release update` command automates the process of updating and delivering releases +according to the version changes of artifacts (container images) following the [GitOps](https://www.gitops.tech) methodology. + +Since RMK is a binary file that can be downloaded and installed on any Unix-based CI/CD system, +it can be integrated with almost any CI/CD system: GitHub Actions, GitLab, Drone CI, Jenkins, etc. + +### Example of integration with GitHub Actions: + +> Prerequisites: +> - The project repository has already been generated and [prepared](../project-management/preparation-of-project-repository.md) using RMK. + +Create the following workflow in your project repository at `.github/workflows/release-update.yaml`. +An example content of the GitHub Actions' workflow: + +```yaml +name: Release update + +on: + workflow_dispatch: + inputs: + image_repository_full_name: + description: Image repository full name of application. + required: true + version: + description: Current application version. + required: true + +jobs: + release-update: + runs-on: ubuntu-22.04 + steps: + - name: Checkout main repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + fetch-depth: 0 + + - name: Release update + env: + AWS_REGION: us-east-1 + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + RMK_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN_REPO }} + RMK_SLACK_WEBHOOK: ${{ secrets.RMK_SLACK_WEBHOOK }} + RMK_SLACK_CHANNEL: project-cd-notifications + RMK_RELEASE_UPDATE_REPOSITORY: ${{ github.event.inputs.image_repository_full_name }} + RMK_RELEASE_UPDATE_TAG: ${{ github.event.inputs.version }} + run: | + curl -sL "https://edenlabllc-rmk-tools-infra.s3.eu-north-1.amazonaws.com/rmk/s3-installer" | bash + + rmk config init --progress-bar=false --slack-notifications + rmk release update --skip-ci --deploy +``` + +In this example, we have prepared a `GitHub Action` that expects two input parameters: +- `image_repository_full_name` +- `version` + +As soon as a request with these parameters is sent to this action, +RMK will be executed, first analyzing all the `releases.yaml` files to match the `image_repository_full_name` and will replace the tag field +with the corresponding version if the versions differ. +After that, it will automatically commit the changes to the current branch in the `releases.yaml` files where changes have been found. +Then, it will synchronize the releases where the version changes were found. + +An example of the `releases.yaml` file: + +```yaml +# ... +foo: + enabled: true + image: + repository: 123456789.dkr.ecr.us-east-1.amazonaws.com/app.foo + tag: v0.11.1 +bar: + enabled: true + image: + repository: 123456789.dkr.ecr.us-east-1.amazonaws.com/app.bar + tag: v0.16.0 +# ... +``` + +To make the code delivery process fully automatic on the CI pipeline's side, after building and pushing +the container image to the image repository, add a step that triggers the deployment action and passes +the container image repository's full name and its final tag. This can be done via an API call using [cURL](https://en.wikipedia.org/wiki/CURL) +or through [GitHub CLI](https://cli.github.com/). This way, we achieve automatic code delivery to the infrastructure environment. diff --git a/docs/configuration/rmk-configuration-management.md b/docs/configuration/rmk-configuration-management.md new file mode 100644 index 0000000..9c45252 --- /dev/null +++ b/docs/configuration/rmk-configuration-management.md @@ -0,0 +1,147 @@ +# RMK configuration management + +To start working with Kubernetes clusters, RMK needs to initialize the configuration for the current environment. +At the time of configuration initialization launch, the RMK prepares +the state in the form of the current environment config with all the required attributes for further work. +It also downloads and resolves and installs all necessary dependencies and tools described +in the [project.yaml](project-management/preparation-of-project-repository.md#projectyaml) file in the root of the project repository. + +## List of main attributes of the RMK configuration + +Example of the configuration: + +```yaml +name: kodjin-develop # RMK config name, a unique identifier which consists of the tenant name and the abbreviated name of the Git branch. +tenant: kodjin # Tenant name. +environment: develop # Environment name. +config-from: kodjin-develop # Configuration name from which the cluster configuration was inherited. +root-domain: kodjin-develop.edenlab.dev # Root domain name used across the cluster. +aws: + profile: kodjin-develop # AWS profile name for the AWS CLI. + region: eu-north-1 # AWS region of the current Kubernetes cluster. + account_id: "123456789" +# ... +``` + +> All attributes can be overridden using RMK flags or environment variables. + +## Initialization of RMK configuration + +> Prerequisites: +> - The `GITHUB_TOKEN` variable or `--github-token` flag are required: [GitHub Personal Access Tokens (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic). +> The token should have the `repo: full control` permissions. +> - [Project repository](project-management/requirement-for-project-repository.md) has already been created and initialized. +> - At least one Git branch for the environment exists already. + +```shell +rmk config init +``` + +### Configuration of AWS profile + +If an AWS profile with the correct name has not been created previously during the first initialization of the configuration, +RMK will start the creation process. The 2 supported configuration scenarios are: + +* **through environment variables:** `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` +* **interactive input**: the AWS credentials will be requested one by one. + +If the environment variables has been declared before the `rmk config init` command was run, RMK will create a profile +based on their values. Otherwise, the interactive mode will begin. + +### Support for Multi-Factor Authentication (MFA) + +The RMK automatically check for an MFA device, when the following command is executed: `rmk config init`. + +To setup an MFA device, if it is required by the administrator, the following actions should be executed: + +1. First, sign in to the AWS Management Console. +2. Then, go to the following page to set up security + credentials: [My security credentials](https://console.aws.amazon.com/iam/home#/security_credentials) +3. Navigate to the "Multi-factor authentication (MFA)" section and set up an MFA device. + If a device name is required, specify a name. +4. After that, sign out and sign in again to refresh AWS policies + (might be required in case of an IAM policy based on the `aws:MultiFactorAuthPresent` condition exists). +5. Finally, on the "My security credentials" page navigate to the "Access keys for CLI, SDK, & API access" section + and create a new AWS access key, if needed. + +> For the detailed documentation regarding the MFA setup in AWS, go to +> [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html#enable-virt-mfa-for-own-iam-user) + +You can also check the lifetime of the session token by running the command: `rmk config init` + +``` +2022-12-14T09:02:20.267+0100 INFO MFA remaining time for token validity: 11:59:48 +``` + +## Initialization of RMK configuration for feature or release clusters + +When initializing the RMK configuration for feature or release clusters, you can use inheritance +from a previously saved configuration that contains the necessary credentials to create a Kubernetes cluster. +Let's say you want to create or connect to the feature cluster with the credentials of the `develop` cluster, +in this case you must run the initialization command with the `--config-from-environment` flag. For example: + +```shell +rmk config init --config-from-environment= +``` + +### Reconfiguration of the AWS profile if wrong credentials has been input + +```shell +rmk config init --aws-reconfigure +``` + +### Initialization of RMK configuration with a custom root domain + +```shell +rmk config init --root-domain="example.com" +``` + +### Deletion of RMK configuration + +```shell +rmk config delete +``` + +> When deleting the current RMK configuration, the respective AWS profile files will be deleted as well. + +## Use upstream artifact for the downstream project's repository + +RMK supports downloading an upstream project's artifact using additional "license" AWS credentials. +To switch RMK to the artifact usage mode, you need to use additional flags when initializing the RMK configuration +for the current project. Additionally, before starting the initialization, you need to install the required version +of the upstream project to which you want to update. +For example: + +```yaml +project: + dependencies: + - name: deps.bootstrap.infra + version: v2.17.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + artifact-url: https://edenlabllc-{{.HelmfileTenant}}-artifacts-infra.s3.eu-north-1.amazonaws.com/{{.Version}}/{{.HelmfileTenant}}-{{.Version}}.tar.gz + # ... +``` + +> The `artifact-url` field is required and contains the artifact URL generation template which consists +> of the following [fields](project-management/preparation-of-project-repository.md#projectyaml). + +Set the `version` field to the version of the upstream project for the current project. For example: + +```shell +# artifact usage modes: none|online|offline (default: "none") +rmk config init --artifact-mode=online +``` + +> Currently, only two artifact modes are supported: +> - `none`: The standard mode of RMK which is used for development normally, the codebase will be downloaded from GitHub repositories. +> The mode does not require the presence of the special "license" credentials. +> - `online`: Switches RMK to work with artifacts. In this mode, RMK will not use any credentials for GitHub +> (e.g., personal access tokens), but will request additional license AWS credentials to download and unpack +> the artifact from a repository like AWS S3. +> The `offline` mode is currently unsupported and will be implemented in future releases. + +To change the "license" AWS credentials when in the online artifact mode, use the following command: + +```shell +rmk config init --aws-reconfigure-artifact-license +``` diff --git a/docs/configuration/secrets-management/secrets-management.md b/docs/configuration/secrets-management/secrets-management.md new file mode 100644 index 0000000..2204adf --- /dev/null +++ b/docs/configuration/secrets-management/secrets-management.md @@ -0,0 +1,209 @@ +# Secrets management + +RMK uses [SOPS](https://github.com/mozilla/sops) and [Age](https://github.com/mozilla/sops#encrypting-using-age) +for secrets management. +All RMK commands related to the secrets management can be found under the [rmk secret](../../commands.md#secret) command category. + +In a project repository, all secrets files are stored in the `etc///secrets/` directories. +For example: + +``` +etc/deps/develop/secrets/mongodb.yaml +etc/kodjin/develop/secrets/kodjin-minio-config-buckets.yaml +``` + +Normally, the files are committed to Git because they are encrypted using SOPS age keys and symmetric-key algorithms. +The keys are stored remotely in an encrypted bucket of AWS S3, downloaded locally when first using RMK +on that machine. + +> The secrets are never inherited by projects, in contrast to values. Each project should have its own unique set +> of secrets for all deployed releases. + +## SOPS age keys management + +Initially, a Kubernetes cluster admin creates the keys using the following command: + +```shell +rmk secret keys create +``` + +This command will create a set of `Age` private keys for each scope in the user's home directory +at `$HOME/.rmk/sops-age-keys/-sops-age-keys-/*.txt`. +The command will create a `Age` private key for the scope for which an empty +SOPS configuration file `etc///secrets/.sops.yaml` was created. +Additionally, `creation_rules` containing the public key and regex for filtering secrets will be automatically added to this file. +Example of a `.sops.yaml` file: + +```yaml +creation_rules: + - path_regex: .+\.yaml$ + age: 'age12ugnsdhxd44fa56q5w22mvf09e93p66xrcrnxmxla4dnqpyugpwqs2swag' +``` + +> The SOPS configuration files for all secret scopes can be generated using the `rmk project generate` command. + +Then the generated keys might be uploaded explicitly to S3: + +```shell +rmk secret keys upload +``` + +> AWS users must have the `AdministratorAccess` permissions to be able to upload the SOPS age keys to S3. + +After the keys have been created by the cluster admin, they can be downloaded from S3: + +```shell +rmk secret keys download +``` + +> AWS users must have read permissions for downloading keys from S3, +> otherwise they won't be able to encode/decode secrets and release services using RMK. + +After that the `$HOME/.rmk/sops-age-keys/-sops-age-keys-` directory will have all the needed keys +needed for secrets encryption or decryption. + +## Generating all secrets from scratch in a batch manner using the RMK secrets manager + +In case of a creating tenant from scratch all the needed directories like `etc///secrets/` should first +be populated with an empty `.sops.yaml` file (acts as an indicator that this scope and environment will have the secrets). +Also, before generating the secret files, you must create or complete a secret `.spec.yaml.gotmpl` file per each scope. +`.spec.yaml.gotmpl` is a template that runs the [sprig](https://masterminds.github.io/sprig) engine with additional functions. + +The additional functions which included in the template functions are: + +- **{{`{{ requiredEnv "ENV_NAME" }}`}}:** The function requires an input of the specified mandatory variable. +- **{{`{{ prompt "password" }}`}}:** The function asks for interactive input. Useful for entering sensitive data. + + +Example of the files needed for the generation: + +```yaml +etc/deps/develop/secrets/.sops.yaml +etc/deps/develop/secrets/.spec.yaml.gotmpl +``` + +An example of `.spec.yaml.gotmpl` for the deps scope: + +```gotemplate +{{- $minioSecretKey := randAlphaNum 16 -}} +generation-rules: + - name: ecr-token-refresh-operator + template: | + envSecret: + AWS_ACCESS_KEY_ID: {{`{{ requiredEnv "AWS_ACCESS_KEY_ID" }}`}} + AWS_SECRET_ACCESS_KEY: {{`{{ requiredEnv "AWS_SECRET_ACCESS_KEY" }}`}} + - name: elastic + template: | + esUserName: admin + esPassword: {{ randAlphaNum 16 }} + # ... + - name: minio + template: | + rootUser: minio + rootPassword: {{ $minioSecretKey }} + - name: redis + template: | + auth: + password: {{ randAlphaNum 16 }} + # ... +``` + +Then run the following command to generate all the secrets is a batch manner: + +```shell +rmk secret manager generate +``` + +After that the directories with the secret files will be generated. +The files will have the plain unencrypted YAML content. +After that, review the content of the new files and run the following command to encode them: + +```shell +rmk secret manager encrypt +``` + +> The directories without the `.sops.yaml` or `.spec.yaml.gotmpl` files will be ignored. + +Also, each of the `.sops.yaml` files will be updated automatically with the correct paths and public keys of the SOPS age keys +used for encryption. + +## Generating a single secret from scratch using the RMK secrets manager + +To create a single secret from scratch (e.g., when a new service (release) is added), add a template of the new secret +to `.spec.yaml.gotmpl`. For example: + +```gotemplate + # ... + - name: new-release + template: | + envSecret: + USERNAME: user + PASSWORD: {{ randAlphaNum 16 }} + # ... +``` + +Then generate the new secret as the plain YAML and encrypt it using RMK for the needed scope and environment. +For example: + +```shell +rmk secret manager generate --scope kodjin --environment develop +rmk secret manager encrypt --scope kodjin --environment develop +``` + +> At this moment, the `.sops.yaml` files has already been populated and therefor need no manual changes. +> The secrets generation process works in an idempotent, declarative mode, which means it will skip previously generated secret files. + +## Editing a single secret + +For some environments where the `.spec.yaml.gotmpl` file and the manager commands were not used for some legacy reasons, +the `rmk secret edit` command can be executed. The command works in an idempotent mode, which means that it can be used +for both creating a new secret (e.g., when adding a new release/service) and editing an existing one: + +```shell +rmk secret edit +``` + +For example: + +```shell +rmk secret edit etc/deps/develop/secrets/mongodb.yaml +``` + +An CLI editor will be displayed (e.g., [vim](https://www.vim.org/)). After the required changes are performed, +save and exit the editor. This will result in an encrypted and edited secret file (no need to encode it explicitly). + +> Manual editing of the encrypted secrets files is strictly forbidden, +> because SOPS automatically controls the checksums of the secret files. + +## Viewing an existing secret + +To view the content of an existing secrets content, use the following command: + +```shell +rmk secret view +``` + +For example: + +```shell +rmk secret view etc/deps/develop/secrets/minio.yaml +``` + +> This might be useful when viewing credentials of the deployed services, e.g., a database or a web UI. + +## Rotate secrets for specific scope and environment + +To force a new generation of the secrets for a specific scope and environment according to the `.spec.yaml.gotmpl` file, +run the following command (in this example, the scope is `kodjin`, the environment is `production`): + +```shell +rmk secret manager generate --scope kodjin --environment production --force +``` + +> You might need to provide the required environment variables + +To encode the generated secrets, run: + +```shell +rmk secret manager encrypt --scope kodjin --environment production +``` diff --git a/docs/development-and-release-flow.md b/docs/development-and-release-flow.md new file mode 100644 index 0000000..30f9f09 --- /dev/null +++ b/docs/development-and-release-flow.md @@ -0,0 +1,50 @@ +# Development and release flow + +## Requirements for the availability of tools during development + +- **[Golang](https://tip.golang.org/doc/install)** = v1.21.X +- **[GoReleaser](https://goreleaser.com/install)** = v1.23.0 + +## Building from source + +To build RMK from source, run the following [GoReleaser](https://goreleaser.com/) command from the root of the repository: + +```shell +goreleaser build --snapshot --clean +``` + +> You can also use this command for recompilation of RMK during development. + +## Git workflow + +In RMK development, we use the classic [GitFlow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) workflow, +embracing all its advantages and disadvantages. + +### Git branch naming conventions + +- `feature/RMK--` +- `release/` +- `hotfix/` + +For example: + +- `feature/RMK-123-add-some-important-feature` +- `release/v0.42.0` +- `hotfix/v0.42.1` + +## Release flow + +After accumulating a certain set of features in the develop branch, +a `release/` branch is created for the next release version. +Then a pull request (PR) is made from the `release/` branch to the master branch. +This triggers a CI process that will build and release an intermediate version, +the `-rc` release candidate. + +> This version is available for update from RMK via the `rmk update --release-candidate` command +and can be used for an intermediate or beta testing. + +After successful testing, the PR is merged into the master branch, +triggering another CI process that will release the stable RMK version. + +> All CI processes are performed using GoReleaser, including the publishing of artifacts +> for both the release and intermediate RMK versions. diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..6f9b031 --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,143 @@ +# Quickstart + +This guide demonstrates how to use `RMK` to prepare the structure of a new project in five steps, +create a local cluster based on `K3D`, deploy your first application ([Nginx](https://nginx.org/)) using `Helmfile` releases. + +> Prerequisite: +> - An active AWS user with access keys and the `AdministratorAccess` permissions. +> - A prepared [project repository](configuration/project-management/preparation-of-project-repository.md#preparation-of-the-project-repository) +> - Installed [RMK](../README.md#installation) +> - The fulfilled [requirements](../README.md#requirements) for proper RMK operation. + +0. Create a [project.yaml](configuration/project-management/preparation-of-project-repository.md#projectyaml) + file in the root of the project repository with the following content: + +```yaml +project: + spec: + environments: + - develop + owners: + - owner + scopes: + - rmk-test +inventory: + clusters: + k3d.provisioner.infra: + version: v0.2.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + helm-plugins: + diff: + version: v3.8.1 + url: https://github.com/databus23/helm-diff + helm-git: + version: v0.15.1 + url: https://github.com/aslafy-z/helm-git + secrets: + version: v4.5.0 + url: https://github.com/jkroepke/helm-secrets + hooks: + helmfile.hooks.infra: + version: v1.18.0 + url: git::https://github.com/edenlabllc/{{.Name}}.git?ref={{.Version}} + tools: + terraform: + version: 1.0.2 + url: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_{{.Os}}_amd64.zip + checksum: https://releases.hashicorp.com/{{.Name}}/{{.Version}}/{{.Name}}_{{.Version}}_SHA256SUMS + os-linux: linux + os-mac: darwin + kubectl: + version: 1.27.6 + url: https://dl.k8s.io/release/v{{.Version}}/bin/{{.Os}}/amd64/{{.Name}} + checksum: https://dl.k8s.io/release/v{{.Version}}/bin/{{.Os}}/amd64/{{.Name}}.sha256 + os-linux: linux + os-mac: darwin + helm: + version: 3.10.3 + url: https://get.helm.sh/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz + checksum: https://get.helm.sh/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz.sha256sum + os-linux: linux + os-mac: darwin + helmfile: + version: 0.157.0 + url: https://github.com/{{.Name}}/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}_{{.Version}}_{{.Os}}_amd64.tar.gz + checksum: https://github.com/{{.Name}}/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}_{{.Version}}_checksums.txt + os-linux: linux + os-mac: darwin + sops: + version: 3.8.1 + url: https://github.com/getsops/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-v{{.Version}}.{{.Os}} + os-linux: linux.amd64 + os-mac: darwin + rename: true + age: + version: 1.1.1 + url: https://github.com/FiloSottile/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-v{{.Version}}-{{.Os}}-amd64.tar.gz + os-linux: linux + os-mac: darwin + k3d: + version: 5.6.0 + url: https://github.com/k3d-io/{{.Name}}/releases/download/v{{.Version}}/{{.Name}}-{{.Os}}-amd64 + os-linux: linux + os-mac: darwin + rename: true +``` + +1. Run the [RMK configuration initialization](configuration/rmk-configuration-management.md#initialization-of-rmk-configuration) command for the repository: + + ```shell + rmk config init --root-domain=localhost --github-token= + ``` + + > When executing the command, properly fill in the AWS credentials and region. + > RMK will save the references for them in the system and use them for subsequent executions of this command. + > In our example, the AWS credentials are used to create an S3 bucket for storing private SOPS Age keys + > and distributing them among team members. + +2. Generate the project structure according to the [project.yaml](configuration/project-management/preparation-of-project-repository.md#projectyaml) file: + + ```shell + rmk project generate --create-sops-age-keys + ``` + +3. Create a local K3D cluster: + + > Before running this step, ensure that Docker is installed in the system according to the [requirements](../README.md#requirements). + + ```shell + rmk cluster k3d create + ``` + +4. Generate and encrypt secrets for the `Helmfile` release (Nginx): + + ```shell + rmk secret manager generate + rmk secret manager encrypt + ``` + +5. Deploy the `Helmfile` release (Nginx) to the local `K3D` cluster: + + ```shell + rmk release sync + ``` + +At this stage, we have completed the deployment of our test application (Nginx) provided by the `Helmfile` release +to the local `K3D` cluster, also the structure of the future project has been prepared. + +We can check the availability of the application in the Kubernetes cluster using the following command: + +```shell +kubectl port-forward $(kubectl get pod --namespace rmk-test --output name) 8088:80 --namespace rmk-test +``` + +Open your browser and enter the http://localhost:8088 address, after which you will see the Nginx welcome page. + +Next, you can commit your changes to a Git branch and push them to your VCS (e.g., GitHub). +You can also upload the private SOPS Age keys using the following command: + +```shell +rmk secret keys upload +``` + +After that, your team members will be able to deploy this project on their own, skipping the 2nd and 4th steps. diff --git a/docs/release-notes.md b/docs/release-notes.md index 55ed972..5c629cb 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1 +1,2 @@ - #2 - Added release notes file, changed release notes publishing template via GoReleaser. +- #3 - Improved and restructured documentation globally.