| # Copyright 2020 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import base64 |
| |
| from google.protobuf import text_format as textpb |
| |
| from recipe_engine import recipe_api |
| |
| from PB.go.chromium.org.luci.buildbucket.proto import project_config as bb_pb2 |
| from PB.go.chromium.org.luci.cv.api.config.v2 import config as cv_config_pb2 |
| from PB.go.chromium.org.luci.milo.api.config import project as milo_pb2 |
| from PB.go.chromium.org.luci.scheduler.appengine.messages import config as scheduler_pb2 |
| from PB.go.chromium.org.luci.config_service.proto import ( |
| config_service as config_service_pb2, |
| ) |
| |
| |
| class LuciConfigApi(recipe_api.RecipeApi): |
| """Module for polling and parsing luci config files via the luci-config API. |
| |
| Depends on `prpc` binary being available in $PATH: |
| https://godoc.org/go.chromium.org/luci/grpc/cmd/prpc |
| """ |
| |
| def fetch_config(self, config_name, message_type, project=None, local_dir=None): |
| """Fetch a config file from the luci-config API and return it as a proto object. |
| |
| Args: |
| config_name (str): The name of the config file to fetch, e.g. |
| "commit-queue.cfg". |
| message_type (type): The Python type corresponding to the |
| config's protobuf message type. |
| project (str): The name of the LUCI project to fetch the config |
| from; e.g. "fuchsia". Defaults to the project that the |
| current Buildbucket build is running in. |
| local_dir (Path): If specified, assumed to point to a local |
| directory of files generated by lucicfg. The specified config |
| file will be read from the corresponding local file rather |
| than fetching it from the LUCI Config service. |
| """ |
| if not project: |
| project = self.m.buildbucket.build.builder.project |
| assert project, "buildbucket input has no project set" |
| |
| if local_dir: |
| text = self.m.file.read_text( |
| f"read {project} {config_name}", local_dir / config_name |
| ) |
| else: |
| with self.m.step.nest(f"fetch {project} {config_name}"): |
| text = self._fetch_config_textproto(project, config_name) |
| |
| cfg = message_type() |
| textpb.Parse(text, cfg) |
| return cfg |
| |
| def _fetch_config_textproto(self, project, config_name): |
| req = config_service_pb2.GetConfigRequest( |
| config_set=f"projects/{project}", path=config_name |
| ) |
| resp = self.m.step( |
| name="get", |
| cmd=[ |
| "prpc", |
| "call", |
| "-format=json", |
| "config.luci.app", |
| "config.service.v2.Configs.GetConfig", |
| ], |
| stdin=self.m.proto.input(req, "JSONPB"), |
| stdout=self.m.proto.output(config_service_pb2.Config, "JSONPB"), |
| infra_step=True, |
| step_test_data=lambda: self.m.proto.test_api.output_stream( |
| config_service_pb2.Config(raw_content=b"") |
| ), |
| ).stdout |
| # Responses for extremely large config files will have the `signed_url` |
| # populated instead of `raw_content` and the file must be fetched using |
| # the signed URL. At time of writing this code is not used to fetch |
| # config files of that size, so support for signed URLs is not required. |
| assert ( |
| resp.WhichOneof("content") == "raw_content" |
| ), f"{config_name} can only be fetched by signed URL. TODO(you): Implemented signed URL fetching :)" |
| return resp.raw_content |
| |
| def buildbucket(self, **kwargs): |
| return self.fetch_config("cr-buildbucket.cfg", bb_pb2.BuildbucketCfg, **kwargs) |
| |
| def commit_queue(self, config_name=None, **kwargs): |
| # Support loading a CQ config file with a non-default name to support |
| # projects that don't want a dedicated CQ instance but for which we |
| # still want to know which tryjobs exist. |
| config_name = config_name or "commit-queue.cfg" |
| return self.fetch_config(config_name, cv_config_pb2.Config, **kwargs) |
| |
| def milo(self, **kwargs): |
| return self.fetch_config("luci-milo.cfg", milo_pb2.Project, **kwargs) |
| |
| def scheduler(self, **kwargs): |
| return self.fetch_config( |
| "luci-scheduler.cfg", scheduler_pb2.ProjectConfig, **kwargs |
| ) |