diff --git a/.editorconfig b/.editorconfig index 14888e4f..630c31be 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,9 +9,9 @@ indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = false -insert_final_newline = false +insert_final_newline = true -[*.{yml,yaml,yml.*,yaml.*}] +[*.{yml,yaml,yml.*,yaml.*,xslt.*,*.xslt}] indent_size = 2 [*.{hcl,tf,tfvars,tfvars.*}] @@ -20,5 +20,8 @@ indent_size = 2 [*.sh] end_of_line = lf +[*.nix] +indent_size = 2 + [Makefile] indent_style = tab diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..bafa5057 --- /dev/null +++ b/.envrc @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +use flake +watch_file flake.nix + +# vim: ft=bash diff --git a/.github/workflows/release-helm.yml b/.github/workflows/release-helm.yml deleted file mode 100644 index 6c94abe5..00000000 --- a/.github/workflows/release-helm.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Release Charts - -on: - push: - branches: - - main - paths: - - 'charts/**' - - .github/workflows/release-helm.yml - -permissions: - contents: write - packages: write - pages: write - id-token: write - -jobs: - release: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - - name: Add repositories workaround - run: | - helm repo add bitnami https://charts.bitnami.com/bitnami - - - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.4.1 - env: - CR_TOKEN: "${{ github.token }}" diff --git a/.github/workflows/release-nixos.yml b/.github/workflows/release-nixos.yml new file mode 100644 index 00000000..6e3f5a38 --- /dev/null +++ b/.github/workflows/release-nixos.yml @@ -0,0 +1,55 @@ +name: Release Packer image +on: + push: + branches: + - main + paths: + - 'nixos/**.yml' + - '!**.md' + - '!playbook/roles/paas/molecule/**' + - 'packer/**' + - .github/workflows/release-packer.yml + +permissions: + contents: write + discussions: write + +jobs: + gh-release-packer: + runs-on: ubuntu-latest + name: Run Packer + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: cachix/install-nix-action@v25 + with: + extra_nix_config: | + experimental-features = nix-command flakes + github_access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore and cache Nix store + uses: nix-community/cache-nix-action@v5 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }} + restore-prefixes-first-match: nix-${{ runner.os }}- + gc-max-store-size-linux: 1073741824 + + - name: Set outputs + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Build + id: build + run: nix build .#nixosConfigurations.x86_64-linux.contabo + + - name: Release + uses: softprops/action-gh-release@v1 + with: + tag_name: nixos-${{ steps.vars.outputs.sha_short }} + token: "${{ secrets.GITHUB_TOKEN }}" + generate_release_notes: true + files: | + result/ diff --git a/.github/workflows/release-packer.yml b/.github/workflows/release-packer.yml deleted file mode 100644 index 09723927..00000000 --- a/.github/workflows/release-packer.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Release Packer image -on: - push: - branches: - - main - paths: - - 'playbook/**.yml' - - '!**.md' - - '!playbook/roles/waypoint/molecule/**' - - 'packer/**' - - .github/workflows/release-packer.yml - -permissions: - contents: write - discussions: write - -jobs: - gh-release-packer: - runs-on: macos-latest - name: Run Packer - env: - PKR_VAR_ssh_password: ${{ secrets.PKR_VAR_SSH_PASSWORD }} - PKR_VAR_ssh_password_hash: ${{ secrets.PKR_VAR_SSH_PASSWORD_HASH }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup `packer` - uses: hashicorp/setup-packer@main - id: setup - with: - version: "1.8.6" - - - name: Set outputs - id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - - name: Run `packer init` - id: init - run: "packer init ubuntu.pkr.hcl" - working-directory: packer - - - name: Run `packer build` - run: >- - packer build -var-file "$(uname -ms | tr ' ' '-')-host.hcl" \ - ubuntu.pkr.hcl - working-directory: packer - env: - PACKER_LOG: 1 - PACKER_LOG_PATH: packer.log - - - name: Upload packer.log on failure - if: failure() - uses: actions/upload-artifact@v3 - with: - name: packer.log - path: packer/packer.log - retention-days: 14 - - - name: Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ubuntu-jammy-${{ steps.vars.outputs.sha_short }} - token: "${{ secrets.GITHUB_TOKEN }}" - generate_release_notes: true - files: | - packer/.qemu-vm/ubuntu-jammy-22.04.2.qcow2 - packer/.qemu-vm/SHA256SUMS diff --git a/.github/workflows/test-helm.yml b/.github/workflows/test-helm.yml deleted file mode 100644 index e567ba78..00000000 --- a/.github/workflows/test-helm.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Test Charts - -on: - pull_request: - branches: - - main - paths: - - 'charts/**' - - .github/workflows/test-helm.yml - -permissions: - contents: write - packages: write - pages: write - id-token: write - -jobs: - release: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - - name: Add repositories workaround - run: | - helm repo add bitnami https://charts.bitnami.com/bitnami - - - name: Dependency build - run: helm dependency build . - working-directory: charts/microservice - - - name: Run lint - run: helm lint . - working-directory: charts/microservice - - - name: Run template - run: helm template . - working-directory: charts/microservice diff --git a/.github/workflows/test-packer.yml b/.github/workflows/test-packer.yml deleted file mode 100644 index 1ae5c0cf..00000000 --- a/.github/workflows/test-packer.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: test-packer -on: - pull_request: - paths: - - 'packer/**.hcl' - - 'packer/**.sh' - - 'packer/**.tmpl' - - .github/workflows/test-packer.yml -jobs: - gh-release-packer: - runs-on: ubuntu-latest - name: Run Packer - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Setup `packer` - uses: hashicorp/setup-packer@main - id: setup - with: - version: "1.8.6" - - - name: Cache packer Iso's - id: cache-packer-iso - uses: actions/cache@v3 - with: - path: ~/.cache/packer/ - key: ${{ runner.os }}-ubuntu-22.04 - - - name: Set outputs - id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - - name: Run `packer init` - id: init - run: "packer init ubuntu.pkr.hcl" - working-directory: packer - env: - PKR_VAR_ssh_password: ${{ secrets.PKR_VAR_SSH_PASSWORD }} - PKR_VAR_ssh_password_hash: ${{ secrets.PKR_VAR_SSH_PASSWORD_HASH }} - - - name: Run `packer validate` - id: validate - run: >- - packer validate \ - -var-file "$(uname -ms | tr ' ' '-')-host.hcl" \ - ubuntu.pkr.hcl - working-directory: packer - env: - PKR_VAR_ssh_password: ${{ secrets.PKR_VAR_SSH_PASSWORD }} - PKR_VAR_ssh_password_hash: ${{ secrets.PKR_VAR_SSH_PASSWORD_HASH }} diff --git a/.github/workflows/test-playbook.yml b/.github/workflows/test-playbook.yml deleted file mode 100644 index f7645015..00000000 --- a/.github/workflows/test-playbook.yml +++ /dev/null @@ -1,81 +0,0 @@ - -name: Molecule Test -on: - pull_request: - branches: - - main - paths: - - 'playbook/**.yaml' - - 'playbook/**.yml' - - 'playbook/**.txt' - - 'playbook/**.j2' - - 'playbook/roles/waypoint/molecule/**' - - .github/workflows/test-playbook.yml -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: 3.11.2 - cache: 'pip' - cache-dependency-path: '**/requirements*.txt' - - - name: Install python dependencies - run: | - sudo python -m pip install --upgrade pip - sudo pip install -r requirements.txt - sudo pip install -r requirements-test.txt - working-directory: playbook - - - uses: actions/cache@v3 - with: - path: | - /root/.ansible/collections/ansible_collections - /root/.ansible/roles - key: ${{ runner.os }}-ansible-${{ hashFiles('playbook/requirements.yaml') }} - restore-keys: | - ${{ runner.os }}-ansible- - - - name: Install ansible dependencies - run: | - sudo ansible-galaxy collection install -r requirements.yaml - sudo ansible-galaxy role install -r requirements.yaml - working-directory: playbook - - - name: Update apt cache - run: sudo apt update - - - uses: awalsh128/cache-apt-pkgs-action@latest - with: - packages: dnsmasq - version: 1.0 - - name: Setup dnsmasq - run: ./scripts/setup_dnsmasq.sh - working-directory: playbook/roles/waypoint - - - name: Setup vault secret - run: echo "$ANSIBLE_VAULT_PASSWORD" | sudo tee /root/.ansible/.vault - env: - ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} - - - name: Enable molecule delegated driver for CI - run: mv -f molecule/default/molecule.ci.yml molecule/default/molecule.yml - working-directory: playbook/roles/waypoint - - - name: Remove kubectl - run: sudo rm -rf /usr/local/bin/kubectl - - - name: Molecule test - run: >- - sudo molecule test -d delegated -- \ - --extra-vars='_hosts=127.0.0.1 k3s_disable_services=[traefik]' - working-directory: playbook/roles/waypoint - env: - ANSIBLE_FORCE_COLOR: 1 diff --git a/.github/workflows/test-terraform.yml b/.github/workflows/test-terraform.yml index 6b71fa23..a3efd3dc 100644 --- a/.github/workflows/test-terraform.yml +++ b/.github/workflows/test-terraform.yml @@ -5,10 +5,10 @@ on: branches: - main paths: - - 'contabo/**.hcl' - - 'contabo/**.tf' - - 'contabo/**.tfvars' - - 'contabo/**.tmpl' + - 'terraform/**.hcl' + - 'terraform/**.tf' + - 'terraform/**.tfvars' + - 'terraform/**.tmpl' - .github/workflows/test-terraform.yml permissions: @@ -24,14 +24,27 @@ jobs: fetch-depth: 0 - uses: hashicorp/setup-terraform@v2 + - name: Configure Terraform plugin cache + run: | + echo "TF_PLUGIN_CACHE_DIR=$HOME/.terraform.d/plugin-cache" >>"$GITHUB_ENV" + mkdir --parents "$HOME/.terraform.d/plugin-cache" + - name: Cache Terraform + uses: actions/cache@v4 + with: + path: | + ~/.terraform.d/plugin-cache + key: terraform-${{ runner.os }}-${{ hashFiles('**/.terraform.lock.hcl') }} + restore-keys: | + terraform-${{ runner.os }}- + - name: Terraform Init id: init - run: terraform init + run: make init - name: Terraform fmt id: fmt - run: terraform fmt -check -diff + run: make fmt ARGS='-check -diff' - name: Terraform Validate id: validate - run: terraform validate -no-color + run: make validate diff --git a/.gitignore b/.gitignore index 6ba21815..e8706862 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,39 @@ -.DS_Store -certs/* -!.gitkeep -.env -playbook/vault-password.txt -charts/**/*.tgz -# mkdocs -site -# helm charts index file -index.yaml \ No newline at end of file +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# nix +result +.direnv +keys diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 00000000..c82ca499 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,142 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/helm" { + version = "2.12.1" + constraints = "2.12.1" + hashes = [ + "h1:aBfcqM4cbywa7TAxfT1YoFS+Cst9waerlm4XErFmJlk=", + "zh:1d623fb1662703f2feb7860e3c795d849c77640eecbc5a776784d08807b15004", + "zh:253a5bc62ba2c4314875139e3fbd2feaad5ef6b0fb420302a474ab49e8e51a38", + "zh:282358f4ad4f20d0ccaab670b8645228bfad1c03ac0d0df5889f0aea8aeac01a", + "zh:4fd06af3091a382b3f0d8f0a60880f59640d2b6d9d6a31f9a873c6f1bde1ec50", + "zh:6816976b1830f5629ae279569175e88b497abbbac30ee809948a1f923c67a80d", + "zh:7d82c4150cdbf48cfeec867be94c7b9bd7682474d4df0ebb7e24e148f964844f", + "zh:83f062049eea2513118a4c6054fb06c8600bac96196f25aed2cc21898ec86e93", + "zh:a79eec0cf4c08fca79e44033ec6e470f25ff23c3e2c7f9bc707ed7771c1072c0", + "zh:b2b2d904b2821a6e579910320605bc478bbef063579a23fbfdd6fcb5871b81f8", + "zh:e91177ca06a15487fc570cb81ecef6359aa399459ea2aa7c4f7367ba86f6fcad", + "zh:e976bcb82996fc4968f8382bbcb6673efb1f586bf92074058a232028d97825b1", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/http" { + version = "3.4.2" + hashes = [ + "h1:vaoPfsLm6mOk6avKTrWi35o+9p4fEeZAY3hzYoXVTfo=", + "zh:0ba051c9c8659ce0fec94a3d50926745f11759509c4d6de0ad5f5eb289f0edd9", + "zh:23e6760e8406fef645913bf47bfab1ca984c1c5805d2bb0ef8310b16913d29cd", + "zh:3c69fde4548bfe65b968534c4df8d699648c921d6a065b97fec5faece73a442b", + "zh:41c7f9a8c117704b7a8fa96a57ebfb92b72129d9625128eeb0dee7d5a09d1110", + "zh:59d09d2e00727df10565cc82a33250b44201fcd353eb2b1579507a5a0adcce18", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:c95b2f63d4357b3068531b90d9dca62a32551d7693defb7ab14b650b5d139c57", + "zh:cc0a3bbd3026191b35f417d3a8f26bdfad376d15be9e8d99a8803487ca5b0105", + "zh:d1185c6abb3ba25123fb7df1ad7dbe2b9cd8f43962628da551040fbe1934656f", + "zh:dfb26fccab7ecdc150f67415e6cfe19d699dc43e8bf5722f36032b17b46a0fbe", + "zh:eb1fcc00073bc0463f64e49600a73d925b1a0c0ae5b94dd7b67d3ebac248a113", + "zh:ec9b9ad69cf790cb0603a1036d758063bbbc35c0c75f72dd04a1eddaf46ad010", + ] +} + +provider "registry.terraform.io/hashicorp/kubernetes" { + version = "2.29.0" + constraints = "2.29.0" + hashes = [ + "h1:7C1MinWhowW8EnlSYhhAFV3bte8x5YcSF5QxUPdoXDk=", + "zh:3edd5dc319b95fe94e61b82d10c1ce7fb53a2f21b067ddb742f2d7d0d19dd113", + "zh:4b9096e6d0cfa0efd4c89270e3d25fea49db570e2cfbe49c5d1de085a15f2578", + "zh:5397573838bcb8844248c8d6ac93cca7f39a0b707ac3ce7a7b306c50c261c195", + "zh:5d635370720d356b7bcb5756ca28de3275ca32ca1ef0201414caecd3a14759ac", + "zh:71a52280408f3fb0ff1866a9ab8059b0d9bde5481869658798e0773461f22eff", + "zh:748663ef0248d2d95f5dea2974332432a395165657856878c5dc6f000b37cc25", + "zh:7fbc1e084bbbb51e31afd3df0c77e833ae59e88cf42b9e2c17b0b1a1e3894723", + "zh:ae89b4be473b446270fa24dc1ef51b0cc4c2a528d9838ec15246d28bac165df3", + "zh:b6433970d680a0cc9898f915224508b5ece86ae4418372fa6bebd2a9d344f226", + "zh:bf871955cf49015e6a0433e814a22a109c1537a775b8b5dc7b37ad05c324904a", + "zh:c16fac91b2197b443a191d98cf37424feed550387ab11bd1427bde819722005e", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.2" + constraints = "3.2.2" + hashes = [ + "h1:IMVAUHKoydFrlPrl9OzasDnw/8ntZFerCC9iXw1rXQY=", + "zh:3248aae6a2198f3ec8394218d05bd5e42be59f43a3a7c0b71c66ec0df08b69e7", + "zh:32b1aaa1c3013d33c245493f4a65465eab9436b454d250102729321a44c8ab9a", + "zh:38eff7e470acb48f66380a73a5c7cdd76cc9b9c9ba9a7249c7991488abe22fe3", + "zh:4c2f1faee67af104f5f9e711c4574ff4d298afaa8a420680b0cb55d7bbc65606", + "zh:544b33b757c0b954dbb87db83a5ad921edd61f02f1dc86c6186a5ea86465b546", + "zh:696cf785090e1e8cf1587499516b0494f47413b43cb99877ad97f5d0de3dc539", + "zh:6e301f34757b5d265ae44467d95306d61bef5e41930be1365f5a8dcf80f59452", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:913a929070c819e59e94bb37a2a253c228f83921136ff4a7aa1a178c7cce5422", + "zh:aa9015926cd152425dbf86d1abdbc74bfe0e1ba3d26b3db35051d7b9ca9f72ae", + "zh:bb04798b016e1e1d49bcc76d62c53b56c88c63d6f2dfe38821afef17c416a0e1", + "zh:c23084e1b23577de22603cff752e59128d83cfecc2e6819edadd8cf7a10af11e", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.1" + constraints = "3.6.1" + hashes = [ + "h1:a+Goawwh6Qtg4/bRWzfDtIdrEFfPlnVy0y4LdUQY3nI=", + "zh:2a0ec154e39911f19c8214acd6241e469157489fc56b6c739f45fbed5896a176", + "zh:57f4e553224a5e849c99131f5e5294be3a7adcabe2d867d8a4fef8d0976e0e52", + "zh:58f09948c608e601bd9d0a9e47dcb78e2b2c13b4bda4d8f097d09152ea9e91c5", + "zh:5c2a297146ed6fb3fe934c800e78380f700f49ff24dbb5fb5463134948e3a65f", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:7ce41e26f0603e31cdac849085fc99e5cd5b3b73414c6c6d955c0ceb249b593f", + "zh:8c9e8d30c4ef08ee8bcc4294dbf3c2115cd7d9049c6ba21422bd3471d92faf8a", + "zh:93e91be717a7ffbd6410120eb925ebb8658cc8f563de35a8b53804d33c51c8b0", + "zh:982542e921970d727ce10ed64795bf36c4dec77a5db0741d4665230d12250a0d", + "zh:b9d1873f14d6033e216510ef541c891f44d249464f13cc07d3f782d09c7d18de", + "zh:cfe27faa0bc9556391c8803ade135a5856c34a3fe85b9ae3bdd515013c0c87c1", + "zh:e4aabf3184bbb556b89e4b195eab1514c86a2914dd01c23ad9813ec17e863a8a", + ] +} + +provider "registry.terraform.io/hashicorp/time" { + version = "0.11.1" + hashes = [ + "h1:pQGSL9mdgw4qsLndFYsEF93mbsIxyxNoAyIbBqhS3Xo=", + "zh:19a393db736ec4fd024d098d55aefaef07056c37a448ece3b55b3f5f4c2c7e4a", + "zh:227fa1e221de2907f37be78d40c06ca6a6f7b243a1ec33ade014dfaf6d92cd9c", + "zh:29970fecbf4a3ca23bacbb05d6b90cdd33dd379f90059fe39e08289951502d9f", + "zh:65024596f22f10e7dcb5e0e4a75277f275b529daa0bc0daf34ca7901c678ab88", + "zh:694d080cb5e3bf5ef08c7409208d061c135a4f5f4cdc93ea8607860995264b2e", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:b29d15d13e1b3412e6a4e1627d378dbd102659132f7488f64017dd6b6d5216d3", + "zh:bb79f4cae9f8c17c73998edc54aa16c2130a03227f7f4e71fc6ac87e230575ec", + "zh:ceccf80e95929d97f62dcf1bb3c7c7553d5757b2d9e7d222518722fc934f7ad5", + "zh:f40e638336527490e294d9c938ae55919069e6987e85a80506784ba90348792a", + "zh:f99ef33b1629a3b2278201142a3011a8489e66d92da832a5b99e442204de18fb", + "zh:fded14754ea46fdecc62a52cd970126420d4cd190e598cb61190b4724a727edb", + ] +} + +provider "registry.terraform.io/integrations/github" { + version = "6.2.1" + constraints = "~> 6.0" + hashes = [ + "h1:uDerb9YJo3vAO+wKw+Z064InX5aXom+nKLDry2eGf14=", + "zh:172aa5141c525174f38504a0d2e69d0d16c0a0b941191b7170fe6ae4d7282e30", + "zh:1a098b731fa658c808b591d030cc17cc7dfca1bf001c3c32e596f8c1bf980e9f", + "zh:245d6a1c7e632d8ae4bdd2da2516610c50051e81505cf420a140aa5fa076ea90", + "zh:43c61c230fb4ed26ff1b04b857778e65be3d8f80292759abbe2a9eb3c95f6d97", + "zh:59bb7dd509004921e4322a196be476a2f70471b462802f09d03d6ce96f959860", + "zh:5cb2ab8035d015c0732107c109210243650b6eb115e872091b0f7b98c2763777", + "zh:69d2a6acfcd686f7e859673d1c8a07fc1fc1598a881493f19d0401eb74c0f325", + "zh:77f36d3f46911ace5c50dee892076fddfd64a289999a5099f8d524c0143456d1", + "zh:87df41097dfcde72a1fbe89caca882af257a4763c2e1af669c74dcb8530f9932", + "zh:899dbe621f32d58cb7c6674073a6db8328a9db66eecfb0cc3fc13299fd4e62e7", + "zh:ad2eb7987f02f7dd002076f65a685730705d04435313b5cf44d3a6923629fb29", + "zh:b2145ae7134dba893c7f74ad7dfdc65fdddf6c7b1d0ce7e2f3baa96212322fd8", + "zh:bd6bae3ac5c3f96ad9219d3404aa006ef1480e9041d4c95df1808737e37d911b", + "zh:e89758b20ae59f1b9a6d32c107b17846ddca9634b868cf8f5c927cbb894b1b1f", + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 2d583e58..2a50732b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -9,6 +9,7 @@ "HashiCorp.terraform", "valentjn.vscode-ltex", "ms-python.python", - "4ops.packer" + "4ops.packer", + "pinage404.nix-extension-pack" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 86bf7726..2911b64a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,8 @@ ], "vs-kubernetes.kubeconfig": "/Users/loic/.kube/config" }, + "nix.enableLanguageServer": true, // Enable LSP. + "nix.serverPath": "nil", "ltex.language": "en", "ltex.dictionary": { "en": [ @@ -13,5 +15,9 @@ "precomputed", "subproblem" ] - } -} \ No newline at end of file + }, + "files.associations": { + "*.y*ml.*": "yaml" + }, + "nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix" +} diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c1413843 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +SHELL:=/usr/bin/env bash +MAKEFLAGS += --no-builtin-rules --no-builtin-variables + +BUILDER_EXEC:= + +ifeq ($(shell uname -s),Darwin) + BUILDER_EXEC:=NIX_CONF_DIR=$(PWD)/bootstrap nix develop .\#builder --command +endif + +bootstrap: + @$(BUILDER_EXEC) echo "Started build environment" + +build: + @$(BUILDER_EXEC) nix build .#nixosConfigurations.aarch64-darwin.default --system aarch64-linux $(ARGS) + +build-x86: + @$(BUILDER_EXEC) nix build .#nixosConfigurations.x86_64-darwin.default --system x86_64-linux $(ARGS) + +#### Terraform + +TF_ROOT_DIRS := $(wildcard tf-root-*) . +TF_ROOT_DIRS_DESTROY:=$(addsuffix -destroy, $(TF_ROOT_DIRS)) +TF_ROOT_DIRS_INIT:=$(addsuffix -init, $(TF_ROOT_DIRS)) +TF_ROOT_DIRS_FMT:=$(addsuffix -fmt, $(TF_ROOT_DIRS)) +TF_ROOT_DIRS_VALIDATE:=$(addsuffix -validate, $(TF_ROOT_DIRS)) + +init: $(TF_ROOT_DIRS_INIT) + +$(TF_ROOT_DIRS_INIT): + @$(eval DIR:=$(subst -init,,$@)) + terraform -chdir=$(DIR) init -upgrade $(ARGS) + +$(TF_ROOT_DIRS): + @terraform -chdir=$@ apply -compact-warnings -auto-approve $(ARGS) + +$(TF_ROOT_DIRS_DESTROY): + @$(eval DIR:=$(subst -destroy,,$@)) + @terraform -chdir=$(DIR) destroy -auto-approve $(ARGS) + +fmt: $(TF_ROOT_DIRS_FMT) + +$(TF_ROOT_DIRS_FMT): + @$(eval DIR:=$(subst -fmt,,$@)) + terraform -chdir=$(DIR) fmt $(ARGS) + +validate: $(TF_ROOT_DIRS_VALIDATE) + +$(TF_ROOT_DIRS_VALIDATE): + @$(eval DIR:=$(subst -validate,,$@)) + terraform -chdir=$(DIR) validate -no-color $(ARGS) + +.PHONY: fmt validate build build-x86 bootstrap init $(TF_ROOT_DIRS) $(TF_ROOT_DIRS_DESTROY) $(TF_ROOT_DIRS_INIT) diff --git a/README.md b/README.md index 3a38a2a6..d961693e 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,220 @@ - [Documentation](https://loic-roux-404.github.io/k3s-paas/) - [Original tutorial (FR)](https://github.com/esgi-lyon/paas-tutorial/blob/main/docs/index.md) + +Compatibility Matrix : + +| OS | Status | +| --- | --- | +| Darwin | OK | +| Linux | missing builder tooling | + +## New Nix system (beta) + +### Setup (Darwin) + +Nix installation : + +```bash +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install + +echo '. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish' >> ~/.config/fish/config.fish +``` + +### Build + +One liner to set up darwin and build the system for aarch64-darwin : + +```bash +nix develop .#builder --extra-experimental-features flakes \ + --extra-experimental-features nix-command \ + --command nix build .#nixosConfigurations.aarch64-darwin.default \ + --system aarch64-linux --refresh +``` + +> For next builds you can discard any `--extra-experimental-features` flags. +> --refresh is optional, it will force a rebuild of the system. + +For native linux simply run : + +```bash +nix build .#nixosConfigurations.aarch64-darwin.default +``` + +> Supported systems are `aarch64-linux`, `x86_64-linux`, `aarch64-darwin` and `x86_64-darwin`. + +On macOS, dnsmasq starts in background, you might need to force a refresh of the dns cache : + +```bash +sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder +``` + +### Uninstall on Darwin: + +> When builder environment not starting (no libvirtd.sock) + +```bash +./result/sw/bin/darwin-uninstaller +``` + +### Terraform local setup + +Bootrap local vm : + +```bash +terraform -chdir=tf-root-libvirt init +terraform -chdir=tf-root-libvirt apply -auto-approve +``` + +Setup k8s modules : + +```bash +terraform init +terraform apply -auto-approve +``` + +## Terraform variables + +### 1. Contabo (VPS) + +**contabo_credentials** : + +```hcl +contabo_credentials = { + oauth2_client_id = "client-id" + oauth2_client_secret = "secret" + oauth2_pass = "password!" + oauth2_user = "mail@mail" +} +``` + +Seek for credentials in [API](https://my.contabo.com/api/details) + +**`contabo_instance` :** + +```bash +cntb config set-credentials --oauth2-clientid id --oauth2-client-secret secret --oauth2-password "contabo-dashboard-pass" +cntb get instances +``` + +### 2. Gandi (domain) + +- **`paas_base_domain`** : Order a domain on [gandi](https://www.gandi.net) +- **`gandi_token`** : Generate a Personal Access Token on [gandi organisation](https://admin.gandi.net/organizations/) + +> **Warn :** Delete `@` record for your domain on [gandi](https://admin.gandi.net/domain/) + +### 3. Tailscale (SSH VPN) + +**`tailscale_api_key`** : Register on tailscale and get key on [admin console](https://login.tailscale.com/admin/settings/keys) +**`tailscale_trusted_device`** : Approve your device on tailscale with **`tailscale login`** and recover its tailscale hostname. + +### 4. Github (Authentication & users) + +**`github_token`** : https://github.com/settings/tokens and create a token with scopes `repo`, `user` and `admin`. +**`github_client_id`** : Create a new OAuth App. +**`github_client_secret`** : On new OAuth App ask for a new client secret. + +### 5. Cert-manager (TLS) + +**`cert_manager_email`** : a valid email to register on letsencrypt. + +## Apply + +Init all terraform providers and modules. + +```bash +make init +``` + +### Cloud (contabo) + +```bash +make tf-root-contabo ARGS=-var-file=$PWD/.prod.tfvars +``` + +### infra (k8s) + +```bash +make . ARGS=-var-file=.prod.tfvars +``` + +## Cheat Sheet + +## Nix + +See derivations of a build : + +```bash +nix derivation show -r '.#nixosConfigurations.aarch64-darwin.default' +``` + +Filter derivations by name : + +```bash +nix derivation show -r '.#nixosConfigurations.aarch64-darwin.default' | jq -r '.[] | select(.name | contains("cert-manager"))' +``` + +Debug flake : + +```bash +nix --extra-experimental-features repl-flake repl '.#' +``` + +Free unused derivations : + +```bash +nix-store --optimise +``` + +Repair nix store : + +```bash +nix-store --verify --check-contents --repair +``` + +### Libvirt + +Undefine pool : + +```bash +virsh -c qemu:///system pool-undefine libvirt-pool-k3s-paas +``` + +Undefine vm to avoid conflicts : + +```bash +virsh -c qemu:///system undefine --nvram vm1 +``` + +Open console : + +```bash +virsh -c qemu:///system console vm1 +``` + +Exit with `Ctrl + +` or `Ctrl + ]` on linux. + +> See [this SO thread](https://superuser.com/questions/637669/how-to-exit-a-virsh-console-connection#:~:text=ctrl%20%2B%20alt%20%2B%206%20(Mac)) if you keep struggling. + +### Openssl + +Generate a sha512crypt password : + +```bash +openssl passwd -salt zizou -6 zizou420! +``` + +### Kubectl + +See all pods : + +```bash +kubectl get po -A +``` + +See any assets : + +```bash +kubectl get all -A +``` diff --git a/bootstrap/nix.conf b/bootstrap/nix.conf new file mode 100644 index 00000000..7aa6e329 --- /dev/null +++ b/bootstrap/nix.conf @@ -0,0 +1 @@ +extra-experimental-features = flakes nix-command diff --git a/charts/microservice/.helmignore b/charts/microservice/.helmignore deleted file mode 100644 index 0e8a0eb3..00000000 --- a/charts/microservice/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/microservice/Chart.lock b/charts/microservice/Chart.lock deleted file mode 100644 index 5b617379..00000000 --- a/charts/microservice/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 12.1.9 -digest: sha256:3615da30e6713b58d131a8323d888e7eae763d6416acf558bbb7c22841bd65ef -generated: "2023-01-20T12:01:38.489726+01:00" diff --git a/charts/microservice/Chart.yaml b/charts/microservice/Chart.yaml deleted file mode 100644 index 4400d131..00000000 --- a/charts/microservice/Chart.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: v2 -name: microservice -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" - -dependencies: - - name: postgresql - version: 12.1.9 - repository: https://charts.bitnami.com/bitnami \ No newline at end of file diff --git a/charts/microservice/templates/NOTES.txt b/charts/microservice/templates/NOTES.txt deleted file mode 100644 index 319f01bd..00000000 --- a/charts/microservice/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "chart.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "chart.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} diff --git a/charts/microservice/templates/_helpers.tpl b/charts/microservice/templates/_helpers.tpl deleted file mode 100644 index 3c0d07ff..00000000 --- a/charts/microservice/templates/_helpers.tpl +++ /dev/null @@ -1,75 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "chart.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "chart.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "chart.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "chart.labels" -}} -helm.sh/chart: {{ include "chart.chart" . }} -{{ include "chart.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "chart.selectorLabels" -}} -app.kubernetes.io/name: {{ include "chart.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "chart.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "chart.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} - -{{/* -Create the secrets required for our app as environment var -*/}} -{{- define "helpers.listEnvVariables"}} -{{- range $key, $val := .Values.env.secret }} -- name: {{ $key }} - valueFrom: - secretKeyRef: - name: {{ $.Values.secret.name }} - key: {{ $key }} -{{- end}} -{{- end }} diff --git a/charts/microservice/templates/deployment.yaml b/charts/microservice/templates/deployment.yaml deleted file mode 100644 index 3af06ce3..00000000 --- a/charts/microservice/templates/deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "chart.fullname" . }} - labels: - {{- include "chart.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "chart.selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "chart.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "chart.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - {{- include "helpers.listEnvVariables" . | indent 10 }} - ports: - - name: http - containerPort: {{ .Values.container.port }} - protocol: TCP - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/microservice/templates/hpa.yaml b/charts/microservice/templates/hpa.yaml deleted file mode 100644 index 548ee03b..00000000 --- a/charts/microservice/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "chart.fullname" . }} - labels: - {{- include "chart.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "chart.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/microservice/templates/ingress.yaml b/charts/microservice/templates/ingress.yaml deleted file mode 100644 index 63c1311c..00000000 --- a/charts/microservice/templates/ingress.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "chart.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "chart.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/charts/microservice/templates/persistence.yaml b/charts/microservice/templates/persistence.yaml deleted file mode 100644 index df3f98b0..00000000 --- a/charts/microservice/templates/persistence.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -kind: PersistentVolume # Create a PV -metadata: - name: postgresql-data # Sets PV's name - labels: - type: local # Sets PV's type to local -spec: - storageClassName: manual - capacity: - storage: 10Gi # Sets PV Volume - accessModes: - - ReadWriteOnce - hostPath: - path: "/data/volume" # Sets the volume's path ---- -apiVersion: v1 -kind: PersistentVolumeClaim # Create PVC -metadata: - name: postgresql-data-claim # Sets name of PV -spec: - storageClassName: manual - accessModes: - - ReadWriteOnce # Sets read and write access - resources: - requests: - storage: 2Gi # Sets volume size diff --git a/charts/microservice/templates/secrets.yaml b/charts/microservice/templates/secrets.yaml deleted file mode 100644 index c1113dac..00000000 --- a/charts/microservice/templates/secrets.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.secret.name }} -type: Opaque -data: - {{- range $key, $val := .Values.env.secret }} - {{ $key }}: {{ $val | b64enc }} - {{- end}} diff --git a/charts/microservice/templates/service.yaml b/charts/microservice/templates/service.yaml deleted file mode 100644 index dfc5b3a3..00000000 --- a/charts/microservice/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "chart.fullname" . }} - labels: - {{- include "chart.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "chart.selectorLabels" . | nindent 4 }} diff --git a/charts/microservice/templates/serviceaccount.yaml b/charts/microservice/templates/serviceaccount.yaml deleted file mode 100644 index 26a57fa8..00000000 --- a/charts/microservice/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "chart.serviceAccountName" . }} - labels: - {{- include "chart.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/microservice/templates/tests/test-connection.yaml b/charts/microservice/templates/tests/test-connection.yaml deleted file mode 100644 index 8dfed872..00000000 --- a/charts/microservice/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "chart.fullname" . }}-test-connection" - labels: - {{- include "chart.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "chart.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/charts/microservice/values.yaml b/charts/microservice/values.yaml deleted file mode 100644 index 6e8301b9..00000000 --- a/charts/microservice/values.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# Default values for chart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: loicroux/client - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -secret: - name: all-secrets -env: - secret: - PG_USER: ekommerce - PG_CONNECTION: jdbc:postgresql://client-postgresql.default.svc.cluster.local:5432/db - PG_PASSWORD: password - -postgresql: - auth: - password: password - enablePostgresUser: false - database: db - username: ekommerce - volumePermissions: - enabled: true - primary: - persistence: - enabled: true - existingClaim: "postgresql-data-claim" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -container: - port: 8080 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: letsencrypt-acme-issuer - hosts: - - host: client.k3s.test - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: client.k3s.test-tls - # hosts: - # - client.k3s.test - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/contabo/.gitignore b/contabo/.gitignore deleted file mode 100644 index 9b8a46e6..00000000 --- a/contabo/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# Local .terraform directories -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Crash log files -crash.log -crash.*.log - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*.tfvars -*.tfvars.json - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -# Include override files you do wish to add to version control using negated pattern -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* - -# Ignore CLI configuration files -.terraformrc -terraform.rc diff --git a/contabo/.terraform.lock.hcl b/contabo/.terraform.lock.hcl deleted file mode 100644 index 95b3fcc8..00000000 --- a/contabo/.terraform.lock.hcl +++ /dev/null @@ -1,132 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/contabo/contabo" { - version = "0.1.17" - constraints = ">= 0.1.17" - hashes = [ - "h1:fwKCl1tj+tOT0bQHiDJCFyGkhp6NuUvoulBTdtBDr5o=", - "zh:0d429602e71642218e8e1b91a82a667e221c43ede66ac3dd331718db3a4853ee", - "zh:10fce9100cb242ce0da2bf6d1bdf9481b043f3eb3833fadba63292624553e981", - "zh:19bcf3660ac7545103cf999e0066442f9d6350db9654e1496726520cef287246", - "zh:2a6504d2573d4d46efa8e7aa0fe57de95edd9055cfbdfd6318b320e59f7e5cd4", - "zh:35596a777e04633cab65966cbf7c4ebae6ad23049c2fc65688f2d7cd94503e3a", - "zh:6f2789874aea9244f3bc0f1594c735591ca4c297acb643be9a0b4a17b4d6554e", - "zh:75eb75486695f3f740b431a3767a574feafa91aa92b4574048e236858c6ce463", - "zh:7d2ae3c7fc0bfbc39465591134c6631e7e591440ed0c4bb25f576dcca615a33d", - "zh:b5d05f31171788b7b061b47ba7e955787f6b2c6e9e5790b389603cba02589215", - "zh:bfea42645edfc77536cbcd99a44764c4e8b6a2f76347fc2f3258a70f0eb2c40b", - "zh:db1a91021a13e3319e9cb06924b638f973457e12196e8269820abd4e45fad232", - "zh:dd0bacf3ce6e560eb41f51d07f8eab393095cb17f563b5d9f7d8d01bffe0f9d2", - "zh:e00eb2c5f6edaa78b17a9f0468dcd6bdea507a2e3a013586c48f2237ddae8995", - "zh:ea3518b23027a8c10f7849025e1a14ef664ab2cfa3caf66c14b24f4496da4e0d", - "zh:faa825eaed93cbb00e18c97019e6cb6d78d5ce4793d9f92d743d9e094160dc51", - ] -} - -provider "registry.terraform.io/hashicorp/random" { - version = "3.4.3" - hashes = [ - "h1:saZR+mhthL0OZl4SyHXZraxyaBNVMxiZzks78nWcZ2o=", - "zh:41c53ba47085d8261590990f8633c8906696fa0a3c4b384ff6a7ecbf84339752", - "zh:59d98081c4475f2ad77d881c4412c5129c56214892f490adf11c7e7a5a47de9b", - "zh:686ad1ee40b812b9e016317e7f34c0d63ef837e084dea4a1f578f64a6314ad53", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:84103eae7251384c0d995f5a257c72b0096605048f757b749b7b62107a5dccb3", - "zh:8ee974b110adb78c7cd18aae82b2729e5124d8f115d484215fd5199451053de5", - "zh:9dd4561e3c847e45de603f17fa0c01ae14cae8c4b7b4e6423c9ef3904b308dda", - "zh:bb07bb3c2c0296beba0beec629ebc6474c70732387477a65966483b5efabdbc6", - "zh:e891339e96c9e5a888727b45b2e1bb3fcbdfe0fd7c5b4396e4695459b38c8cb1", - "zh:ea4739860c24dfeaac6c100b2a2e357106a89d18751f7693f3c31ecf6a996f8d", - "zh:f0c76ac303fd0ab59146c39bc121c5d7d86f878e9a69294e29444d4c653786f8", - "zh:f143a9a5af42b38fed328a161279906759ff39ac428ebcfe55606e05e1518b93", - ] -} - -provider "registry.terraform.io/hashicorp/time" { - version = "0.9.1" - constraints = "0.9.1" - hashes = [ - "h1:VxyoYYOCaJGDmLz4TruZQTSfQhvwEcMxvcKclWdnpbs=", - "zh:00a1476ecf18c735cc08e27bfa835c33f8ac8fa6fa746b01cd3bcbad8ca84f7f", - "zh:3007f8fc4a4f8614c43e8ef1d4b0c773a5de1dcac50e701d8abc9fdc8fcb6bf5", - "zh:5f79d0730fdec8cb148b277de3f00485eff3e9cf1ff47fb715b1c969e5bbd9d4", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:8c8094689a2bed4bb597d24a418bbbf846e15507f08be447d0a5acea67c2265a", - "zh:a6d9206e95d5681229429b406bc7a9ba4b2d9b67470bda7df88fa161508ace57", - "zh:aa299ec058f23ebe68976c7581017de50da6204883950de228ed9246f309e7f1", - "zh:b129f00f45fba1991db0aa954a6ba48d90f64a738629119bfb8e9a844b66e80b", - "zh:ef6cecf5f50cda971c1b215847938ced4cb4a30a18095509c068643b14030b00", - "zh:f1f46a4f6c65886d2dd27b66d92632232adc64f92145bf8403fe64d5ffa5caea", - "zh:f79d6155cda7d559c60d74883a24879a01c4d5f6fd7e8d1e3250f3cd215fb904", - "zh:fd59fa73074805c3575f08cd627eef7acda14ab6dac2c135a66e7a38d262201c", - ] -} - -provider "registry.terraform.io/integrations/github" { - version = "5.21.1" - constraints = "~> 5.0" - hashes = [ - "h1:e0BrGh3T4gimkqgROHyWdOzFRAKRT3U0SUt7vwN9Iac=", - "zh:4a55c2257b108faaded434bbc4491c5efc39dc41e6514d7050e15e39ee1f2ac9", - "zh:765b7b99d9c7522aede4e200166331f3c8093505ba3330309f2fe93d2a4a7f71", - "zh:80fb20f2e83f9eb786e85971a91e3a5ca141a9d68deac6b786c1860ce9b482a8", - "zh:868abaa9ca998e24d84af85a1e3722901d7c274dbf88a53847964450a6931b73", - "zh:8e9254a1508d0afc27510ec6a43d215a600dc7a870cf541803f5298dc32a6c07", - "zh:9249f58e07c8a2b725272c444b2ff70f4e6e0ba59a95433840adde1295246ce0", - "zh:9613d3bc76f64a54d85ba2feb0fc1feac0205437864ce1ff47d38772a9bb9285", - "zh:a153f986cd88ec2ab43c6dae1efb82f66d8277b597619e7ab6b6d1494ea676de", - "zh:a5b37268078be1739915cca7afff017de8543191527cc995ccf33e324b92fad9", - "zh:aca22cd1d5f2c5e6692c0c3392312b160ec70a7087f7075fccb7c3e0996448a2", - "zh:b15a4ce4760f18d6aa03c17fa1049c3ba797ae437452f6277251ac47ba791b60", - "zh:b8b3b0b885e89b449779bf73bfd6dbc53103b9d8eaac13bad2b95b8a035f5100", - "zh:dc11d7ee4bff6990c81216e96de2b9de43ab8b6a8d82532888f587fb6080720f", - "zh:fb8afed924acdd8d9bb773d518a30857b59ca260a94ac8107c03b23afb1ef3e7", - ] -} - -provider "registry.terraform.io/lexfrei/namedotcom" { - version = "1.2.0" - constraints = "1.2.0" - hashes = [ - "h1:q8qEHCrBXxH+aExaiKGiRHSMMyCwuO2AqTPRj18+x5A=", - "zh:0fa82a384b25a58b65523e0ea4768fa1212b1f5cfc0c9379d31162454fedcc9d", - "zh:1397aedab6d34041543b9cbf09ceccd4d792581805c73520e180bcb98a638282", - "zh:161d4cae0a53820da274cb17e2f82ec19b5b620b2a7e33022ff872c325245716", - "zh:200330d4a418a0198b84a8d18aba1df250469748fea39517dfa489d43f8648a9", - "zh:2bb98c73191bfb6a24ad779d7e7da387ef55430f1584d68a38e91856d30ce0b9", - "zh:31700fbf56dab71c13aee5d74bee53033e9a227767404913482910b4bfa320f7", - "zh:3278a4cb1d8b3a34724c49653c1d63a8c76a4b8e0af730cc4fdc7f5d56a1138a", - "zh:441e1f4fb16735a1abebabdec962657ee8b208a40122b173151507641394d623", - "zh:7300395a67eb871ada30896ff69716959a9ee2c7f12523d52a1dbd8157c99855", - "zh:8cdd220918b1d91bcd1940c22879e099c4a8eb363b9c04e09d1cdf025e412dbf", - "zh:a965a107a3684ec64661521b69274d4f8b7a8ff295df58e2ace21ccfa27523d6", - "zh:b2332a1dde48b8b8e7aa8c24de4880b8ee78c61254a79d0bc9599ddf38821923", - "zh:b874fb56950760b3f2e9ba314d1cc62fb927db6caf951aac09bba00feb0ed853", - "zh:c35d1b82e1002367a75af723a0b23ece7d3a9fc4585de37dd53794903482c577", - "zh:e27d030842d9a860dcc384565ca58876f11ad928cd02b10b9f08c6303c42e387", - ] -} - -provider "registry.terraform.io/loic-roux-404/contabo" { - version = "0.1.17-alpha" - constraints = "0.1.17-alpha" - hashes = [ - "h1:7AqLARj1bkDuuj5xTcxnxYDH2J44g7ihLoNGRjiLuO4=", - "zh:02dc2c8fe99588f901d4fb0864b6b78e84a47c1d2083c6641cf84df1b49e34c3", - "zh:19bcf3660ac7545103cf999e0066442f9d6350db9654e1496726520cef287246", - "zh:29e1a6beae23419a7b412523041a2fbf71b521dd9e6c6edee4df3833f146873d", - "zh:429bc7bf9d8b9532340a377839483101a5895731835c925ed2f45e3aca6f2c3f", - "zh:4e015b2a83b6439fc22f9e69ec99f610b9a77e10d00a1d1fbd0aa4a17a06c95b", - "zh:4ee9e4f51fcdd014186913b07cae04cee5bb9f709d1d9815912126354a3f6bc9", - "zh:5599fb5d2fa98e95db91918198ad5d3a3210ec7d5a853a92702cbbf0f86a5ec2", - "zh:7a62df5ba199f3f42304bbbfb6b0d49138d42255d2fd964326f86803f0d5ea7b", - "zh:8500db4e5d8f8bc742cb31f41776f70548396eff1683a6f088a3e5d6c2afaa6c", - "zh:8b4521b3b6383472c23465b7b5499dbbce1d3299c5a7bd49daf19da98d71ce37", - "zh:8f170c725d0f942edbacf346b01e74e04d6cdc4231da2aaeee42a121c496f906", - "zh:9439073578e2649400fdc6924adb78d70377c4ef4d1964ed038679114a2a07f8", - "zh:bd9c5c8fda0036463950d96a2f0e425eb6d21c7cb5468dd16b1f90b349a03524", - "zh:c59a1637a48312247d3d0d79c54cbf49796ddc52b7956f5cbd0ea15b6e411fc5", - "zh:fc93f9ebf42df5a49725761231086cc242b47f5f2cc7ae9aabbb372889e6adf9", - ] -} diff --git a/contabo/Makefile b/contabo/Makefile deleted file mode 100644 index faa6bbe1..00000000 --- a/contabo/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -VAR_FILE?=prod.tfvars -SHELL:=/bin/bash - -read_tfvar=$(shell grep $1 prod.tfvars | cut -d'=' -f2 | tr -d ' ' | tr -d \") - -CLIENT_ID:=$(call read_tfvar,oauth2_client_id) -CLIENT_SECRET:=$(call read_tfvar,oauth2_client_secret) -API_USER:=$(call read_tfvar,oauth2_user) -API_PASSWORD:=$(call read_tfvar,oauth2_pass) -INSTANCE_ID:=$(call read_tfvar,contabo_instance) - -TAILSCALE_KEY:=$(call read_tfvar,tailscale_key) - -.DEFAULT_GOAL := help - -help: - @echo "Contabo tf simple helpers" - @echo "Apply infra on $(INSTANCE_ID) :" - @echo " make apply" - @echo "Debug contabo with cli :" - @echo " make setup_cntb" - @echo "Setup ssh" - @echo " make setup_ssh" - -setup_cntb: - cntb config set-credentials --oauth2-clientid="$(CLIENT_ID)" \ - --oauth2-client-secret="$(CLIENT_SECRET)" --oauth2-user="$(API_USER)" \ - --oauth2-password="$(API_PASSWORD)" - -# Run it if starting from empty infra -apply: - terraform apply -var-file=$(VAR_FILE) -auto-approve - -setup_ssh: - sudo tailscale up --authkey=$(TAILSCALE_KEY) --ssh diff --git a/contabo/data.tf b/contabo/data.tf deleted file mode 100644 index 16f28bb1..00000000 --- a/contabo/data.tf +++ /dev/null @@ -1,20 +0,0 @@ -data "github_organization" "org" { - name = var.github_organization -} - -data "github_membership" "all" { - for_each = toset(data.github_organization.org.members) - username = each.value -} - -data "github_membership" "all_admin" { - for_each = { - for _, member in data.github_membership.all : - _ => member if member.role == "admin" - } - username = each.value.username -} - -data "contabo_instance" "paas_instance" { - id = var.contabo_instance -} diff --git a/contabo/exemple.tfvars.dist b/contabo/exemple.tfvars.dist deleted file mode 100644 index 02dfa971..00000000 --- a/contabo/exemple.tfvars.dist +++ /dev/null @@ -1,37 +0,0 @@ -github_organization = "github-team" -github_team = "ops-team" -domain = "paas-esgi-tutorial.live" -# https://www.name.com/account/login -namedotcom_username = "username" -namedotcom_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaa" -# https://github.com/settings/tokens -github_token = "ghp_aaaaaaaaaaaaaaaaaaxxxxxxxxxxxx" - -# https://login.tailscale.com/admin/settings/keys -tailscale_key = "" - -# https://api.contabo.com/#section/Authentication -contabo_credentials = { - oauth2_client_id = "INT-XXXXX" - oauth2_client_secret = "XXXXXXX-xXXX-XXXX-XXX-XXXXXXXXX" - oauth2_user = "toto@example.com" - oauth2_pass = "password" -} - -# Recovered by CLI -contabo_instance = "XXXXXXXXX" - -ssh_connection = { - password = "badsecret" - password_hash = "$6$salt$FT3avWz0MRNdyK7UcXl8hjbKH/mYmhoIWiqFFUfr4o0fJXJbE3r5fW0vFtZsmbzjyyfvIWbykiHAyx1IiUHrl0" - private_key = "~/.ssh/id_rsa" - public_key = "~/.ssh/id_rsa.pub" - user = "admin" -} - -# https://github.com/organizations/my-organization/settings/applications -secrets = { - dex_github_client_id = "dex-github-oauth2-app-client-id" - dex_github_client_secret = "dex-github-oauth2-app-client-secret" - cert_manager_email = "paas-esgi-tutorial.live4@example.com" -} diff --git a/contabo/main.tf b/contabo/main.tf deleted file mode 100644 index 27f9f69f..00000000 --- a/contabo/main.tf +++ /dev/null @@ -1,135 +0,0 @@ -############ -# Accounts -############ -resource "github_team" "opsteam" { - name = var.github_team - description = "This is the production team" - privacy = "closed" -} - -resource "github_team_membership" "opsteam_members" { - for_each = data.github_membership.all_admin - team_id = github_team.opsteam.id - username = each.value.username - role = "maintainer" -} - -############ -# Security -############ - -# Dex oidc client -resource "random_password" "dex_client_id" { - length = 16 - special = false -} - -resource "random_password" "dex_client_secret" { - length = 24 - special = false -} - -resource "random_password" "cert_manager_private_key_secret" { - length = 12 - special = false -} - -locals { - ssh_connection = merge(var.ssh_connection, { - public_key = trimspace(file(pathexpand(var.ssh_connection.public_key))) - private_key = trimspace(file(pathexpand(var.ssh_connection.private_key))) - }) - ansible_vars = merge( - var.ansible_secrets, - { - dex_client_id = random_password.dex_client_id.result - dex_client_secret = random_password.dex_client_secret.result - waypoint_base_domain = var.domain - dex_github_client_org = data.github_organization.org.orgname - dex_github_client_team = github_team.opsteam.name - cert_manager_private_key_secret = random_password.cert_manager_private_key_secret.result - cert_manager_letsencrypt_env = var.cert_manager_letsencrypt_env - } - ) -} - -# Store secrets to recover them later -resource "contabo_secret" "paas_instance_ssh_key" { - name = "paas_instance_ssh_key" - type = "ssh" - value = local.ssh_connection.public_key -} - -resource "contabo_secret" "paas_instance_password" { - name = "paas_instance_password" - type = "password" - value = local.ssh_connection.password -} - -############ -# Vm -############ - -locals { - iso_version_file = "ubuntu-${var.ubuntu_release_info.name}-${var.ubuntu_release_info.version}.${var.ubuntu_release_info.format}" - image_url = "${var.ubuntu_release_info.url}/${var.ubuntu_release_info.iso_version_tag}/${local.iso_version_file}" -} - -resource "contabo_image" "paas_instance_qcow2" { - name = var.ubuntu_release_info.name - image_url = local.image_url - os_type = "Linux" - version = var.ubuntu_release_info.iso_version_tag - description = "generated PaaS vm image with packer" -} - -resource "namedotcom_record" "dns_zone" { - for_each = toset(["", "*"]) - domain_name = var.domain - host = each.key - record_type = "A" - answer = data.contabo_instance.paas_instance.ip_config[0].v4[0].ip -} - -resource "contabo_instance" "paas_instance" { - existing_instance_id = var.contabo_instance - - depends_on = [ - github_team_membership.opsteam_members - ] - - display_name = "ubuntu-k3s-paas" - image_id = contabo_image.paas_instance_qcow2.id - ssh_keys = [contabo_secret.paas_instance_ssh_key.id] - user_data = sensitive(templatefile( - "${path.root}/user-data.yaml.tmpl", - { - tailscale_key = var.tailscale_key - ubuntu_release_info = var.ubuntu_release_info - ssh_connection = local.ssh_connection - ansible_vars = [ - for k, v in local.ansible_vars : "${k}=${v}" - ] - } - )) -} - -resource "terraform_data" "paas_instance_wait_bootstrap" { - triggers_replace = [ - contabo_instance.paas_instance.id - ] - - connection { - type = "ssh" - user = local.ssh_connection.user - private_key = local.ssh_connection.private_key - host = contabo_instance.paas_instance.ip_config[0].v4[0].ip - } - - provisioner "remote-exec" { - on_failure = fail - inline = [ - "sudo cloud-init status --wait && sudo cloud-init clean" - ] - } -} diff --git a/contabo/terraform.tf b/contabo/terraform.tf deleted file mode 100644 index 49dad480..00000000 --- a/contabo/terraform.tf +++ /dev/null @@ -1,40 +0,0 @@ -terraform { - - required_version = ">=1.4" - - required_providers { - contabo = { - source = "loic-roux-404/contabo" - version = "0.1.17-alpha" - } - github = { - source = "integrations/github" - version = "~> 5.0" - } - namedotcom = { - source = "lexfrei/namedotcom" - version = "1.2.0" - } - time = { - source = "hashicorp/time" - version = "0.9.1" - } - } -} - -provider "github" { - token = var.github_token - owner = var.github_organization -} - -provider "namedotcom" { - token = var.namedotcom_token - username = var.namedotcom_username -} - -provider "contabo" { - oauth2_client_id = var.contabo_credentials.oauth2_client_id - oauth2_client_secret = var.contabo_credentials.oauth2_client_secret - oauth2_user = var.contabo_credentials.oauth2_user - oauth2_pass = var.contabo_credentials.oauth2_pass -} diff --git a/contabo/user-data.yaml.tmpl b/contabo/user-data.yaml.tmpl deleted file mode 100644 index c4ba0f2e..00000000 --- a/contabo/user-data.yaml.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -#cloud-config - -system_info: - default_user: - name: ${ssh_connection.user} - -ssh_deletekeys: false -disable_root: 1 -ssh_pwauth: 0 - -users: - - name: ${ssh_connection.user} - passwd: "${ssh_connection.password_hash}" - groups: [adm, cdrom, dip, plugdev, sudo] - lock-passwd: false - sudo: ALL=(ALL) NOPASSWD:ALL - shell: /bin/bash - ssh_authorized_keys: - - '${trim(jsonencode(ssh_connection.public_key), "\"")}' - -apt: - sources: - tailscale.list: - source: deb https://pkgs.tailscale.com/stable/ubuntu ${ubuntu_release_info.name} main - keyid: 2596A99EAAB33821893C0A79458CA832957F5868 - -package_update: true -packages: - - tailscale - -runcmd: - - [echo, "${ubuntu_release_info.iso_version_tag}"] - - [tailscale, up, -authkey, '${tailscale_key}'] - -ansible: - install_method: pip - package_name: ansible - setup_controller: - run_ansible: - - playbook_dir: /playbook - inventory: /playbook/inventories/contabo/hosts - playbook_name: site.yaml - extra_vars: ${join(" ", ansible_vars)} -o 'IdentitiesOnly=yes' - connection: local diff --git a/contabo/variables.tf b/contabo/variables.tf deleted file mode 100644 index abb499a7..00000000 --- a/contabo/variables.tf +++ /dev/null @@ -1,98 +0,0 @@ - -variable "github_organization" { - type = string -} - -variable "github_team" { - type = string -} - -variable "github_token" { - type = string - sensitive = true -} - -variable "cert_manager_letsencrypt_env" { - type = string - default = "prod" -} - -variable "domain" { - type = string -} - -variable "domain_ttl" { - type = number - default = 3000 -} - -variable "namedotcom_token" { - type = string - sensitive = true -} - -variable "namedotcom_username" { - type = string - sensitive = true -} - -variable "tailscale_key" { - type = string - sensitive = true -} - -variable "contabo_instance" { - type = string -} - -variable "contabo_credentials" { - type = object({ - oauth2_client_id = string - oauth2_client_secret = string - oauth2_user = string - oauth2_pass = string - }) - sensitive = true -} - -variable "ssh_connection" { - type = object({ - user = string - password = string - password_hash = string - public_key = string - private_key = string - }) - default = { - password = "badSecret12!" - password_hash = "$6$zizou$5kLDHHKr97WNOkvnTzpnqIQ/z.n.rJmV0YFdUiy1cwxxdz/wIgnI8Rd7lnO8Ry6t01KT3OLMhrFgOZiR7cMLb1" - private_key = "~/.ssh/id_rsa" - public_key = "~/.ssh/id_rsa.pub" - user = "admin" - } - sensitive = true -} - -variable "ansible_secrets" { - type = map(string) - description = "Define ansible secrets" - default = {} - sensitive = true -} - -variable "ubuntu_release_info" { - type = object({ - name = string - version = string - iso_version_tag = string - url = string - format = string - }) - default = { - name = "jammy" - version = "22.04.2" - iso_version_tag = "ubuntu-jammy-204f221" - url = "https://github.com:443/loic-roux-404/k3s-paas/releases/download" - format = "qcow2" - } -} diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..8447762c --- /dev/null +++ b/default.nix @@ -0,0 +1,10 @@ +# See https://nixos.wiki/wiki/Flakes#Using_flakes_project_from_a_legacy_Nix +(import ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; } +) { + src = ./.; +}).defaultNix diff --git a/docs/1-install.md b/docs/1-install.md index b2fed05e..d2789c2c 100644 --- a/docs/1-install.md +++ b/docs/1-install.md @@ -13,13 +13,10 @@ The optics of this tooling will follow : For this we will use a technical base composed of : - [`k3s`](https://k3s.io/) tool which simplifies the installation of kubernetes on ARM machines while remaining compatible with classic X64 architectures. It provides by default pods (containers in execution) to include features often sought on this type of edge computing configuration (reverse proxy, DNS configuration ...) -- [Packer](https://www.packer.io/) to create iso images of linux machines -- [Ansible](https://www.ansible.com/) to provision this image +- [Nix Os](https://nixos.org/manual/nixpkgs/stable/) to create iso images of linux machines - [Terraform](https://www.terraform.io/) to control azure in an IaC way and to trigger all the PaaS implementation on it. -Translated with www.DeepL.com/Translator (free version) - -## Docker installation +## Usefull links Docker architecture : @@ -31,96 +28,13 @@ K3s Architecture : > Note : Here we are only using single node mode -## Rancher as docker desktop replacement - -[**Rancher**](https://rancherdesktop.io/) Download 1.6.2 (macOS) from [github release](https://github.com/rancher-sandbox/rancher-desktop/releases/tag/v1.6.2) - -At first start configure rancher as follow : -- **Disable kubernetes** -- **dockerd** as engine - -Check command `docker` is available. If not add `~/.rd/bin` to `PATH` : - -```bash -echo 'export PATH="$PATH:$HOME/.rd/bin"' >> ~/.zshrc -``` - ## Installation de vscode - [Avec installer toutes plateformes](https://code.visualstudio.com/download) - Homebrew sur mac `brew install --cask visual-studio-code` - [Avec snap pour linux](https://snapcraft.io/code) sur linux -## Python environment - -**Everything here is done with a `bash` or `zsh shell`** - -**Conda** : [docs.conda.io](https://docs.conda.io/en/latest/miniconda.html). Run `.pkg` for mac. - -> utilisez la ligne de commande ci-dessous pour l'installer -```bash -wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -P /tmp -chmod +x /tmp/Miniconda3-latest-Linux-x86_64.sh -/tmp/Miniconda3-latest-Linux-x86_64.sh -p $HOME/miniconda -``` - -> Pour arm : -```bash -wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.12.0-Linux-aarch64.sh -P /tmp -chmod +x /tmp/Miniconda3-py39_4.12.0-Linux-aarch64.sh -/tmp/Miniconda3-py39_4.12.0-Linux-aarch64.sh -p $HOME/miniconda -``` - -Consent to agreements and licences in next prompts. - -Then run `conda init zsh` (or `bash` if you prefer) - -**Relancer votre shell pour appliquer** (commande `exec $SHELL`) - -## Ansible playbook - -Setup vault password in a file : - -```bash -echo 'pass' > ~/.ansible/.vault -``` - -Then install requirements : - -```bash -cd playbook -pip install -r requirements-test.txt -ansible-galaxy install -r requirements.yml -pip install -r requirements.txt -cd - -``` - -### Test waypoint role with molecule : - - -Setup mac os networking with rancher : - -```bash -cd playbook/roles/waypoint -./scripts/setup_macos.sh -``` - -Recover ip subnet if needed (ex: 172.29.0.20) and edit `metallb_ip_range` accordingly : -```bash -docker network inspect k3snet | jq -r '.[0].IPAM.Config[0].Subnet' | awk -F. '{print $1"."$2}' -``` - -Setup dnsmasq to wildcard domain to localhost : - -```bash -cd playbook/roles/waypoint -./molecule/default/scripts/setup_dnsmasq.sh -``` - -```bash -molecule test --destroy never -``` To open UI with https add pebble certificate to your truststore : @@ -155,74 +69,34 @@ waypoint context create \ ``` -### Debug on rancher vm with a better network +## Libvirt Stack -```bash -rdctl shell -``` +> Define your vars and secrets in a `prod.tfvars` file before. Consult the file to see where to get/generate them. ```bash -wget https://releases.hashicorp.com/waypoint/0.11.0/waypoint_0.11.0_linux_arm64.zip -O /tmp/waypoint.zip -sudo unzip /tmp/waypoint.zip -d /usr/local/bin/ -rm /tmp/waypoint.zip -sudo chmod +x /usr/local/bin/waypoint -``` - -## Packer image +terraform -chdir=libvirt apply -auto-approve -> In folder `packer/` - -```bash -PACKER_LOG=0 PACKER_LOG_PATH=ubuntu-jammy.log packer build -var-file "$(uname -ms | tr " " "-")-host.hcl" -var-file=secrets.pkrvars.hcl ubuntu.pkr.hcl ``` -> use `PACKER_LOG=1` for debug and `-on-error=ask` - -**Simplified usage with makefile** : +## Contabo Stack ```bash -make ubuntu-debug +terraform -chdir=contabo apply -auto-approve ``` -> In debug mode you could need to do `ssh-keygen -f ~/.ssh/known_hosts -R [127.0.0.1]:2225` to delete old ssh trusted key for host - -or for release : - -```bash -make ubuntu -``` - -Release image manually : - -```bash -git tag "ubuntu-jammy-$(git rev-parse --short HEAD)" -git push --tags -``` +For contabo cli usage from your tfvar file : `make setup_cntb` -Open release from tag on [this link](https://github.com/loic-roux-404/k3s-paas/releases/new) -## Terraform +## Then apply k3s stack -> Define your vars and secrets in a `prod.tfvars` file before. Consult the file to see where to get/generate them. +> Adapt url to your stack between libvirt and contabo ```bash -terraform apply -auto-approve -var-file=prod.tfvars - +terraform apply -auto-approve -var k3s_host=k3s.test ``` -For contabo cli usage from your tfvar file : `make setup_cntb` - ## Secure ssh connections -Mac os : - -```bash -brew install tailscale -sudo brew services start tailscale -``` - -Then : `tailscale login` - ### Connect to instance : Setup with `make setup_ssh` diff --git a/docs/2-help.md b/docs/2-help.md index 2da39b9b..55bb67a5 100644 --- a/docs/2-help.md +++ b/docs/2-help.md @@ -12,7 +12,7 @@ Also you can use a global flush cache if it still doesn't work: - [for google dns](https://developers.google.com/speed/public-dns/cache?hl=fr) - [for cloudflare dns](https://1.1.1.1/purge-cache/) -> For real world testing, it's best to use different `dex_hostname` and `waypoint_hostname` entries that you don't use for one environment (staging or production). +> For real world testing, it's best to use different `dex_hostname` and `paas_hostname` entries that you don't use for one environment (staging or production). ### Kubernetes on Vscode @@ -55,4 +55,4 @@ And there you have access to an interface to control your cluster directly from - [coredns wildcard](https://mac-blog.org.ua/kubernetes-coredns-wildcard-ingress/) -Translated with www.DeepL.com/Translator (free version) \ No newline at end of file +Translated with www.DeepL.com/Translator (free version) diff --git a/docs/images/archi.jpg b/docs/images/archi.jpg index b7c9fb6e..0080362d 100644 Binary files a/docs/images/archi.jpg and b/docs/images/archi.jpg differ diff --git a/docs/images/archi.mdj b/docs/images/archi.mdj index 8c0dd004..73e0de8e 100644 --- a/docs/images/archi.mdj +++ b/docs/images/archi.mdj @@ -47,8 +47,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 1008, - "top": 896, + "left": 896, + "top": 880, "height": 13 }, { @@ -58,8 +58,8 @@ "$ref": "AAAAAAGFy5kz94CN24o=" }, "font": "Arial;13;1", - "left": 605, - "top": 609, + "left": 549, + "top": 601, "width": 101.15625, "height": 13, "text": "helm - k3s" @@ -72,8 +72,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 1008, - "top": 896, + "left": 896, + "top": 880, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -86,15 +86,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": 1008, - "top": 896, + "left": 896, + "top": 880, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 600, - "top": 602, + "left": 544, + "top": 594, "width": 111.15625, "height": 25, "stereotypeLabel": { @@ -121,8 +121,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 504, - "top": 448, + "left": 448, + "top": 440, "width": 10, "height": 10 }, @@ -137,8 +137,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 504, - "top": 448, + "left": 448, + "top": 440, "width": 10, "height": 10 }, @@ -153,8 +153,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 504, - "top": 448, + "left": 448, + "top": 440, "width": 10, "height": 10 }, @@ -169,16 +169,16 @@ }, "visible": false, "font": "Arial;13;0", - "left": 504, - "top": 448, + "left": 448, + "top": 440, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 600, - "top": 592, + "left": 544, + "top": 584, "width": 121.15625, "height": 45, "nameCompartment": { @@ -227,8 +227,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -119, - "top": -538, + "left": -87, + "top": -282, "height": 13 }, { @@ -238,11 +238,11 @@ "$ref": "AAAAAAGFy5mjE4D5gMQ=" }, "font": "Arial;13;1", - "left": 373, - "top": 497, + "left": 389, + "top": 625, "width": 60.68359375, "height": 13, - "text": "dex oidc" + "text": "Dex" }, { "_type": "LabelView", @@ -252,8 +252,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -119, - "top": -538, + "left": -87, + "top": -282, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -266,15 +266,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": -119, - "top": -538, + "left": -87, + "top": -282, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 368, - "top": 490, + "left": 384, + "top": 618, "width": 70.68359375, "height": 25, "stereotypeLabel": { @@ -301,8 +301,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -104, - "top": -360, + "left": -168, + "top": -216, "width": 10, "height": 10 }, @@ -317,8 +317,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -104, - "top": -360, + "left": -168, + "top": -216, "width": 10, "height": 10 }, @@ -333,8 +333,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -104, - "top": -360, + "left": -168, + "top": -216, "width": 10, "height": 10 }, @@ -349,16 +349,16 @@ }, "visible": false, "font": "Arial;13;0", - "left": -104, - "top": -360, + "left": -168, + "top": -216, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 368, - "top": 480, + "left": 384, + "top": 608, "width": 80.68359375, "height": 45, "nameCompartment": { @@ -422,7 +422,7 @@ "top": 489, "width": 60.68359375, "height": 13, - "text": "waypoint" + "text": "paas" }, { "_type": "LabelView", @@ -587,8 +587,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -528, - "top": -832, + "left": -384, + "top": -512, "height": 13 }, { @@ -598,8 +598,8 @@ "$ref": "AAAAAAGFy5nuQIJW6Lg=" }, "font": "Arial;13;1", - "left": 141, - "top": 497, + "left": 213, + "top": 657, "width": 39.72998046875, "height": 13, "text": "github" @@ -612,8 +612,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -528, - "top": -832, + "left": -384, + "top": -512, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -626,15 +626,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": -528, - "top": -832, + "left": -384, + "top": -512, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 136, - "top": 490, + "left": 208, + "top": 650, "width": 49.72998046875, "height": 25, "stereotypeLabel": { @@ -661,8 +661,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -264, - "top": -416, + "left": -192, + "top": -256, "width": 10, "height": 10 }, @@ -677,8 +677,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -264, - "top": -416, + "left": -192, + "top": -256, "width": 10, "height": 10 }, @@ -693,8 +693,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -264, - "top": -416, + "left": -192, + "top": -256, "width": 10, "height": 10 }, @@ -709,16 +709,16 @@ }, "visible": false, "font": "Arial;13;0", - "left": -264, - "top": -416, + "left": -192, + "top": -256, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 136, - "top": 480, + "left": 208, + "top": 640, "width": 59.72998046875, "height": 45, "nameCompartment": { @@ -1588,8 +1588,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 668, - "top": 489, + "left": 641, + "top": 481, "height": 13, "alpha": 1.5707963267948966, "distance": 15, @@ -1608,12 +1608,12 @@ "$ref": "AAAAAAGFy59025qAks4=" }, "font": "Arial;13;0", - "left": 627, - "top": 486, + "left": 631, + "top": 440, "width": 52.76806640625, "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, + "alpha": 0.39005232293448877, + "distance": 48.041648597857254, "hostEdge": { "$ref": "AAAAAAGFy59025qC4Os=" }, @@ -1631,7 +1631,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": 697, + "left": 668, "top": 494, "height": 13, "alpha": -1.5707963267948966, @@ -1650,7 +1650,7 @@ "$ref": "AAAAAAGFy5kz9oCMjsw=" }, "lineStyle": 1, - "points": "665:591;701:405", + "points": "614:583;696:405", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGFy59025qD+wg=" @@ -1683,8 +1683,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 608, - "top": 555, + "left": 578, + "top": 546, "height": 13, "alpha": 1.5707963267948966, "distance": 15, @@ -1703,8 +1703,8 @@ "$ref": "AAAAAAGFy5+Ae5uN0HU=" }, "font": "Arial;13;0", - "left": 619, - "top": 544, + "left": 591, + "top": 549, "width": 52.76806640625, "height": 13, "alpha": 4.313168256793966, @@ -1726,8 +1726,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 633, - "top": 540, + "left": 607, + "top": 541, "height": 13, "alpha": -1.5707963267948966, "distance": 15, @@ -1745,7 +1745,7 @@ "$ref": "AAAAAAGFy5kz9oCMjsw=" }, "lineStyle": 1, - "points": "645:591;598:517", + "points": "600:583;587:517", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGFy5+Ae5uQi6g=" @@ -1778,8 +1778,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 521, - "top": 562, + "left": 505, + "top": 627, "height": 13, "alpha": 1.5707963267948966, "distance": 15, @@ -1798,8 +1798,8 @@ "$ref": "AAAAAAGFy5+WQZ3Mk8c=" }, "font": "Arial;13;0", - "left": 527, - "top": 575, + "left": 513, + "top": 623, "width": 52.76806640625, "height": 13, "alpha": 2.734801388447954, @@ -1821,8 +1821,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 534, - "top": 535, + "left": 502, + "top": 598, "height": 13, "alpha": -1.5707963267948966, "distance": 15, @@ -1840,7 +1840,7 @@ "$ref": "AAAAAAGFy5kz9oCMjsw=" }, "lineStyle": 1, - "points": "608:591;449:520", + "points": "543:614;465:624", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGFy5+WQZ3PKlM=" @@ -1872,8 +1872,8 @@ "$ref": "AAAAAAGFy5+0v6Bu4iI=" }, "font": "Arial;13;0", - "left": 233, - "top": 481, + "left": 274, + "top": 626, "width": 97.2080078125, "height": 13, "alpha": 1.5707963267948966, @@ -1895,8 +1895,8 @@ }, "visible": null, "font": "Arial;13;0", - "left": 281, - "top": 466, + "left": 319, + "top": 611, "height": 13, "alpha": 1.5707963267948966, "distance": 30, @@ -1916,8 +1916,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 281, - "top": 511, + "left": 327, + "top": 655, "height": 13, "alpha": -1.5707963267948966, "distance": 15, @@ -1937,8 +1937,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 221, - "top": 481, + "left": 291, + "top": 631, "height": 13, "alpha": 0.5235987755982988, "distance": 30, @@ -1958,8 +1958,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 224, - "top": 467, + "left": 291, + "top": 618, "height": 13, "alpha": 0.7853981633974483, "distance": 40, @@ -1979,8 +1979,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 217, - "top": 508, + "left": 291, + "top": 659, "height": 13, "alpha": -0.5235987755982988, "distance": 25, @@ -2000,8 +2000,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 341, - "top": 481, + "left": 354, + "top": 620, "height": 13, "alpha": -0.5235987755982988, "distance": 30, @@ -2020,8 +2020,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 338, - "top": 467, + "left": 350, + "top": 607, "height": 13, "alpha": -0.7853981633974483, "distance": 40, @@ -2040,8 +2040,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 345, - "top": 508, + "left": 363, + "top": 647, "height": 13, "alpha": 0.5235987755982988, "distance": 25, @@ -2086,7 +2086,7 @@ "$ref": "AAAAAAGFy5nuQIJVdw0=" }, "lineStyle": 1, - "points": "196:502;367:502", + "points": "268:657;383:637", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGFy5+0v6Bz6LE=" @@ -2406,2515 +2406,701 @@ "$ref": "AAAAAAGFy6Rg9CNenLY=" } }, + { + "_type": "UMLFrameView", + "_id": "AAAAAAGFy6vKBlL0sPE=", + "_parent": { + "$ref": "AAAAAAGFy5jjfYCEQQw=" + }, + "model": { + "$ref": "AAAAAAGFy6uSD0T7Qx8=" + }, + "subViews": [ + { + "_type": "LabelView", + "_id": "AAAAAAGFy6vKBlL1ZGw=", + "_parent": { + "$ref": "AAAAAAGFy6vKBlL0sPE=" + }, + "font": "Arial;13;0", + "left": 331, + "top": 317, + "width": 41.47265625, + "height": 13, + "text": "PaaSA" + }, + { + "_type": "LabelView", + "_id": "AAAAAAGFy6vKBlL2qek=", + "_parent": { + "$ref": "AAAAAAGFy6vKBlL0sPE=" + }, + "font": "Arial;13;1", + "left": 325, + "top": 317, + "width": 1, + "height": 13 + } + ], + "font": "Arial;13;0", + "left": 320, + "top": 312, + "width": 561, + "height": 425, + "nameLabel": { + "$ref": "AAAAAAGFy6vKBlL1ZGw=" + }, + "frameTypeLabel": { + "$ref": "AAAAAAGFy6vKBlL2qek=" + } + }, { "_type": "UMLNodeView", - "_id": "AAAAAAGFy6csUHLcgDc=", + "_id": "AAAAAAGFy6ywAGV8HMM=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "subViews": [ { "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy6csUHLdVKs=", + "_id": "AAAAAAGFy6ywAGV9ujA=", "_parent": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "subViews": [ { "_type": "LabelView", - "_id": "AAAAAAGFy6csUHLe8xA=", + "_id": "AAAAAAGFy6ywAGV+cuI=", "_parent": { - "$ref": "AAAAAAGFy6csUHLdVKs=" + "$ref": "AAAAAAGFy6ywAGV9ujA=" }, "visible": false, "font": "Arial;13;0", - "left": -752, - "top": 64, + "left": -32, + "top": -64, "height": 13 }, { "_type": "LabelView", - "_id": "AAAAAAGFy6csUHLfbVs=", + "_id": "AAAAAAGFy6ywAGV/owk=", "_parent": { - "$ref": "AAAAAAGFy6csUHLdVKs=" + "$ref": "AAAAAAGFy6ywAGV9ujA=" }, "font": "Arial;13;1", - "left": 357, - "top": 601, - "width": 102.603515625, + "left": 589, + "top": 769, + "width": 82.3671875, "height": 13, - "text": "kubernetes - k3s" + "text": "Terraform" }, { "_type": "LabelView", - "_id": "AAAAAAGFy6csUHLgiIg=", + "_id": "AAAAAAGFy6ywAGWAlVs=", "_parent": { - "$ref": "AAAAAAGFy6csUHLdVKs=" + "$ref": "AAAAAAGFy6ywAGV9ujA=" }, "visible": false, "font": "Arial;13;0", - "left": -752, - "top": 64, + "left": -32, + "top": -64, "width": 73.67724609375, "height": 13, "text": "(from Model)" }, { "_type": "LabelView", - "_id": "AAAAAAGFy6csUHLhZ3s=", + "_id": "AAAAAAGFy6ywAGWB3A4=", "_parent": { - "$ref": "AAAAAAGFy6csUHLdVKs=" + "$ref": "AAAAAAGFy6ywAGV9ujA=" }, "visible": false, "font": "Arial;13;0", - "left": -752, - "top": 64, + "left": -32, + "top": -64, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 352, - "top": 594, - "width": 112.603515625, + "left": 584, + "top": 762, + "width": 92.3671875, "height": 25, "stereotypeLabel": { - "$ref": "AAAAAAGFy6csUHLe8xA=" + "$ref": "AAAAAAGFy6ywAGV+cuI=" }, "nameLabel": { - "$ref": "AAAAAAGFy6csUHLfbVs=" + "$ref": "AAAAAAGFy6ywAGV/owk=" }, "namespaceLabel": { - "$ref": "AAAAAAGFy6csUHLgiIg=" + "$ref": "AAAAAAGFy6ywAGWAlVs=" }, "propertyLabel": { - "$ref": "AAAAAAGFy6csUHLhZ3s=" + "$ref": "AAAAAAGFy6ywAGWB3A4=" } }, { "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy6csUHLiu9U=", + "_id": "AAAAAAGFy6ywAGWCCHA=", "_parent": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "visible": false, "font": "Arial;13;0", - "left": -376, - "top": 32, + "left": -16, + "top": -32, "width": 10, "height": 10 }, { "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy6csUHLjIk8=", + "_id": "AAAAAAGFy6ywAGWDdDY=", "_parent": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "visible": false, "font": "Arial;13;0", - "left": -376, - "top": 32, + "left": -16, + "top": -32, "width": 10, "height": 10 }, { "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy6csUHLkbks=", + "_id": "AAAAAAGFy6ywAGWE8VU=", "_parent": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "visible": false, "font": "Arial;13;0", - "left": -376, - "top": 32, + "left": -16, + "top": -32, "width": 10, "height": 10 }, { "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy6csUHLliv8=", + "_id": "AAAAAAGFy6ywAGWFdqc=", "_parent": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "model": { - "$ref": "AAAAAAGFy6csT3Lab+c=" + "$ref": "AAAAAAGFy6ywAGV6azA=" }, "visible": false, "font": "Arial;13;0", - "left": -376, - "top": 32, + "left": -16, + "top": -32, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 352, - "top": 584, - "width": 122.603515625, + "left": 584, + "top": 752, + "width": 102.3671875, "height": 45, "nameCompartment": { - "$ref": "AAAAAAGFy6csUHLdVKs=" + "$ref": "AAAAAAGFy6ywAGV9ujA=" }, "suppressAttributes": true, "suppressOperations": true, "attributeCompartment": { - "$ref": "AAAAAAGFy6csUHLiu9U=" + "$ref": "AAAAAAGFy6ywAGWCCHA=" }, "operationCompartment": { - "$ref": "AAAAAAGFy6csUHLjIk8=" + "$ref": "AAAAAAGFy6ywAGWDdDY=" }, "receptionCompartment": { - "$ref": "AAAAAAGFy6csUHLkbks=" + "$ref": "AAAAAAGFy6ywAGWE8VU=" }, "templateParameterCompartment": { - "$ref": "AAAAAAGFy6csUHLliv8=" + "$ref": "AAAAAAGFy6ywAGWFdqc=" } }, { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGFy6eIB5HsRGI=", + "_type": "UMLNodeView", + "_id": "AAAAAAGFy61rGZucTjI=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHoc2Y=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, "subViews": [ { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5HtmQg=", + "_type": "UMLNameCompartmentView", + "_id": "AAAAAAGFy61rGZudcE4=", "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "$ref": "AAAAAAGFy61rGZucTjI=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHoc2Y=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, + "subViews": [ + { + "_type": "LabelView", + "_id": "AAAAAAGFy61rGZueEv4=", + "_parent": { + "$ref": "AAAAAAGFy61rGZudcE4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -432, + "top": -432, + "height": 13 + }, + { + "_type": "LabelView", + "_id": "AAAAAAGFy61rGZufhb4=", + "_parent": { + "$ref": "AAAAAAGFy61rGZudcE4=" + }, + "font": "Arial;13;1", + "left": 325, + "top": 713, + "width": 106.9072265625, + "height": 13, + "text": "contabo instance" + }, + { + "_type": "LabelView", + "_id": "AAAAAAGFy61rGZugSX8=", + "_parent": { + "$ref": "AAAAAAGFy61rGZudcE4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -432, + "top": -432, + "width": 73.67724609375, + "height": 13, + "text": "(from Model)" + }, + { + "_type": "LabelView", + "_id": "AAAAAAGFy61rGZuhkns=", + "_parent": { + "$ref": "AAAAAAGFy61rGZudcE4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -432, + "top": -432, + "height": 13, + "horizontalAlignment": 1 + } + ], "font": "Arial;13;0", - "left": 479, - "top": 618, - "width": 115.2607421875, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "left": 320, + "top": 706, + "width": 116.9072265625, + "height": 25, + "stereotypeLabel": { + "$ref": "AAAAAAGFy61rGZueEv4=" }, - "edgePosition": 1, - "text": "+manage" + "nameLabel": { + "$ref": "AAAAAAGFy61rGZufhb4=" + }, + "namespaceLabel": { + "$ref": "AAAAAAGFy61rGZugSX8=" + }, + "propertyLabel": { + "$ref": "AAAAAAGFy61rGZuhkns=" + } }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5Hu4T0=", + "_type": "UMLAttributeCompartmentView", + "_id": "AAAAAAGFy61rGZuic7I=", "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "$ref": "AAAAAAGFy61rGZucTjI=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHoc2Y=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, - "visible": null, + "visible": false, "font": "Arial;13;0", - "left": 536, - "top": 633, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "edgePosition": 1 + "left": -216, + "top": -216, + "width": 10, + "height": 10 }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5Hv+jc=", + "_type": "UMLOperationCompartmentView", + "_id": "AAAAAAGFy61rGZuj/MU=", "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "$ref": "AAAAAAGFy61rGZucTjI=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHoc2Y=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, "visible": false, "font": "Arial;13;0", - "left": 537, - "top": 589, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "edgePosition": 1 + "left": -216, + "top": -216, + "width": 10, + "height": 10 }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5HwF1E=", + "_type": "UMLReceptionCompartmentView", + "_id": "AAAAAAGFy61rGZuk4Ik=", "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "$ref": "AAAAAAGFy61rGZucTjI=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHpp4s=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, "visible": false, "font": "Arial;13;0", - "left": 572, - "top": 620, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "edgePosition": 2 + "left": -216, + "top": -216, + "width": 10, + "height": 10 }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5HxR8I=", + "_type": "UMLTemplateParameterCompartmentView", + "_id": "AAAAAAGFy61rGZultvg=", "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" + "$ref": "AAAAAAGFy61rGZucTjI=" }, "model": { - "$ref": "AAAAAAGFy6eIBpHpp4s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 569, - "top": 633, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5HyLHI=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHpp4s=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 577, - "top": 592, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5HzjS0=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHq6l8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 500, - "top": 617, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5H0yqA=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHq6l8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 502, - "top": 631, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy6eIB5H1pjU=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHq6l8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 497, - "top": 590, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFy6eIB5H21Lg=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHpp4s=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFy6eIB5H3Yls=", - "_parent": { - "$ref": "AAAAAAGFy6eIB5HsRGI=" - }, - "model": { - "$ref": "AAAAAAGFy6eIBpHq6l8=" + "$ref": "AAAAAAGFy61rGZuaTAE=" }, "visible": false, "font": "Arial;13;0", + "left": -216, + "top": -216, "width": 10, "height": 10 } ], "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy6csUHLcgDc=" - }, - "tail": { - "$ref": "AAAAAAGFy5kz9oCMjsw=" - }, - "lineStyle": 1, - "points": "599:612;475:608", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFy6eIB5HtmQg=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFy6eIB5Hu4T0=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy6eIB5Hv+jc=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGFy6eIB5HwF1E=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGFy6eIB5HxR8I=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGFy6eIB5HyLHI=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGFy6eIB5HzjS0=" + "containerChangeable": true, + "left": 320, + "top": 696, + "width": 126.9072265625, + "height": 45, + "nameCompartment": { + "$ref": "AAAAAAGFy61rGZudcE4=" }, - "headPropertyLabel": { - "$ref": "AAAAAAGFy6eIB5H0yqA=" + "suppressAttributes": true, + "suppressOperations": true, + "attributeCompartment": { + "$ref": "AAAAAAGFy61rGZuic7I=" }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGFy6eIB5H1pjU=" + "operationCompartment": { + "$ref": "AAAAAAGFy61rGZuj/MU=" }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGFy6eIB5H21Lg=" + "receptionCompartment": { + "$ref": "AAAAAAGFy61rGZuk4Ik=" }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGFy6eIB5H3Yls=" + "templateParameterCompartment": { + "$ref": "AAAAAAGFy61rGZultvg=" } }, { - "_type": "UMLFrameView", - "_id": "AAAAAAGFy6vKBlL0sPE=", + "_type": "UMLTextView", + "_id": "AAAAAAGFy6/3P0UVpW0=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, - "model": { - "$ref": "AAAAAAGFy6uSD0T7Qx8=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGFy6vKBlL1ZGw=", - "_parent": { - "$ref": "AAAAAAGFy6vKBlL0sPE=" - }, - "font": "Arial;13;0", - "left": 331, - "top": 317, - "width": 41.47265625, - "height": 13, - "text": "PaaSA" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy6vKBlL2qek=", - "_parent": { - "$ref": "AAAAAAGFy6vKBlL0sPE=" - }, - "font": "Arial;13;1", - "left": 325, - "top": 317, - "width": 1, - "height": 13 - } - ], "font": "Arial;13;0", - "left": 320, - "top": 312, - "width": 561, - "height": 425, - "nameLabel": { - "$ref": "AAAAAAGFy6vKBlL1ZGw=" - }, - "frameTypeLabel": { - "$ref": "AAAAAAGFy6vKBlL2qek=" - } + "left": 328, + "top": 344, + "width": 161, + "height": 55, + "text": "Nix Os\nSSH\nTailscale Server" }, { "_type": "UMLNodeView", - "_id": "AAAAAAGFy6ywAGV8HMM=", + "_id": "AAAAAAGFy7UbEZeKLiU=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "subViews": [ { "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy6ywAGV9ujA=", + "_id": "AAAAAAGFy7UbEZeLwUc=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "subViews": [ { "_type": "LabelView", - "_id": "AAAAAAGFy6ywAGV+cuI=", + "_id": "AAAAAAGFy7UbEpeMMto=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV9ujA=" + "$ref": "AAAAAAGFy7UbEZeLwUc=" }, "visible": false, "font": "Arial;13;0", - "left": 64, - "top": -240, + "left": 384, + "top": 144, "height": 13 }, { "_type": "LabelView", - "_id": "AAAAAAGFy6ywAGV/owk=", + "_id": "AAAAAAGFy7UbEpeNJwM=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV9ujA=" + "$ref": "AAAAAAGFy7UbEZeLwUc=" }, "font": "Arial;13;1", - "left": 637, - "top": 681, - "width": 82.3671875, + "left": 1021, + "top": 897, + "width": 41.919921875, "height": 13, - "text": "Ansible" + "text": "nix" }, { "_type": "LabelView", - "_id": "AAAAAAGFy6ywAGWAlVs=", + "_id": "AAAAAAGFy7UbEpeO6gI=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV9ujA=" + "$ref": "AAAAAAGFy7UbEZeLwUc=" }, "visible": false, "font": "Arial;13;0", - "left": 64, - "top": -240, + "left": 384, + "top": 144, "width": 73.67724609375, "height": 13, "text": "(from Model)" }, { "_type": "LabelView", - "_id": "AAAAAAGFy6ywAGWB3A4=", + "_id": "AAAAAAGFy7UbEpePx1Y=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV9ujA=" + "$ref": "AAAAAAGFy7UbEZeLwUc=" }, "visible": false, "font": "Arial;13;0", - "left": 64, - "top": -240, + "left": 384, + "top": 144, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 632, - "top": 674, - "width": 92.3671875, + "left": 1016, + "top": 890, + "width": 51.919921875, "height": 25, "stereotypeLabel": { - "$ref": "AAAAAAGFy6ywAGV+cuI=" + "$ref": "AAAAAAGFy7UbEpeMMto=" }, "nameLabel": { - "$ref": "AAAAAAGFy6ywAGV/owk=" + "$ref": "AAAAAAGFy7UbEpeNJwM=" }, "namespaceLabel": { - "$ref": "AAAAAAGFy6ywAGWAlVs=" + "$ref": "AAAAAAGFy7UbEpeO6gI=" }, "propertyLabel": { - "$ref": "AAAAAAGFy6ywAGWB3A4=" + "$ref": "AAAAAAGFy7UbEpePx1Y=" } }, { "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy6ywAGWCCHA=", + "_id": "AAAAAAGFy7UbEpeQs5M=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "visible": false, "font": "Arial;13;0", - "left": 32, - "top": -120, + "left": 192, + "top": 72, "width": 10, "height": 10 }, { "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy6ywAGWDdDY=", + "_id": "AAAAAAGFy7UbEpeRqCw=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "visible": false, "font": "Arial;13;0", - "left": 32, - "top": -120, + "left": 192, + "top": 72, "width": 10, "height": 10 }, { "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy6ywAGWE8VU=", + "_id": "AAAAAAGFy7UbEpeS6/c=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "visible": false, "font": "Arial;13;0", - "left": 32, - "top": -120, + "left": 192, + "top": 72, "width": 10, "height": 10 }, { "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy6ywAGWFdqc=", + "_id": "AAAAAAGFy7UbEpeT2U0=", "_parent": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "model": { - "$ref": "AAAAAAGFy6ywAGV6azA=" + "$ref": "AAAAAAGFy7UbEZeIvD0=" }, "visible": false, "font": "Arial;13;0", - "left": 32, - "top": -120, + "left": 192, + "top": 72, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 632, - "top": 664, - "width": 102.3671875, + "left": 1016, + "top": 880, + "width": 61.919921875, "height": 45, "nameCompartment": { - "$ref": "AAAAAAGFy6ywAGV9ujA=" + "$ref": "AAAAAAGFy7UbEZeLwUc=" }, "suppressAttributes": true, "suppressOperations": true, "attributeCompartment": { - "$ref": "AAAAAAGFy6ywAGWCCHA=" + "$ref": "AAAAAAGFy7UbEpeQs5M=" }, "operationCompartment": { - "$ref": "AAAAAAGFy6ywAGWDdDY=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGFy6ywAGWE8VU=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGFy6ywAGWFdqc=" - } - }, - { - "_type": "UMLNodeView", - "_id": "AAAAAAGFy6zKKmkZQQU=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy6zKKmkaP9c=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGFy6zKKmkbvyA=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkaP9c=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 144, - "top": -496, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy6zKKmkc9bQ=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkaP9c=" - }, - "font": "Arial;13;1", - "left": 581, - "top": 753, - "width": 157, - "height": 13, - "text": "Instance" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy6zKKmkdfDg=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkaP9c=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 144, - "top": -496, - "width": 73.67724609375, - "height": 13, - "text": "(from Model)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy6zKKmkedJg=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkaP9c=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 144, - "top": -496, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 576, - "top": 746, - "width": 167, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAGFy6zKKmkbvyA=" - }, - "nameLabel": { - "$ref": "AAAAAAGFy6zKKmkc9bQ=" - }, - "namespaceLabel": { - "$ref": "AAAAAAGFy6zKKmkdfDg=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy6zKKmkedJg=" - } - }, - { - "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy6zKKmkfHuE=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 72, - "top": -248, - "width": 10, - "height": 10 - }, - { - "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy6zKKmkg4kQ=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 72, - "top": -248, - "width": 10, - "height": 10 - }, - { - "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy6zKKmkhWiI=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 72, - "top": -248, - "width": 10, - "height": 10 - }, - { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy6zKKmkiWG8=", - "_parent": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "model": { - "$ref": "AAAAAAGFy6zKKmkXaSM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 72, - "top": -248, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 576, - "top": 736, - "width": 177, - "height": 81, - "nameCompartment": { - "$ref": "AAAAAAGFy6zKKmkaP9c=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGFy6zKKmkfHuE=" - }, - "operationCompartment": { - "$ref": "AAAAAAGFy6zKKmkg4kQ=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGFy6zKKmkhWiI=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGFy6zKKmkiWG8=" - } - }, - { - "_type": "UMLNodeView", - "_id": "AAAAAAGFy61rGZucTjI=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy61rGZudcE4=", - "_parent": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGFy61rGZueEv4=", - "_parent": { - "$ref": "AAAAAAGFy61rGZudcE4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -112, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy61rGZufhb4=", - "_parent": { - "$ref": "AAAAAAGFy61rGZudcE4=" - }, - "font": "Arial;13;1", - "left": 637, - "top": 873, - "width": 50.552734375, - "height": 13, - "text": "contabo" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy61rGZugSX8=", - "_parent": { - "$ref": "AAAAAAGFy61rGZudcE4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -112, - "width": 73.67724609375, - "height": 13, - "text": "(from Model)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy61rGZuhkns=", - "_parent": { - "$ref": "AAAAAAGFy61rGZudcE4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -112, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 632, - "top": 866, - "width": 60.552734375, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAGFy61rGZueEv4=" - }, - "nameLabel": { - "$ref": "AAAAAAGFy61rGZufhb4=" - }, - "namespaceLabel": { - "$ref": "AAAAAAGFy61rGZugSX8=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy61rGZuhkns=" - } - }, - { - "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy61rGZuic7I=", - "_parent": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 96, - "top": -56, - "width": 10, - "height": 10 - }, - { - "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy61rGZuj/MU=", - "_parent": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 96, - "top": -56, - "width": 10, - "height": 10 - }, - { - "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy61rGZuk4Ik=", - "_parent": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 96, - "top": -56, - "width": 10, - "height": 10 - }, - { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy61rGZultvg=", - "_parent": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "model": { - "$ref": "AAAAAAGFy61rGZuaTAE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 96, - "top": -56, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 632, - "top": 856, - "width": 70.552734375, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGFy61rGZudcE4=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGFy61rGZuic7I=" - }, - "operationCompartment": { - "$ref": "AAAAAAGFy61rGZuj/MU=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGFy61rGZuk4Ik=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGFy61rGZultvg=" - } - }, - { - "_type": "UMLDeploymentView", - "_id": "AAAAAAGFy65xLwGy6Is=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy65xLwGwm1Y=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy65xLwGzCQQ=", - "_parent": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "model": { - "$ref": "AAAAAAGFy65xLwGwm1Y=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 659, - "top": 712, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy65xLwG0ZXI=", - "_parent": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "model": { - "$ref": "AAAAAAGFy65xLwGwm1Y=" - }, - "font": "Arial;13;0", - "left": 618, - "top": 709, - "width": 52.76806640625, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "edgePosition": 1, - "text": "«deploy»" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy65xLwG1VWA=", - "_parent": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "model": { - "$ref": "AAAAAAGFy65xLwGwm1Y=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 688, - "top": 719, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy65xLwGy6Is=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" - }, - "tail": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "lineStyle": 1, - "points": "672:735;677:709", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFy65xLwGzCQQ=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFy65xLwG0ZXI=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy65xLwG1VWA=" - } - }, - { - "_type": "UMLDeploymentView", - "_id": "AAAAAAGFy67dHhhZobk=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy67dHhhXkgQ=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy67dHhha4Ig=", - "_parent": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "model": { - "$ref": "AAAAAAGFy67dHhhXkgQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 650, - "top": 829, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy67dHhhbOno=", - "_parent": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "model": { - "$ref": "AAAAAAGFy67dHhhXkgQ=" - }, - "font": "Arial;13;0", - "left": 609, - "top": 829, - "width": 52.76806640625, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "edgePosition": 1, - "text": "«deploy»" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy67dHhhcVMQ=", - "_parent": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "model": { - "$ref": "AAAAAAGFy67dHhhXkgQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 679, - "top": 830, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy67dHhhZobk=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" - }, - "tail": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "lineStyle": 1, - "points": "666:855;665:817", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFy67dHhha4Ig=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFy67dHhhbOno=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy67dHhhcVMQ=" - } - }, - { - "_type": "UMLTextView", - "_id": "AAAAAAGFy6/3P0UVpW0=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "font": "Arial;13;0", - "left": 592, - "top": 771, - "width": 161, - "height": 40, - "text": "cloud-init\nUbuntu 22.04 jammy" - }, - { - "_type": "UMLNodeView", - "_id": "AAAAAAGFy7UbEZeKLiU=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy7UbEZeLwUc=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGFy7UbEpeMMto=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeLwUc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 384, - "top": -160, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy7UbEpeNJwM=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeLwUc=" - }, - "font": "Arial;13;1", - "left": 1021, - "top": 745, - "width": 41.919921875, - "height": 13, - "text": "packer" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy7UbEpeO6gI=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeLwUc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 384, - "top": -160, - "width": 73.67724609375, - "height": 13, - "text": "(from Model)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy7UbEpePx1Y=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeLwUc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 384, - "top": -160, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 1016, - "top": 738, - "width": 51.919921875, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAGFy7UbEpeMMto=" - }, - "nameLabel": { - "$ref": "AAAAAAGFy7UbEpeNJwM=" - }, - "namespaceLabel": { - "$ref": "AAAAAAGFy7UbEpeO6gI=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy7UbEpePx1Y=" - } - }, - { - "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy7UbEpeQs5M=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -80, - "width": 10, - "height": 10 - }, - { - "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy7UbEpeRqCw=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -80, - "width": 10, - "height": 10 - }, - { - "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy7UbEpeS6/c=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -80, - "width": 10, - "height": 10 - }, - { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy7UbEpeT2U0=", - "_parent": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "model": { - "$ref": "AAAAAAGFy7UbEZeIvD0=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 192, - "top": -80, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 1016, - "top": 728, - "width": 61.919921875, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGFy7UbEZeLwUc=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGFy7UbEpeQs5M=" - }, - "operationCompartment": { - "$ref": "AAAAAAGFy7UbEpeRqCw=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGFy7UbEpeS6/c=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGFy7UbEpeT2U0=" - } - }, - { - "_type": "UMLDeploymentView", - "_id": "AAAAAAGFy7WAbbqodLY=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy7WAbbqmOdM=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7WAbbqpj28=", - "_parent": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "model": { - "$ref": "AAAAAAGFy7WAbbqmOdM=" - }, - "font": "Arial;13;0", - "left": 1008, - "top": 800, - "width": 99.37890625, - "height": 13, - "alpha": 3.833005744716315, - "distance": 42.95346318982906, - "hostEdge": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "edgePosition": 1, - "text": "+build image and" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7WAbbqq95Q=", - "_parent": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "model": { - "$ref": "AAAAAAGFy7WAbbqmOdM=" - }, - "font": "Arial;13;0", - "left": 1047, - "top": 824, - "width": 52.76806640625, - "height": 13, - "alpha": 3.0973258420898966, - "distance": 30.805843601498726, - "hostEdge": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "edgePosition": 1, - "text": "«deploy»" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7WAbbqr3kA=", - "_parent": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "model": { - "$ref": "AAAAAAGFy7WAbbqmOdM=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 1038, - "top": 830, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy7WAbbqodLY=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGHRz/XjqO1Kd8=" - }, - "tail": { - "$ref": "AAAAAAGFy7UbEZeKLiU=" - }, - "lineStyle": 1, - "points": "1046:773;1048:848;1029:863", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFy7WAbbqpj28=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFy7WAbbqq95Q=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy7WAbbqr3kA=" - } - }, - { - "_type": "UMLNodeView", - "_id": "AAAAAAGFy71ZRLAsbD4=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "subViews": [ - { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGFy71ZRLAtYAc=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGFy71ZRLAuVdA=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAtYAc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -16, - "top": -16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy71ZRLAvHCU=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAtYAc=" - }, - "font": "Arial;13;1", - "left": 629, - "top": 969, - "width": 60.4423828125, - "height": 13, - "text": "Terraform" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy71ZRLAwj5E=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAtYAc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -16, - "top": -16, - "width": 73.67724609375, - "height": 13, - "text": "(from Model)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGFy71ZRLAxBOg=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAtYAc=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -16, - "top": -16, - "height": 13, - "horizontalAlignment": 1 - } - ], - "font": "Arial;13;0", - "left": 624, - "top": 962, - "width": 70.4423828125, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAGFy71ZRLAuVdA=" - }, - "nameLabel": { - "$ref": "AAAAAAGFy71ZRLAvHCU=" - }, - "namespaceLabel": { - "$ref": "AAAAAAGFy71ZRLAwj5E=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy71ZRLAxBOg=" - } - }, - { - "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGFy71ZRLAy5zI=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -8, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGFy71ZRLAzCHk=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -8, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGFy71ZRLA0ft8=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -8, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGFy71ZRLA1fDE=", - "_parent": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "model": { - "$ref": "AAAAAAGFy71ZQ7AqT90=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -8, - "top": -8, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 624, - "top": 952, - "width": 80.4423828125, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGFy71ZRLAtYAc=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGFy71ZRLAy5zI=" - }, - "operationCompartment": { - "$ref": "AAAAAAGFy71ZRLAzCHk=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGFy71ZRLA0ft8=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGFy71ZRLA1fDE=" - } - }, - { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGFy7305tMtzdA=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMpVyE=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMuQ2o=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMpVyE=" - }, - "font": "Arial;13;0", - "left": 605, - "top": 917, - "width": 54.57080078125, - "height": 13, - "alpha": 1.530818039032635, - "distance": 32, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 1, - "text": "+manage" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMvjlM=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMpVyE=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 634, - "top": 918, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMwxdY=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMpVyE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 678, - "top": 920, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMx1xw=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMqj3A=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 649, - "top": 918, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMyYjw=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMqj3A=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 636, - "top": 916, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tMzXlY=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMqj3A=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 676, - "top": 923, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tM0OnY=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMrpuE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 649, - "top": 920, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy7305tM1qwg=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMrpuE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 636, - "top": 922, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFy73059M2/pM=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMrpuE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 677, - "top": 916, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFy7305tMtzdA=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFy73059M3Hm0=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMqj3A=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFy73059M4n1g=", - "_parent": { - "$ref": "AAAAAAGFy7305tMtzdA=" - }, - "model": { - "$ref": "AAAAAAGFy7305tMrpuE=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "tail": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "lineStyle": 1, - "points": "664:951;665:901", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFy7305tMuQ2o=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFy7305tMvjlM=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFy7305tMwxdY=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGFy7305tMx1xw=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGFy7305tMyYjw=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGFy7305tMzXlY=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGFy7305tM0OnY=" - }, - "headPropertyLabel": { - "$ref": "AAAAAAGFy7305tM1qwg=" - }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGFy73059M2/pM=" - }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGFy73059M3Hm0=" - }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGFy73059M4n1g=" - } - }, - { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGFzhLFCkP/WrM=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP7hrs=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQA8jQ=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP7hrs=" - }, - "font": "Arial;13;0", - "left": 327, - "top": 545, - "width": 88.54345703125, - "height": 13, - "alpha": -1.6922362347142992, - "distance": 38.05259518088089, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 1, - "text": "+authentication" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQBaac=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP7hrs=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 438, - "top": 545, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQCmk0=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP7hrs=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 394, - "top": 549, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQDVAQ=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP8RlE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 424, - "top": 544, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQEKOo=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP8RlE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 437, - "top": 545, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQF3XY=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP8RlE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 396, - "top": 541, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQG+0w=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP9lKk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 424, - "top": 550, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQH+Go=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP9lKk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 437, - "top": 547, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhLFCkQI7AI=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP9lKk=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 397, - "top": 556, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFzhLFCkQJATY=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP8RlE=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFzhLFCkQKoHg=", - "_parent": { - "$ref": "AAAAAAGFzhLFCkP/WrM=" - }, - "model": { - "$ref": "AAAAAAGFzhLFCUP9lKk=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy6csUHLcgDc=" - }, - "tail": { - "$ref": "AAAAAAGFy5mjE4D4dMc=" - }, - "lineStyle": 1, - "points": "408:525;411:583", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGFzhLFCkQA8jQ=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGFzhLFCkQBaac=" - }, - "propertyLabel": { - "$ref": "AAAAAAGFzhLFCkQCmk0=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGFzhLFCkQDVAQ=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGFzhLFCkQEKOo=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGFzhLFCkQF3XY=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGFzhLFCkQG+0w=" - }, - "headPropertyLabel": { - "$ref": "AAAAAAGFzhLFCkQH+Go=" - }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGFzhLFCkQI7AI=" + "$ref": "AAAAAAGFy7UbEpeRqCw=" }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGFzhLFCkQJATY=" + "receptionCompartment": { + "$ref": "AAAAAAGFy7UbEpeS6/c=" }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGFzhLFCkQKoHg=" + "templateParameterCompartment": { + "$ref": "AAAAAAGFy7UbEpeT2U0=" } }, { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGFzhMBMVLLYUI=", + "_type": "UMLDeploymentView", + "_id": "AAAAAAGFy7WAbbqodLY=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGFzhMBMFLHFPE=" + "$ref": "AAAAAAGFy7WAbbqmOdM=" }, "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLMjPQ=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLHFPE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 505, - "top": 556, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLNdAw=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLHFPE=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 513, - "top": 569, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLOJKg=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLHFPE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 488, - "top": 531, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLPsgM=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLI7IY=" - }, - "font": "Arial;13;0", - "left": 511, - "top": 536, - "width": 54.57080078125, - "height": 13, - "alpha": 0.6114827887618697, - "distance": 26.92582403567252, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "edgePosition": 2, - "text": "+manage" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLQIg8=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLI7IY=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 539, - "top": 550, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLRXbU=", + "_id": "AAAAAAGFy7WAbbqpj28=", "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" + "$ref": "AAAAAAGFy7WAbbqodLY=" }, "model": { - "$ref": "AAAAAAGFzhMBMFLI7IY=" + "$ref": "AAAAAAGFy7WAbbqmOdM=" }, - "visible": false, "font": "Arial;13;0", - "left": 523, - "top": 512, + "left": 945, + "top": 868, + "width": 99.37890625, "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, + "alpha": 3.833005744716315, + "distance": 42.95346318982906, "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" + "$ref": "AAAAAAGFy7WAbbqodLY=" }, - "edgePosition": 2 + "edgePosition": 1, + "text": "+build image and" }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLSEzs=", + "_id": "AAAAAAGFy7WAbbqq95Q=", "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" + "$ref": "AAAAAAGFy7WAbbqodLY=" }, "model": { - "$ref": "AAAAAAGFzhMBMFLJuQA=" + "$ref": "AAAAAAGFy7WAbbqmOdM=" }, - "visible": false, "font": "Arial;13;0", - "left": 476, - "top": 575, + "left": 965, + "top": 897, + "width": 52.76806640625, "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, + "alpha": 3.0973258420898966, + "distance": 30.805843601498726, "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLTVyY=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLJuQA=" + "$ref": "AAAAAAGFy7WAbbqodLY=" }, - "visible": false, - "font": "Arial;13;0", - "left": 486, - "top": 585, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - } + "edgePosition": 1, + "text": "«deploy»" }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGFzhMBMVLUeu4=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLJuQA=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 458, - "top": 554, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFzhMBMVLVpkI=", - "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" - }, - "model": { - "$ref": "AAAAAAGFzhMBMFLI7IY=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGFzhMBMVLWN2c=", + "_id": "AAAAAAGFy7WAbbqr3kA=", "_parent": { - "$ref": "AAAAAAGFzhMBMVLLYUI=" + "$ref": "AAAAAAGFy7WAbbqodLY=" }, "model": { - "$ref": "AAAAAAGFzhMBMFLJuQA=" + "$ref": "AAAAAAGFy7WAbbqmOdM=" }, "visible": false, "font": "Arial;13;0", - "width": 10, - "height": 10 + "left": 961, + "top": 881, + "height": 13, + "alpha": -1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGFy7WAbbqodLY=" + }, + "edgePosition": 1 } ], "font": "Arial;13;0", "head": { - "$ref": "AAAAAAGFy6csUHLcgDc=" + "$ref": "AAAAAAGHRz/XjqO1Kd8=" }, "tail": { - "$ref": "AAAAAAGFy5naJYIq7/Q=" + "$ref": "AAAAAAGFy7UbEZeKLiU=" }, "lineStyle": 1, - "points": "548:517;447:583", + "points": "1015:902;907:902", "showVisibility": true, "nameLabel": { - "$ref": "AAAAAAGFzhMBMVLMjPQ=" + "$ref": "AAAAAAGFy7WAbbqpj28=" }, "stereotypeLabel": { - "$ref": "AAAAAAGFzhMBMVLNdAw=" + "$ref": "AAAAAAGFy7WAbbqq95Q=" }, "propertyLabel": { - "$ref": "AAAAAAGFzhMBMVLOJKg=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGFzhMBMVLPsgM=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGFzhMBMVLQIg8=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGFzhMBMVLRXbU=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGFzhMBMVLSEzs=" - }, - "headPropertyLabel": { - "$ref": "AAAAAAGFzhMBMVLTVyY=" - }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGFzhMBMVLUeu4=" - }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGFzhMBMVLVpkI=" - }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGFzhMBMVLWN2c=" + "$ref": "AAAAAAGFy7WAbbqr3kA=" } }, { @@ -5125,8 +3311,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -80, - "top": -80, + "left": -432, + "top": -32, "height": 13 }, { @@ -5136,8 +3322,8 @@ "$ref": "AAAAAAGHQZBoVP5Okf4=" }, "font": "Arial;13;1", - "left": 741, - "top": 873, + "left": 565, + "top": 897, "width": 117.0380859375, "height": 13, "text": "contabo images s3" @@ -5150,8 +3336,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -80, - "top": -80, + "left": -432, + "top": -32, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -5164,15 +3350,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": -80, - "top": -80, + "left": -432, + "top": -32, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 736, - "top": 866, + "left": 560, + "top": 890, "width": 127.0380859375, "height": 25, "stereotypeLabel": { @@ -5199,8 +3385,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -40, - "top": -40, + "left": -216, + "top": -16, "width": 10, "height": 10 }, @@ -5215,8 +3401,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -40, - "top": -40, + "left": -216, + "top": -16, "width": 10, "height": 10 }, @@ -5231,8 +3417,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -40, - "top": -40, + "left": -216, + "top": -16, "width": 10, "height": 10 }, @@ -5245,306 +3431,36 @@ "model": { "$ref": "AAAAAAGHQZBoU/5LdFQ=" }, - "visible": false, - "font": "Arial;13;0", - "left": -40, - "top": -40, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 736, - "top": 856, - "width": 137.0380859375, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGHQZBoVP5Okf4=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGHQZBoVP5THRY=" - }, - "operationCompartment": { - "$ref": "AAAAAAGHQZBoVP5UTKY=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGHQZBoVP5Vsxs=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGHQZBoVP5WV5c=" - } - }, - { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGHQZDECjNk4nw=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNgFkI=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNlk7M=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNgFkI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 719, - "top": 857, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNmH8A=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNgFkI=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 719, - "top": 842, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNnKe4=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNgFkI=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 719, - "top": 887, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNodU8=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNhHm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 728, - "top": 857, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNpHSA=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNhHm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 731, - "top": 843, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNqWqo=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNhHm8=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 724, - "top": 884, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNr+t8=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNiQTQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 709, - "top": 857, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNscC8=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNiQTQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 706, - "top": 843, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHQZDECjNt+DA=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNiQTQ=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 713, - "top": 884, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHQZDECjNuXEE=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNhHm8=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHQZDECjNvuuo=", - "_parent": { - "$ref": "AAAAAAGHQZDECjNk4nw=" - }, - "model": { - "$ref": "AAAAAAGHQZDECjNiQTQ=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGHQZBoVP5NGtM=" - }, - "tail": { - "$ref": "AAAAAAGFy61rGZucTjI=" - }, - "lineStyle": 1, - "points": "703:878;735:878", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGHQZDECjNlk7M=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGHQZDECjNmH8A=" - }, - "propertyLabel": { - "$ref": "AAAAAAGHQZDECjNnKe4=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGHQZDECjNodU8=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGHQZDECjNpHSA=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGHQZDECjNqWqo=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGHQZDECjNr+t8=" + "visible": false, + "font": "Arial;13;0", + "left": -216, + "top": -16, + "width": 10, + "height": 10 + } + ], + "font": "Arial;13;0", + "containerChangeable": true, + "left": 560, + "top": 880, + "width": 137.0380859375, + "height": 45, + "nameCompartment": { + "$ref": "AAAAAAGHQZBoVP5Okf4=" }, - "headPropertyLabel": { - "$ref": "AAAAAAGHQZDECjNscC8=" + "suppressAttributes": true, + "suppressOperations": true, + "attributeCompartment": { + "$ref": "AAAAAAGHQZBoVP5THRY=" }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGHQZDECjNt+DA=" + "operationCompartment": { + "$ref": "AAAAAAGHQZBoVP5UTKY=" }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGHQZDECjNuXEE=" + "receptionCompartment": { + "$ref": "AAAAAAGHQZBoVP5Vsxs=" }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGHQZDECjNvuuo=" + "templateParameterCompartment": { + "$ref": "AAAAAAGHQZBoVP5WV5c=" } }, { @@ -5588,12 +3504,12 @@ "$ref": "AAAAAAGHQZFjC4d1p1I=" }, "font": "Arial;13;0", - "left": 685, - "top": 475, + "left": 727, + "top": 488, "width": 52.76806640625, "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, + "alpha": 0.49296593281389556, + "distance": 48.104053883222775, "hostEdge": { "$ref": "AAAAAAGHQZFjC4d3qd8=" }, @@ -5819,8 +3735,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -96, - "top": -96, + "left": -400, + "top": -64, "height": 13 }, { @@ -5830,8 +3746,8 @@ "$ref": "AAAAAAGHRz/XjqO2bGY=" }, "font": "Arial;13;1", - "left": 949, - "top": 881, + "left": 797, + "top": 897, "width": 95.3671875, "height": 13, "text": "github releases" @@ -5844,8 +3760,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -96, - "top": -96, + "left": -400, + "top": -64, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -5858,15 +3774,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": -96, - "top": -96, + "left": -400, + "top": -64, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 944, - "top": 874, + "left": 792, + "top": 890, "width": 105.3671875, "height": 25, "stereotypeLabel": { @@ -5893,8 +3809,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -48, + "left": -200, + "top": -32, "width": 10, "height": 10 }, @@ -5909,8 +3825,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -48, + "left": -200, + "top": -32, "width": 10, "height": 10 }, @@ -5925,321 +3841,50 @@ }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -48, - "width": 10, - "height": 10 - }, - { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGHRz/Xj6O+cBw=", - "_parent": { - "$ref": "AAAAAAGHRz/XjqO1Kd8=" - }, - "model": { - "$ref": "AAAAAAGHRz/XjqOztWE=" - }, - "visible": false, - "font": "Arial;13;0", - "left": -48, - "top": -48, - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "containerChangeable": true, - "left": 944, - "top": 864, - "width": 115.3671875, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGHRz/XjqO2bGY=" - }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGHRz/Xj6O7bNE=" - }, - "operationCompartment": { - "$ref": "AAAAAAGHRz/Xj6O8sY0=" - }, - "receptionCompartment": { - "$ref": "AAAAAAGHRz/Xj6O9NdE=" - }, - "templateParameterCompartment": { - "$ref": "AAAAAAGHRz/Xj6O+cBw=" - } - }, - { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGHR0C3MuDNQbA=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDJGa4=" - }, - "subViews": [ - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDOMZk=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDJGa4=" - }, - "font": "Arial;13;0", - "left": 735, - "top": 927, - "width": 54.57080078125, - "height": 13, - "alpha": -0.8633809828122675, - "distance": 30.083217912982647, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 1, - "text": "+manage" - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDPMHw=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDJGa4=" - }, - "visible": null, - "font": "Arial;13;0", - "left": 716, - "top": 895, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDQOqQ=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDJGa4=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 741, - "top": 932, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 1 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDRJT8=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDKt2I=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 709, - "top": 917, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDSmpY=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDKt2I=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 704, - "top": 905, - "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDT3Zc=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDKt2I=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 721, - "top": 943, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "edgePosition": 2 - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDUYPg=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDLSvw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 740, - "top": 897, - "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDVxiM=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDLSvw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 730, - "top": 887, - "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - } - }, - { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0C3MuDW6Lk=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDLSvw=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 759, - "top": 917, - "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, - "hostEdge": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0C3MuDXH/Y=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDKt2I=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0C3MuDY9Gw=", - "_parent": { - "$ref": "AAAAAAGHR0C3MuDNQbA=" - }, - "model": { - "$ref": "AAAAAAGHR0C3MuDLSvw=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGHQZBoVP5NGtM=" - }, - "tail": { - "$ref": "AAAAAAGFy71ZRLAsbD4=" - }, - "lineStyle": 1, - "points": "697:951;770:901", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGHR0C3MuDOMZk=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGHR0C3MuDPMHw=" - }, - "propertyLabel": { - "$ref": "AAAAAAGHR0C3MuDQOqQ=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGHR0C3MuDRJT8=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGHR0C3MuDSmpY=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGHR0C3MuDT3Zc=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGHR0C3MuDUYPg=" + "left": -200, + "top": -32, + "width": 10, + "height": 10 + }, + { + "_type": "UMLTemplateParameterCompartmentView", + "_id": "AAAAAAGHRz/Xj6O+cBw=", + "_parent": { + "$ref": "AAAAAAGHRz/XjqO1Kd8=" + }, + "model": { + "$ref": "AAAAAAGHRz/XjqOztWE=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -200, + "top": -32, + "width": 10, + "height": 10 + } + ], + "font": "Arial;13;0", + "containerChangeable": true, + "left": 792, + "top": 880, + "width": 115.3671875, + "height": 45, + "nameCompartment": { + "$ref": "AAAAAAGHRz/XjqO2bGY=" }, - "headPropertyLabel": { - "$ref": "AAAAAAGHR0C3MuDVxiM=" + "suppressAttributes": true, + "suppressOperations": true, + "attributeCompartment": { + "$ref": "AAAAAAGHRz/Xj6O7bNE=" }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGHR0C3MuDW6Lk=" + "operationCompartment": { + "$ref": "AAAAAAGHRz/Xj6O8sY0=" }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGHR0C3MuDXH/Y=" + "receptionCompartment": { + "$ref": "AAAAAAGHRz/Xj6O9NdE=" }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGHR0C3MuDY9Gw=" + "templateParameterCompartment": { + "$ref": "AAAAAAGHRz/Xj6O+cBw=" } }, { @@ -6263,8 +3908,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 907, - "top": 890, + "left": 743, + "top": 911, "height": 13, "alpha": 1.5707963267948966, "distance": 15, @@ -6283,8 +3928,8 @@ "$ref": "AAAAAAGHR0EBVPHBf3w=" }, "font": "Arial;13;0", - "left": 881, - "top": 905, + "left": 717, + "top": 926, "width": 52.76806640625, "height": 13, "alpha": 1.5707963267948966, @@ -6306,8 +3951,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 908, - "top": 861, + "left": 744, + "top": 881, "height": 13, "alpha": -1.5707963267948966, "distance": 15, @@ -6325,7 +3970,7 @@ "$ref": "AAAAAAGHRz/XjqO1Kd8=" }, "lineStyle": 1, - "points": "943:884;873:881", + "points": "791:902;697:902", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGHR0EBVPHEwSA=" @@ -6365,8 +4010,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": -16, - "top": 16, + "top": -16, "height": 13 }, { @@ -6376,8 +4020,8 @@ "$ref": "AAAAAAGHR0G4b/iwwNA=" }, "font": "Arial;13;1", - "left": 781, - "top": 729, + "left": 789, + "top": 713, "width": 51.314453125, "height": 13, "text": "tailscale" @@ -6390,8 +4034,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": -16, - "top": 16, + "top": -16, "width": 73.67724609375, "height": 13, "text": "(from Model)" @@ -6404,15 +4047,14 @@ }, "visible": false, "font": "Arial;13;0", - "left": -16, - "top": 16, + "top": -16, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 776, - "top": 722, + "left": 784, + "top": 706, "width": 61.314453125, "height": 25, "stereotypeLabel": { @@ -6439,8 +4081,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": -8, - "top": 8, + "top": -8, "width": 10, "height": 10 }, @@ -6455,8 +4096,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": -8, - "top": 8, + "top": -8, "width": 10, "height": 10 }, @@ -6471,8 +4111,7 @@ }, "visible": false, "font": "Arial;13;0", - "left": -8, - "top": 8, + "top": -8, "width": 10, "height": 10 }, @@ -6487,16 +4126,15 @@ }, "visible": false, "font": "Arial;13;0", - "left": -8, - "top": 8, + "top": -8, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 776, - "top": 712, + "left": 784, + "top": 696, "width": 71.314453125, "height": 45, "nameCompartment": { @@ -6537,8 +4175,8 @@ "$ref": "AAAAAAGHR0YbhibkWWw=" }, "font": "Arial;13;0", - "left": 480, - "top": 506, + "left": 496, + "top": 567, "width": 32.8935546875, "height": 13, "alpha": 1.5707963267948966, @@ -6560,8 +4198,8 @@ }, "visible": null, "font": "Arial;13;0", - "left": 497, - "top": 521, + "left": 522, + "top": 578, "height": 13, "alpha": 1.5707963267948966, "distance": 30, @@ -6581,8 +4219,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 495, - "top": 477, + "left": 493, + "top": 544, "height": 13, "alpha": -1.5707963267948966, "distance": 15, @@ -6602,8 +4240,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 517, - "top": 506, + "left": 545, + "top": 539, "height": 13, "alpha": 0.5235987755982988, "distance": 30, @@ -6623,8 +4261,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 515, - "top": 519, + "left": 552, + "top": 550, "height": 13, "alpha": 0.7853981633974483, "distance": 40, @@ -6644,8 +4282,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 520, - "top": 478, + "left": 531, + "top": 515, "height": 13, "alpha": -0.5235987755982988, "distance": 25, @@ -6665,8 +4303,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 475, - "top": 507, + "left": 479, + "top": 595, "height": 13, "alpha": -0.5235987755982988, "distance": 30, @@ -6685,8 +4323,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 478, - "top": 521, + "left": 489, + "top": 604, "height": 13, "alpha": -0.7853981633974483, "distance": 40, @@ -6705,8 +4343,8 @@ }, "visible": false, "font": "Arial;13;0", - "left": 470, - "top": 480, + "left": 458, + "top": 577, "height": 13, "alpha": 0.5235987755982988, "distance": 25, @@ -6751,7 +4389,7 @@ "$ref": "AAAAAAGFy5naJYIq7/Q=" }, "lineStyle": 1, - "points": "543:496;449:500", + "points": "556:517;450:607", "showVisibility": true, "nameLabel": { "$ref": "AAAAAAGHR0YbhybptF0=" @@ -7019,800 +4657,959 @@ "hostEdge": { "$ref": "AAAAAAGHR0eBscjvkOo=" }, - "edgePosition": 1, - "text": "«deploy»" + "edgePosition": 1, + "text": "«deploy»" + }, + { + "_type": "EdgeLabelView", + "_id": "AAAAAAGHR0eBscjyxso=", + "_parent": { + "$ref": "AAAAAAGHR0eBscjvkOo=" + }, + "model": { + "$ref": "AAAAAAGHR0eBscjtVYY=" + }, + "visible": false, + "font": "Arial;13;0", + "left": 984, + "top": 579, + "height": 13, + "alpha": -1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGHR0eBscjvkOo=" + }, + "edgePosition": 1 + } + ], + "font": "Arial;13;0", + "head": { + "$ref": "AAAAAAGHR0dJ7rRCahE=" + }, + "tail": { + "$ref": "AAAAAAGFy5s524LfGfM=" + }, + "lineStyle": 1, + "points": "985:425;984:600;836:598", + "showVisibility": true, + "nameLabel": { + "$ref": "AAAAAAGHR0eBscjwU5U=" + }, + "stereotypeLabel": { + "$ref": "AAAAAAGHR0eBscjxFvk=" + }, + "propertyLabel": { + "$ref": "AAAAAAGHR0eBscjyxso=" + } + }, + { + "_type": "UMLDeploymentView", + "_id": "AAAAAAGHR0jgtCGCuKs=", + "_parent": { + "$ref": "AAAAAAGFy5jjfYCEQQw=" + }, + "model": { + "$ref": "AAAAAAGHR0jgtCGA6No=" + }, + "subViews": [ + { + "_type": "EdgeLabelView", + "_id": "AAAAAAGHR0jgtSGDuSQ=", + "_parent": { + "$ref": "AAAAAAGHR0jgtCGCuKs=" + }, + "model": { + "$ref": "AAAAAAGHR0jgtCGA6No=" + }, + "font": "Arial;13;0", + "left": 580, + "top": 686, + "width": 49.51171875, + "height": 13, + "alpha": 1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGHR0jgtCGCuKs=" + }, + "edgePosition": 1, + "text": "+2" + }, + { + "_type": "EdgeLabelView", + "_id": "AAAAAAGHR0jgtSGFH0w=", + "_parent": { + "$ref": "AAAAAAGHR0jgtCGCuKs=" + }, + "model": { + "$ref": "AAAAAAGHR0jgtCGA6No=" + }, + "visible": false, + "font": "Arial;13;0", + "left": 633, + "top": 681, + "height": 13, + "alpha": -1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGHR0jgtCGCuKs=" + }, + "edgePosition": 1 + } + ], + "font": "Arial;13;0", + "head": { + "$ref": "AAAAAAGFy5kz9oCMjsw=" + }, + "tail": { + "$ref": "AAAAAAGFy6ywAGV8HMM=" + }, + "lineStyle": 1, + "points": "630:751;608:629", + "showVisibility": true, + "nameLabel": { + "$ref": "AAAAAAGHR0jgtSGDuSQ=" + }, + "propertyLabel": { + "$ref": "AAAAAAGHR0jgtSGFH0w=" + } + }, + { + "_type": "UMLNodeView", + "_id": "AAAAAAGHR0vrAN4Aqjg=", + "_parent": { + "$ref": "AAAAAAGFy5jjfYCEQQw=" + }, + "model": { + "$ref": "AAAAAAGHR0vrAN3+JAI=" + }, + "subViews": [ + { + "_type": "UMLNameCompartmentView", + "_id": "AAAAAAGHR0vrAN4BLR4=", + "_parent": { + "$ref": "AAAAAAGHR0vrAN4Aqjg=" + }, + "model": { + "$ref": "AAAAAAGHR0vrAN3+JAI=" + }, + "subViews": [ + { + "_type": "LabelView", + "_id": "AAAAAAGHR0vrAN4CjsY=", + "_parent": { + "$ref": "AAAAAAGHR0vrAN4BLR4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -208, + "top": 48, + "height": 13 + }, + { + "_type": "LabelView", + "_id": "AAAAAAGHR0vrAN4Drxg=", + "_parent": { + "$ref": "AAAAAAGHR0vrAN4BLR4=" + }, + "font": "Arial;13;1", + "left": 757, + "top": 825, + "width": 113.43896484375, + "height": 13, + "text": "external ssh client" + }, + { + "_type": "LabelView", + "_id": "AAAAAAGHR0vrAN4EzVs=", + "_parent": { + "$ref": "AAAAAAGHR0vrAN4BLR4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -208, + "top": 48, + "width": 73.67724609375, + "height": 13, + "text": "(from Model)" + }, + { + "_type": "LabelView", + "_id": "AAAAAAGHR0vrAN4Fg28=", + "_parent": { + "$ref": "AAAAAAGHR0vrAN4BLR4=" + }, + "visible": false, + "font": "Arial;13;0", + "left": -208, + "top": 48, + "height": 13, + "horizontalAlignment": 1 + } + ], + "font": "Arial;13;0", + "left": 752, + "top": 818, + "width": 123.43896484375, + "height": 25, + "stereotypeLabel": { + "$ref": "AAAAAAGHR0vrAN4CjsY=" + }, + "nameLabel": { + "$ref": "AAAAAAGHR0vrAN4Drxg=" + }, + "namespaceLabel": { + "$ref": "AAAAAAGHR0vrAN4EzVs=" + }, + "propertyLabel": { + "$ref": "AAAAAAGHR0vrAN4Fg28=" + } }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0eBscjyxso=", + "_type": "UMLAttributeCompartmentView", + "_id": "AAAAAAGHR0vrAN4G9NM=", "_parent": { - "$ref": "AAAAAAGHR0eBscjvkOo=" + "$ref": "AAAAAAGHR0vrAN4Aqjg=" }, "model": { - "$ref": "AAAAAAGHR0eBscjtVYY=" + "$ref": "AAAAAAGHR0vrAN3+JAI=" }, "visible": false, "font": "Arial;13;0", - "left": 984, - "top": 579, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHR0eBscjvkOo=" - }, - "edgePosition": 1 - } - ], - "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGHR0dJ7rRCahE=" - }, - "tail": { - "$ref": "AAAAAAGFy5s524LfGfM=" - }, - "lineStyle": 1, - "points": "985:425;984:600;836:598", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGHR0eBscjwU5U=" - }, - "stereotypeLabel": { - "$ref": "AAAAAAGHR0eBscjxFvk=" - }, - "propertyLabel": { - "$ref": "AAAAAAGHR0eBscjyxso=" - } - }, - { - "_type": "UMLDeploymentView", - "_id": "AAAAAAGHR0jgtCGCuKs=", - "_parent": { - "$ref": "AAAAAAGFy5jjfYCEQQw=" - }, - "model": { - "$ref": "AAAAAAGHR0jgtCGA6No=" - }, - "subViews": [ + "left": -104, + "top": 24, + "width": 10, + "height": 10 + }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0jgtSGDuSQ=", + "_type": "UMLOperationCompartmentView", + "_id": "AAAAAAGHR0vrAN4H/3I=", "_parent": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" + "$ref": "AAAAAAGHR0vrAN4Aqjg=" }, "model": { - "$ref": "AAAAAAGHR0jgtCGA6No=" + "$ref": "AAAAAAGHR0vrAN3+JAI=" }, "visible": false, "font": "Arial;13;0", - "left": 656, - "top": 648, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" - }, - "edgePosition": 1 + "left": -104, + "top": 24, + "width": 10, + "height": 10 }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0jgtSGE8i8=", + "_type": "UMLReceptionCompartmentView", + "_id": "AAAAAAGHR0vrAd4IHq8=", "_parent": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" + "$ref": "AAAAAAGHR0vrAN4Aqjg=" }, "model": { - "$ref": "AAAAAAGHR0jgtCGA6No=" + "$ref": "AAAAAAGHR0vrAN3+JAI=" }, + "visible": false, "font": "Arial;13;0", - "left": 616, - "top": 652, - "width": 52.76806640625, - "height": 13, - "alpha": 1.5707963267948966, - "distance": 30, - "hostEdge": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" - }, - "edgePosition": 1, - "text": "«deploy»" + "left": -104, + "top": 24, + "width": 10, + "height": 10 }, { - "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0jgtSGFH0w=", + "_type": "UMLTemplateParameterCompartmentView", + "_id": "AAAAAAGHR0vrAd4J4aY=", "_parent": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" + "$ref": "AAAAAAGHR0vrAN4Aqjg=" }, "model": { - "$ref": "AAAAAAGHR0jgtCGA6No=" + "$ref": "AAAAAAGHR0vrAN3+JAI=" }, "visible": false, "font": "Arial;13;0", - "left": 685, - "top": 639, - "height": 13, - "alpha": -1.5707963267948966, - "distance": 15, - "hostEdge": { - "$ref": "AAAAAAGHR0jgtCGCuKs=" - }, - "edgePosition": 1 + "left": -104, + "top": 24, + "width": 10, + "height": 10 } ], "font": "Arial;13;0", - "head": { - "$ref": "AAAAAAGFy5kz9oCMjsw=" + "containerChangeable": true, + "left": 752, + "top": 808, + "width": 133.43896484375, + "height": 45, + "nameCompartment": { + "$ref": "AAAAAAGHR0vrAN4BLR4=" }, - "tail": { - "$ref": "AAAAAAGFy6ywAGV8HMM=" + "suppressAttributes": true, + "suppressOperations": true, + "attributeCompartment": { + "$ref": "AAAAAAGHR0vrAN4G9NM=" }, - "lineStyle": 1, - "points": "675:663;667:637", - "showVisibility": true, - "nameLabel": { - "$ref": "AAAAAAGHR0jgtSGDuSQ=" + "operationCompartment": { + "$ref": "AAAAAAGHR0vrAN4H/3I=" }, - "stereotypeLabel": { - "$ref": "AAAAAAGHR0jgtSGE8i8=" + "receptionCompartment": { + "$ref": "AAAAAAGHR0vrAd4IHq8=" }, - "propertyLabel": { - "$ref": "AAAAAAGHR0jgtSGFH0w=" + "templateParameterCompartment": { + "$ref": "AAAAAAGHR0vrAd4J4aY=" } }, { "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGHR0vaftu+GpA=", + "_id": "AAAAAAGHR0wlnOrP4NE=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGHR0vaftu6Ovg=" + "$ref": "AAAAAAGHR0wlnOrLHbw=" }, "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftu/kjY=", + "_id": "AAAAAAGHR0wlnOrQ03k=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu6Ovg=" + "$ref": "AAAAAAGHR0wlnOrLHbw=" }, "visible": false, "font": "Arial;13;0", - "left": 760, - "top": 726, + "left": 832, + "top": 767, "height": 13, "alpha": 1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvAWWE=", + "_id": "AAAAAAGHR0wlnOrRCIY=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu6Ovg=" + "$ref": "AAAAAAGHR0wlnOrLHbw=" }, "visible": null, "font": "Arial;13;0", - "left": 756, - "top": 712, + "left": 847, + "top": 767, "height": 13, "alpha": 1.5707963267948966, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvBfOw=", + "_id": "AAAAAAGHR0wlnOrShhI=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu6Ovg=" + "$ref": "AAAAAAGHR0wlnOrLHbw=" }, "visible": false, "font": "Arial;13;0", - "left": 767, - "top": 755, + "left": 803, + "top": 768, "height": 13, "alpha": -1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvCNJQ=", + "_id": "AAAAAAGHR0wlnOrTzRo=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu77To=" + "$ref": "AAAAAAGHR0wlnOrMtmA=" }, "visible": false, "font": "Arial;13;0", - "left": 773, - "top": 722, + "left": 833, + "top": 761, "height": 13, "alpha": 0.5235987755982988, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvD9L0=", + "_id": "AAAAAAGHR0wlnOrUDQ4=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu77To=" + "$ref": "AAAAAAGHR0wlnOrMtmA=" }, "visible": false, "font": "Arial;13;0", - "left": 771, - "top": 709, + "left": 846, + "top": 763, "height": 13, "alpha": 0.7853981633974483, "distance": 40, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvEFvU=", + "_id": "AAAAAAGHR0wlnOrVVtw=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu77To=" + "$ref": "AAAAAAGHR0wlnOrMtmA=" }, "visible": false, "font": "Arial;13;0", - "left": 777, - "top": 750, + "left": 806, + "top": 756, "height": 13, "alpha": -0.5235987755982988, "distance": 25, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvFkv0=", + "_id": "AAAAAAGHR0wlnOrWGxA=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu8Jqs=" + "$ref": "AAAAAAGHR0wlnOrNDOk=" }, "visible": false, "font": "Arial;13;0", - "left": 745, - "top": 731, + "left": 833, + "top": 775, "height": 13, "alpha": -0.5235987755982988, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" } }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaftvGV0A=", + "_id": "AAAAAAGHR0wlnOrXZFM=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu8Jqs=" + "$ref": "AAAAAAGHR0wlnOrNDOk=" }, "visible": false, "font": "Arial;13;0", - "left": 739, - "top": 719, + "left": 846, + "top": 773, "height": 13, "alpha": -0.7853981633974483, "distance": 40, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" } }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0vaf9vHgg0=", + "_id": "AAAAAAGHR0wlnOrY7YA=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu8Jqs=" + "$ref": "AAAAAAGHR0wlnOrNDOk=" }, "visible": false, "font": "Arial;13;0", - "left": 758, - "top": 756, + "left": 805, + "top": 779, "height": 13, "alpha": 0.5235987755982988, "distance": 25, "hostEdge": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" } }, { "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0vaf9vIGfY=", + "_id": "AAAAAAGHR0wlnOrZDyk=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu77To=" + "$ref": "AAAAAAGHR0wlnOrMtmA=" }, "visible": false, "font": "Arial;13;0", + "top": 136, "width": 10, "height": 10 }, { "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0vaf9vJndE=", + "_id": "AAAAAAGHR0wlnOraUws=", "_parent": { - "$ref": "AAAAAAGHR0vaftu+GpA=" + "$ref": "AAAAAAGHR0wlnOrP4NE=" }, "model": { - "$ref": "AAAAAAGHR0vaftu8Jqs=" + "$ref": "AAAAAAGHR0wlnOrNDOk=" }, "visible": false, "font": "Arial;13;0", + "top": 136, "width": 10, "height": 10 } ], "font": "Arial;13;0", "head": { - "$ref": "AAAAAAGHR0G4b/ivmr0=" + "$ref": "AAAAAAGHR0vrAN4Aqjg=" }, "tail": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" + "$ref": "AAAAAAGHR0G4b/ivmr0=" }, "lineStyle": 1, - "points": "753:751;775:744", + "points": "819:741;818:807", "showVisibility": true, "nameLabel": { - "$ref": "AAAAAAGHR0vaftu/kjY=" + "$ref": "AAAAAAGHR0wlnOrQ03k=" }, "stereotypeLabel": { - "$ref": "AAAAAAGHR0vaftvAWWE=" + "$ref": "AAAAAAGHR0wlnOrRCIY=" }, "propertyLabel": { - "$ref": "AAAAAAGHR0vaftvBfOw=" + "$ref": "AAAAAAGHR0wlnOrShhI=" }, "showEndOrder": "hide", "tailRoleNameLabel": { - "$ref": "AAAAAAGHR0vaftvCNJQ=" + "$ref": "AAAAAAGHR0wlnOrTzRo=" }, "tailPropertyLabel": { - "$ref": "AAAAAAGHR0vaftvD9L0=" + "$ref": "AAAAAAGHR0wlnOrUDQ4=" }, "tailMultiplicityLabel": { - "$ref": "AAAAAAGHR0vaftvEFvU=" + "$ref": "AAAAAAGHR0wlnOrVVtw=" }, "headRoleNameLabel": { - "$ref": "AAAAAAGHR0vaftvFkv0=" + "$ref": "AAAAAAGHR0wlnOrWGxA=" }, "headPropertyLabel": { - "$ref": "AAAAAAGHR0vaftvGV0A=" + "$ref": "AAAAAAGHR0wlnOrXZFM=" }, "headMultiplicityLabel": { - "$ref": "AAAAAAGHR0vaf9vHgg0=" + "$ref": "AAAAAAGHR0wlnOrY7YA=" }, "tailQualifiersCompartment": { - "$ref": "AAAAAAGHR0vaf9vIGfY=" + "$ref": "AAAAAAGHR0wlnOrZDyk=" }, "headQualifiersCompartment": { - "$ref": "AAAAAAGHR0vaf9vJndE=" + "$ref": "AAAAAAGHR0wlnOraUws=" } }, { "_type": "UMLNodeView", - "_id": "AAAAAAGHR0vrAN4Aqjg=", + "_id": "AAAAAAGHThGfj6SgxP8=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "subViews": [ { "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGHR0vrAN4BLR4=", + "_id": "AAAAAAGHThGfj6Sh+10=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "subViews": [ { "_type": "LabelView", - "_id": "AAAAAAGHR0vrAN4CjsY=", + "_id": "AAAAAAGHThGfj6Si0u4=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4BLR4=" + "$ref": "AAAAAAGHThGfj6Sh+10=" }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -32, + "left": 64, + "top": -112, "height": 13 }, { "_type": "LabelView", - "_id": "AAAAAAGHR0vrAN4Drxg=", + "_id": "AAAAAAGHThGfj6SjiFI=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4BLR4=" + "$ref": "AAAAAAGHThGfj6Sh+10=" }, "font": "Arial;13;1", - "left": 837, - "top": 785, - "width": 113.43896484375, + "left": 349, + "top": 761, + "width": 64.3017578125, "height": 13, - "text": "external ssh client" + "text": "name.com" }, { "_type": "LabelView", - "_id": "AAAAAAGHR0vrAN4EzVs=", + "_id": "AAAAAAGHThGfj6SkiiQ=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4BLR4=" + "$ref": "AAAAAAGHThGfj6Sh+10=" }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -32, + "left": 64, + "top": -112, "width": 73.67724609375, "height": 13, "text": "(from Model)" }, { "_type": "LabelView", - "_id": "AAAAAAGHR0vrAN4Fg28=", + "_id": "AAAAAAGHThGfj6Slzwc=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4BLR4=" + "$ref": "AAAAAAGHThGfj6Sh+10=" }, "visible": false, "font": "Arial;13;0", - "left": -48, - "top": -32, + "left": 64, + "top": -112, "height": 13, "horizontalAlignment": 1 } ], "font": "Arial;13;0", - "left": 832, - "top": 778, - "width": 123.43896484375, + "left": 344, + "top": 754, + "width": 74.3017578125, "height": 25, "stereotypeLabel": { - "$ref": "AAAAAAGHR0vrAN4CjsY=" + "$ref": "AAAAAAGHThGfj6Si0u4=" }, "nameLabel": { - "$ref": "AAAAAAGHR0vrAN4Drxg=" + "$ref": "AAAAAAGHThGfj6SjiFI=" }, "namespaceLabel": { - "$ref": "AAAAAAGHR0vrAN4EzVs=" + "$ref": "AAAAAAGHThGfj6SkiiQ=" }, "propertyLabel": { - "$ref": "AAAAAAGHR0vrAN4Fg28=" + "$ref": "AAAAAAGHThGfj6Slzwc=" } }, { "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGHR0vrAN4G9NM=", + "_id": "AAAAAAGHThGfj6SmnSU=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "visible": false, "font": "Arial;13;0", - "left": -24, - "top": -16, + "left": 32, + "top": -56, "width": 10, "height": 10 }, { "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGHR0vrAN4H/3I=", + "_id": "AAAAAAGHThGfj6SnB8o=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "visible": false, "font": "Arial;13;0", - "left": -24, - "top": -16, + "left": 32, + "top": -56, "width": 10, "height": 10 }, { "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGHR0vrAd4IHq8=", + "_id": "AAAAAAGHThGfj6So9fI=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "visible": false, "font": "Arial;13;0", - "left": -24, - "top": -16, + "left": 32, + "top": -56, "width": 10, "height": 10 }, { "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGHR0vrAd4J4aY=", + "_id": "AAAAAAGHThGfj6SpTho=", "_parent": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "model": { - "$ref": "AAAAAAGHR0vrAN3+JAI=" + "$ref": "AAAAAAGHThGfj6SeVCQ=" }, "visible": false, "font": "Arial;13;0", - "left": -24, - "top": -16, + "left": 32, + "top": -56, "width": 10, "height": 10 } ], "font": "Arial;13;0", "containerChangeable": true, - "left": 832, - "top": 768, - "width": 133.43896484375, + "left": 344, + "top": 744, + "width": 84.3017578125, "height": 45, "nameCompartment": { - "$ref": "AAAAAAGHR0vrAN4BLR4=" + "$ref": "AAAAAAGHThGfj6Sh+10=" }, "suppressAttributes": true, "suppressOperations": true, "attributeCompartment": { - "$ref": "AAAAAAGHR0vrAN4G9NM=" + "$ref": "AAAAAAGHThGfj6SmnSU=" }, "operationCompartment": { - "$ref": "AAAAAAGHR0vrAN4H/3I=" + "$ref": "AAAAAAGHThGfj6SnB8o=" }, "receptionCompartment": { - "$ref": "AAAAAAGHR0vrAd4IHq8=" + "$ref": "AAAAAAGHThGfj6So9fI=" }, "templateParameterCompartment": { - "$ref": "AAAAAAGHR0vrAd4J4aY=" + "$ref": "AAAAAAGHThGfj6SpTho=" } }, { "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGHR0wlnOrP4NE=", + "_id": "AAAAAAGOfNfIzH59+9U=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrLHbw=" + "$ref": "AAAAAAGOfNfIzH55o0I=" }, "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrQ03k=", + "_id": "AAAAAAGOfNfIzH5+nQM=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrLHbw=" + "$ref": "AAAAAAGOfNfIzH55o0I=" }, - "visible": false, "font": "Arial;13;0", - "left": 861, - "top": 743, + "left": 503, + "top": 708, + "width": 32.8935546875, "height": 13, "alpha": 1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, - "edgePosition": 1 + "edgePosition": 1, + "text": "+auth" }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrRCIY=", + "_id": "AAAAAAGOfNfIzH5/qyw=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrLHbw=" + "$ref": "AAAAAAGOfNfIzH55o0I=" }, "visible": null, "font": "Arial;13;0", - "left": 869, - "top": 730, + "left": 510, + "top": 720, "height": 13, "alpha": 1.5707963267948966, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrShhI=", + "_id": "AAAAAAGOfNfIzH6AO+A=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrLHbw=" + "$ref": "AAAAAAGOfNfIzH55o0I=" }, "visible": false, "font": "Arial;13;0", - "left": 846, - "top": 768, + "left": 536, + "top": 683, "height": 13, "alpha": -1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrTzRo=", + "_id": "AAAAAAGOfNfIzH6B7bA=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrMtmA=" + "$ref": "AAAAAAGOfNfIzH56zxg=" }, "visible": false, "font": "Arial;13;0", - "left": 876, - "top": 752, + "left": 570, + "top": 742, "height": 13, "alpha": 0.5235987755982988, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrUDQ4=", + "_id": "AAAAAAGOfNfIzH6C7JE=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrMtmA=" + "$ref": "AAAAAAGOfNfIzH56zxg=" }, "visible": false, "font": "Arial;13;0", - "left": 886, - "top": 743, + "left": 560, + "top": 752, "height": 13, "alpha": 0.7853981633974483, "distance": 40, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrVVtw=", + "_id": "AAAAAAGOfNfIzH6Dhkk=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrMtmA=" + "$ref": "AAAAAAGOfNfIzH56zxg=" }, "visible": false, "font": "Arial;13;0", - "left": 858, - "top": 773, + "left": 589, + "top": 722, "height": 13, "alpha": -0.5235987755982988, "distance": 25, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "edgePosition": 2 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrWGxA=", + "_id": "AAAAAAGOfNfIzH6EhfA=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrNDOk=" + "$ref": "AAAAAAGOfNfIzH57x8E=" }, "visible": false, "font": "Arial;13;0", - "left": 848, - "top": 734, + "left": 469, + "top": 674, "height": 13, "alpha": -0.5235987755982988, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" } }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrXZFM=", + "_id": "AAAAAAGOfNfIzH6FoLw=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrNDOk=" + "$ref": "AAAAAAGOfNfIzH57x8E=" }, "visible": false, "font": "Arial;13;0", - "left": 854, - "top": 721, + "left": 464, + "top": 686, "height": 13, "alpha": -0.7853981633974483, "distance": 40, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" } }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHR0wlnOrY7YA=", + "_id": "AAAAAAGOfNfIzH6GX+Y=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrNDOk=" + "$ref": "AAAAAAGOfNfIzH57x8E=" }, "visible": false, "font": "Arial;13;0", - "left": 837, - "top": 759, + "left": 481, + "top": 648, "height": 13, "alpha": 0.5235987755982988, "distance": 25, "hostEdge": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" } }, { "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0wlnOrZDyk=", + "_id": "AAAAAAGOfNfIzH6Hprw=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrMtmA=" + "$ref": "AAAAAAGOfNfIzH56zxg=" }, "visible": false, "font": "Arial;13;0", @@ -7821,12 +5618,12 @@ }, { "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHR0wlnOraUws=", + "_id": "AAAAAAGOfNfIzH6IGCw=", "_parent": { - "$ref": "AAAAAAGHR0wlnOrP4NE=" + "$ref": "AAAAAAGOfNfIzH59+9U=" }, "model": { - "$ref": "AAAAAAGHR0wlnOrNDOk=" + "$ref": "AAAAAAGOfNfIzH57x8E=" }, "visible": false, "font": "Arial;13;0", @@ -7836,498 +5633,426 @@ ], "font": "Arial;13;0", "head": { - "$ref": "AAAAAAGHR0vrAN4Aqjg=" + "$ref": "AAAAAAGFy5mjE4D4dMc=" }, "tail": { - "$ref": "AAAAAAGHR0G4b/ivmr0=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "lineStyle": 1, - "points": "847:757;862:767", + "points": "600:751;457:653", "showVisibility": true, "nameLabel": { - "$ref": "AAAAAAGHR0wlnOrQ03k=" + "$ref": "AAAAAAGOfNfIzH5+nQM=" }, "stereotypeLabel": { - "$ref": "AAAAAAGHR0wlnOrRCIY=" + "$ref": "AAAAAAGOfNfIzH5/qyw=" }, "propertyLabel": { - "$ref": "AAAAAAGHR0wlnOrShhI=" + "$ref": "AAAAAAGOfNfIzH6AO+A=" }, "showEndOrder": "hide", "tailRoleNameLabel": { - "$ref": "AAAAAAGHR0wlnOrTzRo=" + "$ref": "AAAAAAGOfNfIzH6B7bA=" }, "tailPropertyLabel": { - "$ref": "AAAAAAGHR0wlnOrUDQ4=" + "$ref": "AAAAAAGOfNfIzH6C7JE=" }, "tailMultiplicityLabel": { - "$ref": "AAAAAAGHR0wlnOrVVtw=" + "$ref": "AAAAAAGOfNfIzH6Dhkk=" }, "headRoleNameLabel": { - "$ref": "AAAAAAGHR0wlnOrWGxA=" + "$ref": "AAAAAAGOfNfIzH6EhfA=" }, "headPropertyLabel": { - "$ref": "AAAAAAGHR0wlnOrXZFM=" + "$ref": "AAAAAAGOfNfIzH6FoLw=" }, "headMultiplicityLabel": { - "$ref": "AAAAAAGHR0wlnOrY7YA=" + "$ref": "AAAAAAGOfNfIzH6GX+Y=" }, "tailQualifiersCompartment": { - "$ref": "AAAAAAGHR0wlnOrZDyk=" + "$ref": "AAAAAAGOfNfIzH6Hprw=" }, "headQualifiersCompartment": { - "$ref": "AAAAAAGHR0wlnOraUws=" + "$ref": "AAAAAAGOfNfIzH6IGCw=" } }, { - "_type": "UMLNodeView", - "_id": "AAAAAAGHThGfj6SgxP8=", + "_type": "UMLDependencyView", + "_id": "AAAAAAGOfNsXEieumcc=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "$ref": "AAAAAAGOfNsXEiesEWA=" }, "subViews": [ { - "_type": "UMLNameCompartmentView", - "_id": "AAAAAAGHThGfj6Sh+10=", + "_type": "EdgeLabelView", + "_id": "AAAAAAGOfNsXEievc/o=", "_parent": { - "$ref": "AAAAAAGHThGfj6SgxP8=" + "$ref": "AAAAAAGOfNsXEieumcc=" }, "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "$ref": "AAAAAAGOfNsXEiesEWA=" }, - "subViews": [ - { - "_type": "LabelView", - "_id": "AAAAAAGHThGfj6Si0u4=", - "_parent": { - "$ref": "AAAAAAGHThGfj6Sh+10=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 48, - "top": -16, - "height": 13 - }, - { - "_type": "LabelView", - "_id": "AAAAAAGHThGfj6SjiFI=", - "_parent": { - "$ref": "AAAAAAGHThGfj6Sh+10=" - }, - "font": "Arial;13;1", - "left": 341, - "top": 809, - "width": 64.3017578125, - "height": 13, - "text": "name.com" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGHThGfj6SkiiQ=", - "_parent": { - "$ref": "AAAAAAGHThGfj6Sh+10=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 48, - "top": -16, - "width": 73.67724609375, - "height": 13, - "text": "(from Model)" - }, - { - "_type": "LabelView", - "_id": "AAAAAAGHThGfj6Slzwc=", - "_parent": { - "$ref": "AAAAAAGHThGfj6Sh+10=" - }, - "visible": false, - "font": "Arial;13;0", - "left": 48, - "top": -16, - "height": 13, - "horizontalAlignment": 1 - } - ], "font": "Arial;13;0", - "left": 336, - "top": 802, - "width": 74.3017578125, - "height": 25, - "stereotypeLabel": { - "$ref": "AAAAAAGHThGfj6Si0u4=" - }, - "nameLabel": { - "$ref": "AAAAAAGHThGfj6SjiFI=" - }, - "namespaceLabel": { - "$ref": "AAAAAAGHThGfj6SkiiQ=" - }, - "propertyLabel": { - "$ref": "AAAAAAGHThGfj6Slzwc=" - } - }, - { - "_type": "UMLAttributeCompartmentView", - "_id": "AAAAAAGHThGfj6SmnSU=", - "_parent": { - "$ref": "AAAAAAGHThGfj6SgxP8=" - }, - "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "left": 615, + "top": 832, + "width": 61.7880859375, + "height": 13, + "alpha": 1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGOfNsXEieumcc=" }, - "visible": false, - "font": "Arial;13;0", - "left": 24, - "top": -8, - "width": 10, - "height": 10 + "edgePosition": 1, + "text": "+pull qcow" }, { - "_type": "UMLOperationCompartmentView", - "_id": "AAAAAAGHThGfj6SnB8o=", + "_type": "EdgeLabelView", + "_id": "AAAAAAGOfNsXEiewBY8=", "_parent": { - "$ref": "AAAAAAGHThGfj6SgxP8=" + "$ref": "AAAAAAGOfNsXEieumcc=" }, "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "$ref": "AAAAAAGOfNsXEiesEWA=" }, - "visible": false, + "visible": null, "font": "Arial;13;0", - "left": 24, - "top": -8, - "width": 10, - "height": 10 - }, - { - "_type": "UMLReceptionCompartmentView", - "_id": "AAAAAAGHThGfj6So9fI=", - "_parent": { - "$ref": "AAAAAAGHThGfj6SgxP8=" - }, - "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "left": 660, + "top": 833, + "height": 13, + "alpha": 1.5707963267948966, + "distance": 30, + "hostEdge": { + "$ref": "AAAAAAGOfNsXEieumcc=" }, - "visible": false, - "font": "Arial;13;0", - "left": 24, - "top": -8, - "width": 10, - "height": 10 + "edgePosition": 1 }, { - "_type": "UMLTemplateParameterCompartmentView", - "_id": "AAAAAAGHThGfj6SpTho=", + "_type": "EdgeLabelView", + "_id": "AAAAAAGOfNsXEiext8Y=", "_parent": { - "$ref": "AAAAAAGHThGfj6SgxP8=" + "$ref": "AAAAAAGOfNsXEieumcc=" }, "model": { - "$ref": "AAAAAAGHThGfj6SeVCQ=" + "$ref": "AAAAAAGOfNsXEiesEWA=" }, "visible": false, "font": "Arial;13;0", - "left": 24, - "top": -8, - "width": 10, - "height": 10 + "left": 616, + "top": 831, + "height": 13, + "alpha": -1.5707963267948966, + "distance": 15, + "hostEdge": { + "$ref": "AAAAAAGOfNsXEieumcc=" + }, + "edgePosition": 1 } ], "font": "Arial;13;0", - "containerChangeable": true, - "left": 336, - "top": 792, - "width": 84.3017578125, - "height": 45, - "nameCompartment": { - "$ref": "AAAAAAGHThGfj6Sh+10=" + "head": { + "$ref": "AAAAAAGHQZBoVP5NGtM=" }, - "suppressAttributes": true, - "suppressOperations": true, - "attributeCompartment": { - "$ref": "AAAAAAGHThGfj6SmnSU=" + "tail": { + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, - "operationCompartment": { - "$ref": "AAAAAAGHThGfj6SnB8o=" + "lineStyle": 1, + "points": "633:797;629:879", + "showVisibility": true, + "nameLabel": { + "$ref": "AAAAAAGOfNsXEievc/o=" }, - "receptionCompartment": { - "$ref": "AAAAAAGHThGfj6So9fI=" + "stereotypeLabel": { + "$ref": "AAAAAAGOfNsXEiewBY8=" }, - "templateParameterCompartment": { - "$ref": "AAAAAAGHThGfj6SpTho=" + "propertyLabel": { + "$ref": "AAAAAAGOfNsXEiext8Y=" } }, { - "_type": "UMLCommunicationPathView", - "_id": "AAAAAAGHThHtV72N+6M=", + "_type": "UMLDependencyView", + "_id": "AAAAAAGOfNtH7S6PbVs=", "_parent": { "$ref": "AAAAAAGFy5jjfYCEQQw=" }, "model": { - "$ref": "AAAAAAGHThHtV72JmpA=" + "$ref": "AAAAAAGOfNtH7S6NYbU=" }, "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72OEDc=", + "_id": "AAAAAAGOfNtH7S6QeB8=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "model": { - "$ref": "AAAAAAGHThHtV72JmpA=" + "$ref": "AAAAAAGOfNtH7S6NYbU=" }, "font": "Arial;13;0", - "left": 451, - "top": 777, - "width": 88.51806640625, + "left": 483, + "top": 755, + "width": 56.74169921875, "height": 13, "alpha": 1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "edgePosition": 1, - "text": "+Domain name" + "text": "+deploy" }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72PfNQ=", + "_id": "AAAAAAGOfNtH7S6Rvjc=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "model": { - "$ref": "AAAAAAGHThHtV72JmpA=" + "$ref": "AAAAAAGOfNtH7S6NYbU=" }, "visible": null, "font": "Arial;13;0", - "left": 493, - "top": 762, + "left": 508, + "top": 770, "height": 13, "alpha": 1.5707963267948966, "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72Qxfg=", + "_id": "AAAAAAGOfNtH7S6Sl8E=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "model": { - "$ref": "AAAAAAGHThHtV72JmpA=" + "$ref": "AAAAAAGOfNtH7S6NYbU=" }, "visible": false, "font": "Arial;13;0", - "left": 498, - "top": 806, + "left": 518, + "top": 726, "height": 13, "alpha": -1.5707963267948966, "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNtH7S6PbVs=" }, "edgePosition": 1 - }, + } + ], + "font": "Arial;13;0", + "head": { + "$ref": "AAAAAAGFy61rGZucTjI=" + }, + "tail": { + "$ref": "AAAAAAGFy6ywAGV8HMM=" + }, + "lineStyle": 1, + "points": "583:763;447:732", + "showVisibility": true, + "nameLabel": { + "$ref": "AAAAAAGOfNtH7S6QeB8=" + }, + "stereotypeLabel": { + "$ref": "AAAAAAGOfNtH7S6Rvjc=" + }, + "propertyLabel": { + "$ref": "AAAAAAGOfNtH7S6Sl8E=" + } + }, + { + "_type": "UMLDeploymentView", + "_id": "AAAAAAGOfNxdI1FxNtY=", + "_parent": { + "$ref": "AAAAAAGFy5jjfYCEQQw=" + }, + "model": { + "$ref": "AAAAAAGOfNxdI1FvDXM=" + }, + "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72RUxM=", + "_id": "AAAAAAGOfNxdI1FyRho=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, "model": { - "$ref": "AAAAAAGHThHtV72KMTA=" + "$ref": "AAAAAAGOfNxdI1FvDXM=" }, "visible": false, "font": "Arial;13;0", - "left": 443, - "top": 783, + "left": 604, + "top": 686, "height": 13, - "alpha": 0.5235987755982988, - "distance": 30, + "alpha": 1.5707963267948966, + "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, - "edgePosition": 2 + "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72Suag=", + "_id": "AAAAAAGOfNxdI1FzHnE=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, "model": { - "$ref": "AAAAAAGHThHtV72KMTA=" + "$ref": "AAAAAAGOfNxdI1FvDXM=" }, - "visible": false, "font": "Arial;13;0", - "left": 444, - "top": 770, + "left": 563, + "top": 689, + "width": 52.76806640625, "height": 13, - "alpha": 0.7853981633974483, - "distance": 40, + "alpha": 1.5707963267948966, + "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, - "edgePosition": 2 + "edgePosition": 1, + "text": "«deploy»" }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72TbRc=", + "_id": "AAAAAAGOfNxdI1F0UcQ=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, "model": { - "$ref": "AAAAAAGHThHtV72KMTA=" + "$ref": "AAAAAAGOfNxdI1FvDXM=" }, "visible": false, "font": "Arial;13;0", - "left": 443, - "top": 811, + "left": 633, + "top": 681, "height": 13, - "alpha": -0.5235987755982988, - "distance": 25, + "alpha": -1.5707963267948966, + "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNxdI1FxNtY=" }, - "edgePosition": 2 - }, + "edgePosition": 1 + } + ], + "font": "Arial;13;0", + "head": { + "$ref": "AAAAAAGFy5kz9oCMjsw=" + }, + "tail": { + "$ref": "AAAAAAGFy6ywAGV8HMM=" + }, + "lineStyle": 1, + "points": "630:751;608:629", + "showVisibility": true, + "nameLabel": { + "$ref": "AAAAAAGOfNxdI1FyRho=" + }, + "stereotypeLabel": { + "$ref": "AAAAAAGOfNxdI1FzHnE=" + }, + "propertyLabel": { + "$ref": "AAAAAAGOfNxdI1F0UcQ=" + } + }, + { + "_type": "UMLDependencyView", + "_id": "AAAAAAGOfNy5s2wt6cE=", + "_parent": { + "$ref": "AAAAAAGFy5jjfYCEQQw=" + }, + "model": { + "$ref": "AAAAAAGOfNy5s2wrO8Q=" + }, + "subViews": [ { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72UgzI=", + "_id": "AAAAAAGOfNy5s2wu5oQ=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNy5s2wt6cE=" }, "model": { - "$ref": "AAAAAAGHThHtV72LLfs=" + "$ref": "AAAAAAGOfNy5s2wrO8Q=" }, "visible": false, "font": "Arial;13;0", - "left": 547, - "top": 770, + "left": 504, + "top": 777, "height": 13, - "alpha": -0.5235987755982988, - "distance": 30, + "alpha": 1.5707963267948966, + "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" - } + "$ref": "AAAAAAGOfNy5s2wt6cE=" + }, + "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72VoHI=", + "_id": "AAAAAAGOfNy5s2wvvj4=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNy5s2wt6cE=" }, "model": { - "$ref": "AAAAAAGHThHtV72LLfs=" + "$ref": "AAAAAAGOfNy5s2wrO8Q=" }, - "visible": false, + "visible": null, "font": "Arial;13;0", - "left": 543, - "top": 757, + "left": 504, + "top": 792, "height": 13, - "alpha": -0.7853981633974483, - "distance": 40, + "alpha": 1.5707963267948966, + "distance": 30, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" - } + "$ref": "AAAAAAGOfNy5s2wt6cE=" + }, + "edgePosition": 1 }, { "_type": "EdgeLabelView", - "_id": "AAAAAAGHThHtV72WmqA=", + "_id": "AAAAAAGOfNy5s2wwUV8=", "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" + "$ref": "AAAAAAGOfNy5s2wt6cE=" }, "model": { - "$ref": "AAAAAAGHThHtV72LLfs=" + "$ref": "AAAAAAGOfNy5s2wrO8Q=" }, "visible": false, "font": "Arial;13;0", - "left": 555, - "top": 797, + "left": 505, + "top": 748, "height": 13, - "alpha": 0.5235987755982988, - "distance": 25, + "alpha": -1.5707963267948966, + "distance": 15, "hostEdge": { - "$ref": "AAAAAAGHThHtV72N+6M=" - } - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHThHtV72XoIY=", - "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" - }, - "model": { - "$ref": "AAAAAAGHThHtV72KMTA=" - }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 - }, - { - "_type": "UMLQualifierCompartmentView", - "_id": "AAAAAAGHThHtV72Ykig=", - "_parent": { - "$ref": "AAAAAAGHThHtV72N+6M=" - }, - "model": { - "$ref": "AAAAAAGHThHtV72LLfs=" + "$ref": "AAAAAAGOfNy5s2wt6cE=" }, - "visible": false, - "font": "Arial;13;0", - "width": 10, - "height": 10 + "edgePosition": 1 } ], "font": "Arial;13;0", "head": { - "$ref": "AAAAAAGFy6zKKmkZQQU=" + "$ref": "AAAAAAGHThGfj6SgxP8=" }, "tail": { - "$ref": "AAAAAAGHThGfj6SgxP8=" + "$ref": "AAAAAAGFy6ywAGV8HMM=" }, "lineStyle": 1, - "points": "420:808;575:788", + "points": "583:772;428:767", "showVisibility": true, "nameLabel": { - "$ref": "AAAAAAGHThHtV72OEDc=" + "$ref": "AAAAAAGOfNy5s2wu5oQ=" }, "stereotypeLabel": { - "$ref": "AAAAAAGHThHtV72PfNQ=" + "$ref": "AAAAAAGOfNy5s2wvvj4=" }, "propertyLabel": { - "$ref": "AAAAAAGHThHtV72Qxfg=" - }, - "showEndOrder": "hide", - "tailRoleNameLabel": { - "$ref": "AAAAAAGHThHtV72RUxM=" - }, - "tailPropertyLabel": { - "$ref": "AAAAAAGHThHtV72Suag=" - }, - "tailMultiplicityLabel": { - "$ref": "AAAAAAGHThHtV72TbRc=" - }, - "headRoleNameLabel": { - "$ref": "AAAAAAGHThHtV72UgzI=" - }, - "headPropertyLabel": { - "$ref": "AAAAAAGHThHtV72VoHI=" - }, - "headMultiplicityLabel": { - "$ref": "AAAAAAGHThHtV72WmqA=" - }, - "tailQualifiersCompartment": { - "$ref": "AAAAAAGHThHtV72XoIY=" - }, - "headQualifiersCompartment": { - "$ref": "AAAAAAGHThHtV72Ykig=" + "$ref": "AAAAAAGOfNy5s2wwUV8=" } } ] @@ -8441,7 +6166,7 @@ "_parent": { "$ref": "AAAAAAFF+qBWK6M3Z8Y=" }, - "name": "dex oidc", + "name": "Dex", "ownedElements": [ { "_type": "UMLCommunicationPath", @@ -8507,7 +6232,7 @@ "_parent": { "$ref": "AAAAAAFF+qBWK6M3Z8Y=" }, - "name": "waypoint", + "name": "paas", "ownedElements": [ { "_type": "UMLCommunicationPath", @@ -9013,7 +6738,7 @@ "_parent": { "$ref": "AAAAAAFF+qBWK6M3Z8Y=" }, - "name": "Ansible", + "name": "Terraform", "ownedElements": [ { "_type": "UMLDeployment", @@ -9021,12 +6746,95 @@ "_parent": { "$ref": "AAAAAAGFy6ywAGV6azA=" }, + "name": "2", + "source": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "target": { + "$ref": "AAAAAAGFy5kz9oCKvMA=" + } + }, + { + "_type": "UMLCommunicationPath", + "_id": "AAAAAAGOfNfIzH55o0I=", + "_parent": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "name": "auth", + "end1": { + "_type": "UMLAssociationEnd", + "_id": "AAAAAAGOfNfIzH56zxg=", + "_parent": { + "$ref": "AAAAAAGOfNfIzH55o0I=" + }, + "reference": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + } + }, + "end2": { + "_type": "UMLAssociationEnd", + "_id": "AAAAAAGOfNfIzH57x8E=", + "_parent": { + "$ref": "AAAAAAGOfNfIzH55o0I=" + }, + "reference": { + "$ref": "AAAAAAGFy5mK8YC2hJw=" + } + } + }, + { + "_type": "UMLDependency", + "_id": "AAAAAAGOfNsXEiesEWA=", + "_parent": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "name": "pull qcow", + "source": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "target": { + "$ref": "AAAAAAGHQZBoU/5LdFQ=" + } + }, + { + "_type": "UMLDependency", + "_id": "AAAAAAGOfNtH7S6NYbU=", + "_parent": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "name": "deploy", + "source": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "target": { + "$ref": "AAAAAAGFy61rGZuaTAE=" + } + }, + { + "_type": "UMLDeployment", + "_id": "AAAAAAGOfNxdI1FvDXM=", + "_parent": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, "source": { "$ref": "AAAAAAGFy6ywAGV6azA=" }, "target": { "$ref": "AAAAAAGFy5kz9oCKvMA=" } + }, + { + "_type": "UMLDependency", + "_id": "AAAAAAGOfNy5s2wrO8Q=", + "_parent": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "source": { + "$ref": "AAAAAAGFy6ywAGV6azA=" + }, + "target": { + "$ref": "AAAAAAGHThGfj6SeVCQ=" + } } ] }, @@ -9086,7 +6894,7 @@ "_parent": { "$ref": "AAAAAAFF+qBWK6M3Z8Y=" }, - "name": "contabo", + "name": "contabo instance", "ownedElements": [ { "_type": "UMLDependency", @@ -9162,7 +6970,7 @@ "_parent": { "$ref": "AAAAAAFF+qBWK6M3Z8Y=" }, - "name": "packer", + "name": "nix", "ownedElements": [ { "_type": "UMLDeployment", @@ -9581,4 +7389,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..66c0ea57 --- /dev/null +++ b/flake.lock @@ -0,0 +1,234 @@ +{ + "nodes": { + "darwin": { + "inputs": { + "nixpkgs": [ + "srvos", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1716329735, + "narHash": "sha256-ap51w+VqG21vuzyQ04WrhI2YbWHd3UGz0e7dc/QQmoA=", + "owner": "LnL7", + "repo": "nix-darwin", + "rev": "eac4f25028c1975a939c8f8fba95c12f8a25e01c", + "type": "github" + }, + "original": { + "owner": "LnL7", + "repo": "nix-darwin", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "srvos", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1715930644, + "narHash": "sha256-W9pyM3/vePxrffHtzlJI6lDS3seANQ+Nqp+i58O46LI=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "e3ad5108f54177e6520535768ddbf1e6af54b59d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "home-manager", + "type": "github" + } + }, + "nixlib": { + "locked": { + "lastModified": 1712450863, + "narHash": "sha256-K6IkdtMtq9xktmYPj0uaYc8NsIqHuaAoRBaMgu9Fvrw=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "3c62b6a12571c9a7f65ab037173ee153d539905f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixos-generators": { + "inputs": { + "nixlib": "nixlib", + "nixpkgs": [ + "srvos", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1716210724, + "narHash": "sha256-iqQa3omRcHGpWb1ds75jS9ruA5R39FTmAkeR3J+ve1w=", + "owner": "nix-community", + "repo": "nixos-generators", + "rev": "d14b286322c7f4f897ca4b1726ce38cb68596c94", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixos-generators", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1716127062, + "narHash": "sha256-2rk8FqB/iQV2d0vQLs684/Tj5PUHaS1sFwG7fng5vXE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8a2555763c48e2410054de3f52f7310ce3241ec5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1701282334, + "narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable-darwin": { + "locked": { + "lastModified": 1716389660, + "narHash": "sha256-K8xKOu3/ix1Ki25Qa7Xq0qo/+eYmrGJNCRdelhp0QDI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "cbaa9d85551c96310edf4f388e17a49ec846223e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1716293225, + "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "darwin": "darwin", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "home-manager": "home-manager", + "nixos-generators": "nixos-generators", + "nixpkgs-srvos": [ + "srvos", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable", + "nixpkgs-stable-darwin": "nixpkgs-stable-darwin", + "nixpkgs-unstable": "nixpkgs-unstable", + "srvos": "srvos" + } + }, + "srvos": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1716233075, + "narHash": "sha256-XG+BujRzINjMEPg8FevP2UnFgT4UyTaOEZq/d2r9LYQ=", + "owner": "numtide", + "repo": "srvos", + "rev": "c8022a61950cb41db586ab4ae7079b68cbc18fff", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "srvos", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..217b5373 --- /dev/null +++ b/flake.nix @@ -0,0 +1,203 @@ +{ + description = "Nix Darwin configuration for my systems (from https://github.com/malob/nixpkgs)"; + + inputs = { + # Package sets + nixpkgs-stable.url = "github:NixOS/nixpkgs/23.11"; + nixpkgs-stable-darwin.url = "github:NixOS/nixpkgs/nixpkgs-23.11-darwin"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + srvos.url = "github:numtide/srvos"; + nixpkgs-srvos.follows = "srvos/nixpkgs"; + + # Environment/system management + darwin.url = "github:LnL7/nix-darwin"; + darwin.inputs.nixpkgs.follows = "srvos/nixpkgs"; + + home-manager = { + url = "github:nix-community/home-manager/master"; + inputs.nixpkgs.follows = "srvos/nixpkgs"; + }; + + nixos-generators = { + url = "github:nix-community/nixos-generators"; + inputs.nixpkgs.follows = "srvos/nixpkgs"; + }; + + # Flake utilities + flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; + flake-utils.url = "github:numtide/flake-utils"; + + }; + + outputs = { self, srvos, darwin, nixos-generators, flake-utils, ... }@inputs: + let + inherit (self.lib) attrValues makeOverridable mkForce optionalAttrs singleton; + nixpkgsDefaults = { + config = { + allowUnfree = true; + }; + }; + in + { + lib = inputs.nixpkgs-stable-darwin.lib.extend (_: _: { + mkDarwinSystem = import ./nix-lib/mkDarwinSystem.nix inputs; + }); + + overlays = { + pkgs-stable = _: prev: { + pkgs-stable = import inputs.nixpkgs-stable { + inherit (prev.stdenv) system; + inherit (nixpkgsDefaults) config; + }; + }; + pkgs-unstable = _: prev: { + pkgs-unstable = import inputs.nixpkgs-unstable { + inherit (prev.stdenv) system; + inherit (nixpkgsDefaults) config; + }; + }; + apple-silicon = _: prev: optionalAttrs (prev.stdenv.system == "aarch64-darwin") { + # Add access to x86 packages system is running Apple Silicon + pkgs-x86 = import inputs.nixpkgs-unstable { + system = "x86_64-darwin"; + inherit (nixpkgsDefaults) config; + }; + }; + + tweaks = _: _: { + # Add temporary overrides here + }; + }; + + darwinModules = { + config = ./nixos-options/default.nix; + os = ./nixos-darwin/configuration.nix; + }; + + nixosModules = { + common = srvos.nixosModules.common; + server = srvos.nixosModules.server; + home-manager = inputs.home-manager.nixosModules.home-manager; + os = ./nixos/configuration.nix; + config = ./nixos-options/default.nix; + }; + + darwinConfigurations = { + default = self.darwinConfigurations.builder; + builder = makeOverridable self.lib.mkDarwinSystem ({ + modules = attrValues self.darwinModules ++ singleton { + nixpkgs = nixpkgsDefaults; + nix.registry.my.flake = inputs.self; + }; + extraModules = singleton {}; + }); + + # Need a bare darwinConfigurations.builder started before building this one. + builder-docker = self.darwinConfigurations.builder.override { + extraModules = singleton { + nix.linux-builder.config = ./nixos-darwin/linux-builder-docker.nix; + }; + }; + + # Config with small modifications needed/desired for CI with GitHub workflow + githubCI = self.darwinConfigurations.k3s-paas-host.override { + system = "x86_64-darwin"; + username = "runner"; + nixConfigDirectory = "/Users/runner/work/nixpkgs/nixpkgs"; + extraModules = singleton { + environment.etc.shells.enable = mkForce false; + environment.etc."nix/nix.conf".enable = mkForce false; + homebrew.enable = mkForce false; + }; + }; + }; + + } // flake-utils.lib.eachDefaultSystem (system: + let + linux = builtins.replaceStrings ["darwin"] ["linux"] system; + legacyPackages = import inputs.nixpkgs-srvos (nixpkgsDefaults // { inherit system; }); + stableLegacyPackages = import inputs.nixpkgs-stable (nixpkgsDefaults // { inherit system; }); + in { + # Re-export `nixpkgs-stable` with overlays. + # This is handy in combination with setting `nix.registry.my.flake = inputs.self`. + # Allows doing things like `nix run my#prefmanager -- watch --all` + inherit legacyPackages; + inherit stableLegacyPackages; + + nixosConfigurations = rec { + default = qcow; + + qcow = makeOverridable nixos-generators.nixosGenerate { + system = linux; + modules = attrValues self.nixosModules; + format = "qcow"; + specialArgs = { + inherit stableLegacyPackages; + }; + }; + + contabo = self.nixosConfigurations.${system}.qcow.override { + modules = attrValues self.nixosModules ++ [ + ./nixos/contabo.nix + ]; + }; + + docker = self.nixosConfigurations.${system}.qcow.override { + modules = attrValues self.nixosModules ++ [ + ./nixos/docker.nix + ]; + format = "docker"; + }; + }; + + # Development shells + # Shell environments for development + # With `nix.registry.my.flake = inputs.self`, development shells can be created by running, + # e.g., `nix develop my#python`. + devShells = let + pkgs = self.legacyPackages.${system}; + stablePkgs = self.stableLegacyPackages.${system}; + in + { + default = pkgs.mkShell { + name = "default"; + packages = attrValues { + inherit (pkgs) bashInteractive grpcurl jq coreutils e2fsprogs + docker-client kubectl kubernetes-helm libvirt qemu + tailscale pebble cntb + nil nix-tree; + inherit (stablePkgs) terraform waypoint; + }; + shellHook = '' + export DOCKER_HOST=tcp://127.0.0.1:2375 + ''; + }; + + builder-docker = pkgs.mkShell { + name = "docker"; + packages = attrValues { + inherit (pkgs) nil bashInteractive docker-client; + }; + shellHook = '' + set -e + nix build .#darwinConfigurations.builder-docker.system + ./result/sw/bin/darwin-rebuild switch --flake .#builder-docker + export DOCKER_HOST=tcp://127.0.0.1:2375 + ''; + }; + + builder = pkgs.mkShell { + name = "builder"; + packages = attrValues { + inherit (pkgs) nil bashInteractive; + }; + shellHook = (if pkgs.system == "aarch64-darwin" then '' + set -e + nix build .#darwinConfigurations.builder.system + ./result/sw/bin/darwin-rebuild switch --flake .#builder + '' else "echo 'Linux not implemented'"); + }; + }; + }); +} +# vim: foldmethod=marker diff --git a/main.tf b/main.tf new file mode 100644 index 00000000..0bfe6834 --- /dev/null +++ b/main.tf @@ -0,0 +1,96 @@ +locals { + cert_manager_acme_url = var.letsencrypt_envs[var.cert_manager_letsencrypt_env] + cert_manager_acme_ca_cert_url = var.letsencrypt_envs_ca_certs[var.cert_manager_letsencrypt_env] + ingress_hosts_internals = [var.paas_base_domain, local.dex_hostname, var.paas_hostname] + dex_hostname = "dex.${var.paas_base_domain}" + paas_hostname = "paas.${var.paas_base_domain}" + internal_acme_hostname = "acme-internal.${var.paas_base_domain}" +} + +data "http" "paas_internal_acme_ca" { + url = local.cert_manager_acme_ca_cert_url + count = var.cert_manager_letsencrypt_env != "prod" ? 1 : 0 + insecure = var.cert_manager_letsencrypt_env == "local" +} + +module "metallb" { + source = "./tf-modules-k8s/metallb" + metallb_ip_range = var.metallb_ip_range + for_each = var.metallb_ip_range != null ? toset(["metallb"]) : toset([]) +} + +module "cert_manager" { + depends_on = [module.metallb[0]] + source = "./tf-modules-k8s/cert-manager" + internal_acme_ca_content = length(data.http.paas_internal_acme_ca) > 0 ? data.http.paas_internal_acme_ca[0].response_body : null + cert_manager_acme_url = replace(local.cert_manager_acme_url, "localhost", local.internal_acme_hostname) + letsencrypt_env = var.cert_manager_letsencrypt_env +} + +module "ingress-nginx" { + source = "./tf-modules-k8s/nginx-ingress-controller" + cert_manager_cluster_issuer = module.cert_manager.issuer + paas_base_domain = var.paas_base_domain + default_ssl_certificate = true +} + +module "internal_ca" { + source = "./tf-modules-k8s/internal-ca" + for_each = var.cert_manager_letsencrypt_env == "local" ? toset(["internal-ca"]) : toset([]) + internal_acme_hostname = local.internal_acme_hostname + internal_acme_network_ip = var.internal_network_ip + ingress_hosts_internals = local.ingress_hosts_internals + ingress_controller_ip = module.ingress-nginx.ingress_controller_ip +} + +module "github" { + source = "./tf-modules-k8s/github" + github_token = var.github_token + github_organization = var.github_organization + github_team = var.github_team +} + +module "dex" { + depends_on = [ + module.cert_manager.reflector_metadata_name + ] + source = "./tf-modules-k8s/dex" + dex_namespace = var.dex_namespace + dex_hostname = local.dex_hostname + github_client_id = var.github_client_id + github_client_secret = var.github_client_secret + dex_github_orgs = [{ + name = var.github_organization + teams = [module.github.team_name] + }] + k8s_ingress_class = var.k8s_ingress_class + paas_hostname = local.paas_hostname + cert_manager_cluster_issuer = module.cert_manager.issuer +} + +module "paas" { + depends_on = [module.dex.dex_ingress] + source = "./tf-modules-k8s/waypoint" + paas_hostname = local.paas_hostname + k8s_ingress_class = var.k8s_ingress_class + waypoint_extra_volume_mounts = module.cert_manager.root_ca_config_map_volume_mounts + waypoint_extra_volumes = module.cert_manager.root_ca_config_map_volume + cert_manager_cluster_issuer = module.cert_manager.issuer +} + +module "paas_config" { + source = "./tf-modules-k8s/waypoint-config" + paas_hostname = local.paas_hostname + paas_token = module.paas.token + dex_hostname = local.dex_hostname + dex_client_id = module.dex.dex_client_id + dex_client_secret = module.dex.dex_client_secret + github_team = var.github_team + tls_skip_verify = var.cert_manager_letsencrypt_env == "local" + internal_acme_ca_content = length(data.http.paas_internal_acme_ca) > 0 ? data.http.paas_internal_acme_ca[0].response_body : null +} + +output "paas_token" { + value = module.paas.token + sensitive = true +} diff --git a/nix-lib/mkDarwinSystem.nix b/nix-lib/mkDarwinSystem.nix new file mode 100644 index 00000000..8aed5f5b --- /dev/null +++ b/nix-lib/mkDarwinSystem.nix @@ -0,0 +1,20 @@ +inputs: + +{ system ? "aarch64-darwin" +# `nix-darwin` modules to include +, modules ? [ ] +# Additional `nix-darwin` modules to include, useful when reusing a configuration with +# `lib.makeOverridable`. +, extraModules ? [ ] +, specialArgs ? {} +}: + +inputs.darwin.lib.darwinSystem { + inherit system; + inherit specialArgs; + modules = modules ++ extraModules ++ [ + ({ config, ... }: { + nix.nixPath.nixpkgs = "${inputs.nixpkgs-stable-darwin}"; + }) + ]; +} diff --git a/nixos-darwin/configuration.nix b/nixos-darwin/configuration.nix new file mode 100644 index 00000000..c8574de5 --- /dev/null +++ b/nixos-darwin/configuration.nix @@ -0,0 +1,107 @@ +{ + pkgs, + config, + ... }: +{ + programs.fish.enable = true; + programs.bash.enable = true; + programs.direnv.enable = true; + environment.systemPackages = [ pkgs.bashInteractive ]; + + services.dnsmasq = { + enable = true; + addresses = { + ".${config.k3s-paas.dns.name}" = config.k3s-paas.dns.dest-ip; + }; + }; + launchd.daemons."libvirt" = { + path = [ pkgs.gcc pkgs.qemu pkgs.dnsmasq pkgs.libvirt ]; + serviceConfig = { + KeepAlive = true; + RunAtLoad = true; + ProgramArguments = [ + "${pkgs.libvirt}/bin/libvirtd" "-f" "/etc/libvirt/libvirtd.conf" "-v" + ]; + StandardOutPath = "/var/log/libvirt/libvirt.log"; + StandardErrorPath = "/var/log/libvirt/libvirt-error.log"; + }; + }; + launchd.daemons."virtlogd" = { + path = [ pkgs.libvirt ]; + serviceConfig = { + KeepAlive = true; + RunAtLoad = true; + ProgramArguments = [ "${pkgs.libvirt}/bin/virtlogd" "-d" ]; + StandardOutPath = "/var/log/libvirt/virtlogd.log"; + StandardErrorPath = "/var/log/libvirt/virtlogd-error.log"; + }; + }; + launchd.daemons."pebble" = { + path = [ pkgs.pebble ]; + serviceConfig = { + KeepAlive = true; + RunAtLoad = true; + ProgramArguments = [ "${pkgs.pebble}/bin/pebble" "-config" "/etc/pebble/config.json" ]; + StandardOutPath = "/var/log/pebble.log"; + StandardErrorPath = "/var/log/pebble-error.log"; + }; + }; + environment.etc."libvirt/libvirtd.conf".text = '' + mode = "direct" + unix_sock_group = "staff" + unix_sock_ro_perms = "0770" + unix_sock_rw_perms = "0770" + unix_sock_admin_perms = "0770" + auth_unix_ro = "none" + auth_unix_rw = "none" + log_level = 1 + log_outputs="1:stderr" + ''; + environment.etc."libvirt/qemu.conf".text = '' + security_driver = "none" + dynamic_ownership = 0 + remember_owner = 0 + ''; + security.pki.certificateFiles = [ + "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + ./pebble/cert.pem + ]; + environment.etc."pebble/config.json".text = builtins.toJSON { + pebble = { + listenAddress = "0.0.0.0:14000"; + managementListenAddress = "0.0.0.0:15000"; + certificate = pkgs.writeText "pebble-cert" (builtins.readFile ./pebble/cert.pem); + privateKey = pkgs.writeText "pebble-key" (builtins.readFile ./pebble/key.pem); + httpPort = 80; + tlsPort = 443; + ocspResponderURL = ""; + externalAccountBindingRequired = false; + }; + }; + environment.etc."resolver/${config.k3s-paas.dns.name}".text = "nameserver ${config.k3s-paas.dns.dest-ip}"; + nix.settings = { + trusted-users = [ "staff" "admin" "nixbld" "loic"]; + keep-derivations = true; + keep-outputs = false; + # https://github.com/NixOS/nix/issues/7273 + auto-optimise-store = false; + system-features = [ + "nixos-test" + "apple-virt" + ]; + }; + nix.gc = { + automatic = true; + interval = { Weekday = 0; Hour = 0; Minute = 0; }; + options = "--delete-older-than 30d"; + }; + nix.linux-builder = { + enable = true; + maxJobs = 8; + package = pkgs.darwin.linux-builder-x86_64; + ephemeral = true; + }; + nix.configureBuildUsers = true; + services.nix-daemon.enable = true; + nix.settings.experimental-features = "nix-command flakes"; +} diff --git a/nixos-darwin/linux-builder-docker.nix b/nixos-darwin/linux-builder-docker.nix new file mode 100644 index 00000000..b630a934 --- /dev/null +++ b/nixos-darwin/linux-builder-docker.nix @@ -0,0 +1,13 @@ +{ pkgs, lib, ... }: { + virtualisation.docker.enable = true; + virtualisation.docker.daemon.settings = { + hosts = [ "tcp://0.0.0.0:2375" ]; + }; + networking.firewall.enable = lib.mkForce false; + virtualisation.forwardPorts = lib.mkForce [ + { from = "host"; guest.port = 22; host.port = 31022; } + { from = "host"; guest.port = 2375; host.port = 2375; } + ]; + security.sudo.wheelNeedsPassword = false; + users.users.builder.extraGroups = lib.mkForce [ "docker" "wheel" ]; +} diff --git a/playbook/roles/waypoint/molecule/default/pebble/cert.pem b/nixos-darwin/pebble/cert.pem similarity index 100% rename from playbook/roles/waypoint/molecule/default/pebble/cert.pem rename to nixos-darwin/pebble/cert.pem diff --git a/playbook/roles/waypoint/molecule/default/pebble/key.pem b/nixos-darwin/pebble/key.pem similarity index 100% rename from playbook/roles/waypoint/molecule/default/pebble/key.pem rename to nixos-darwin/pebble/key.pem diff --git a/nixos-options/default.nix b/nixos-options/default.nix new file mode 100644 index 00000000..00c92831 --- /dev/null +++ b/nixos-options/default.nix @@ -0,0 +1,57 @@ +{ lib, ... }: + +{ + options.k3s-paas = { + + certs = lib.mkOption { + default = [{ + url = "https://localhost:15000/intermediates/0"; + sha256 = "06fpbiljbzmcnfsxnr92p7mhm6i4yglbhj5q7csw2pcsklw68z8n"; + }]; + type = lib.types.listOf (lib.types.attrs); + description = "Ca url to fetch and trust (need to be impure)"; + }; + + dns.name = lib.mkOption { + default = "k3s.test"; + type = lib.types.str; + description = "hostname for k3s-paas"; + }; + + dns.dest-ip = lib.mkOption { + default = "127.0.0.1"; + type = lib.types.str; + description = "Target IP address for dns.name (only in local dev)"; + }; + + user.name = lib.mkOption { + default = "zizou"; + type = lib.types.str; + description = "User name"; + }; + + user.password = lib.mkOption { + default = "$6$zizou$reVO3q7LFsUq.GT5P5pYFFcpxCo7eTRT5yJTD.gVoOy/FSzHEtXdofvZ7E04Rej.jiQHKaWJB0Qob5FHov1WU/"; + type = lib.types.str; + description = "User password"; + }; + + user.key = lib.mkOption { + default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC94/4uRn429xMGLFWZMyJWlhb5D0L3EoO8HxzN4q1ps loic@Windows-8-Phone.local"; + type = lib.types.str; + description = "SSH public key for k3s-paas."; + }; + + k3s.disableServices = lib.mkOption { + default = "traefik"; + type = lib.types.str; + description = "Disable k3s services eg: traefik,servicelb"; + }; + + dex.dex_client_id = lib.mkOption { + default = "client-id"; + type = lib.types.str; + description = "Client ID for Dex"; + }; + }; +} diff --git a/nixos/configuration.nix b/nixos/configuration.nix new file mode 100644 index 00000000..d17c89bb --- /dev/null +++ b/nixos/configuration.nix @@ -0,0 +1,189 @@ +{ + config, + lib, + pkgs, + stableLegacyPackages, + ... +}: + +let + dex_hostname = "https://dex.${config.k3s-paas.dns.name}"; + certs = builtins.map (cert: builtins.fetchurl { inherit (cert) url sha256; }) config.k3s-paas.certs; + certManagerCrds = builtins.fetchurl { + url = "https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.crds.yaml"; + sha256 = "060bn3gvrr5jphaig1g195prip5rn0x1s7qrp09q47719fgc6636"; + }; + manifests = builtins.filter (d: d != "") [certManagerCrds]; +in { + console = { + earlySetup = true; + keyMap = "fr"; + }; + + boot.kernelPackages = pkgs.linuxPackages_latest; + fileSystems."/".autoResize = true; + fileSystems."/boot" = + { device = "/dev/disk/by-label/boot"; + fsType = "vfat"; + }; + + swapDevices = [ { + device = "/var/lib/swapfile"; + size = 16 * 1024; + } ]; + + boot.loader.systemd-boot.consoleMode = "auto"; + + system.stateVersion = "23.05"; + # FIXME: when branch is merged, uncomment the following line + # system.autoUpgrade.flake = "github:loic-roux-404/k3s-paas#nixosConfigurations.${pkgs.system}.default"; + + time = { + timeZone = lib.mkForce "Europe/Paris"; + }; + + i18n.defaultLocale = "en_US.UTF-8"; + + programs.ssh.package = pkgs.openssh_hpn; + + services = { + openssh = { + enable = true; + settings = { + # Allow forwarding ports to everywhere + GatewayPorts = "clientspecified"; + PasswordAuthentication = lib.mkForce false; + StreamLocalBindUnlink = lib.mkForce "yes"; + PermitRootLogin = "no"; + }; + }; + tailscale = { + enable = true; + }; + k3s = { + enable = true; + role = "server"; + extraFlags = with config.k3s-paas; toString [ + "--kube-apiserver-arg authorization-mode=Node,RBAC" + "--kube-apiserver-arg oidc-issuer-url=${dex_hostname}" + "--kube-apiserver-arg oidc-client-id=${dex.dex_client_id}" + "--kube-apiserver-arg oidc-username-claim=email" + "--kube-apiserver-arg oidc-groups-claim=groups" + (if k3s.disableServices != "" then "--disable=${k3s.disableServices}" else "") + ]; + }; + + fail2ban.enable = true; + }; + + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.users.${config.k3s-paas.user.name} = { + xdg.enable = true; + home.stateVersion = "23.05"; + home.sessionVariables = { + EDITOR = "vim"; + PAGER = "less -FirSwX"; + }; + programs.bash = { + enable = true; + historyControl = [ "ignoredups" "ignorespace" ]; + }; + }; + + system.activationScripts.k3s-certs.text = '' + mkdir -p /var/lib/rancher/k3s/server/manifests + '' + lib.strings.concatMapStrings + (drv: "cp -fp ${drv} /var/lib/rancher/k3s/server/manifests;") manifests; + + environment = { + shells = [ pkgs.bashInteractive ]; + systemPackages = with pkgs; [ + glibcLocales + systemd + coreutils + gawk + bashInteractive + vim + gitMinimal + openssh_hpn + btop + curl + dnsutils + jq + wget + k3s + kubectl + stableLegacyPackages.waypoint + tailscale + ]; + }; + + security.sudo.wheelNeedsPassword = false; + + users = { + defaultUserShell = pkgs.bashInteractive; + allowNoPasswordLogin = true; + users = { + ${config.k3s-paas.user.name} = { + password = config.k3s-paas.user.password; + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" ]; + openssh = { + authorizedKeys = { + keys = [ + config.k3s-paas.user.key + ]; + }; + }; + }; + }; + }; + + networking = { + hostName = "k3s-paas"; + useNetworkd = true; + useDHCP = false; + firewall = { + enable = true; + allowedTCPPorts = lib.mkForce [80 443 22 6443]; + }; + nftables.enable = true; + networkmanager.enable = true; + usePredictableInterfaceNames = true; + }; + + systemd.network = { + enable = true; + wait-online.anyInterface = true; + }; + + security.pki.certificateFiles = certs; + + nixpkgs = { + config = { + allowUnfree = true; + allowUnsupportedSystem = true; + }; + }; + + nix = { + optimise = { + automatic = true; + }; + + settings.auto-optimise-store = true; + + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + + extraOptions = '' + experimental-features = nix-command flakes + keep-outputs = true + keep-derivations = true + ''; + }; +} diff --git a/nixos/contabo.nix b/nixos/contabo.nix new file mode 100644 index 00000000..f5c005c6 --- /dev/null +++ b/nixos/contabo.nix @@ -0,0 +1,12 @@ +{ lib, ... }: +{ + # boot.loader.grub = { + # efiSupport = true; + # efiInstallAsRemovable = true; + # device = "nodev"; + # }; + + boot.initrd.kernelModules = lib.mkForce ["dm-snapshot"]; + k3s-paas.dns.name = "404-tools.xyz"; + k3s-paas.certs = []; +} diff --git a/nixos/docker.nix b/nixos/docker.nix new file mode 100644 index 00000000..8134286d --- /dev/null +++ b/nixos/docker.nix @@ -0,0 +1,5 @@ +{ lib , ... }: +{ + networking.useHostResolvConf = lib.mkForce false; + services.resolved.enable = true; +} \ No newline at end of file diff --git a/packer/.gitignore b/packer/.gitignore deleted file mode 100644 index 599ac8e3..00000000 --- a/packer/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Cache objects -packer_cache/ - -# Crash log -crash.log - -# https://www.packer.io/guides/hcl/variables -# Exclude all .pkrvars.hcl files, which are likely to contain sensitive data, -# such as password, private keys, and other secrets. These should not be part of -# version control as they are data points which are potentially sensitive and -# subject to change depending on the environment. -# -*.pkrvars.hcl - -# For built boxes -*.box -# plugins -.packer.d/ - -# qemu build -.qemu-*/ - -# Logs -ubuntu-*.log diff --git a/packer/Darwin-arm64-host.hcl b/packer/Darwin-arm64-host.hcl deleted file mode 100644 index 88543c53..00000000 --- a/packer/Darwin-arm64-host.hcl +++ /dev/null @@ -1 +0,0 @@ -accelerator = "tcg" diff --git a/packer/Darwin-x86_64-host.hcl b/packer/Darwin-x86_64-host.hcl deleted file mode 100644 index 88543c53..00000000 --- a/packer/Darwin-x86_64-host.hcl +++ /dev/null @@ -1 +0,0 @@ -accelerator = "tcg" diff --git a/packer/Linux-x86_64-host.hcl b/packer/Linux-x86_64-host.hcl deleted file mode 100644 index 88543c53..00000000 --- a/packer/Linux-x86_64-host.hcl +++ /dev/null @@ -1 +0,0 @@ -accelerator = "tcg" diff --git a/packer/Makefile b/packer/Makefile deleted file mode 100644 index 7307edc5..00000000 --- a/packer/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -UBUNTU_TPL:=ubuntu.pkr.hcl -SECRETS?=secrets.pkrvars.hcl -HOST_OS:=$(shell uname -ms | tr " " "-") -UBUNTU_RELEASE:=jammy - -.DEFAULT_GOAL := ubuntu - -ubuntu-debug: - PACKER_LOG=1 PACKER_LOG_PATH=ubuntu-$(UBUNTU_RELEASE).log \ - packer build -on-error ask -var-file "$(HOST_OS)-host.hcl" \ - -var-file=$(SECRETS) $(UBUNTU_TPL) - -ubuntu: - packer build -var-file "$(HOST_OS)-host.hcl" \ - -var-file=$(SECRETS) $(UBUNTU_TPL) - -ubuntu-console: - packer console -var-file "$(HOST_OS)-host.hcl" \ - -var-file=$(SECRETS) $(UBUNTU_TPL) - -clean: - rm -rf ubuntu-$(UBUNTU_RELEASE).log - diff --git a/packer/cloud-init.yaml.tmpl b/packer/cloud-init.yaml.tmpl deleted file mode 100644 index 0da14e4b..00000000 --- a/packer/cloud-init.yaml.tmpl +++ /dev/null @@ -1,52 +0,0 @@ -#cloud-config -autoinstall: - version: 1 - locale: ${locale} - keyboard: - variant: ${keyboard.variant} - layout: ${keyboard.layout} - refresh-installer: - update: yes - storage: - layout: - name: direct - network: - ethernets: - eth0: - dhcp4: true - dhcp-identifier: mac - version: 2 - ssh: - install-server: true - allow-pw: true - user-data: - hostname: ${hostname}-server - disable_root: 0 - timezone: Europe/Paris - preserve_hostname: false - resize_rootfs: true - growpart: - mode: auto - devices: ["/"] - ignore_growroot_disabled: false - package_update: true - packages: - - curl - - ca-certificates - - wget - - unzip - users: - - name: ${ssh_username} - passwd: "${ssh_password_hash}" - groups: [adm, cdrom, dip, plugdev, sudo] - lock-passwd: false - sudo: ALL=(ALL) NOPASSWD:ALL - shell: /bin/bash - system_info: - default_user: - name: ${ssh_username} - - late-commands: - # Cgroup ensure v1 as v2 is not supported by some tooling (k8s,...) - - sed -ie 's/GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 systemd.unified_cgroup_hierarchy=0"/' /target/etc/default/grub - - curtin in-target --target /target update-grub2 diff --git a/packer/config.pkr.hcl b/packer/config.pkr.hcl deleted file mode 100644 index 19ad4065..00000000 --- a/packer/config.pkr.hcl +++ /dev/null @@ -1,8 +0,0 @@ -packer { - required_plugins { - qemu = { - version = ">= 1.0.9" - source = "github.com/hashicorp/qemu" - } - } -} diff --git a/packer/scripts/cleanup.sh b/packer/scripts/cleanup.sh deleted file mode 100644 index d27c1cd0..00000000 --- a/packer/scripts/cleanup.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -sudo apt autoremove -y --purge -sudo apt autoclean -y -sudo journalctl --rotate -sudo journalctl --vacuum-size 10M - -# Zero out the free space to save space in the final image: -sudo dd if=/dev/zero of=zero.small.file bs=1024 count=102400 -sudo dd if=/dev/zero of=zero.file bs=1024 -sudo sync ; sleep 60 ; sudo sync -sudo rm zero.small.file -sudo rm zero.file diff --git a/packer/scripts/remove-snap.sh b/packer/scripts/remove-snap.sh deleted file mode 100755 index 5cc98004..00000000 --- a/packer/scripts/remove-snap.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -echo "Removing snap..." - -# Stop the daemon -sudo systemctl disable --now snapd - -# Uninstall -sudo apt purge -y snapd - -# Tidy up dirs -sudo rm -rf /snap /var/snap /var/lib/snapd /var/cache/snapd /usr/lib/snapd ~/snap - -# Stop it from being reinstalled by 'mistake' when installing other packages -cat << EOF | sudo tee -a /etc/apt/preferences.d/no-snap.pref -Package: snapd -Pin: release a=* -Pin-Priority: -10 -EOF - -sudo chown root:root /etc/apt/preferences.d/no-snap.pref - -# done -echo "Snap removed" diff --git a/packer/ubuntu.pkr.hcl b/packer/ubuntu.pkr.hcl deleted file mode 100644 index 753b2f01..00000000 --- a/packer/ubuntu.pkr.hcl +++ /dev/null @@ -1,186 +0,0 @@ -variable "accelerator" { - type = string - default = "kvm" -} - -variable "cpus" { - type = number - default = 4 -} - -variable "disk_size" { - type = string - default = "5120M" -} - -variable "qemu_binary" { - type = string - default = "qemu-system-x86_64" -} - -variable "headless" { - type = bool - default = true -} - -variable "memory" { - type = number - default = 8192 -} - -variable "format" { - type = string - default = "qcow2" -} - -variable "packer_log" { - type = string - default = env("PACKER_LOG") -} - -variable "ssh_password" { - type = string - sensitive = true -} - -variable "ssh_password_hash" { - type = string - sensitive = true -} - -variable "ssh_username" { - type = string - sensitive = true - default = "admin" -} - -variable "locale" { - type = string - default = "fr_FR.UTF-8" -} - -variable "ubuntu_release_info" { - type = object({ - name = string - version = string - }) - default = { - name = "jammy" - version = "22.04.2" - } -} - -variable "keyboard" { - type = object({ - layout = string - variant = string - }) - default = { - layout = "fr" - variant = "fr" - } -} - -variable "playbook" { - type = object({ - dir = string - file = string - extra_arguments = list(string) - }) - default = { - dir = "../playbook" - file = "site.yaml" - extra_arguments = ["--skip-tags waypoint"] - } -} - -locals { - ubuntu_download_url = "https://releases.ubuntu.com/${var.ubuntu_release_info.name}" - ubuntu_image = "ubuntu-${var.ubuntu_release_info.version}-live-server-amd64.iso" -} - -source "qemu" "vm" { - http_content = { - "/meta-data" = "" - "/user-data" = templatefile("${abspath(path.root)}/cloud-init.yaml.tmpl", { - ssh_username = var.ssh_username - ssh_password_hash = var.ssh_password_hash - locale = var.locale - keyboard = var.keyboard - hostname = var.ubuntu_release_info.name - }) - } - boot_command = [ - "c", - "linux /casper/vmlinuz --- autoinstall ds='nocloud-net;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/' ", - "", - "initrd /casper/initrd", - "boot" - ] - iso_urls = ["${local.ubuntu_download_url}/${local.ubuntu_image}"] - iso_checksum = "file:${local.ubuntu_download_url}/SHA256SUMS" - format = var.format - boot_wait = "10s" - shutdown_command = "echo '${var.ssh_password}' | sudo -S shutdown -P now" - disk_compression = true - memory = "${var.memory}" - cpus = "${var.cpus}" - disk_size = "${var.disk_size}" - accelerator = "${var.accelerator}" - vnc_port_max = 5904 - headless = var.headless - communicator = "ssh" - ssh_timeout = var.packer_log == "1" ? "50m" : "35m" - ssh_password = var.ssh_password - ssh_username = var.ssh_username - qemu_binary = var.qemu_binary - host_port_max = 2226 - vm_name = "ubuntu-${var.ubuntu_release_info.name}-${var.ubuntu_release_info.version}.${var.format}" - output_directory = ".qemu-{{build_name}}/" -} - -build { - sources = ["source.qemu.vm"] - - provisioner "shell" { - inline = [ - "sudo cloud-init status --wait", - "sudo cloud-init clean --logs" - ] - } - - provisioner "shell" { - inline = [ - "curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py", - "sudo python3 /tmp/get-pip.py", - "sudo mkdir /playbook && sudo chown -R ${var.ssh_username}:${var.ssh_username} /playbook", - "sudo pip3 install ${replace(file("${var.playbook.dir}/requirements.txt"), "\n", " ")}" - ] - } - - provisioner "ansible-local" { - command = "sudo ansible-playbook" - galaxy_command = "sudo ansible-galaxy" - galaxy_roles_path = "/usr/share/ansible/roles" - galaxy_collections_path = "/usr/share/ansible/collections" - staging_directory = "/playbook/" - playbook_file = "${var.playbook.dir}/${var.playbook.file}" - playbook_dir = var.playbook.dir - extra_arguments = var.playbook.extra_arguments - galaxy_file = "${var.playbook.dir}/requirements.yaml" - } - - # Cleanup and minimize - provisioner "shell" { - script = "scripts/remove-snap.sh" - } - - provisioner "shell" { - script = "scripts/cleanup.sh" - } - - post-processor "checksum" { - checksum_types = ["sha256"] - output = ".qemu-{{build_name}}/SHA256SUMS" - } -} diff --git a/playbook/inventories/contabo/hosts b/playbook/inventories/contabo/hosts deleted file mode 100644 index e56ea71e..00000000 --- a/playbook/inventories/contabo/hosts +++ /dev/null @@ -1 +0,0 @@ -127.0.0.1 \ No newline at end of file diff --git a/playbook/requirements-test.txt b/playbook/requirements-test.txt deleted file mode 100644 index d3b0cf66..00000000 --- a/playbook/requirements-test.txt +++ /dev/null @@ -1,2 +0,0 @@ -molecule==4.0.4 -molecule-plugins[docker] diff --git a/playbook/requirements.txt b/playbook/requirements.txt deleted file mode 100644 index d83f4c59..00000000 --- a/playbook/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -ansible==7.3.0 -PyYAML -kubernetes diff --git a/playbook/requirements.yaml b/playbook/requirements.yaml deleted file mode 100644 index 4db1facf..00000000 --- a/playbook/requirements.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -roles: - - name: xanmanning.k3s - src: https://github.com/PyratLabs/ansible-role-k3s.git - version: v3.3.1 - -collections: - - name: community.general - - name: kubernetes.core diff --git a/playbook/roles/waypoint/.yamllint b/playbook/roles/waypoint/.yamllint deleted file mode 100644 index 88276760..00000000 --- a/playbook/roles/waypoint/.yamllint +++ /dev/null @@ -1,33 +0,0 @@ ---- -# Based on ansible-lint config -extends: default - -rules: - braces: - max-spaces-inside: 1 - level: error - brackets: - max-spaces-inside: 1 - level: error - colons: - max-spaces-after: -1 - level: error - commas: - max-spaces-after: -1 - level: error - comments: disable - comments-indentation: disable - document-start: disable - empty-lines: - max: 3 - level: error - hyphens: - level: error - indentation: disable - key-duplicates: enable - line-length: disable - new-line-at-end-of-file: disable - new-lines: - type: unix - trailing-spaces: disable - truthy: disable diff --git a/playbook/roles/waypoint/README.md b/playbook/roles/waypoint/README.md deleted file mode 100644 index 225dd44b..00000000 --- a/playbook/roles/waypoint/README.md +++ /dev/null @@ -1,38 +0,0 @@ -Role Name -========= - -A brief description of the role goes here. - -Requirements ------------- - -Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. - -Role Variables --------------- - -A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. - -Dependencies ------------- - -A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. - -Example Playbook ----------------- - -Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - - - hosts: servers - roles: - - { role: username.rolename, x: 42 } - -License -------- - -BSD - -Author Information ------------------- - -An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/playbook/roles/waypoint/defaults/main.yml b/playbook/roles/waypoint/defaults/main.yml deleted file mode 100644 index bb588dbe..00000000 --- a/playbook/roles/waypoint/defaults/main.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- - -# Metallb -metallb_ip_default_mask: "{{ (ansible_default_ipv4.address + '/' + ansible_default_ipv4.netmask) }}" -metallb_ip_default_range: "{{ (metallb_ip_default_mask | ansible.utils.ipaddr('range_usable')) }}" -metallb_ip_range: ~ - -# waypoint external networking -waypoint_base_domain: "k3s.test" -# Use nginx ingress controller by default -k3s_disable_services: [traefik] - -# waypoint internal networking -waypoint_internal_acme_network_ip: ~ -waypoint_internal_acme_host: "acme-internal.{{ waypoint_base_domain }}" - -# HelmChart Custom Resource Definition for cert manager -# see https://cert-manager.io/docs/configuration/acme/ -cert_manager_letsencrypt_env: prod -cert_manager_namespace: kube-system -cert_manager_acme_url: "{{ letsencrypt_envs[cert_manager_letsencrypt_env] }}" -cert_manager_staging_ca_cert_url: "{{ letsencrypt_envs_ca_certs[cert_manager_letsencrypt_env] | d(none) }}" -cert_manager_email: "" -cert_manager_private_key_secret: test_secret -cert_manager_is_internal: "{{ (cert_manager_staging_ca_cert_url | d('')) != '' }}" - -# HelmChart Custom Resource Definition for dex oidc connector -dex_namespace: dex -dex_hostname: "dex.{{ waypoint_base_domain }}" -dex_client_id: waypoint -dex_client_secret: ZXhhbXBsZS1hcHAtc2VjcmV0 -dex_github_client_id: ~ -dex_github_client_secret: ~ -dex_github_client_org: ~ -dex_github_client_team: ~ - -# HelmChart Custom Resource Definition for waypoint variables -waypoint_namespace: default -waypoint_hostname: "waypoint.{{ waypoint_base_domain }}" -api_waypoint_hostname: api.{{ waypoint_hostname }} -waypoint_version: 0.11.0 - -dex_github_orgs: - - name: '{{ dex_github_client_org }}' - teams: - - '{{ dex_github_client_team }}' diff --git a/playbook/roles/waypoint/handlers/main.yml b/playbook/roles/waypoint/handlers/main.yml deleted file mode 100644 index 73b314ff..00000000 --- a/playbook/roles/waypoint/handlers/main.yml +++ /dev/null @@ -1 +0,0 @@ ---- \ No newline at end of file diff --git a/playbook/roles/waypoint/meta/main.yml b/playbook/roles/waypoint/meta/main.yml deleted file mode 100644 index 6d09ebc5..00000000 --- a/playbook/roles/waypoint/meta/main.yml +++ /dev/null @@ -1,62 +0,0 @@ -galaxy_info: - author: loic-roux-404 - namespace: k3s_paas - description: waypoint deployment - role_name: waypoint - - # If the issue tracker for your role is not on github, uncomment the - # next line and provide a value - # issue_tracker_url: http://example.com/issue/tracker - - # Choose a valid license ID from https://spdx.org - some suggested licenses: - # - BSD-3-Clause (default) - # - MIT - # - GPL-2.0-or-later - # - GPL-3.0-only - # - Apache-2.0 - # - CC-BY-4.0 - license: license (GPL-2.0-or-later, MIT, etc) - - min_ansible_version: 2.1 - - # If this a Container Enabled role, provide the minimum Ansible Container version. - # min_ansible_container_version: - - # - # Provide a list of supported platforms, and for each platform a list of versions. - # If you don't wish to enumerate all versions for a particular platform, use 'all'. - # To view available platforms and versions (or releases), visit: - # https://galaxy.ansible.com/api/v1/platforms/ - # - # platforms: - # - name: Fedora - # versions: - # - all - # - 25 - # - name: SomePlatform - # versions: - # - all - # - 1.0 - # - 7 - # - 99.99 - - galaxy_tags: [] - # List tags for your role here, one per line. A tag is a keyword that describes - # and categorizes the role. Users find roles by searching for tags. Be sure to - # remove the '[]' above, if you add tags to this list. - # - # NOTE: A tag is limited to a single word comprised of alphanumeric characters. - # Maximum 20 tags per role. - -dependencies: - - src: xanmanning.k3s - version: v3.3.1 - vars: - k3s_release_version: v1.23.8+k3s2 - k3s_server: - kube-apiserver-arg=authorization-mode: Node,RBAC - kube-apiserver-arg=oidc-issuer-url: "https://{{ dex_hostname }}" - kube-apiserver-arg=oidc-client-id: "{{ dex_client_id }}" - kube-apiserver-arg=oidc-username-claim: email - kube-apiserver-arg=oidc-groups-claim: groups - disable: "{{ (k3s_disable_services | list) | d([]) }}" diff --git a/playbook/roles/waypoint/molecule/default/converge.yml b/playbook/roles/waypoint/molecule/default/converge.yml deleted file mode 100644 index b4a2a9ae..00000000 --- a/playbook/roles/waypoint/molecule/default/converge.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -- name: Converge - hosts: "{{ _hosts | default('node-0') }}" - become: true - gather_facts: True - vars: - molecule_is_test: true - cert_manager_acme_url: https://{{ waypoint_internal_acme_host }}:14000/dir - cert_manager_staging_ca_cert_url: https://localhost:15000/roots/0 - k3s_disable_services: [traefik, servicelb] - metallb_ip_range: 172.29.0.20-172.29.0.50 - roles: - - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - pre_tasks: - - name: Ensure required dependencies are installed. - package: - name: - - curl - - wget - - unzip - update_cache: yes - state: present - - - name: Check dns connectivity - ansible.builtin.command: ping -c 1 k3s.test - changed_when: false - - - name: Ensure test dependencies are installed. - apt: - name: - - less - - vim - - golang - state: present - update_cache: yes - when: ansible_os_family == 'Debian' - - - name: Install pre-requisites for k8s module - ansible.builtin.pip: - name: - - PyYAML - - kubernetes - - - name: Copy pebble config and certs - ansible.builtin.copy: - src: "{{ playbook_dir }}/pebble" - dest: "/" - directory_mode: 0755 - remote_src: false - - - name: Install pebble - ansible.builtin.command: go install github.com/letsencrypt/pebble/...@HEAD - changed_when: false - register: pebble_install - retries: 5 - until: - - '"downloading" not in pebble_install.stderr' - - '"downloading" not in pebble_install.stdout' - - - name: Run pebble - command: ~/go/bin/pebble -config /pebble/pebble-config.json - async: 2592000 # 60*60*24*30 - 1 month - poll: 0 - changed_when: false - - - name: Wait for pebble port - ansible.builtin.wait_for: - port: 15000 - delay: 25 - - - ansible.builtin.set_fact: - waypoint_internal_acme_network_ip: "{{ ansible_default_ipv4.address }}" - tags: [waypoint] - - - name: Import acme certificates - import_tasks: "../../tasks/pre-import-cert.yml" - tags: [waypoint] diff --git a/playbook/roles/waypoint/molecule/default/group_vars/molecule/secrets.yml b/playbook/roles/waypoint/molecule/default/group_vars/molecule/secrets.yml deleted file mode 100644 index 9363d7c7..00000000 --- a/playbook/roles/waypoint/molecule/default/group_vars/molecule/secrets.yml +++ /dev/null @@ -1,16 +0,0 @@ -$ANSIBLE_VAULT;1.1;AES256 -37663536386165386234653339353832636165306465383931633665326337616264333361313931 -6333303630646666336435643363316566323364646361630a336334313336626634636361306263 -32336236316339393662613034653839376165616538336364656433356161393035356164623333 -3038303031396664310a646162366461356333366132303131363638343734616463376431323235 -61366136666464663464363662306630346164666534343831633731323566346239353238383936 -31373631336636336239303031613639663563353837636232663134663834356361363632386539 -33373637356534613562623663373731663632313462626335653065343766373639636230653763 -35613939653461316664626135393765623361323436333833356535663936343362366430333762 -62333438313465386234633637643039333433623939613766363637326237623162643165636433 -35613864626438646333353663376263333339383762323036633637373231376562613661646138 -63386462313131303635363164376233356261333566613338396638373261383266663866613533 -39303762333630303536633466316632626439383837643266643864653635636137613464396166 -39613235396533626664646261336165636166636632373933323932613665353038363666643838 -39363838616366666434663331663363323635343935336661623231313864656361393539343635 -386233633938643030326264326265366165 diff --git a/playbook/roles/waypoint/molecule/default/molecule.ci.yml b/playbook/roles/waypoint/molecule/default/molecule.ci.yml deleted file mode 100644 index 7dd2d42d..00000000 --- a/playbook/roles/waypoint/molecule/default/molecule.ci.yml +++ /dev/null @@ -1,22 +0,0 @@ -dependency: - name: galaxy - -driver: - name: delegated - options: - managed: False - ansible_connection_options: - ansible_connection: local - -platforms: - - name: 127.0.0.1 - groups: - - molecule - -provisioner: - name: ansible - config_options: - defaults: - vault_password_file: ${HOME}/.ansible/.vault -verifier: - name: ansible diff --git a/playbook/roles/waypoint/molecule/default/molecule.yml b/playbook/roles/waypoint/molecule/default/molecule.yml deleted file mode 100644 index 51b3b4fc..00000000 --- a/playbook/roles/waypoint/molecule/default/molecule.yml +++ /dev/null @@ -1,39 +0,0 @@ ---- -dependency: - name: galaxy -driver: - name: docker -platforms: - - name: node-0 - image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest - command: ${MOLECULE_DOCKER_COMMAND:-""} - privileged: true - pre_build_image: true - capabilities: - - ALL - groups: - - molecule - published_ports: - - 6443:6443 - - 80:80 - - 443:443 - - 32701:32701 - - 15000:15000 - - 14000:14000 - volumes: - - /sys/fs/cgroup:/sys/fs/cgroup:rw - - /var/lib/rancher/k3s - networks: - - name: k3snet - tmpfs: - - /var/run - - /run - - /tmp - -provisioner: - name: ansible - config_options: - defaults: - vault_password_file: ${HOME}/.ansible/.vault -verifier: - name: ansible diff --git a/playbook/roles/waypoint/molecule/default/pebble/pebble-config.json b/playbook/roles/waypoint/molecule/default/pebble/pebble-config.json deleted file mode 100644 index 1cc80c69..00000000 --- a/playbook/roles/waypoint/molecule/default/pebble/pebble-config.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "pebble": { - "listenAddress": "0.0.0.0:14000", - "managementListenAddress": "0.0.0.0:15000", - "certificate": "/pebble/cert.pem", - "privateKey": "/pebble/key.pem", - "httpPort": 80, - "tlsPort": 443, - "ocspResponderURL": "", - "externalAccountBindingRequired": false - } - } \ No newline at end of file diff --git a/playbook/roles/waypoint/molecule/default/prepare.yml b/playbook/roles/waypoint/molecule/default/prepare.yml deleted file mode 100644 index 4a7ca239..00000000 --- a/playbook/roles/waypoint/molecule/default/prepare.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -- name: Prepare - hosts: localhost - connection: local - gather_facts: false - no_log: "{{ molecule_no_log }}" - collections: - - community.docker - tasks: - - name: Create a network with custom IPAM config - docker_network: - name: k3snet - driver: bridge - attachable: false - scope: local - internal: false - ipam_config: - - subnet: "172.29.0.0/16" - gateway: "172.29.0.1" - labels: - owner: molecule - driver_options: - com.docker.network.bridge.name: k3snet - com.docker.network.bridge.enable_ip_masquerade: "true" - com.docker.network.bridge.enable_icc: "true" - com.docker.network.driver.mtu: "1500" diff --git a/playbook/roles/waypoint/molecule/default/scripts/setup_dnsmasq.sh b/playbook/roles/waypoint/molecule/default/scripts/setup_dnsmasq.sh deleted file mode 100755 index 97dbe329..00000000 --- a/playbook/roles/waypoint/molecule/default/scripts/setup_dnsmasq.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash - -############################################################ -# -# Install dnsmasq and configure it to resolve wildcard domains -# to a specific IP address. -# -############################################################ - -set -e - -WILDCARD_DOMAIN="${1:-k3s.test}" -TARGET_IP="${2:-127.0.0.1}" - -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - DNSMASQ_CNF="/etc/dnsmasq.conf" - sudo systemctl stop systemd-resolved - echo 'DNSStubListener=no' | sudo tee -a /etc/systemd/resolved.conf - sudo systemctl start systemd-resolved - sudo apt install -y dnsmasq - sudo rm -rf /etc/resolv.conf - echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf - sudo chattr +i /etc/resolv.conf - - sudo tee -a $DNSMASQ_CNF < 0 - - containers_statuses | selectattr('ready', 'equalto', true) | list | count == 2 diff --git a/playbook/roles/waypoint/tasks/checks.yml b/playbook/roles/waypoint/tasks/checks.yml deleted file mode 100644 index 1bdb8519..00000000 --- a/playbook/roles/waypoint/tasks/checks.yml +++ /dev/null @@ -1,16 +0,0 @@ -- name: check email when cert-manager - assert: - that: - - cert_manager_email | default(false) - -- name: Stat acme ca cert path - stat: - path: "{{ waypoint_internal_acme_ca_file }}" - register: acmeca_result - when: cert_manager_is_internal - -- name: Assert cert is present - assert: - that: - - acmeca_result.stat.exists - when: cert_manager_is_internal diff --git a/playbook/roles/waypoint/tasks/main.yml b/playbook/roles/waypoint/tasks/main.yml deleted file mode 100644 index d61b3049..00000000 --- a/playbook/roles/waypoint/tasks/main.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- - -- import_tasks: checks.yml - tags: [waypoint] - -- import_tasks: setup-metallb.yml - tags: [waypoint, metallb] - when: - - metallb_ip_range | d(False) - - '"servicelb" in k3s_disable_services' - -- import_tasks: setup-ingress.yml - tags: [waypoint, ingress-nginx] - -- include_tasks: manifests.yml - tags: [waypoint] - when: item.condition | default(true) - args: { apply: { tags: [waypoint] } } - loop: - - src: coredns-custom.yml - condition: "{{ waypoint_internal_acme_network_ip is not none }}" - deploy: coredns - ns: kube-system - tasks: restart-coredns.yml - - src: reflector-chart-crd.yml - deploy: reflector - ns: "{{ cert_manager_namespace }}" - condition: "{{ cert_manager_is_internal }}" - - src: reflector-shared.yml - condition: "{{ cert_manager_is_internal }}" - - src: cert-manager-chart-crd.yml - deploy: "cert-manager" - ns: "{{ cert_manager_namespace }}" - - { src: dex-chart-crd.yml , deploy: "{{ dex_namespace }}" } - - src: waypoint-chart-crd.yml - deploy: waypoint-runner - kind: StatefulSet - ns: default - -- import_tasks: setup-waypoint.yml - tags: [waypoint, finalize] diff --git a/playbook/roles/waypoint/tasks/manifests.yml b/playbook/roles/waypoint/tasks/manifests.yml deleted file mode 100644 index 33301649..00000000 --- a/playbook/roles/waypoint/tasks/manifests.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- - -- name: Download file to k3s manifest folder - ansible.builtin.get_url: - url: "{{ item.url_manifest.url }}" - dest: /var/lib/rancher/k3s/server/manifests/{{ item.url_manifest.filename }} - when: item.url_manifest | d(False) - -- name: "Deploy {{ item.src }} to k3s crd processor" - ansible.builtin.template: - src: "{{ item.src }}.j2" - dest: "/var/lib/rancher/k3s/server/manifests/{{ item.src }}" - owner: "{{ waypoint_user | d('root') }}" - group: "{{ waypoint_user | d('root') }}" - mode: '0600' - when: item.src | d(False) - -- include_tasks: "{{ item.task }}" - when: item.task | d(False) - -- name: "Wait {{ item.deploy }} available" - kubernetes.core.k8s_info: - api_version: v1 - kind: "{{ item.kind | d('Deployment') }}" - name: "{{ item.deploy }}" - kubeconfig: /etc/rancher/k3s/k3s.yaml - # Many times deployment name is the same that namespace - namespace: "{{ item.ns | d(item.deploy) }}" - until: - - deployment_infos.resources | map(attribute='status') | select() | length > 0 - - deployment_infos.resources[0].status.readyReplicas | d(False) - - deployment_infos.resources[0].status.replicas | d(False) - - deployment_infos.resources[0].status.readyReplicas == deployment_infos.resources[0].status.replicas - when: - - item.deploy | default(false) - delay: 5 - retries: 30 - register: deployment_infos diff --git a/playbook/roles/waypoint/tasks/pre-import-cert.yml b/playbook/roles/waypoint/tasks/pre-import-cert.yml deleted file mode 100644 index 949d90b5..00000000 --- a/playbook/roles/waypoint/tasks/pre-import-cert.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -- name: Download certificate file - uri: - url: "{{ cert_manager_staging_ca_cert_url }}" - validate_certs: "{{ waypoint_internal_acme_network_ip is none }}" - return_content: True - register: ca_file - -- name: Trust cert inside current machine - ansible.builtin.copy: - dest: "{{ waypoint_internal_acme_ca_file }}" - content: "{{ ca_file.content }}" - -- name: Create cert facts - set_fact: - waypoint_internal_acme_ca_content: "{{ ca_file.content }}" - waypoint_internal_acme_ca_extra_volumes: - - name: acme-internal-ca-share - configMap: - name: acme-internal-ca-share - waypoint_internal_acme_ca_extra_volumes_mounts: - - name: acme-internal-ca-share - mountPath: "{{ waypoint_internal_acme_ca_in_volume_crt }}" - subPath: ca.crt diff --git a/playbook/roles/waypoint/tasks/restart-coredns.yml b/playbook/roles/waypoint/tasks/restart-coredns.yml deleted file mode 100644 index 54d1098f..00000000 --- a/playbook/roles/waypoint/tasks/restart-coredns.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: Restart coredns - command: kubectl rollout restart -n kube-system deployment/coredns - environment: - KUBECONFIG: /etc/rancher/k3s/k3s.yaml diff --git a/playbook/roles/waypoint/tasks/setup-ingress.yml b/playbook/roles/waypoint/tasks/setup-ingress.yml deleted file mode 100644 index df42da93..00000000 --- a/playbook/roles/waypoint/tasks/setup-ingress.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -- name: Install nginx ingress - include_tasks: manifests.yml - loop: - - src: nginx-ingress-chart-crd.yml - when: '"traefik" in k3s_disable_services' - -- name: Get Ingress service infos - kubernetes.core.k8s_info: - api_version: v1 - kind: Service - name: "{{ ingress_expected_svc }}" - kubeconfig: /etc/rancher/k3s/k3s.yaml - wait: yes - namespace: kube-system - register: ingress_infos - -- name: Check ingress service infos available - assert: - that: - - ingress_infos.resources | length > 0 - -- name: Set ingress ip fact - set_fact: - waypoint_ingress_controller_ip: "{{ ingress_infos.resources[0].spec.clusterIP | d(none) }}" diff --git a/playbook/roles/waypoint/tasks/setup-metallb.yml b/playbook/roles/waypoint/tasks/setup-metallb.yml deleted file mode 100644 index 25e4de8d..00000000 --- a/playbook/roles/waypoint/tasks/setup-metallb.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -- name: Install metallb servicelb - include_tasks: manifests.yml - loop: "{{ metallb_manifests }}" - -- name: Wait crd available - kubernetes.core.k8s_info: - api_version: v1 - name: speaker - kind: DaemonSet - kubeconfig: /etc/rancher/k3s/k3s.yaml - namespace: metallb-system - wait: yes - -- name: Install metallb config - include_tasks: manifests.yml - loop: - - src: metallb-config.yml - ns: metallb-system diff --git a/playbook/roles/waypoint/tasks/setup-waypoint.yml b/playbook/roles/waypoint/tasks/setup-waypoint.yml deleted file mode 100644 index a2ed7ea5..00000000 --- a/playbook/roles/waypoint/tasks/setup-waypoint.yml +++ /dev/null @@ -1,75 +0,0 @@ ---- - -- name: Map ports - kubernetes.core.k8s: - api_version: v1 - kind: Service - name: waypoint-ui - namespace: "{{ waypoint_namespace }}" - kubeconfig: /etc/rancher/k3s/k3s.yaml - apply: yes - force: yes - definition: - spec: - ports: - - name: http - port: 80 - targetPort: http - nodePort: 30080 - - name: https - port: 443 - targetPort: https - nodePort: 30443 - - name: grpc - port: 9701 - targetPort: grpc - nodePort: 32701 - - name: https-2 - port: 9702 - targetPort: https - nodePort: 32702 - -- set_fact: - waypoint_arch_lookup: - amd64: amd64 - x86_64: amd64 - arm64: arm64 - aarch64: arm64 - -- set_fact: - waypoint_arch: "{{ waypoint_arch_lookup[ansible_architecture] }}" - -- name: Unzip waypoint binary - ansible.builtin.unarchive: - src: "https://releases.hashicorp.com/waypoint/{{ waypoint_version }}/waypoint_{{ waypoint_version }}_linux_{{ waypoint_arch }}.zip" - dest: /usr/local/bin/ - remote_src: yes - -- name: Waypoint login - command: waypoint login -server-addr={{ waypoint_hostname }}:443 -from-kubernetes - environment: - KUBECONFIG: /etc/rancher/k3s/k3s.yaml - changed_when: false - -- name: Waypoint oidc - command: | - waypoint auth-method set oidc \ - -client-id="{{ dex_client_id }}" \ - -display-name="GitHub" \ - -description="GitHub Oauth2 over Dex Idp open id connect adapter" \ - -client-secret="{{ dex_client_secret }}" \ - -issuer=https://{{ dex_hostname }} \ - -allowed-redirect-uri="https://{{ waypoint_hostname }}/auth/oidc-callback" \ - -claim-scope="groups" \ - -list-claim-mapping="groups=groups" \ - -access-selector="\"{{ dex_github_client_org }}:{{ dex_github_client_team }}\" in list.groups" dex - changed_when: false - -- name: Recover base runner - shell: waypoint runner list | tail -n 1 | awk '{print $1}' | xargs - register: waypoint_runner_id - changed_when: false - -- name: Adopt runner - command: "waypoint runner adopt {{ waypoint_runner_id.stdout }}" - changed_when: false diff --git a/playbook/roles/waypoint/templates/cert-manager-chart-crd.yml.j2 b/playbook/roles/waypoint/templates/cert-manager-chart-crd.yml.j2 deleted file mode 100644 index 9659feaf..00000000 --- a/playbook/roles/waypoint/templates/cert-manager-chart-crd.yml.j2 +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: {{ cert_manager_namespace }} ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: cert-manager - namespace: kube-system -spec: - chart: cert-manager - targetNamespace: {{ cert_manager_namespace }} - repo: https://charts.jetstack.io - valuesContent: |- - installCRDs: true - ---- -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-acme-issuer -spec: - acme: - skipTLSVerify: {{ waypoint_internal_acme_network_ip is not none }} - email: {{ cert_manager_email }} - server: {{ cert_manager_acme_url }} - privateKeySecretRef: - name: acme-account-key - solvers: - - selector: {} - http01: - ingress: - class: {{ waypoint_k8s_ingress_class }} - diff --git a/playbook/roles/waypoint/templates/coredns-custom.yml.j2 b/playbook/roles/waypoint/templates/coredns-custom.yml.j2 deleted file mode 100644 index fe5cc2f9..00000000 --- a/playbook/roles/waypoint/templates/coredns-custom.yml.j2 +++ /dev/null @@ -1,23 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: coredns-custom - namespace: kube-system -data: - ingress-hosts.server: | - {{ ingress_hosts_internals_joined }} { - hosts { - {{ waypoint_ingress_controller_ip }} {{ ingress_hosts_internals_joined }} - fallthrough - } - whoami - } - acme-internal.server: | - {{ waypoint_internal_acme_host }} { - hosts { - {{ waypoint_internal_acme_network_ip }} {{ waypoint_internal_acme_host }} - fallthrough - } - whoami - } diff --git a/playbook/roles/waypoint/templates/dex-chart-crd.yml.j2 b/playbook/roles/waypoint/templates/dex-chart-crd.yml.j2 deleted file mode 100644 index 4205dac0..00000000 --- a/playbook/roles/waypoint/templates/dex-chart-crd.yml.j2 +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: {{ dex_namespace }} - ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: dex - namespace: kube-system -spec: - chart: dex - targetNamespace: {{ dex_namespace }} - repo: https://charts.dexidp.io - valuesContent: |- - config: - issuer: "https://{{ dex_hostname }}" - web: - http: 0.0.0.0:5556 - storage: - type: kubernetes - config: - inCluster: true - connectors: - - type: github - id: github - name: GitHub - config: - clientID: '{{ dex_github_client_id }}' - clientSecret: '{{ dex_github_client_secret }}' - redirectURI: "https://{{ dex_hostname }}/callback" - orgs: - {{ dex_github_orgs | to_yaml | indent(12) }} - oauth2: - skipApprovalScreen: true - staticClients: - - id: "{{ dex_client_id }}" - redirectURIs: - - http://127.0.0.1/oidc/callback - - 'https://{{ waypoint_hostname }}/auth/oidc-callback' - name: waypoint - secret: "{{ dex_client_secret }}" - ingress: - enabled: true - annotations: - cert-manager.io/cluster-issuer: letsencrypt-acme-issuer - kubernetes.io/ingress.class: "{{ waypoint_k8s_ingress_class }}" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - nginx.ingress.kubernetes.io/rewrite-target: / - hosts: - - host: {{ dex_hostname }} - paths: - - path: / - pathType: ImplementationSpecific - tls: - - secretName: {{ dex_hostname }}-tls - hosts: - - {{ dex_hostname }} diff --git a/playbook/roles/waypoint/templates/metallb-config.yml.j2 b/playbook/roles/waypoint/templates/metallb-config.yml.j2 deleted file mode 100644 index bebb7d7b..00000000 --- a/playbook/roles/waypoint/templates/metallb-config.yml.j2 +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: metallb-system - labels: - app: metallb - ---- -apiVersion: metallb.io/v1beta1 -kind: IPAddressPool -metadata: - name: kind-pool - namespace: metallb-system -spec: - addresses: - - {{ metallb_ip_range }} - ---- -apiVersion: metallb.io/v1beta1 -kind: L2Advertisement -metadata: - name: kind-l2 - namespace: metallb-system diff --git a/playbook/roles/waypoint/templates/nginx-ingress-chart-crd.yml.j2 b/playbook/roles/waypoint/templates/nginx-ingress-chart-crd.yml.j2 deleted file mode 100644 index 99012494..00000000 --- a/playbook/roles/waypoint/templates/nginx-ingress-chart-crd.yml.j2 +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: ingress-nginx - namespace: kube-system -spec: - chart: nginx-ingress-controller - repo: https://charts.bitnami.com/bitnami - targetNamespace: kube-system - version: 9.5.1 - valuesContent: | - fullnameOverride: nginx-ingress-controller - extraArgs: - v: 3 - kind: DaemonSet - useHostPort: true - defaultBackend: - service: - ports: - http: 8080 diff --git a/playbook/roles/waypoint/templates/reflector-chart-crd.yml.j2 b/playbook/roles/waypoint/templates/reflector-chart-crd.yml.j2 deleted file mode 100644 index d34c429c..00000000 --- a/playbook/roles/waypoint/templates/reflector-chart-crd.yml.j2 +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: reflector - namespace: kube-system -spec: - version: 7.0.151 - chart: reflector - targetNamespace: {{ cert_manager_namespace }} - repo: https://emberstack.github.io/helm-charts diff --git a/playbook/roles/waypoint/templates/reflector-shared.yml.j2 b/playbook/roles/waypoint/templates/reflector-shared.yml.j2 deleted file mode 100644 index d9ef4257..00000000 --- a/playbook/roles/waypoint/templates/reflector-shared.yml.j2 +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: acme-internal-root-ca - namespace: kube-system - annotations: - reflector.v1.k8s.emberstack.com/reflection-allowed: "true" - reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true" -data: - ca.crt: | - {{ waypoint_internal_acme_ca_content | indent(4) }} diff --git a/playbook/roles/waypoint/templates/waypoint-chart-crd.yml.j2 b/playbook/roles/waypoint/templates/waypoint-chart-crd.yml.j2 deleted file mode 100644 index 8100e87c..00000000 --- a/playbook/roles/waypoint/templates/waypoint-chart-crd.yml.j2 +++ /dev/null @@ -1,89 +0,0 @@ ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: {{ waypoint_hostname }}-tls - namespace: {{ waypoint_namespace }} -spec: - dnsNames: - - {{ waypoint_hostname }} - issuerRef: - kind: ClusterIssuer - name: letsencrypt-acme-issuer - secretName: {{ waypoint_hostname }}-tls - ---- -apiVersion: helm.cattle.io/v1 -kind: HelmChart -metadata: - name: waypoint - namespace: kube-system -spec: - version: 0.1.18 - chart: waypoint - targetNamespace: {{ waypoint_namespace }} - repo: https://helm.releases.hashicorp.com - valuesContent: |- - odr: - image: - repository: "ghcr.io/hashicorp/waypoint/alpha" - tag: "c0f0e03b1" - server: - image: - repository: "ghcr.io/hashicorp/waypoint/alpha-odr" - tag: "c0f0e03b1" - runArgs: ["-vvv"] - cert: - secretName: {{ waypoint_hostname }}-tls - ui: - service: - type: NodePort - ingress: - enabled: true - annotations: - cert-manager.io/cluster-issuer: letsencrypt-acme-issuer - kubernetes.io/ingress.class: "{{ waypoint_k8s_ingress_class }}" - hosts: - - host: "{{ waypoint_hostname }}" - paths: ["/"] - tls: - - hosts: - - "{{ waypoint_hostname }}" - secretName: {{ waypoint_hostname }}-tls - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: "{{ waypoint_k8s_ingress_class }}" - nginx.ingress.kubernetes.io/backend-protocol: GRPCS - nginx.ingress.kubernetes.io/ssl-redirect: "true" - nginx.ingress.kubernetes.io/grpc-backend: "true" - cert-manager.io/cluster-issuer: letsencrypt-acme-issuer - name: waypoint-grpc - namespace: {{ waypoint_namespace }} -spec: - rules: - - host: {{ waypoint_hostname }} - http: - paths: - - backend: - service: - name: waypoint-server - port: - name: grpc - path: /hashicorp.waypoint.Waypoint/ - pathType: ImplementationSpecific - - - backend: - service: - name: waypoint-server - port: - name: grpc - path: /grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo - pathType: ImplementationSpecific - tls: - - hosts: - - "{{ waypoint_hostname }}" - secretName: {{ waypoint_hostname }}-tls diff --git a/playbook/roles/waypoint/vars/main.yml b/playbook/roles/waypoint/vars/main.yml deleted file mode 100644 index 07a58f9a..00000000 --- a/playbook/roles/waypoint/vars/main.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -# vars file for role-waypoint -waypoint_k8s_ingress_class: nginx -letsencrypt_staging: https://acme-staging-v02.api.letsencrypt.org/directory -letsencrypt_prod: https://acme-v02.api.letsencrypt.org/directory - -letsencrypt_envs: - staging: '{{ letsencrypt_staging }}' - prod: '{{ letsencrypt_prod }}' - -letsencrypt_envs_ca_certs: - staging: https://letsencrypt.org/certs/staging/letsencrypt-stg-root-x1.pem - -# Mounted in acme internal -waypoint_internal_acme_ca_file: /etc/ssl/certs/acmeca.crt -waypoint_internal_acme_ca_in_volume_crt: /etc/ssl/certs/acmeca.crt -waypoint_internal_acme_ca_extra_volumes: [] -waypoint_internal_acme_ca_extra_volumes_mounts: [] - -# Metallb -metallb_manifests: - - url_manifest: - url: https://raw.githubusercontent.com/metallb/metallb/v0.13.5/config/manifests/metallb-native.yaml - filename: metallb-native.yaml - deploy: controller - ns: metallb-system - - url_manifest: - url: https://raw.githubusercontent.com/metallb/metallb/v0.13.5/config/manifests/metallb-frr.yaml - filename: metallb-frr.yaml - -# Ingress facts -ingress_expected_svc: "{{'nginx-ingress-controller' - if 'traefik' in k3s_disable_services else 'traefik' }}" -ingress_hosts_internals: - - "{{ dex_hostname }}" - - "{{ waypoint_hostname }}" - -ingress_hosts_internals_joined: "{{ ingress_hosts_internals | join(' ') }}" diff --git a/playbook/site.yaml b/playbook/site.yaml deleted file mode 100644 index d0d6f841..00000000 --- a/playbook/site.yaml +++ /dev/null @@ -1,8 +0,0 @@ -- hosts: all - gather_facts: True - become: True - pre_tasks: - - include_tasks: roles/waypoint/tasks/pre-import-cert.yml - when: cert_manager_is_internal - roles: - - role: roles/waypoint diff --git a/playbook/terraform.tfstate b/playbook/terraform.tfstate deleted file mode 100644 index d684e3bb..00000000 --- a/playbook/terraform.tfstate +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.3.6", - "serial": 1, - "lineage": "b4d33ab3-3d91-dc22-cd89-b9c5e5f9c48d", - "outputs": {}, - "resources": [], - "check_results": null -} diff --git a/requirements.txt b/requirements.txt index b9e9fe43..3f8b89b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,3 @@ mkdocs==1.4.2 mkdocs-material==9.0.6 mkdocs-material-extensions==1.1.1 mkdocs-print-site-plugin==2.3.4 -pre-commit==3.2.2 \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..e859dcc0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,5 @@ +{ system ? builtins.currentSystem }: +let + d = import ./. { inherit system; src = ./.; }; +in +d.devShells.${system}.default diff --git a/terraform.tf b/terraform.tf new file mode 100644 index 00000000..cfec1fac --- /dev/null +++ b/terraform.tf @@ -0,0 +1,37 @@ +terraform { + required_providers { + helm = { + source = "hashicorp/helm" + version = "2.12.1" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.29.0" + } + random = { + source = "hashicorp/random" + version = "3.6.1" + } + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + +provider "kubernetes" { + host = "https://${var.vm_ip}:6443" + config_path = "~/.kube/config" + exec { + api_version = "client.authentication.k8s.io/v1beta1" + args = ["zizou@localhost", "-p", "2222", "sudo", "cat", "/etc/rancher/k3s/k3s.yaml", ">", "~/.kube/config"] + command = "ssh" + } +} + +provider "helm" { + kubernetes { + host = "https://${var.vm_ip}:6443" + config_path = "~/.kube/config" + } +} diff --git a/tf-modules-k8s/cert-manager/main.tf b/tf-modules-k8s/cert-manager/main.tf new file mode 100644 index 00000000..25f7444f --- /dev/null +++ b/tf-modules-k8s/cert-manager/main.tf @@ -0,0 +1,113 @@ +resource "kubernetes_namespace" "cert-manager" { + metadata { + name = var.cert_manager_namespace + } +} + +resource "helm_release" "cert_manager" { + name = "cert-manager" + namespace = kubernetes_namespace.cert-manager.metadata.0.name + repository = "https://charts.jetstack.io" + chart = "cert-manager" + version = "1.14.4" + wait_for_jobs = true + wait = true + + set { + name = "installCRDs" + value = false + } +} + +resource "kubernetes_manifest" "issuer" { + depends_on = [helm_release.cert_manager] + manifest = { + apiVersion = "cert-manager.io/v1" + kind = "ClusterIssuer" + metadata = { + name = "letsencrypt-acme-issuer" + } + spec = { + acme = { + skipTLSVerify = var.letsencrypt_env != "prod" + email = var.cert_manager_email + server = var.cert_manager_acme_url + privateKeySecretRef = { + name = "letsencrypt-acme-priv-key" + } + solvers = [ + { + selector = {} + http01 = { + ingress = { + class = var.k8s_ingress_class + } + } + } + ] + } + } + } +} + +resource "helm_release" "reflector" { + name = "reflector" + namespace = kubernetes_namespace.cert-manager.metadata.0.name + repository = "https://emberstack.github.io/helm-charts" + chart = "reflector" + version = "7.1.262" + wait_for_jobs = true + wait = true + + set { + name = "targetNamespace" + value = kubernetes_namespace.cert-manager.metadata.0.name + } + +} + +resource "kubernetes_config_map" "acme_internal_root_ca" { + count = var.letsencrypt_env == "local" ? 1 : 0 + metadata { + name = "acme-internal-root-ca" + namespace = kubernetes_namespace.cert-manager.metadata.0.name + annotations = { + "reflector.v1.k8s.emberstack.com/reflection-allowed" = "true" + "reflector.v1.k8s.emberstack.com/reflection-auto-enabled" = "true" + } + } + + data = { + "ca.crt" = indent(4, var.internal_acme_ca_content) + } +} + +locals { + root_ca_config_map = kubernetes_config_map.acme_internal_root_ca.0.metadata[0].name +} + +output "issuer" { + value = kubernetes_manifest.issuer.object.metadata.name +} + +output "reflector_metadata_name" { + value = helm_release.reflector.metadata.0.name +} + +output "root_ca_config_map_volume" { + value = local.root_ca_config_map != null ? [{ + name = local.root_ca_config_map + configMap = { + name = local.root_ca_config_map + } + }] : [] +} + +output "root_ca_config_map_volume_mounts" { + value = local.root_ca_config_map != null ? [{ + name = local.root_ca_config_map + mountPath = "/etc/ssl/certs/ca.crt" + subPath = "ca.crt" + readOnly = true + }] : [] +} diff --git a/tf-modules-k8s/cert-manager/terraform.tf b/tf-modules-k8s/cert-manager/terraform.tf new file mode 100644 index 00000000..a30496cd --- /dev/null +++ b/tf-modules-k8s/cert-manager/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + helm = { + source = "hashicorp/helm" + } + kubernetes = { + source = "hashicorp/kubernetes" + } + } +} diff --git a/tf-modules-k8s/cert-manager/variables.tf b/tf-modules-k8s/cert-manager/variables.tf new file mode 100644 index 00000000..852a0095 --- /dev/null +++ b/tf-modules-k8s/cert-manager/variables.tf @@ -0,0 +1,32 @@ +variable "cert_manager_namespace" { + description = "The namespace to install cert-manager into" + type = string + default = "cert-manager" +} + +variable "k8s_ingress_class" { + description = "The ingress class to use for cert-manager" + type = string + default = "nginx" +} + +variable "cert_manager_email" { + description = "The email to use for the letsencrypt account" + type = string + default = "test@k3s.test" +} + +variable "internal_acme_ca_content" { + description = "value of the acme ca cert" + type = string +} + +variable "cert_manager_acme_url" { + description = "The url of the acme server" + type = string +} + +variable "letsencrypt_env" { + description = "Environment to use for letsencrypt" + default = "local" +} diff --git a/tf-modules-k8s/dex/main.tf b/tf-modules-k8s/dex/main.tf new file mode 100644 index 00000000..6642f9c1 --- /dev/null +++ b/tf-modules-k8s/dex/main.tf @@ -0,0 +1,74 @@ +resource "kubernetes_namespace" "cert-manager" { + metadata { + name = var.dex_namespace + } +} + +resource "random_password" "dex_client_id" { + length = 16 + special = false +} + +resource "random_password" "dex_client_secret" { + length = 24 + special = false +} + +locals { + dex_client_id = random_password.dex_client_id.result + dex_client_secret = random_password.dex_client_secret.result +} + +resource "helm_release" "dex" { + repository = "https://charts.dexidp.io" + name = "dex" + namespace = kubernetes_namespace.cert-manager.metadata[0].name + chart = "dex" + timeout = 600 + wait_for_jobs = true + wait = true + + values = [ + templatefile("${path.module}/values.yaml.tmpl", { + dex_hostname = var.dex_hostname, + github_client_id = var.github_client_id, + github_client_secret = var.github_client_secret, + dex_github_orgs = jsonencode(var.dex_github_orgs), + dex_client_id = local.dex_client_id, + paas_hostname = var.paas_hostname, + dex_client_secret = local.dex_client_secret, + k8s_ingress_class = var.k8s_ingress_class + cert_manager_cluster_issuer = var.cert_manager_cluster_issuer + }) + ] +} + +data "kubernetes_service" "dex_service" { + metadata { + name = "dex" + namespace = kubernetes_namespace.cert-manager.metadata[0].name + } +} + +data "kubernetes_ingress" "dex_ingress" { + metadata { + name = "dex" + namespace = kubernetes_namespace.cert-manager.metadata[0].name + } +} + +output "dex_ingress" { + value = data.kubernetes_ingress.dex_ingress.id +} + +output "dex_service" { + value = data.kubernetes_service.dex_service.id +} + +output "dex_client_id" { + value = local.dex_client_id +} + +output "dex_client_secret" { + value = local.dex_client_secret +} diff --git a/tf-modules-k8s/dex/terraform.tf b/tf-modules-k8s/dex/terraform.tf new file mode 100644 index 00000000..a30496cd --- /dev/null +++ b/tf-modules-k8s/dex/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + helm = { + source = "hashicorp/helm" + } + kubernetes = { + source = "hashicorp/kubernetes" + } + } +} diff --git a/tf-modules-k8s/dex/values.yaml.tmpl b/tf-modules-k8s/dex/values.yaml.tmpl new file mode 100644 index 00000000..e87b159d --- /dev/null +++ b/tf-modules-k8s/dex/values.yaml.tmpl @@ -0,0 +1,46 @@ +--- +config: + issuer: "https://${dex_hostname}" + web: + http: 0.0.0.0:5556 + storage: + type: kubernetes + config: + inCluster: true + connectors: + - type: github + id: github + name: GitHub + config: + clientID: '${github_client_id}' + clientSecret: '${github_client_secret}' + redirectURI: "https://${dex_hostname}/callback" + orgs: + ${dex_github_orgs} + oauth2: + skipApprovalScreen: true + staticClients: + - id: "${dex_client_id}" + redirectURIs: + - http://127.0.0.1/oidc/callback + - 'https://${paas_hostname}/auth/oidc-callback' + name: paas + secret: "${dex_client_secret}" +ingress: + enabled: true + className: "${k8s_ingress_class}" + annotations: + cert-manager.io/cluster-issuer: ${cert_manager_cluster_issuer} + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/rewrite-target: / + traefik.ingress.kubernetes.io/rule.type: PathPrefixStrip + traefik.ingress.kubernets.io/router.tls: "true" + hosts: + - host: ${dex_hostname} + paths: + - path: / + pathType: ImplementationSpecific + tls: + - secretName: ${dex_hostname}-tls + hosts: + - ${dex_hostname} diff --git a/tf-modules-k8s/dex/variables.tf b/tf-modules-k8s/dex/variables.tf new file mode 100644 index 00000000..cdb9d371 --- /dev/null +++ b/tf-modules-k8s/dex/variables.tf @@ -0,0 +1,42 @@ +variable "dex_namespace" { + default = "dex" +} + +variable "dex_hostname" { + description = "Hostname for DEX" + type = string +} + +variable "github_client_id" { + description = "GitHub client ID for DEX" + type = string +} + +variable "github_client_secret" { + description = "GitHub client secret for DEX" + type = string +} + +variable "dex_github_orgs" { + description = "Github Orgs for Dex OIDC Connector" + type = list(object({ + name = string + teams = list(string) + })) + default = [] +} + +variable "paas_hostname" { + description = "Hostname for paas" + type = string +} + +variable "k8s_ingress_class" { + description = "ingress class" + type = string + default = "nginx" +} + +variable "cert_manager_cluster_issuer" { + description = "value of the cert-manager cluster issuer" +} diff --git a/tf-modules-k8s/github/main.tf b/tf-modules-k8s/github/main.tf new file mode 100644 index 00000000..05695f39 --- /dev/null +++ b/tf-modules-k8s/github/main.tf @@ -0,0 +1,33 @@ +data "github_organization" "org" { + name = var.github_organization +} + +data "github_membership" "all" { + for_each = toset(data.github_organization.org.members) + username = each.value +} + +data "github_membership" "all_admin" { + for_each = { + for _, member in data.github_membership.all : + _ => member if member.role == "admin" + } + username = each.value.username +} + +resource "github_team" "opsteam" { + name = var.github_team + description = "This is the production team" + privacy = "closed" +} + +resource "github_team_membership" "opsteam_members" { + for_each = data.github_membership.all_admin + team_id = github_team.opsteam.id + username = each.value.username + role = "maintainer" +} + +output "team_name" { + value = github_team.opsteam.name +} diff --git a/tf-modules-k8s/github/terraform.tf b/tf-modules-k8s/github/terraform.tf new file mode 100644 index 00000000..7ee37b0d --- /dev/null +++ b/tf-modules-k8s/github/terraform.tf @@ -0,0 +1,14 @@ + +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + +provider "github" { + owner = var.github_organization + token = var.github_token +} diff --git a/tf-modules-k8s/github/variables.tf b/tf-modules-k8s/github/variables.tf new file mode 100644 index 00000000..0a679053 --- /dev/null +++ b/tf-modules-k8s/github/variables.tf @@ -0,0 +1,14 @@ +variable "github_organization" { + type = string + default = "org-404" +} + +variable "github_team" { + type = string + default = "ops-team" +} + +variable "github_token" { + type = string + sensitive = true +} diff --git a/tf-modules-k8s/internal-ca/main.tf b/tf-modules-k8s/internal-ca/main.tf new file mode 100644 index 00000000..07c465a0 --- /dev/null +++ b/tf-modules-k8s/internal-ca/main.tf @@ -0,0 +1,50 @@ +locals { + ingress_hosts_internals_joined = join(" ", var.ingress_hosts_internals) +} + +resource "kubernetes_config_map" "coredns-custom" { + metadata { + name = "coredns-custom" + namespace = "kube-system" + } + + data = { + "ingress-hosts.server" = < + + + + + + + + + + + + + + + + + + %{ for arg in args ~} + + %{ endfor ~} + + + + diff --git a/tf-root-libvirt/terraform.tf b/tf-root-libvirt/terraform.tf new file mode 100644 index 00000000..19721422 --- /dev/null +++ b/tf-root-libvirt/terraform.tf @@ -0,0 +1,23 @@ +terraform { + required_version = ">= 0.13" + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + } + null = { + source = "hashicorp/null" + version = "3.2.2" + } + healthcheck = { + source = "Ferlab-Ste-Justine/healthcheck" + version = "0.2.0" + } + } +} + +provider "libvirt" { + uri = "qemu:///system" +} + +provider "healthcheck" { +} diff --git a/tf-root-libvirt/variables.tf b/tf-root-libvirt/variables.tf new file mode 100644 index 00000000..eee68175 --- /dev/null +++ b/tf-root-libvirt/variables.tf @@ -0,0 +1,44 @@ +variable "port_mappings" { + type = map(number) + default = { + 2222 = 22 + 6443 = 6443 + 443 = 443 + 80 = 80 + } +} + +variable "qemu_network_interface" { + default = "en0" +} + +variable "vm_size" { + description = "vm size in MB" + default = 8092 +} + +variable "debug" { + type = bool + default = false +} + +variable "darwin" { + type = bool + default = true +} + +variable "ssh_connection" { + description = "values for the ssh connection" + type = object({ + private_key = string + user = string + }) + default = { + private_key = "~/.ssh/id_ed25519" + user = "zizou" + } +} + +variable "libvirt_pool_path" { + default = "/etc/libvirt/k3s-paas-pool" +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 00000000..631ec848 --- /dev/null +++ b/variables.tf @@ -0,0 +1,103 @@ +variable "k3s_token" { + default = "example-token" +} + +variable "paas_base_domain" { + default = "k3s.test" +} + +variable "cert_manager_letsencrypt_env" { + default = "local" +} + +variable "cert_manager_namespace" { + default = "cert-manager" +} + +variable "cert_manager_email" { + default = "toto@k3s.test" +} + +variable "cert_manager_private_key_secret" { + default = "test_secret" +} + +variable "dex_namespace" { + default = "dex" +} + +variable "github_token" { + sensitive = true + type = string +} + +variable "github_client_id" { + default = "client-id-example" +} + +variable "github_client_secret" { + default = "secret-example" +} + +variable "github_organization" { + default = "org-404" +} + +variable "github_team" { + default = "ops-team" +} + +variable "paas_namespace" { + default = "default" +} + +variable "paas_hostname" { + default = "paas.k3s.test" +} + +variable "k8s_ingress_class" { + default = "nginx" + description = "ingress class" +} + +variable "letsencrypt_envs" { + description = "Letsencrypt Envs" + type = object({ + local = string + staging = string + prod = string + }) + default = { + local = "https://localhost:14000/dir" + staging = "https://acme-v02.api.letsencrypt.org/directory" + prod = "https://acme-staging-v02.api.letsencrypt.org/directory" + } +} + +variable "letsencrypt_envs_ca_certs" { + description = "Letsencrypt Envs CA Certs" + type = object({ + local = string + staging = string + prod = string + }) + default = { + local = "https://localhost:15000/roots/0" + staging = "https://letsencrypt.org/certs/staging/letsencrypt-stg-root-x1.pem" + prod = null + } +} + +variable "metallb_ip_range" { + type = string + description = "value of the ip range" + default = null +} + +variable "vm_ip" { + default = "localhost" +} + +variable "internal_network_ip" { + default = "10.0.2.2" +} diff --git a/playbook/roles/waypoint/files/.gitkeep b/xchg/.gitkeep similarity index 100% rename from playbook/roles/waypoint/files/.gitkeep rename to xchg/.gitkeep