from __future__ import absolute_import

import json

from changes.config import db
from changes.constants import Result
from changes.models import FailureReason
from .base import ArtifactHandler


class ManifestJsonHandler(ArtifactHandler):
    """
    Artifact handler for manifest.json files. Makes sure their contents are valid.
    """
    FILENAMES = ('manifest.json',)

    def process(self, fp):
        try:
            contents = json.load(fp)
            if contents['job_step_id'] != self.step.id.hex:
                self.logger.warning('manifest.json had wrong step id (build=%s): expected %s but got %s',
                                    self.step.job.build_id.hex, self.step.id.hex, contents['job_step_id'])
                # TODO(nate): temporarily disabled
                # self._add_failure_reason()
        except Exception:
            self.logger.exception('Failed to parse manifest.json; (build=%s, step=%s)',
                                self.step.job.build_id.hex, self.step.id.hex)
            # TODO(nate): temporarily disabled
            # self._add_failure_reason()

    def _add_failure_reason(self):
        db.session.add(FailureReason(
            step_id=self.step.id,
            job_id=self.step.job_id,
            build_id=self.step.job.build_id,
            project_id=self.step.project_id,
            reason='malformed_manifest_json'
        ))
        self.step.result = Result.infra_failed
        db.session.add(self.step)
        db.session.commit()

from __future__ import absolute_import

import json

from changes.config import db
from changes.constants import Result
from changes.models import FailureReason
from .base import ArtifactHandler


class ManifestJsonHandler(ArtifactHandler):
    """
    Artifact handler for manifest.json files. Makes sure their contents are valid.
    """
    FILENAMES = ('manifest.json',)

    def process(self, fp):
        try:
            contents = json.load(fp)
            if contents['job_step_id'] != self.step.id.hex:
                self.logger.warning('manifest.json had wrong step id (build=%s): expected %s but got %s',
                                    self.step.job.build_id.hex, self.step.id.hex, contents['job_step_id'])
                self._add_failure_reason()
        except Exception:
            self.logger.warning('Failed to parse manifest.json; (build=%s, step=%s)',
                                self.step.job.build_id.hex, self.step.id.hex, exc_info=True)
            self._add_failure_reason()

    def _add_failure_reason(self):
        db.session.add(FailureReason(
            step_id=self.step.id,
            job_id=self.step.job_id,
            build_id=self.step.job.build_id,
            project_id=self.step.project_id,
            reason='malformed_manifest_json'
        ))
        self.step.result = Result.infra_failed
        db.session.add(self.step)
        db.session.commit()

Make malformed/mismatched manifest.json an infra failure
Make malformed/mismatched manifest.json an infra failure

Summary:
- This was disabled for a while due to problems with phased jobsteps, but
  Sentry shows that these errors occasionally still happen, but seem to be
  "real" errors now. Should almost certainly be marked infra failures.
- Also make malformed manifest a warning (and use exc_info=True) since
  malformed vs mismatched jobstep id seem like approximately the same severity.

Test Plan:
Sentry indicates these happen rarely and when they do are probably
legitimate infra failures.

Reviewers: paulruan

Reviewed By: paulruan

Subscribers: changesbot, kylec

Differential Revision: https://tails.corp.dropbox.com/D152534
