diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index 11cdb176..f2ad28d5 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -36,17 +36,19 @@ jobs: - uses: actions/checkout@v3 - name: Start minikube - uses: medyagh/setup-minikube@latest + uses: medyagh/setup-minikube@master + with: + cpus: max + memory: max - name: Try the cluster! run: kubectl get pods -A - - name: Pull images + - name: Build images run: | export SHELL=/bin/bash eval $(minikube -p minikube docker-env) - docker pull cvat/server - docker pull cvat/ui + docker compose -f docker-compose.yml -f docker-compose.dev.yml build echo -n "verifying images:" docker images @@ -56,12 +58,12 @@ jobs: - name: Deploy to minikube run: | - printf "traefik:\n service:\n externalIPs:\n - $(minikube ip)\n" > helm-chart/values.override.yaml + printf "traefik:\n service:\n externalIPs:\n - $(minikube ip)\n" >> tests/values.test.yaml find cvat/apps/iam/rules -name "*.rego" -and ! -name '*test*' -exec basename {} \; | tar -czf helm-chart/rules.tar.gz -C cvat/apps/iam/rules/ -T - cd helm-chart helm dependency update cd .. - helm upgrade -n default cvat -i --create-namespace helm-chart -f helm-chart/values.yaml -f helm-chart/values.override.yaml + helm upgrade -n default cvat -i --create-namespace helm-chart -f helm-chart/values.yaml -f tests/values.test.yaml - name: Update test config run: | @@ -71,10 +73,13 @@ jobs: - name: Wait for CVAT to be ready run: | - max_tries=30 + max_tries=60 while [[ $(kubectl get pods -l component=server -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" && max_tries -gt 0 ]]; do echo "waiting for CVAT pod" && (( max_tries-- )) && sleep 5; done while [[ $(kubectl get pods -l app.kubernetes.io/name=postgresql -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" && max_tries -gt 0 ]]; do echo "waiting for DB pod" && (( max_tries-- )) && sleep 5; done + while [[ $(curl -s -o /tmp/server_response -w "%{http_code}" cvat.local/api/server/about) != "200" && max_tries -gt 0 ]]; do echo "waiting for CVAT" && (( max_tries-- )) && sleep 5; done kubectl get pods + kubectl logs $(kubectl get pods -l component=server -o jsonpath='{.items[0].metadata.name}') + - name: Generate schema run: | @@ -94,9 +99,11 @@ jobs: - name: REST API and SDK tests run: | + kubectl cp tests/mounted_file_share/images $(kubectl get pods -l component=server -o jsonpath='{.items[0].metadata.name}'):/home/django/share pytest --platform=kube \ --ignore=tests/python/rest_api/test_cloud_storages.py \ --ignore=tests/python/rest_api/test_analytics.py \ --ignore=tests/python/rest_api/test_resource_import_export.py \ + --ignore=tests/python/rest_api/test_webhooks_sender.py \ -k 'not create_task_with_cloud_storage_files' \ tests/python diff --git a/helm-chart/Chart.yaml b/helm-chart/Chart.yaml index 27083aaa..9056e562 100644 --- a/helm-chart/Chart.yaml +++ b/helm-chart/Chart.yaml @@ -15,7 +15,7 @@ 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.3.1 +version: 0.4.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 diff --git a/helm-chart/templates/cvat_backend/cvat_worker_webhooks/deployment.yml b/helm-chart/templates/cvat_backend/cvat_worker_webhooks/deployment.yml new file mode 100644 index 00000000..6bf8a0ab --- /dev/null +++ b/helm-chart/templates/cvat_backend/cvat_worker_webhooks/deployment.yml @@ -0,0 +1,117 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-backend-worker-webhooks + namespace: {{ .Release.Namespace }} + labels: + app: cvat-app + tier: backend + component: worker-webhooks + {{- include "cvat.labels" . | nindent 4 }} + {{- with .Values.cvat.backend.worker.webhooks.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.cvat.backend.worker.webhooks.replicas }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "cvat.labels" . | nindent 6 }} + {{- with .Values.cvat.backend.worker.webhooks.labels }} + {{- toYaml . | nindent 6 }} + {{- end }} + app: cvat-app-worker-webhooks + tier: backend + component: worker-webhooks + template: + metadata: + labels: + app: cvat-app-worker-webhooks + tier: backend + component: worker-webhooks + {{- include "cvat.labels" . | nindent 8 }} + {{- with .Values.cvat.backend.worker.webhooks.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + containers: + - name: cvat-app-backend-worker-webhooks-container + image: {{ .Values.cvat.backend.image }}:{{ .Values.cvat.backend.tag }} + {{- with .Values.cvat.backend.worker.webhooks.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + args: ["-c", "supervisord/worker.webhooks.conf"] + env: + {{- if .Values.redis.enabled }} + - name: CVAT_REDIS_HOST + value: "{{ .Release.Name }}-redis-master" + {{- else }} + - name: CVAT_REDIS_HOST + value: "{{ .Values.redis.external.host }}" + {{- end }} + {{- if .Values.postgresql.enabled }} + - name: CVAT_POSTGRES_HOST + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.postgresql.secret.name }}" + key: postgresql-hostname + - name: CVAT_POSTGRES_USER + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.postgresql.secret.name }}" + key: postgresql-username + - name: CVAT_POSTGRES_DBNAME + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.postgresql.secret.name }}" + key: postgresql-database + - name: CVAT_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-{{ .Values.postgresql.secret.name }}" + key: postgresql-password + {{- else }} + - name: CVAT_POSTGRES_HOST + value: "{{ .Values.postgresql.external.host }}" + - name: CVAT_POSTGRES_USER + value: "{{ .Values.postgresql.external.user }}" + - name: CVAT_POSTGRES_DBNAME + value: "{{ .Values.postgresql.external.dbname }}" + - name: CVAT_POSTGRES_PASSWORD + value: "{{ .Values.postgresql.external.password }}" + - name: CVAT_POSTGRES_PORT + value: "{{ .Values.postgresql.external.port }}" + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.additionalEnv }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.additionalVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.cvat.backend.worker.webhooks.additionalVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index a1f87153..63d50109 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -43,6 +43,16 @@ cvat: additionalEnv: [] additionalVolumes: [] additionalVolumeMounts: [] + webhooks: + replicas: 1 + labels: {} + annotations: {} + resources: {} + affinity: {} + tolerations: [] + additionalEnv: [] + additionalVolumes: [] + additionalVolumeMounts: [] utils: replicas: 1 labels: {} @@ -55,7 +65,7 @@ cvat: additionalVolumeMounts: [] replicas: 1 image: cvat/server - tag: latest + tag: dev permissionFix: enabled: true service: @@ -74,7 +84,7 @@ cvat: frontend: replicas: 1 image: cvat/ui - tag: latest + tag: dev labels: {} # test: test annotations: {} diff --git a/supervisord/worker.webhooks.conf b/supervisord/worker.webhooks.conf index 67ca2745..72d99c95 100644 --- a/supervisord/worker.webhooks.conf +++ b/supervisord/worker.webhooks.conf @@ -17,20 +17,8 @@ loglevel=debug ; info, debug, warn, trace pidfile=/tmp/supervisord/supervisord.pid ; pidfile location childlogdir=%(ENV_HOME)s/logs/ ; where child log files will live -[program:ssh-agent] -command=bash -c "rm /tmp/ssh-agent.sock -f && /usr/bin/ssh-agent -d -a /tmp/ssh-agent.sock" -priority=1 -autorestart=true - [program:rqworker_webhooks] command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic \ "exec python3 %(ENV_HOME)s/manage.py rqworker -v 3 webhooks" environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" numprocs=%(ENV_NUMPROCS)s - -[program:clamav_update] -command=bash -c "if [ \"${CLAM_AV}\" = 'yes' ]; then /usr/bin/freshclam -d \ - -l %(ENV_HOME)s/logs/freshclam.log --foreground=true; fi" -numprocs=1 - -environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" diff --git a/tests/values.test.yaml b/tests/values.test.yaml new file mode 100644 index 00000000..94d9a4c6 --- /dev/null +++ b/tests/values.test.yaml @@ -0,0 +1,17 @@ +cvat: + backend: + server: + additionalVolumeMounts: + - mountPath: /home/django/share + name: cvat-backend-data + subPath: share + worker: + default: + additionalVolumeMounts: + - mountPath: /home/django/share + name: cvat-backend-data + subPath: share +traefik: + service: + externalIPs: + - 192.168.49.2 diff --git a/utils/update_version/update_version.py b/utils/update_version/update_version.py index d0507ec7..34a07611 100644 --- a/utils/update_version/update_version.py +++ b/utils/update_version/update_version.py @@ -4,8 +4,12 @@ from pathlib import Path import re +SUCCESS_CHAR = '\u2714' +FAIL_CHAR = '\u2716' + CVAT_VERSION_PATTERN = r'VERSION\s*=\s*\((\d+),\s*(\d*),\s*(\d+),\s*[\',\"](\w+)[\',\"],\s*(\d+)\)' COMPOSE_VERSION_PATTERN = r'(\$\{CVAT_VERSION:-)([\w.]+)(\})' +HELM_VERSION_PATTERN = r'(^ image: cvat/(?:ui|server)\n tag: )([\w.]+)' @dataclass() class Version: @@ -90,10 +94,10 @@ def update_compose_config(new_version: Version) -> None: with open(compose_file, 'w') as fp: fp.write(compose_text) - print(f'\u2714 {compose_file} was updated. {match[2]} -> {new_version_repr}\n') + print(f'{SUCCESS_CHAR} {compose_file} was updated. {match[2]} -> {new_version_repr}\n') else: - print(f'\u2714 {compose_file} no need to update.') + print(f'{SUCCESS_CHAR} {compose_file} no need to update.') def update_cvat_version(old_version: str, new_version: Version) -> None: version_file = get_cvat_version_filename() @@ -106,7 +110,32 @@ def update_cvat_version(old_version: str, new_version: Version) -> None: with open(version_file, 'w') as fp: fp.write(version_text) - print(f'\u2714 {version_file} was updated. {old_version} -> {new_version_str}\n') + print(f'{SUCCESS_CHAR} {version_file} was updated. {old_version} -> {new_version_str}\n') + +def update_helm_version(new_version: Version) -> None: + helm_values_file = get_helm_version_filename() + with open(helm_values_file, 'r') as fp: + helm_values_text = fp.read() + + if new_version.prerelease == 'final': + new_version_repr = new_version.compose_repr() + else: + new_version_repr = 'dev' + + match = re.search(HELM_VERSION_PATTERN, helm_values_text, re.M) + if not match: + raise RuntimeError('Cannot match version pattern') + + if match[2] != new_version_repr: + helm_values_text = re.sub(HELM_VERSION_PATTERN, f'\\g<1>{new_version_repr}', helm_values_text, 2, re.M) + with open(helm_values_file, 'w') as fp: + fp.write(helm_values_text) + + print(f'{SUCCESS_CHAR} {helm_values_file} was updated. {match[2]} -> {new_version_repr}\n') + + else: + print(f'{SUCCESS_CHAR} {helm_values_file} no need to update.') + def verify_input(version_types: dict, args: dict) -> None: versions_to_bump = [args[v_type] for v_type in version_types] @@ -122,7 +151,10 @@ def get_cvat_version_filename() -> Path: def get_compose_filename() -> Path: return Path(__file__).resolve().parents[2] / 'docker-compose.yml' -def get_current_version() -> tuple[str, Version]: +def get_helm_version_filename() -> Path: + return Path(__file__).resolve().parents[2] / 'helm-chart' / 'values.yaml' + +def get_current_version() -> 'tuple[str, Version]': version_file = get_cvat_version_filename() with open(version_file, 'r') as fp: @@ -180,7 +212,7 @@ def main() -> None: try: verify_input(version_types, vars(args)) except ValueError as e: - print(f'\u2716 ERROR: {e}\n') + print(f'{FAIL_CHAR} ERROR: {e}\n') parser.print_help() return @@ -202,10 +234,11 @@ def main() -> None: elif args.major: version.increment_major() - print(f'\u2714 Bump version to {version}\n') + print(f'{SUCCESS_CHAR} Bump version to {version}\n') update_cvat_version(version_str, version) update_compose_config(version) + update_helm_version(version) if __name__ == '__main__': main()