blob: 0cc3eb3ddd74841d900ea895297a656df66a767a [file] [log] [blame]
# -*- 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.
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
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,
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.
raw_json: String containing valid JSON representing a BuildSummary.
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)
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.
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.
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'