diff --git a/apps/challenges/aws_utils.py b/apps/challenges/aws_utils.py
index d9fae7905c..0a8b2b03bb 100644
--- a/apps/challenges/aws_utils.py
+++ b/apps/challenges/aws_utils.py
@@ -16,6 +16,16 @@
construct_and_send_worker_start_mail,
construct_and_send_eks_cluster_creation_mail,
)
+from .task_definitions import (
+ container_definition_code_upload_worker,
+ container_definition_submission_worker,
+ delete_service_args,
+ task_definition,
+ task_definition_code_upload_worker,
+ task_definition_static_code_upload_worker,
+ service_definition,
+ update_service_args,
+)
from base.utils import get_boto3_client, send_email
from evalai.celery import app
@@ -83,489 +93,6 @@
}
-task_definition = """
-{{
- "family":"{queue_name}",
- "executionRoleArn":"{EXECUTION_ROLE_ARN}",
- "networkMode":"awsvpc",
- "containerDefinitions":[
- {{
- "name": "{container_name}",
- "image": "{WORKER_IMAGE}",
- "essential": True,
- "environment": [
- {{
- "name": "AWS_DEFAULT_REGION",
- "value": "{AWS_REGION}"
- }},
- {{
- "name": "AWS_ACCOUNT_ID",
- "value": "{AWS_ACCOUNT_ID}"
- }},
- {{
- "name": "AWS_ACCESS_KEY_ID",
- "value": "{AWS_ACCESS_KEY_ID}"
- }},
- {{
- "name": "AWS_SECRET_ACCESS_KEY",
- "value": "{AWS_SECRET_ACCESS_KEY}"
- }},
- {{
- "name": "AWS_STORAGE_BUCKET_NAME",
- "value": "{AWS_STORAGE_BUCKET_NAME}"
- }},
- {{
- "name": "CHALLENGE_PK",
- "value": "{challenge_pk}"
- }},
- {{
- "name": "CHALLENGE_QUEUE",
- "value": "{queue_name}"
- }},
- {{
- "name": "DJANGO_SERVER",
- "value": "{DJANGO_SERVER}"
- }},
- {{
- "name": "DJANGO_SETTINGS_MODULE",
- "value": "settings.{ENV}"
- }},
- {{
- "name": "DEBUG",
- "value": "{DEBUG}"
- }},
- {{
- "name": "EMAIL_HOST",
- "value": "{EMAIL_HOST}"
- }},
- {{
- "name": "EMAIL_HOST_PASSWORD",
- "value": "{EMAIL_HOST_PASSWORD}"
- }},
- {{
- "name": "EMAIL_HOST_USER",
- "value": "{EMAIL_HOST_USER}"
- }},
- {{
- "name": "EMAIL_PORT",
- "value": "{EMAIL_PORT}"
- }},
- {{
- "name": "EMAIL_USE_TLS",
- "value": "{EMAIL_USE_TLS}"
- }},
- {{
- "name": "MEMCACHED_LOCATION",
- "value": "{MEMCACHED_LOCATION}"
- }},
- {{
- "name": "PYTHONUNBUFFERED",
- "value": "1"
- }},
- {{
- "name": "RDS_DB_NAME",
- "value": "{RDS_DB_NAME}"
- }},
- {{
- "name": "RDS_HOSTNAME",
- "value": "{RDS_HOSTNAME}"
- }},
- {{
- "name": "RDS_PASSWORD",
- "value": "{RDS_PASSWORD}"
- }},
- {{
- "name": "RDS_USERNAME",
- "value": "{RDS_USERNAME}"
- }},
- {{
- "name": "RDS_PORT",
- "value": "{RDS_PORT}"
- }},
- {{
- "name": "SECRET_KEY",
- "value": "{SECRET_KEY}"
- }},
- {{
- "name": "SENTRY_URL",
- "value": "{SENTRY_URL}"
- }},
- {{
- "name": "AWS_SES_REGION_NAME",
- "value": "{AWS_SES_REGION_NAME}"
- }},
- {{
- "name": "AWS_SES_REGION_ENDPOINT",
- "value": "{AWS_SES_REGION_ENDPOINT}"
- }}
- ],
- "workingDirectory": "/code",
- "readonlyRootFilesystem": False,
- "logConfiguration": {{
- "logDriver": "awslogs",
- "options": {{
- "awslogs-group": "{log_group_name}",
- "awslogs-region": "us-east-1",
- "awslogs-stream-prefix": "{queue_name}",
- "awslogs-create-group": "true",
- }},
- }},
- }}
- ],
- "requiresCompatibilities":[
- "FARGATE"
- ],
- "cpu": "{CPU}",
- "memory": "{MEMORY}",
-}}
-"""
-
-task_definition_code_upload_worker = """
-{{
- "family":"{queue_name}",
- "executionRoleArn":"{EXECUTION_ROLE_ARN}",
- "networkMode":"awsvpc",
- "containerDefinitions":[
- {{
- "name": "{code_upload_container_name}",
- "image": "{CODE_UPLOAD_WORKER_IMAGE}",
- "essential": True,
- "environment": [
- {{
- "name": "AWS_DEFAULT_REGION",
- "value": "{AWS_REGION}"
- }},
- {{
- "name": "AWS_ACCESS_KEY_ID",
- "value": "{AWS_ACCESS_KEY_ID}"
- }},
- {{
- "name": "AWS_SECRET_ACCESS_KEY",
- "value": "{AWS_SECRET_ACCESS_KEY}"
- }},
- {{
- "name": "CLUSTER_NAME",
- "value": "{cluster_name}"
- }},
- {{
- "name": "CLUSTER_ENDPOINT",
- "value": "{cluster_endpoint}"
- }},
- {{
- "name": "CERTIFICATE",
- "value": "{certificate}"
- }},
- {{
- "name": "CIDR",
- "value": "{CIDR}"
- }},
- {{
- "name": "QUEUE_NAME",
- "value": "{queue_name}"
- }},
- {{
- "name": "EVALAI_API_SERVER",
- "value": "{EVALAI_API_SERVER}"
- }},
-
- {{
- "name": "AUTH_TOKEN",
- "value": "{auth_token}"
- }},
-
- {{
- "name": "EVALAI_DNS",
- "value": "{EVALAI_DNS}"
- }},
-
- {{
- "name": "EFS_ID",
- "value": "{EFS_ID}"
- }}
-
- ],
- "workingDirectory": "/code",
- "readonlyRootFilesystem": False,
- "logConfiguration": {{
- "logDriver": "awslogs",
- "options": {{
- "awslogs-group": "{log_group_name}",
- "awslogs-region": "us-east-1",
- "awslogs-stream-prefix": "{queue_name}",
- "awslogs-create-group": "true",
- }},
- }},
- }}
- ],
- "requiresCompatibilities":[
- "FARGATE"
- ],
- "cpu": "{CPU}",
- "memory": "{MEMORY}",
-}}
-"""
-
-task_definition_static_code_upload_worker = """
-{{
- "family":"{queue_name}",
- "executionRoleArn":"{EXECUTION_ROLE_ARN}",
- "networkMode":"awsvpc",
- "containerDefinitions":[
- {{
- "name": "{code_upload_container_name}",
- "image": "{CODE_UPLOAD_WORKER_IMAGE}",
- "essential": True,
- "environment": [
- {{
- "name": "AWS_DEFAULT_REGION",
- "value": "{AWS_REGION}"
- }},
- {{
- "name": "AWS_ACCESS_KEY_ID",
- "value": "{AWS_ACCESS_KEY_ID}"
- }},
- {{
- "name": "AWS_SECRET_ACCESS_KEY",
- "value": "{AWS_SECRET_ACCESS_KEY}"
- }},
- {{
- "name": "CLUSTER_NAME",
- "value": "{cluster_name}"
- }},
- {{
- "name": "CLUSTER_ENDPOINT",
- "value": "{cluster_endpoint}"
- }},
- {{
- "name": "CERTIFICATE",
- "value": "{certificate}"
- }},
- {{
- "name": "CIDR",
- "value": "{CIDR}"
- }},
- {{
- "name": "QUEUE_NAME",
- "value": "{queue_name}"
- }},
- {{
- "name": "EVALAI_API_SERVER",
- "value": "{EVALAI_API_SERVER}"
- }},
-
- {{
- "name": "AUTH_TOKEN",
- "value": "{auth_token}"
- }},
-
- {{
- "name": "EVALAI_DNS",
- "value": "{EVALAI_DNS}"
- }},
-
- {{
- "name": "EFS_ID",
- "value": "{EFS_ID}"
- }}
-
- ],
- "workingDirectory": "/code",
- "readonlyRootFilesystem": False,
- "logConfiguration": {{
- "logDriver": "awslogs",
- "options": {{
- "awslogs-group": "{log_group_name}",
- "awslogs-region": "us-east-1",
- "awslogs-stream-prefix": "{queue_name}",
- "awslogs-create-group": "true",
- }},
- }},
- }},
- {{
- "name": "{container_name}",
- "image": "{WORKER_IMAGE}",
- "essential": True,
- "environment": [
- {{
- "name": "AWS_DEFAULT_REGION",
- "value": "{AWS_REGION}"
- }},
- {{
- "name": "AWS_ACCOUNT_ID",
- "value": "{AWS_ACCOUNT_ID}"
- }},
- {{
- "name": "AWS_ACCESS_KEY_ID",
- "value": "{AWS_ACCESS_KEY_ID}"
- }},
- {{
- "name": "AWS_SECRET_ACCESS_KEY",
- "value": "{AWS_SECRET_ACCESS_KEY}"
- }},
- {{
- "name": "AWS_STORAGE_BUCKET_NAME",
- "value": "{AWS_STORAGE_BUCKET_NAME}"
- }},
- {{
- "name": "CHALLENGE_PK",
- "value": "{challenge_pk}"
- }},
- {{
- "name": "CHALLENGE_QUEUE",
- "value": "{queue_name}"
- }},
- {{
- "name": "DJANGO_SERVER",
- "value": "{DJANGO_SERVER}"
- }},
- {{
- "name": "DJANGO_SETTINGS_MODULE",
- "value": "settings.{ENV}"
- }},
- {{
- "name": "DEBUG",
- "value": "{DEBUG}"
- }},
- {{
- "name": "EMAIL_HOST",
- "value": "{EMAIL_HOST}"
- }},
- {{
- "name": "EMAIL_HOST_PASSWORD",
- "value": "{EMAIL_HOST_PASSWORD}"
- }},
- {{
- "name": "EMAIL_HOST_USER",
- "value": "{EMAIL_HOST_USER}"
- }},
- {{
- "name": "EMAIL_PORT",
- "value": "{EMAIL_PORT}"
- }},
- {{
- "name": "EMAIL_USE_TLS",
- "value": "{EMAIL_USE_TLS}"
- }},
- {{
- "name": "MEMCACHED_LOCATION",
- "value": "{MEMCACHED_LOCATION}"
- }},
- {{
- "name": "PYTHONUNBUFFERED",
- "value": "1"
- }},
- {{
- "name": "RDS_DB_NAME",
- "value": "{RDS_DB_NAME}"
- }},
- {{
- "name": "RDS_HOSTNAME",
- "value": "{RDS_HOSTNAME}"
- }},
- {{
- "name": "RDS_PASSWORD",
- "value": "{RDS_PASSWORD}"
- }},
- {{
- "name": "RDS_USERNAME",
- "value": "{RDS_USERNAME}"
- }},
- {{
- "name": "RDS_PORT",
- "value": "{RDS_PORT}"
- }},
- {{
- "name": "SECRET_KEY",
- "value": "{SECRET_KEY}"
- }},
- {{
- "name": "SENTRY_URL",
- "value": "{SENTRY_URL}"
- }},
- {{
- "name": "AWS_SES_REGION_NAME",
- "value": "{AWS_SES_REGION_NAME}"
- }},
- {{
- "name": "AWS_SES_REGION_ENDPOINT",
- "value": "{AWS_SES_REGION_ENDPOINT}"
- }}
- ],
- "workingDirectory": "/code",
- "readonlyRootFilesystem": False,
- "logConfiguration": {{
- "logDriver": "awslogs",
- "options": {{
- "awslogs-group": "{log_group_name}",
- "awslogs-region": "us-east-1",
- "awslogs-stream-prefix": "{queue_name}",
- "awslogs-create-group": "true",
- }},
- }},
- }}
-
- ],
- "requiresCompatibilities":[
- "FARGATE"
- ],
- "cpu": "{CPU}",
- "memory": "{MEMORY}",
-}}
-"""
-
-service_definition = """
-{{
- "cluster":"{CLUSTER}",
- "serviceName":"{service_name}",
- "taskDefinition":"{task_def_arn}",
- "desiredCount":1,
- "clientToken":"{client_token}",
- "launchType":"FARGATE",
- "platformVersion":"LATEST",
- "networkConfiguration":{{
- "awsvpcConfiguration": {{
- "subnets": [
- "{SUBNET_1}",
- "{SUBNET_2}",
- ],
- 'securityGroups': [
- "{SUBNET_SECURITY_GROUP}",
- ],
- "assignPublicIp": "ENABLED"
- }}
- }},
- "schedulingStrategy":"REPLICA",
- "deploymentController":{{
- "type": "ECS"
- }},
- "deploymentConfiguration":{{
- "deploymentCircuitBreaker":{{
- "enable": True,
- "rollback": False
- }}
- }}
-}}
-"""
-
-update_service_args = """
-{{
- "cluster":"{CLUSTER}",
- "service":"{service_name}",
- "desiredCount":num_of_tasks,
- "taskDefinition":"{task_def_arn}",
- "forceNewDeployment":{force_new_deployment}
-}}
-"""
-
-delete_service_args = """
-{{
- "cluster": "{CLUSTER}",
- "service": "{service_name}",
- "force": False
-}}
-"""
-
-
def get_code_upload_setup_meta_for_challenge(challenge_pk):
"""
Return the EKS cluster network and arn meta for a challenge
@@ -668,25 +195,41 @@ def register_task_def_by_challenge_pk(client, queue_name, challenge):
# challenge host auth token to be used by code-upload-worker
token = JwtToken.objects.get(user=challenge.creator.created_by)
if challenge.is_static_dataset_code_upload:
+ code_upload_container = (
+ container_definition_code_upload_worker.format(
+ queue_name=queue_name,
+ code_upload_container_name=code_upload_container_name,
+ auth_token=token.refresh_token,
+ cluster_name=cluster_name,
+ cluster_endpoint=cluster_endpoint,
+ certificate=cluster_certificate,
+ log_group_name=log_group_name,
+ EVALAI_DNS=EVALAI_DNS,
+ EFS_ID=efs_id,
+ **COMMON_SETTINGS_DICT,
+ **challenge_aws_keys,
+ )
+ )
+ submission_container = (
+ container_definition_submission_worker.format(
+ queue_name=queue_name,
+ container_name=container_name,
+ ENV=ENV,
+ challenge_pk=challenge.pk,
+ log_group_name=log_group_name,
+ AWS_SES_REGION_NAME=AWS_SES_REGION_NAME,
+ AWS_SES_REGION_ENDPOINT=AWS_SES_REGION_ENDPOINT,
+ **COMMON_SETTINGS_DICT,
+ **aws_keys,
+ )
+ )
definition = task_definition_static_code_upload_worker.format(
queue_name=queue_name,
- container_name=container_name,
- code_upload_container_name=code_upload_container_name,
- ENV=ENV,
- challenge_pk=challenge.pk,
- auth_token=token.refresh_token,
- cluster_name=cluster_name,
- cluster_endpoint=cluster_endpoint,
- certificate=cluster_certificate,
+ code_upload_container=code_upload_container,
+ submission_container=submission_container,
CPU=worker_cpu_cores,
MEMORY=worker_memory,
- log_group_name=log_group_name,
- EVALAI_DNS=EVALAI_DNS,
- EFS_ID=efs_id,
- AWS_SES_REGION_NAME=AWS_SES_REGION_NAME,
- AWS_SES_REGION_ENDPOINT=AWS_SES_REGION_ENDPOINT,
**COMMON_SETTINGS_DICT,
- **challenge_aws_keys,
)
else:
definition = task_definition_code_upload_worker.format(
diff --git a/apps/challenges/serializers.py b/apps/challenges/serializers.py
index 4fa6a24bee..e93250a399 100644
--- a/apps/challenges/serializers.py
+++ b/apps/challenges/serializers.py
@@ -64,6 +64,7 @@ class Meta:
"cli_version",
"remote_evaluation",
"workers",
+ "created_at",
)
diff --git a/apps/challenges/task_definitions.py b/apps/challenges/task_definitions.py
new file mode 100644
index 0000000000..a737513594
--- /dev/null
+++ b/apps/challenges/task_definitions.py
@@ -0,0 +1,488 @@
+task_definition = """
+{{
+ "family":"{queue_name}",
+ "executionRoleArn":"{EXECUTION_ROLE_ARN}",
+ "networkMode":"awsvpc",
+ "containerDefinitions":[
+ {{
+ "name": "{container_name}",
+ "image": "{WORKER_IMAGE}",
+ "essential": True,
+ "environment": [
+ {{
+ "name": "AWS_DEFAULT_REGION",
+ "value": "{AWS_REGION}"
+ }},
+ {{
+ "name": "AWS_ACCOUNT_ID",
+ "value": "{AWS_ACCOUNT_ID}"
+ }},
+ {{
+ "name": "AWS_ACCESS_KEY_ID",
+ "value": "{AWS_ACCESS_KEY_ID}"
+ }},
+ {{
+ "name": "AWS_SECRET_ACCESS_KEY",
+ "value": "{AWS_SECRET_ACCESS_KEY}"
+ }},
+ {{
+ "name": "AWS_STORAGE_BUCKET_NAME",
+ "value": "{AWS_STORAGE_BUCKET_NAME}"
+ }},
+ {{
+ "name": "CHALLENGE_PK",
+ "value": "{challenge_pk}"
+ }},
+ {{
+ "name": "CHALLENGE_QUEUE",
+ "value": "{queue_name}"
+ }},
+ {{
+ "name": "DJANGO_SERVER",
+ "value": "{DJANGO_SERVER}"
+ }},
+ {{
+ "name": "DJANGO_SETTINGS_MODULE",
+ "value": "settings.{ENV}"
+ }},
+ {{
+ "name": "DEBUG",
+ "value": "{DEBUG}"
+ }},
+ {{
+ "name": "EMAIL_HOST",
+ "value": "{EMAIL_HOST}"
+ }},
+ {{
+ "name": "EMAIL_HOST_PASSWORD",
+ "value": "{EMAIL_HOST_PASSWORD}"
+ }},
+ {{
+ "name": "EMAIL_HOST_USER",
+ "value": "{EMAIL_HOST_USER}"
+ }},
+ {{
+ "name": "EMAIL_PORT",
+ "value": "{EMAIL_PORT}"
+ }},
+ {{
+ "name": "EMAIL_USE_TLS",
+ "value": "{EMAIL_USE_TLS}"
+ }},
+ {{
+ "name": "MEMCACHED_LOCATION",
+ "value": "{MEMCACHED_LOCATION}"
+ }},
+ {{
+ "name": "PYTHONUNBUFFERED",
+ "value": "1"
+ }},
+ {{
+ "name": "RDS_DB_NAME",
+ "value": "{RDS_DB_NAME}"
+ }},
+ {{
+ "name": "RDS_HOSTNAME",
+ "value": "{RDS_HOSTNAME}"
+ }},
+ {{
+ "name": "RDS_PASSWORD",
+ "value": "{RDS_PASSWORD}"
+ }},
+ {{
+ "name": "RDS_USERNAME",
+ "value": "{RDS_USERNAME}"
+ }},
+ {{
+ "name": "RDS_PORT",
+ "value": "{RDS_PORT}"
+ }},
+ {{
+ "name": "SECRET_KEY",
+ "value": "{SECRET_KEY}"
+ }},
+ {{
+ "name": "SENTRY_URL",
+ "value": "{SENTRY_URL}"
+ }},
+ {{
+ "name": "AWS_SES_REGION_NAME",
+ "value": "{AWS_SES_REGION_NAME}"
+ }},
+ {{
+ "name": "AWS_SES_REGION_ENDPOINT",
+ "value": "{AWS_SES_REGION_ENDPOINT}"
+ }}
+ ],
+ "workingDirectory": "/code",
+ "readonlyRootFilesystem": False,
+ "logConfiguration": {{
+ "logDriver": "awslogs",
+ "options": {{
+ "awslogs-group": "{log_group_name}",
+ "awslogs-region": "us-east-1",
+ "awslogs-stream-prefix": "{queue_name}",
+ "awslogs-create-group": "true",
+ }},
+ }},
+ }}
+ ],
+ "requiresCompatibilities":[
+ "FARGATE"
+ ],
+ "cpu": "{CPU}",
+ "memory": "{MEMORY}",
+}}
+"""
+
+task_definition_code_upload_worker = """
+{{
+ "family":"{queue_name}",
+ "executionRoleArn":"{EXECUTION_ROLE_ARN}",
+ "networkMode":"awsvpc",
+ "containerDefinitions":[
+ {{
+ "name": "{code_upload_container_name}",
+ "image": "{CODE_UPLOAD_WORKER_IMAGE}",
+ "essential": True,
+ "environment": [
+ {{
+ "name": "AWS_DEFAULT_REGION",
+ "value": "{AWS_REGION}"
+ }},
+ {{
+ "name": "AWS_ACCESS_KEY_ID",
+ "value": "{AWS_ACCESS_KEY_ID}"
+ }},
+ {{
+ "name": "AWS_SECRET_ACCESS_KEY",
+ "value": "{AWS_SECRET_ACCESS_KEY}"
+ }},
+ {{
+ "name": "CLUSTER_NAME",
+ "value": "{cluster_name}"
+ }},
+ {{
+ "name": "CLUSTER_ENDPOINT",
+ "value": "{cluster_endpoint}"
+ }},
+ {{
+ "name": "CERTIFICATE",
+ "value": "{certificate}"
+ }},
+ {{
+ "name": "CIDR",
+ "value": "{CIDR}"
+ }},
+ {{
+ "name": "QUEUE_NAME",
+ "value": "{queue_name}"
+ }},
+ {{
+ "name": "EVALAI_API_SERVER",
+ "value": "{EVALAI_API_SERVER}"
+ }},
+
+ {{
+ "name": "AUTH_TOKEN",
+ "value": "{auth_token}"
+ }},
+
+ {{
+ "name": "EVALAI_DNS",
+ "value": "{EVALAI_DNS}"
+ }},
+
+ {{
+ "name": "EFS_ID",
+ "value": "{EFS_ID}"
+ }}
+
+ ],
+ "workingDirectory": "/code",
+ "readonlyRootFilesystem": False,
+ "logConfiguration": {{
+ "logDriver": "awslogs",
+ "options": {{
+ "awslogs-group": "{log_group_name}",
+ "awslogs-region": "us-east-1",
+ "awslogs-stream-prefix": "{queue_name}",
+ "awslogs-create-group": "true",
+ }},
+ }},
+ }}
+ ],
+ "requiresCompatibilities":[
+ "FARGATE"
+ ],
+ "cpu": "{CPU}",
+ "memory": "{MEMORY}",
+}}
+"""
+
+task_definition_static_code_upload_worker = """
+{{
+ "family":"{queue_name}",
+ "executionRoleArn":"{EXECUTION_ROLE_ARN}",
+ "networkMode":"awsvpc",
+ "containerDefinitions":[
+ {code_upload_container},
+ {submission_container}
+ ],
+ "requiresCompatibilities":[
+ "FARGATE"
+ ],
+ "cpu": "{CPU}",
+ "memory": "{MEMORY}",
+}}
+"""
+
+container_definition_submission_worker = """
+{{
+ "name": "{container_name}",
+ "image": "{WORKER_IMAGE}",
+ "essential": True,
+ "environment": [
+ {{
+ "name": "AWS_DEFAULT_REGION",
+ "value": "{AWS_REGION}"
+ }},
+ {{
+ "name": "AWS_ACCOUNT_ID",
+ "value": "{AWS_ACCOUNT_ID}"
+ }},
+ {{
+ "name": "AWS_ACCESS_KEY_ID",
+ "value": "{AWS_ACCESS_KEY_ID}"
+ }},
+ {{
+ "name": "AWS_SECRET_ACCESS_KEY",
+ "value": "{AWS_SECRET_ACCESS_KEY}"
+ }},
+ {{
+ "name": "AWS_STORAGE_BUCKET_NAME",
+ "value": "{AWS_STORAGE_BUCKET_NAME}"
+ }},
+ {{
+ "name": "CHALLENGE_PK",
+ "value": "{challenge_pk}"
+ }},
+ {{
+ "name": "CHALLENGE_QUEUE",
+ "value": "{queue_name}"
+ }},
+ {{
+ "name": "DJANGO_SERVER",
+ "value": "{DJANGO_SERVER}"
+ }},
+ {{
+ "name": "DJANGO_SETTINGS_MODULE",
+ "value": "settings.{ENV}"
+ }},
+ {{
+ "name": "DEBUG",
+ "value": "{DEBUG}"
+ }},
+ {{
+ "name": "EMAIL_HOST",
+ "value": "{EMAIL_HOST}"
+ }},
+ {{
+ "name": "EMAIL_HOST_PASSWORD",
+ "value": "{EMAIL_HOST_PASSWORD}"
+ }},
+ {{
+ "name": "EMAIL_HOST_USER",
+ "value": "{EMAIL_HOST_USER}"
+ }},
+ {{
+ "name": "EMAIL_PORT",
+ "value": "{EMAIL_PORT}"
+ }},
+ {{
+ "name": "EMAIL_USE_TLS",
+ "value": "{EMAIL_USE_TLS}"
+ }},
+ {{
+ "name": "MEMCACHED_LOCATION",
+ "value": "{MEMCACHED_LOCATION}"
+ }},
+ {{
+ "name": "PYTHONUNBUFFERED",
+ "value": "1"
+ }},
+ {{
+ "name": "RDS_DB_NAME",
+ "value": "{RDS_DB_NAME}"
+ }},
+ {{
+ "name": "RDS_HOSTNAME",
+ "value": "{RDS_HOSTNAME}"
+ }},
+ {{
+ "name": "RDS_PASSWORD",
+ "value": "{RDS_PASSWORD}"
+ }},
+ {{
+ "name": "RDS_USERNAME",
+ "value": "{RDS_USERNAME}"
+ }},
+ {{
+ "name": "RDS_PORT",
+ "value": "{RDS_PORT}"
+ }},
+ {{
+ "name": "SECRET_KEY",
+ "value": "{SECRET_KEY}"
+ }},
+ {{
+ "name": "SENTRY_URL",
+ "value": "{SENTRY_URL}"
+ }},
+ {{
+ "name": "AWS_SES_REGION_NAME",
+ "value": "{AWS_SES_REGION_NAME}"
+ }},
+ {{
+ "name": "AWS_SES_REGION_ENDPOINT",
+ "value": "{AWS_SES_REGION_ENDPOINT}"
+ }}
+ ],
+ "workingDirectory": "/code",
+ "readonlyRootFilesystem": False,
+ "logConfiguration": {{
+ "logDriver": "awslogs",
+ "options": {{
+ "awslogs-group": "{log_group_name}",
+ "awslogs-region": "us-east-1",
+ "awslogs-stream-prefix": "{queue_name}",
+ "awslogs-create-group": "true",
+ }},
+ }},
+}}
+"""
+
+container_definition_code_upload_worker = """
+{{
+ "name": "{code_upload_container_name}",
+ "image": "{CODE_UPLOAD_WORKER_IMAGE}",
+ "essential": True,
+ "environment": [
+ {{
+ "name": "AWS_DEFAULT_REGION",
+ "value": "{AWS_REGION}"
+ }},
+ {{
+ "name": "AWS_ACCESS_KEY_ID",
+ "value": "{AWS_ACCESS_KEY_ID}"
+ }},
+ {{
+ "name": "AWS_SECRET_ACCESS_KEY",
+ "value": "{AWS_SECRET_ACCESS_KEY}"
+ }},
+ {{
+ "name": "CLUSTER_NAME",
+ "value": "{cluster_name}"
+ }},
+ {{
+ "name": "CLUSTER_ENDPOINT",
+ "value": "{cluster_endpoint}"
+ }},
+ {{
+ "name": "CERTIFICATE",
+ "value": "{certificate}"
+ }},
+ {{
+ "name": "CIDR",
+ "value": "{CIDR}"
+ }},
+ {{
+ "name": "QUEUE_NAME",
+ "value": "{queue_name}"
+ }},
+ {{
+ "name": "EVALAI_API_SERVER",
+ "value": "{EVALAI_API_SERVER}"
+ }},
+
+ {{
+ "name": "AUTH_TOKEN",
+ "value": "{auth_token}"
+ }},
+
+ {{
+ "name": "EVALAI_DNS",
+ "value": "{EVALAI_DNS}"
+ }},
+
+ {{
+ "name": "EFS_ID",
+ "value": "{EFS_ID}"
+ }}
+
+ ],
+ "workingDirectory": "/code",
+ "readonlyRootFilesystem": False,
+ "logConfiguration": {{
+ "logDriver": "awslogs",
+ "options": {{
+ "awslogs-group": "{log_group_name}",
+ "awslogs-region": "us-east-1",
+ "awslogs-stream-prefix": "{queue_name}",
+ "awslogs-create-group": "true",
+ }},
+ }},
+}}
+"""
+
+service_definition = """
+{{
+ "cluster":"{CLUSTER}",
+ "serviceName":"{service_name}",
+ "taskDefinition":"{task_def_arn}",
+ "desiredCount":1,
+ "clientToken":"{client_token}",
+ "launchType":"FARGATE",
+ "platformVersion":"LATEST",
+ "networkConfiguration":{{
+ "awsvpcConfiguration": {{
+ "subnets": [
+ "{SUBNET_1}",
+ "{SUBNET_2}",
+ ],
+ 'securityGroups': [
+ "{SUBNET_SECURITY_GROUP}",
+ ],
+ "assignPublicIp": "ENABLED"
+ }}
+ }},
+ "schedulingStrategy":"REPLICA",
+ "deploymentController":{{
+ "type": "ECS"
+ }},
+ "deploymentConfiguration":{{
+ "deploymentCircuitBreaker":{{
+ "enable": True,
+ "rollback": False
+ }}
+ }}
+}}
+"""
+
+update_service_args = """
+{{
+ "cluster":"{CLUSTER}",
+ "service":"{service_name}",
+ "desiredCount":num_of_tasks,
+ "taskDefinition":"{task_def_arn}",
+ "forceNewDeployment":{force_new_deployment}
+}}
+"""
+
+delete_service_args = """
+{{
+ "cluster": "{CLUSTER}",
+ "service": "{service_name}",
+ "force": False
+}}
+"""
diff --git a/apps/challenges/views.py b/apps/challenges/views.py
index f6f2a7b014..1397f6142b 100644
--- a/apps/challenges/views.py
+++ b/apps/challenges/views.py
@@ -123,6 +123,7 @@
)
from .aws_utils import (
+ delete_workers,
start_workers,
stop_workers,
restart_workers,
@@ -2822,12 +2823,19 @@ def manage_worker(request, challenge_pk, action):
return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
# make sure that the action is valid.
- if action not in ("start", "stop", "restart"):
+ if action not in ("start", "stop", "restart", "delete"):
response_data = {
"error": "The action {} is invalid for worker".format(action)
}
return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE)
+ # Only allow EvalAI admins to delete workers
+ if action == "delete" and not request.user.is_staff:
+ response_data = {
+ "error": "Sorry, you are not authorized for access worker operations."
+ }
+ return Response(response_data, status=status.HTTP_400_BAD_REQUEST)
+
challenge = get_challenge_model(challenge_pk)
response_data = {}
@@ -2838,6 +2846,8 @@ def manage_worker(request, challenge_pk, action):
response = stop_workers([challenge])
elif action == "restart":
response = restart_workers([challenge])
+ elif action == "delete":
+ response = delete_workers([challenge])
if response:
count, failures = response["count"], response["failures"]
diff --git a/apps/jobs/sender.py b/apps/jobs/sender.py
index 0927a9685e..ef67410786 100644
--- a/apps/jobs/sender.py
+++ b/apps/jobs/sender.py
@@ -11,6 +11,7 @@
from base.utils import send_slack_notification
from challenges.models import Challenge
from .utils import get_submission_model
+from monitoring.statsd.metrics import NUM_SUBMISSIONS_IN_QUEUE, increment_statsd_counter
logger = logging.getLogger(__name__)
@@ -83,6 +84,11 @@ def publish_submission_message(message):
queue_name = challenge.queue
slack_url = challenge.slack_webhook_url
queue = get_or_create_sqs_queue(queue_name)
+ # increase counter for submission pushed into queue
+ submission_metric_tags = [
+ "queue_name:%s" % queue_name,
+ ]
+ increment_statsd_counter(NUM_SUBMISSIONS_IN_QUEUE, submission_metric_tags, 1)
response = queue.send_message(MessageBody=json.dumps(message))
# send slack notification
if slack_url:
diff --git a/apps/jobs/views.py b/apps/jobs/views.py
index 29b4850da0..cef16b95d9 100644
--- a/apps/jobs/views.py
+++ b/apps/jobs/views.py
@@ -50,6 +50,7 @@
get_challenge_model,
get_challenge_phase_model,
get_challenge_phase_split_model,
+ get_participant_model,
)
from hosts.models import ChallengeHost
from hosts.utils import is_user_a_host_of_challenge
@@ -2824,7 +2825,35 @@ def update_submission_meta(request, challenge_pk, submission_pk):
serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
else:
- response_data = {
- "error": "Sorry, you are not authorized to make this request"
- }
- return Response(response_data, status=status.HTTP_403_FORBIDDEN)
+ participant_team_pk = get_participant_team_id_of_user_for_a_challenge(
+ request.user, challenge_pk
+ )
+
+ participant_team = get_participant_model(participant_team_pk)
+
+ try:
+ submission = Submission.objects.get(
+ id=submission_pk,
+ participant_team=participant_team,
+ )
+ except Submission.DoesNotExist:
+ response_data = {
+ "error": "Submission {} does not exist".format(submission_pk)
+ }
+ return Response(response_data, status=status.HTTP_404_NOT_FOUND)
+
+ serializer = SubmissionSerializer(
+ submission,
+ data=request.data,
+ context={"request": request},
+ partial=True,
+ )
+
+ if serializer.is_valid():
+ serializer.save()
+ response_data = serializer.data
+ return Response(response_data, status=status.HTTP_200_OK)
+ else:
+ return Response(
+ serializer.errors, status=status.HTTP_400_BAD_REQUEST
+ )
diff --git a/docker-compose-monitoring.yml b/docker-compose-monitoring.yml
new file mode 100644
index 0000000000..33bef404e0
--- /dev/null
+++ b/docker-compose-monitoring.yml
@@ -0,0 +1,32 @@
+version: "3"
+services:
+ prometheus:
+ image: prom/prometheus:latest
+ user: "1000"
+ volumes:
+ - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
+ - ./monitoring/prometheus/prometheus_db:/var/lib/prometheus
+ - ./monitoring/prometheus/prometheus_db:/prometheus
+ - ./monitoring/prometheus/prometheus_db:/etc/prometheus
+ command:
+ - "--config.file=/etc/prometheus/prometheus.yml"
+ ports:
+ - "9090:9090"
+
+ grafana:
+ image: grafana/grafana:latest
+ user: "1000"
+ env_file:
+ - docker/dev/docker.env
+ volumes:
+ - ./monitoring/grafana/grafana_db:/var/lib/grafana
+ depends_on:
+ - prometheus
+ ports:
+ - "3000:3000"
+
+ node_exporter:
+ hostname: node_exporter
+ image: prom/node-exporter
+ ports:
+ - "9100:9100"
diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml
index 4789017ebd..6f5342a969 100644
--- a/docker-compose-staging.yml
+++ b/docker-compose-staging.yml
@@ -130,7 +130,8 @@ services:
- '--log.level=info'
- '--web.telemetry-path=/statsd/metrics'
ports:
- - '9125:9125'
+ - '9125:9125/udp'
+ - '9125:9125/tcp'
- '9102:9102'
node_exporter:
@@ -149,6 +150,7 @@ services:
depends_on:
- prometheus
- grafana
+ - statsd-exporter
ports:
- '80:80'
- '443:443'
diff --git a/docker-compose.yml b/docker-compose.yml
index b3d7cab852..e0d1f7e833 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -66,31 +66,6 @@ services:
- ./frontend_v2:/code
- /code/node_modules
- prometheus:
- image: prom/prometheus:latest
- user: "1000"
- volumes:
- - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- - ./monitoring/prometheus/prometheus_db:/var/lib/prometheus
- - ./monitoring/prometheus/prometheus_db:/prometheus
- - ./monitoring/prometheus/prometheus_db:/etc/prometheus
- command:
- - '--config.file=/etc/prometheus/prometheus.yml'
- ports:
- - '9090:9090'
-
- grafana:
- image: grafana/grafana:latest
- user: "1000"
- env_file:
- - docker/dev/docker.env
- volumes:
- - ./monitoring/grafana/grafana_db:/var/lib/grafana
- depends_on:
- - prometheus
- ports:
- - '3000:3000'
-
statsd-exporter:
hostname: statsd
image: prom/statsd-exporter:latest
@@ -100,9 +75,3 @@ services:
ports:
- '9125:9125'
- '9102:9102'
-
- node_exporter:
- hostname: node_exporter
- image: prom/node-exporter
- ports:
- - '9100:9100'
diff --git a/docker/prod/docker_staging.env b/docker/prod/docker_staging.env
index e561b8a2be..c1df14a55a 100644
--- a/docker/prod/docker_staging.env
+++ b/docker/prod/docker_staging.env
@@ -61,5 +61,5 @@ GF_SECURITY_ADMIN_PASSWORD=x
GF_SERVER_ROOT_URL=http://localhost:3000/grafana
GF_SERVER_SERVE_FROM_SUB_PATH=true
-STATSD_ENDPOINT=statsd
+STATSD_ENDPOINT=x
STATSD_PORT=9125
diff --git a/docker/prod/nginx-ingress/nginx_staging.conf b/docker/prod/nginx-ingress/nginx_staging.conf
index b8e31a57ec..d65f61b1a0 100644
--- a/docker/prod/nginx-ingress/nginx_staging.conf
+++ b/docker/prod/nginx-ingress/nginx_staging.conf
@@ -6,6 +6,10 @@ upstream grafana {
server grafana:3000 fail_timeout=0;
}
+upstream statsd_exporter {
+ server statsd:9102 fail_timeout=0;
+}
+
server {
server_name monitoring-staging.eval.ai;
listen 80;
@@ -34,14 +38,20 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
- proxy_pass https://prometheus;
+ proxy_pass http://prometheus;
}
location /grafana {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
- proxy_pass https://grafana;
+ proxy_pass http://grafana;
}
+ location /statsd {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_pass http://statsd_exporter;
+ }
}
diff --git a/docker/prod/nodejs/nginx_staging.conf b/docker/prod/nodejs/nginx_staging.conf
index 8e7cdd02ce..28c01898e3 100644
--- a/docker/prod/nodejs/nginx_staging.conf
+++ b/docker/prod/nodejs/nginx_staging.conf
@@ -2,10 +2,6 @@ upstream django_app {
server django:8000 fail_timeout=0;
}
-upstream statsd_exporter {
- server statsd:9102 fail_timeout=0;
-}
-
upstream node_exporter {
server node_exporter:9100 fail_timeout=0;
}
@@ -61,13 +57,6 @@ server {
proxy_pass http://django_app;
}
- location /statsd {
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_pass http://statsd_exporter;
- }
-
location /node_exporter {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
diff --git a/docker/prod/nodejs_v2/nginx_staging.conf b/docker/prod/nodejs_v2/nginx_staging.conf
index 74214c17a1..86dd7e669b 100644
--- a/docker/prod/nodejs_v2/nginx_staging.conf
+++ b/docker/prod/nodejs_v2/nginx_staging.conf
@@ -2,10 +2,6 @@ upstream django_app {
server django:8000 fail_timeout=0;
}
-upstream statsd_exporter {
- server statsd:9102 fail_timeout=0;
-}
-
upstream node_exporter {
server node_exporter:9100 fail_timeout=0;
}
@@ -52,13 +48,6 @@ server {
proxy_pass http://django_app;
}
- location /statsd {
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $host;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_pass http://statsd_exporter;
- }
-
location /node_exporter {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
diff --git a/frontend/src/css/modules/leaderboard.scss b/frontend/src/css/modules/leaderboard.scss
index a2e7884dbd..48a6e0c765 100644
--- a/frontend/src/css/modules/leaderboard.scss
+++ b/frontend/src/css/modules/leaderboard.scss
@@ -1,60 +1,61 @@
/* Styles for leaderboard page */
.horizontal-scroll {
- overflow-x: auto;
+ overflow-x: auto;
}
#baseline-badge {
- font-weight: 300;
- min-width: 0rem;
- font-size: 16px;
- float: right;
- margin-left: 5px;
+ font-weight: 300;
+ min-width: 0rem;
+ font-size: 16px;
+ float: right;
+ margin-left: 5px;
}
#baseline-badge-desc {
- font-weight: 300;
- min-width: 0rem;
- font-size: 16px;
- margin-left: 0px;
- float: none;
+ font-weight: 300;
+ min-width: 0rem;
+ font-size: 16px;
+ margin-left: 0px;
+ float: none;
}
#verified-badge {
- font-weight: 300;
- min-width: 0rem;
- font-size: 16px;
- float: right;
- margin-left: 5px;
+ font-weight: 300;
+ min-width: 0rem;
+ font-size: 16px;
+ float: right;
+ margin-left: 5px;
}
#verified-badge-desc {
- font-weight: 300;
- min-width: 0rem;
- font-size: 16px;
- margin-left: 0px;
- float: none;
+ font-weight: 300;
+ min-width: 0rem;
+ font-size: 16px;
+ margin-left: 0px;
+ float: none;
}
.highlightLeaderboard {
- border-color: #fff3e0;
- box-shadow: 0 0 0 0.2em #ffcc80 !important;
- border: 1px solid #d1d5da;
- border-radius: 3px;
+ border-color: #fff3e0;
+ box-shadow: 0 0 0 0.2em #ffcc80 !important;
+ border: 1px solid #d1d5da;
+ border-radius: 3px;
+ background-color: #f2f2f2;
}
.sort-leaderboard-switch {
- vertical-align: middle;
- display: inline-block;
+ vertical-align: middle;
+ display: inline-block;
}
.baseline-tag {
- margin-right: 4%;
+ margin-right: 4%;
}
.complete-leaderboard {
- vertical-align:top;
+ vertical-align:top;
}
span.badge.partial-evaluation {
@@ -66,3 +67,24 @@ span.badge.partial-evaluation {
float: left;
box-sizing: border-box;
}
+
+.leaderboard-label .description {
+ visibility: hidden;
+ width: auto;
+ max-width: 250px;
+ height: auto;
+ background-color: #f2f2f2;
+ color: #000000;
+ text-align: center;
+ border-radius: 6px;
+ padding: 10px 10px 10px 10px;
+ font-size: 1.05rem;
+
+ /* Position the tooltip */
+ position: absolute;
+ z-index: 1;
+}
+
+.leaderboard-label:hover .description {
+ visibility: visible;
+}
diff --git a/frontend/src/js/controllers/challengeCtrl.js b/frontend/src/js/controllers/challengeCtrl.js
index 264c52cf52..8fbce15eb1 100644
--- a/frontend/src/js/controllers/challengeCtrl.js
+++ b/frontend/src/js/controllers/challengeCtrl.js
@@ -87,6 +87,7 @@
vm.refreshJWT = utilities.getData('refreshJWT');
vm.subErrors = {};
+ vm.currentHighlightedLeaderboardEntry = null;
vm.isChallengeLeaderboardPrivate = false;
vm.previousPublicSubmissionId = null;
@@ -204,16 +205,18 @@
$interval.cancel(vm.logs_poller);
};
- // scroll to the specific entry of the leaderboard
- vm.scrollToSpecificEntryLeaderboard = function (elementId) {
- var newHash = elementId.toString();
- if ($location.hash() !== newHash) {
- $location.hash(elementId);
- } else {
- $anchorScroll();
+ // highlight the specific entry of the leaderboard
+ vm.highlightSpecificLeaderboardEntry = function (key) {
+ key = '#' + key;
+ // Remove highlight from previous clicked entry
+ if (vm.currentHighlightedLeaderboardEntry != null) {
+ let prevEntry = angular.element(vm.currentHighlightedLeaderboardEntry)[0];
+ prevEntry.setAttribute("class", "");
}
+ let entry = angular.element(key)[0];
+ entry.setAttribute("class", "highlightLeaderboard");
+ vm.currentHighlightedLeaderboardEntry = key;
$scope.isHighlight = false;
- $anchorScroll.yOffset = 90;
};
// get names of the team that has participated in the current challenge
@@ -873,6 +876,32 @@
scope.sortColumn = column;
};
+ vm.isMetricOrderedAscending = function(metric) {
+ let schema = vm.leaderboard[0].leaderboard__schema;
+ let metadata = schema.metadata;
+ if (metadata != null && metadata != undefined) {
+ // By default all metrics are considered higher is better
+ if (metadata[metric] == undefined) {
+ return false;
+ }
+ return metadata[metric].sort_ascending;
+ }
+ return false;
+ };
+
+ vm.getLabelDescription = function(metric) {
+ let schema = vm.leaderboard[0].leaderboard__schema;
+ let metadata = schema.metadata;
+ if (metadata != null && metadata != undefined) {
+ // By default all metrics are considered higher is better
+ if (metadata[metric] == undefined || metadata[metric].description == undefined) {
+ return "";
+ }
+ return metadata[metric].description;
+ }
+ return "";
+ };
+
// my submissions
vm.isResult = false;
@@ -2001,7 +2030,44 @@
}
};
- vm.hideVisibilityDialog = function() {
+ vm.cancelSubmission = function(submissionId) {
+ parameters.url = "jobs/challenges/" + vm.challengeId + "/submissions/" + submissionId + "/update_submission_meta/";
+ parameters.method = 'PATCH';
+ parameters.data = {
+ "status": "cancelled",
+ };
+ parameters.callback = {
+ onSuccess: function(response) {
+ var status = response.status;
+ if (status === 200) {
+ $mdDialog.hide();
+ $rootScope.notify("success", "Submission cancelled successfully!");
+ }
+ },
+ onError: function(response) {
+ $mdDialog.hide();
+ var error = response.data;
+ $rootScope.notify("error", error);
+ }
+ };
+
+ utilities.sendRequest(parameters);
+ };
+
+ vm.showCancelSubmissionDialog = function(submissionId, status) {
+ if (status != "submitted") {
+ $rootScope.notify("error", "Only unproccessed submissions can be cancelled");
+ return;
+ }
+ vm.submissionId = submissionId;
+ $mdDialog.show({
+ scope: $scope,
+ preserveScope: true,
+ templateUrl: 'dist/views/web/challenge/cancel-submission.html'
+ });
+ };
+
+ vm.hideDialog = function() {
$mdDialog.hide();
};
diff --git a/frontend/src/views/web/challenge/cancel-submission.html b/frontend/src/views/web/challenge/cancel-submission.html
new file mode 100644
index 0000000000..5b6370963a
--- /dev/null
+++ b/frontend/src/views/web/challenge/cancel-submission.html
@@ -0,0 +1,16 @@
+
Username is too short.
Username is required.
@@ -50,9 +50,9 @@ (paste)="authService.getUser['password'] = ''" class="dark-autofill" name="password" - (focusin)="ispasswordFocused = true" - (focusout)="ispasswordFocused = authService.getUser['password'] !== ''" - (change)="ispasswordFocused = authService.getUser['password'] !== ''" + (focusin)="isPasswordFocused = true" + (focusout)="isPasswordFocused = authService.getUser['password'] !== ''" + (change)="isPasswordFocused = authService.getUser['password'] !== ''" [(ngModel)]="authService.getUser['password']" #password="ngModel" autocomplete="new-password" @@ -63,7 +63,7 @@ - +You haven't hosted any challenge. Please @@ -57,8 +64,10 @@
No upcoming hosted challenges found. Please @@ -72,8 +81,10 @@
No past hosted challenges found.
No unapproved challenges found.
You haven't participated in any challenge. Please @@ -118,8 +133,10 @@
You haven't participated in any of the past challenges.