| # -*- coding: utf-8 -*- |
| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module to manage local build state.""" |
| |
| from __future__ import print_function |
| |
| import json |
| import sys |
| |
| from chromite.lib import constants |
| from chromite.lib import cros_logging as logging |
| from chromite.lib import pformat |
| |
| |
| assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' |
| |
| |
| class BuildSummary(object): |
| """Summarizes a build state without any external references. |
| |
| This is basically a dictionary that can convert itself back and forth from |
| JSON, but it provides the convenience of attribute-based access instead of |
| carrying around dictionary key constants. |
| |
| Attributes: |
| build_number: The build number of this build, as passed in with |
| --buildnumber, or 0 if --buildnumber wasn't passed. |
| buildbucket_id: The buildbucket id of this build, as passed in with |
| --buildbucket-id, or 0 if --buildbucket-id wasn't passed. |
| master_build_id: The CIDB id of the associated master build if there is one, |
| or 0 if there isn't one. |
| status: One of the status constants from |
| chromite.lib.constants.BUILDER_ALL_STATUSES |
| buildroot_layout: Version of the buildroot layout. |
| branch: Name of the branch this repository is associated with. |
| distfiles_ts: Float unix timestamp recording the last time the distfiles |
| cache was cleaned. |
| """ |
| |
| # List of attributes that should be saved and restored to represent |
| # this object. We use an explicit list instead of vars() so that future |
| # additions can be handled explicitly. |
| _PERSIST_ATTRIBUTES = ('build_number', 'buildbucket_id', 'master_build_id', |
| 'status', 'buildroot_layout', 'branch', 'distfiles_ts') |
| |
| def __init__(self, build_number=0, buildbucket_id=0, master_build_id=0, |
| status=constants.BUILDER_STATUS_MISSING, |
| buildroot_layout=0, branch='', distfiles_ts=None): |
| self.build_number = build_number |
| self.buildbucket_id = buildbucket_id |
| self.master_build_id = master_build_id |
| self.status = status |
| self.buildroot_layout = buildroot_layout |
| self.branch = branch |
| self.distfiles_ts = distfiles_ts |
| |
| def __eq__(self, other): |
| for a in self._PERSIST_ATTRIBUTES: |
| if hasattr(other, a) != hasattr(self, a): |
| return False |
| if getattr(other, a) != getattr(self, a): |
| return False |
| return True |
| |
| def __repr__(self): |
| return 'BuildSummary(%s)' % self.to_json() |
| |
| def from_json(self, raw_json): |
| """Merge the state encoded in |raw_json| into this object. |
| |
| Unknown keys will be ignored (with a warning). Values for missing keys will |
| remain unchanged. |
| |
| Args: |
| raw_json: String containing valid JSON representing a BuildSummary. |
| |
| Raises: |
| ValueError: |raw_json| is not valid JSON. |
| """ |
| new_state = json.loads(raw_json) |
| for key, val in new_state.items(): |
| if key in self._PERSIST_ATTRIBUTES: |
| setattr(self, key, val) |
| else: |
| logging.warning('Ignoring unrecognized JSON key "%s"', key) |
| |
| if self.status not in constants.BUILDER_ALL_STATUSES: |
| logging.warning('Ingoring unknown build status "%s"', self.status) |
| self.status = constants.BUILDER_STATUS_MISSING |
| |
| def to_json(self): |
| """Serialize this object to JSON. |
| |
| Attributes that have an empty/zero value are omitted from the output. The |
| output of this function can be passed to from_json() to get back another |
| BuildSummary with the same values. |
| |
| Returns: |
| A string containing a JSON-encoded representation of this object. |
| """ |
| state = {} |
| for a in self._PERSIST_ATTRIBUTES: |
| val = getattr(self, a) |
| if val: |
| state[a] = getattr(self, a) |
| return pformat.json(state, compact=True) |
| |
| def is_valid(self): |
| """Indicate whether this object has a valid status.""" |
| return self.status != constants.BUILDER_STATUS_MISSING |
| |
| def build_description(self): |
| """Get a human-readable description of which build id is set. |
| |
| If multiple fields are set, buildbucket is preferred. The result can be |
| used for logging or display, but should not be parsed for database lookups. |
| |
| Returns: |
| String describing which build id is set, or "local build" if none are set. |
| """ |
| if self.buildbucket_id: |
| return 'buildbucket_id=%s' % self.buildbucket_id |
| if self.build_number: |
| return 'build_number=%s' % self.build_number |
| return 'local build' |