示例#1
0
    def retrieve(self, request, *args, **kwargs):
        """
        Retrieves the deployment status for a given betatest instance.

        This API will check for provisioning appservers or changes in settings
        that need to be deployed and return a status code to the frontend.
        """
        application = self.get_object()
        instance = application.instance
        undeployed_changes = build_instance_config_diff(application)
        deployed_changes = None
        deployment_type = None

        if not instance or not instance.get_latest_deployment():
            deployment_status = DeploymentState.preparing
        else:
            deployment = instance.get_latest_deployment()
            deployment_status = deployment.status()
            if deployment_status == DeploymentState.healthy and undeployed_changes:
                deployment_status = DeploymentState.changes_pending
            deployment_type = deployment.type
            deployed_changes = deployment.changes

        data = {
            'undeployed_changes': undeployed_changes,
            'deployed_changes': deployed_changes,
            'status': deployment_status.name,
            'deployment_type': deployment_type,
        }

        return Response(
            status=status.HTTP_200_OK,
            data=OpenEdXInstanceDeploymentStatusSerializer(data).data)
示例#2
0
def create_new_deployment(
    instance_ref_id: int,
    mark_active_on_success: bool = False,
    num_attempts: int = 1,
    success_tag: Optional[str] = None,
    failure_tag: Optional[str] = None,
    creator: Optional[int] = None,
    deployment_type: Optional[DeploymentType] = DeploymentType.unknown,
) -> Optional[int]:
    """
    Create a new Deployment for an existing instance.

    :param instance_ref_id: ID of an InstanceReference (instance.ref.pk)
    :param mark_active_on_success: Optionally mark the new AppServer as active when the provisioning completes.
    :param num_attempts: Optionally retry up to 'num_attempts' times.
    :param success_tag: Optionally tag the instance with 'success_tag' when the deployment succeeds.
    :param failure_tag: Optionally tag the instance with 'failure_tag' when the deployment fails.
    :param creator: Optionally associate deployment with this user.
    :param deployment_type: Optionally specify a deployment type.
    :return: The ID of the new deployment.
    """
    logger.info('Retrieving instance: ID=%s', instance_ref_id)
    instance = OpenEdXInstance.objects.get(ref_set__pk=instance_ref_id)
    changes = None
    if instance.betatestapplication_set.exists():
        changes = build_instance_config_diff(
            instance.betatestapplication_set.get())

    creator_profile = creator and UserProfile.objects.get(user_id=creator)

    deployment = OpenEdXDeployment.objects.create(
        instance_id=instance_ref_id,
        creator=creator_profile,
        type=deployment_type,
        changes=changes,
    )
    logger.info('Spawning servers for deployment %s [%s]', deployment,
                deployment.id)
    # Launch configured number of appservers for instance
    appserver_spawn_tasks = spawn_appserver.map(
        (instance_ref_id, mark_active_on_success, False, num_attempts,
         success_tag, failure_tag, deployment.id)
        for _ in range(instance.openedx_appserver_count))

    appserver_ids = appserver_spawn_tasks.get(blocking=True)

    if not all(appserver_ids):
        return False

    # If this deployment is to be marked active on success, others should be deactivated automatically
    if mark_active_on_success:
        other_appservers = instance.appserver_set.filter(
            _is_active=True).exclude(pk__in=appserver_ids)
        for appserver_to_deactivate in other_appservers:
            logger.info('Deactivating %s [%s]', appserver_to_deactivate,
                        appserver_to_deactivate.id)
            appserver_to_deactivate.make_active(active=False)
    return deployment.pk
示例#3
0
    def list(self, request, *args, **kwargs):
        """
        Returns Open edX deployments information in form of status notifications.

        If there is no deployments at all, this endpoint returns blank notification,
        that deployment is preparing.
        """

        application = self.get_application()
        deployments = self.get_queryset(application)

        undeployed_changes = build_instance_config_diff(application)

        notifications = []
        for deployment in deployments:
            deployment_status = deployment.status()
            notifications.append({
                "deployed_changes": deployment.changes or [],
                "status": deployment_status.name,
                "date": deployment.created,
            })

        if not notifications:
            notifications.append({
                "deployed_changes": [],
                "status": DeploymentState.preparing.name,
                "date": application.created,
            })

        # Configuration diff relates only to last created deployment, so if
        # last deployment is healthy and diff is not empty,
        # we manually change its status.
        last_notification = notifications[0]
        if last_notification['status'] == DeploymentState.healthy.name:
            undeployed_changes = build_instance_config_diff(application)
            if undeployed_changes:
                last_notification['status'] = DeploymentState.changes_pending

        return Response(
            status=status.HTTP_200_OK,
            data=OpenEdXInstanceDeploymentNotificationSerializer(
                notifications, many=True
            ).data
        )
示例#4
0
    def test_get_deployment_notifications(self, mock_create_new_deployment,
                                          mock_consul):
        """
        Ensures that notification contain deployment's deployed changes and
        that status of last healthy deployment with undeployed changes
        is 'changes_pending'.
        """

        instance = self._setup_user_instance()
        first_deployment = make_test_deployment(
            instance, appserver_states=[Status.Running], active=True)
        second_deployment = make_test_deployment(
            instance, appserver_states=[Status.Running], active=True)

        changes = build_instance_config_diff(self.instance_config)
        first_deployment.changes = changes
        first_deployment.save()

        url = reverse("api:v2:notifications-list")
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            dict(response.data[0]), {
                "deployed_changes": [],
                "status":
                "changes_pending",
                "date":
                second_deployment.created.isoformat().replace("+00:00", "Z"),
            }, response.data)

        self.assertEqual(
            dict(response.data[1]),
            {
                # simulate postgres json field serialization and deserialization
                "deployed_changes":
                json.loads(json.dumps(changes)),
                "status":
                "healthy",
                "date":
                first_deployment.created.isoformat().replace("+00:00", "Z"),
            },
            response.data)