diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 64c1f601f02768763ea69fa7ebe48f5fff2fc87b..5696eb0652acddfaf329b19104bc75eddad8d96f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,5 @@ +include: + - .gitlab/ci_templates/kaniko.yml stages: - build - setup-cluster @@ -5,8 +7,6 @@ stages: - health-test - integration-test - cleanup - -image: "${CI_REGISTRY_IMAGE}/openappstack-ci:${CI_COMMIT_REF_NAME}" variables: SSH_KEY_ID: "411" HOSTNAME: "ci-${CI_PIPELINE_ID}" @@ -15,6 +15,10 @@ variables: DOMAIN: "openappstack.net" ADDRESS: "ci-${CI_PIPELINE_ID}.ci.openappstack.net" ANSIBLE_HOST_KEY_CHECKING: "False" + KANIKO_BUILD_IMAGENAME: "openappstack-ci" + +default: + image: "${CI_REGISTRY_IMAGE}/${KANIKO_BUILD_IMAGENAME}:${CI_COMMIT_REF_NAME}" ci_test_image: stage: build @@ -23,13 +27,12 @@ ci_test_image: # kaniko debug image (https://github.com/GoogleContainerTools/kaniko#debug-image) name: gcr.io/kaniko-project/executor:debug entrypoint: [""] - script: - - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json - - /kaniko/executor --context ${CI_PROJECT_DIR} --dockerfile ${CI_PROJECT_DIR}/Dockerfile --destination $CI_REGISTRY_IMAGE/openappstack-ci:${CI_COMMIT_REF_NAME} only: changes: + - .gitlab-ci.yml - Dockerfile - requirements.txt + extends: .kaniko_build bootstrap: stage: setup-cluster @@ -56,7 +59,6 @@ bootstrap: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml install: stage: install-apps @@ -82,7 +84,6 @@ install: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml testinfra: stage: health-test @@ -100,7 +101,6 @@ testinfra: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml certs: stage: health-test @@ -119,7 +119,6 @@ certs: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml prometheus-alerts: stage: health-test @@ -154,7 +153,6 @@ behave-nextcloud: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml behave-grafana: stage: integration-test @@ -172,7 +170,6 @@ behave-grafana: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml terminate: stage: cleanup @@ -188,7 +185,6 @@ terminate: - helmfiles/**/* - test/**/* - openappstack/**/* - - .gitlab-ci.yml # This trivial job works around a Gitlab bug: if no job runs at all due to # `only`, Gitlab gets confused and doesn't allow you to merge the MR: diff --git a/.gitlab/ci_templates/kaniko.yml b/.gitlab/ci_templates/kaniko.yml new file mode 100644 index 0000000000000000000000000000000000000000..967b8b3316f47da0832d7f06c6b4d0717cd891e9 --- /dev/null +++ b/.gitlab/ci_templates/kaniko.yml @@ -0,0 +1,14 @@ +# Optional environment variables: +# - KANIKO_BUILD_IMAGENAME: Build/target image image +# - KANIKO_CONTEXT: The subdir which holds the Dockerfile, leave unset if +# the Dockerfile is located at root level of the project. +.kaniko_build: + stage: build + image: + # We need a shell to provide the registry credentials, so we need to use the + # kaniko debug image (https://github.com/GoogleContainerTools/kaniko#debug-image) + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + script: + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR}/${KANIKO_CONTEXT:-.} --dockerfile ${CI_PROJECT_DIR}/${KANIKO_CONTEXT:-.}/Dockerfile --destination $CI_REGISTRY_IMAGE/${KANIKO_BUILD_IMAGENAME/#//}:${CI_COMMIT_REF_NAME} diff --git a/openappstack/cluster.py b/openappstack/cluster.py index 7bf626fc5c7efcc2951ef22d7b5eec0b7e737fc8..d728c54a1a37dc19433a079688ccc929de5a5400 100644 --- a/openappstack/cluster.py +++ b/openappstack/cluster.py @@ -1,4 +1,4 @@ -"""Contains code for managing the files related to an OpenAppStack cluster""" +"""Contains code for managing the files related to an OpenAppStack cluster.""" import configparser import logging @@ -22,6 +22,7 @@ DEFAULT_MEMORY_SIZE_MB = 6144 """Default "image" (operating system): 19 = Debian buster-x64 """ DEFAULT_IMAGE = 19 + class Cluster: """ Helper class for cluster-related paths, files, etc. @@ -68,12 +69,14 @@ class Cluster: # Work with the master node from the inventory self.hostname = inventory['all']['children']['master']['hosts'] - log.debug('Read data from inventory.yml:\n\thostname: %s', self.hostname) + log.debug( + 'Read data from inventory.yml:\n\thostname: %s', self.hostname) else: log.debug('Not loading cluster data from file. Set ' 'Cluster.data_loaded to False if you want a reload.') self.data_loaded = True + def create_droplet(self, ssh_key_id=0, hostname=None): """ Uses the Cosmos API to create a droplet with OAS default spec @@ -217,7 +220,7 @@ Cluster "{name}": Configuration: - Inventory file: {inventory_file} - Settings file: {settings_file} - + Kubectl: To use kubectl with this cluster, copy-paste this in your terminal: diff --git a/openappstack/cosmos.py b/openappstack/cosmos.py index fc69c25113f2453a3dbb27589b45f499a8307230..c23b7506dd155e526dd135d613baa75f6aa4b099 100755 --- a/openappstack/cosmos.py +++ b/openappstack/cosmos.py @@ -14,6 +14,7 @@ import requests from tabulate import tabulate from pytz import timezone + # Helper functions def request_api(resource: str, request_type: str = 'GET', data: str = ''): @@ -129,11 +130,12 @@ def delete_domain_record(domain: str, id: int): def delete_domain_records_by_name(domain: str, name_regex: str): - """Delete all domain records in a given domain matching a regex. + r"""Delete all domain records in a given domain matching a regex. Examples: delete_domain_records_by_name('openappstack.net', '^\*.ci-') delete_domain_records_by_name('openappstack.net', '^ci-') + """ all = get_domain_records_by_name(domain, name_regex) for record in all: @@ -227,7 +229,7 @@ def list_domain_records(domain: str): record['id'], record['name'], record['type'], record['data']] for record in records] log.info(tabulate(table_records, - headers=['ID', 'Name', 'Type', 'Data'])) + headers=['ID', 'Name', 'Type', 'Data'])) def list_droplets(): @@ -270,17 +272,21 @@ def terminate_droplet(id: int): delete_droplet(id) -def terminate_droplets_by_name(name_regex: str, ndays: int = 0, domain: str = 'openappstack.net'): +def terminate_droplets_by_name(name_regex: str, ndays: int = 0, + domain: str = 'openappstack.net'): r""" - Terminate droplets matching a regex and for x days older than current day. - Droplets defined on the env variable NO_TERMINATE_DROPLETS will not be delated + Terminate droplets matching a regex and for x days older than current day. + + Droplets defined on the env variable NO_TERMINATE_DROPLETS will not be + delated Example how to terminate all CI instances: terminate_old_droplets(name_regex='^ci\d+', ndays=5) will match i.e 'ci1234' , 'ci1', with a creation time older than 5 days """ - - threshold_time = (datetime.now(tz=timezone('Europe/Stockholm')) - timedelta(days=ndays)).strftime("%Y-%m-%dT%H:%M:%S+00:00") + threshold_time = (datetime.now(tz=timezone('Europe/Stockholm')) - + timedelta(days=ndays)).\ + strftime("%Y-%m-%dT%H:%M:%S+00:00") all = get_droplets() noterminate_droplets = [] @@ -291,10 +297,12 @@ def terminate_droplets_by_name(name_regex: str, ndays: int = 0, domain: str = 'o if droplet['name'] not in noterminate_droplets: if re.match(name_regex, droplet['name']): if droplet['created_at'] < threshold_time: - delete_domain_records_by_name(domain, '^\*.'+droplet['name']) + delete_domain_records_by_name( + domain, '^\*.'+droplet['name']) delete_domain_records_by_name(domain, '^'+droplet['name']) terminate_droplet(droplet['id']) + def wait_for_ssh(ip: str): """Wait for ssh to be reachable on port 22.""" log.info('Waiting for ssh to become available on ip %s', ip)