go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/starlark/stdlib/internal/luci/rules/bucket_constraints.star (about)

     1  # Copyright 2022 The LUCI Authors.
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #      http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  
    15  """Defines luci.bucket_constraints(...) rule."""
    16  
    17  load("@stdlib//internal/graph.star", "graph")
    18  load("@stdlib//internal/luci/common.star", "keys", "kinds")
    19  load("@stdlib//internal/luci/rules/binding.star", "binding")
    20  load("@stdlib//internal/lucicfg.star", "lucicfg")
    21  load("@stdlib//internal/validate.star", "validate")
    22  
    23  def _bucket_constraints(
    24          ctx,  # @unused
    25          bucket = None,
    26          pools = None,
    27          service_accounts = None):
    28      """Adds constraints to a bucket.
    29  
    30      `service_accounts` added as the bucket's constraints will also be granted
    31      `role/buildbucket.builderServiceAccount` role.
    32  
    33      Used inline in luci.bucket(...) declarations to provide `pools` and
    34      `service_accounts` constraints for a bucket. `bucket` argument can be
    35      omitted in this case:
    36  
    37          luci.bucket(
    38              name = 'try.shadow',
    39              shadows ='try',
    40              ...
    41              constraints = luci.bucket_constraints(
    42                  pools = ['luci.project.shadow'],
    43                  service_accounts = [`shadow@chops-service-account.com`],
    44              ),
    45          )
    46  
    47      luci.builder function implicitly populates the constraints to the
    48      builder’s bucket. I.e.
    49  
    50          luci.builder(
    51              'builder',
    52              bucket = 'ci',
    53              service_account = 'ci-sa@service-account.com',
    54          )
    55  
    56      adds 'ci-sa@service-account.com' to bucket ci’s constraints.
    57  
    58      Can also be used to add constraints to a bucket outside of
    59      the bucket declaration. For example:
    60  
    61          luci.bucket(name = 'ci')
    62          luci.bucket(name = 'ci.shadow', shadows = 'ci')
    63          luci.bucket_constraints(bucket = 'ci.shadow', pools = [shadow_pool])
    64  
    65      Args:
    66        ctx: the implicit rule context, see lucicfg.rule(...).
    67        bucket: name of the bucket to add the constrains.
    68        pools: list of allowed swarming pools to add to the bucket's constraints.
    69        service_accounts: list of allowed service accounts to add to the bucket's
    70          constraints.
    71      """
    72      pools = validate.str_list("pools", pools, required = False)
    73      service_accounts = validate.str_list("service_accounts", service_accounts, required = False)
    74  
    75      # This will be attached to the realm that has this constraint.
    76      binding_key = None
    77      if service_accounts:
    78          binding_key = binding(
    79              roles = "role/buildbucket.builderServiceAccount",
    80              users = service_accounts,
    81          ).get(kinds.BINDING)
    82  
    83      # Note: name of this node is important only for error messages. It isn't
    84      # showing up in any generated files and by construction it can't
    85      # accidentally collide with some other name.
    86      if bucket == None:
    87          key = keys.unique(kinds.BUCKET_CONSTRAINTS, "")
    88      else:
    89          key = keys.unique(kinds.BUCKET_CONSTRAINTS, keys.bucket(bucket).id)
    90  
    91      graph.add_node(key, props = {
    92          "pools": pools,
    93          "service_accounts": service_accounts,
    94      })
    95      if bucket != None:
    96          graph.add_edge(parent = keys.bucket(bucket), child = key)
    97          if binding_key:
    98              graph.add_edge(parent = keys.realm(bucket), child = binding_key)
    99  
   100      # This is used to detect bucket_constraints nodes that aren't connected to
   101      # any bucket. Such orphan nodes aren't allowed.
   102      graph.add_node(keys.bucket_constraints_root(), idempotent = True)
   103      graph.add_edge(parent = keys.bucket_constraints_root(), child = key)
   104  
   105      keyset = [key]
   106      if binding_key:
   107          keyset.append(binding_key)
   108      return graph.keyset(*keyset)
   109  
   110  bucket_constraints = lucicfg.rule(impl = _bucket_constraints)