diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eddd484878fd549b17f218a10bba4f1b6ae74cc4..cac7c3b1967a3ae2fb7bff785648539a8cfb025b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -176,7 +176,8 @@ create-vps: stage: create-vps script: - *debug_information - # Creates the VPS only if an old VPS for this branch is not re-usable + # Creates a VPS based on a custom CI image for which --install-kubernetes + # has already run. See CONTRIBUTING.md#ci-pipeline-image for more info - sh .gitlab/ci_scripts/create_vps.sh artifacts: paths: diff --git a/.gitlab/ci_scripts/create_vps.sh b/.gitlab/ci_scripts/create_vps.sh index ab6df85f5361906c9a5d713ff9052063ea1e36f8..123755568eb647318f686134c4e94b8b889bbdb0 100644 --- a/.gitlab/ci_scripts/create_vps.sh +++ b/.gitlab/ci_scripts/create_vps.sh @@ -7,6 +7,7 @@ set -ve echo "Deleting old machine" python3 -c "import greenhost_cloud; greenhost_cloud.terminate_droplets_by_name(\"^${HOSTNAME}$\")" echo "Creating new machine" +# Uses a custom disk image. See CONTRIBUTING.md#ci-pipeline-image for more info. python3 -m openappstack $HOSTNAME create \ --acme-staging \ --local-flux \ @@ -16,4 +17,5 @@ python3 -m openappstack $HOSTNAME create \ --ssh-key-id $SSH_KEY_ID \ --create-domain-records \ --subdomain $SUBDOMAIN \ + --disk-image-id '-7121' \ --truncate-subdomain diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a8b888b993e43910a9fb9b9b8ff0e35523bd64f..bf7445e13d27d3cc9a4bb246b2a16bd4283fa7a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,3 +46,46 @@ but also its dependencies. If the new package you are adding is only used by developers, please add it to the `requirements-dev.txt` file. + +## CI pipeline image + +We use a custom disk image for the VPSs used by the CI pipeline. On this image, +the `install-kubernetes.yaml` playbook has already been applied, which usually +saves a few minutes of pipeline running time. + +### What to do when I change a part of the `install-kubernetes.yaml` playbook? + +Don't worry, the playbook *runs* in the CI (just faster, because usually +nothing needs to change). So if you make changes, you can test those in the CI +without problems. + +If you want to start with a clean slate, however, you might want to change +`.gitlab/ci_scripts/create_vps.sh` and temporarily remove the `--disk-image-id` +argument. + +#### Before you merge, make sure your changes are applied to a new custom image: + +If you changed the `install-kubernetes.yaml` playbook, for example to upgrade +the k3s version in use, you'll want to generate a new disk image template and +use it. This is a manual process for now. Follow these steps: + +1. Create a new VPS +2. Run the following to install *only kubernetes* on the VPS: + ``` + $ python3 -m openappstack <cluster> install --install-kubernetes --no-install-openappstack + ``` +3. Log into Cosmos with the OpenAppStack account +4. Go to VPS Cloud -> VPS and shut down your VPS +5. Go to VPS Cloud -> Disk Images and click edit for your VPSs disk image + 1. Change the Disk Label to something like `k3s-template` + 2. Set VPS to `-- not assigned --` + 3. Click 'make template' + 4. Remember the disk image ID that you can see in the current URL as `id=...` + 5. Click save +6. Change the `--disk-image-id` argument in `.gitlab/ci_scripts/create_vps.sh` + to your current disk-image-id **with a minus in front of it**. This is + because custom images are negative integers, whereas Greenhost's disk images + are positive integers + +You are now ready to merge the changes you made to the `install-kubernetes` +playbook diff --git a/openappstack/__main__.py b/openappstack/__main__.py index d68a9a9bab65858ed6adb6cd3bf3feb809937697..b8d0ed6c19e55e9a05b0af2ff33f289b4c7af802 100755 --- a/openappstack/__main__.py +++ b/openappstack/__main__.py @@ -147,6 +147,11 @@ def main(): # pylint: disable=too-many-statements,too-many-branches,too-many-lo help=("Use this for development clusters. Uses an in-cluster " 'auto-update feed')) + droplet_creation_group.add_argument( + '--disk-image-id', + help=("Custom disk image ID. Use negative value for a custom template " + "ID, use positive values for operating system IDs")) + install_parser = subparsers.add_parser( 'install', help=("Use this to run the ansible playbook that sets up your VPS to run " @@ -182,6 +187,12 @@ def main(): # pylint: disable=too-many-statements,too-many-branches,too-many-lo action='store_true', help="Installs k3s on your VPS before installing OpenAppStack") + install_parser.add_argument( + '--no-install-openappstack', + action='store_true', + help=("Skip openappstack installation. This is useful if you only " + "want a kubernetes cluster.")) + test_parser = subparsers.add_parser( 'test', help=("Write test configuration and run tests on your cluster")) @@ -312,6 +323,9 @@ def create(clus, args): # pylint: disable=too-many-branches clus.domain = fqdn + if args.disk_image_id: + clus.disk_image_id = args.disk_image_id + # Set acme_staging to False so we use Let's Encrypt's live environment if args.acme_staging: clus.acme_staging = True @@ -360,10 +374,11 @@ def install(clus, args): os.path.join(ansible.ANSIBLE_PATH, 'install-kubernetes.yml'), args.ansible_param) - ansible.run_ansible( - clus, - os.path.join(ansible.ANSIBLE_PATH, 'install-openappstack.yml'), - args.ansible_param) + if not args.no_install_openappstack: + ansible.run_ansible( + clus, + os.path.join(ansible.ANSIBLE_PATH, 'install-openappstack.yml'), + args.ansible_param) def test(clus, args): diff --git a/openappstack/cluster.py b/openappstack/cluster.py index d106dfff89ccaff31beaee13f1fc112af035b29d..44cc0364b98123d4c68adb729f3ed9eb39633774 100644 --- a/openappstack/cluster.py +++ b/openappstack/cluster.py @@ -65,6 +65,8 @@ class Cluster: # Load data from inventory.yml and settings.yml if load_data: self.load_data() + # Can be used to use a custom disk image. + self.disk_image_id = DEFAULT_IMAGE def load_data(self): """ @@ -119,7 +121,7 @@ class Cluster: region=DEFAULT_REGION, size=ram, disk=DEFAULT_DISK_SIZE_GB, - image=DEFAULT_IMAGE) + image=self.disk_image_id) droplet_id = droplet['droplet']['id'] log.info('Created droplet id: %s', droplet_id) greenhost_cloud.wait_for_state(droplet_id, 'running')