| ... | ... | @@ -124,6 +124,8 @@ class Context(): | 
| 124 | 124 |          self._workspaces = None
 | 
| 125 | 125 |          self._log_handle = None
 | 
| 126 | 126 |          self._log_filename = None
 | 
|  | 127 | +        self._config_cache_quota = None
 | 
|  | 128 | +        self._artifactdir_volume = None
 | 
| 127 | 129 |  
 | 
| 128 | 130 |      # load()
 | 
| 129 | 131 |      #
 | 
| ... | ... | @@ -187,67 +189,19 @@ class Context(): | 
| 187 | 189 |          while not os.path.exists(artifactdir_volume):
 | 
| 188 | 190 |              artifactdir_volume = os.path.dirname(artifactdir_volume)
 | 
| 189 | 191 |  
 | 
|  | 192 | +        self._artifactdir_volume = artifactdir_volume
 | 
|  | 193 | +
 | 
| 190 | 194 |          # We read and parse the cache quota as specified by the user
 | 
| 191 | 195 |          cache_quota = _yaml.node_get(cache, str, 'quota', default_value='infinity')
 | 
| 192 | 196 |          try:
 | 
| 193 |  | -            cache_quota = utils._parse_size(cache_quota, artifactdir_volume)
 | 
|  | 197 | +            cache_quota = utils._parse_size(cache_quota, self._artifactdir_volume)
 | 
| 194 | 198 |          except utils.UtilError as e:
 | 
| 195 | 199 |              raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
| 196 | 200 |                              "{}\nPlease specify the value in bytes or as a % of full disk space.\n"
 | 
| 197 | 201 |                              "\nValid values are, for example: 800M 10G 1T 50%\n"
 | 
| 198 | 202 |                              .format(str(e))) from e
 | 
| 199 | 203 |  
 | 
| 200 |  | -        # Headroom intended to give BuildStream a bit of leeway.
 | 
| 201 |  | -        # This acts as the minimum size of cache_quota and also
 | 
| 202 |  | -        # is taken from the user requested cache_quota.
 | 
| 203 |  | -        #
 | 
| 204 |  | -        if 'BST_TEST_SUITE' in os.environ:
 | 
| 205 |  | -            headroom = 0
 | 
| 206 |  | -        else:
 | 
| 207 |  | -            headroom = 2e9
 | 
| 208 |  | -
 | 
| 209 |  | -        stat = os.statvfs(artifactdir_volume)
 | 
| 210 |  | -        available_space = (stat.f_bsize * stat.f_bavail)
 | 
| 211 |  | -
 | 
| 212 |  | -        # Again, the artifact directory may not yet have been created yet
 | 
| 213 |  | -        #
 | 
| 214 |  | -        if not os.path.exists(self.artifactdir):
 | 
| 215 |  | -            cache_size = 0
 | 
| 216 |  | -        else:
 | 
| 217 |  | -            cache_size = utils._get_dir_size(self.artifactdir)
 | 
| 218 |  | -
 | 
| 219 |  | -        # Ensure system has enough storage for the cache_quota
 | 
| 220 |  | -        #
 | 
| 221 |  | -        # If cache_quota is none, set it to the maximum it could possibly be.
 | 
| 222 |  | -        #
 | 
| 223 |  | -        # Also check that cache_quota is atleast as large as our headroom.
 | 
| 224 |  | -        #
 | 
| 225 |  | -        if cache_quota is None:  # Infinity, set to max system storage
 | 
| 226 |  | -            cache_quota = cache_size + available_space
 | 
| 227 |  | -        if cache_quota < headroom:  # Check minimum
 | 
| 228 |  | -            raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
| 229 |  | -                            "Invalid cache quota ({}): ".format(utils._pretty_size(cache_quota)) +
 | 
| 230 |  | -                            "BuildStream requires a minimum cache quota of 2G.")
 | 
| 231 |  | -        elif cache_quota > cache_size + available_space:  # Check maximum
 | 
| 232 |  | -            raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
| 233 |  | -                            ("Your system does not have enough available " +
 | 
| 234 |  | -                             "space to support the cache quota specified.\n" +
 | 
| 235 |  | -                             "You currently have:\n" +
 | 
| 236 |  | -                             "- {used} of cache in use at {local_cache_path}\n" +
 | 
| 237 |  | -                             "- {available} of available system storage").format(
 | 
| 238 |  | -                                 used=utils._pretty_size(cache_size),
 | 
| 239 |  | -                                 local_cache_path=self.artifactdir,
 | 
| 240 |  | -                                 available=utils._pretty_size(available_space)))
 | 
| 241 |  | -
 | 
| 242 |  | -        # Place a slight headroom (2e9 (2GB) on the cache_quota) into
 | 
| 243 |  | -        # cache_quota to try and avoid exceptions.
 | 
| 244 |  | -        #
 | 
| 245 |  | -        # Of course, we might still end up running out during a build
 | 
| 246 |  | -        # if we end up writing more than 2G, but hey, this stuff is
 | 
| 247 |  | -        # already really fuzzy.
 | 
| 248 |  | -        #
 | 
| 249 |  | -        self.cache_quota = cache_quota - headroom
 | 
| 250 |  | -        self.cache_lower_threshold = self.cache_quota / 2
 | 
|  | 204 | +        self._config_cache_quota = cache_quota
 | 
| 251 | 205 |  
 | 
| 252 | 206 |          # Load artifact share configuration
 | 
| 253 | 207 |          self.artifact_cache_specs = ArtifactCache.specs_from_config_node(defaults)
 | 
| ... | ... | @@ -571,6 +525,53 @@ class Context(): | 
| 571 | 525 |      def get_log_filename(self):
 | 
| 572 | 526 |          return self._log_filename
 | 
| 573 | 527 |  
 | 
|  | 528 | +    def set_cache_quota(self, cache_size):
 | 
|  | 529 | +        # Headroom intended to give BuildStream a bit of leeway.
 | 
|  | 530 | +        # This acts as the minimum size of cache_quota and also
 | 
|  | 531 | +        # is taken from the user requested cache_quota.
 | 
|  | 532 | +        #
 | 
|  | 533 | +        if 'BST_TEST_SUITE' in os.environ:
 | 
|  | 534 | +            headroom = 0
 | 
|  | 535 | +        else:
 | 
|  | 536 | +            headroom = 2e9
 | 
|  | 537 | +
 | 
|  | 538 | +        stat = os.statvfs(self._artifactdir_volume)
 | 
|  | 539 | +        available_space = (stat.f_bsize * stat.f_bavail)
 | 
|  | 540 | +
 | 
|  | 541 | +        # Ensure system has enough storage for the cache_quota
 | 
|  | 542 | +        #
 | 
|  | 543 | +        # If cache_quota is none, set it to the maximum it could possibly be.
 | 
|  | 544 | +        #
 | 
|  | 545 | +        # Also check that cache_quota is atleast as large as our headroom.
 | 
|  | 546 | +        #
 | 
|  | 547 | +        cache_quota = self._config_cache_quota
 | 
|  | 548 | +        if cache_quota is None:  # Infinity, set to max system storage
 | 
|  | 549 | +            cache_quota = cache_size + available_space
 | 
|  | 550 | +        if cache_quota < headroom:  # Check minimum
 | 
|  | 551 | +            raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
|  | 552 | +                            "Invalid cache quota ({}): ".format(utils._pretty_size(cache_quota)) +
 | 
|  | 553 | +                            "BuildStream requires a minimum cache quota of 2G.")
 | 
|  | 554 | +        elif cache_quota > cache_size + available_space:  # Check maximum
 | 
|  | 555 | +            raise LoadError(LoadErrorReason.INVALID_DATA,
 | 
|  | 556 | +                            ("Your system does not have enough available " +
 | 
|  | 557 | +                             "space to support the cache quota specified.\n" +
 | 
|  | 558 | +                             "You currently have:\n" +
 | 
|  | 559 | +                             "- {used} of cache in use at {local_cache_path}\n" +
 | 
|  | 560 | +                             "- {available} of available system storage").format(
 | 
|  | 561 | +                                 used=utils._pretty_size(cache_size),
 | 
|  | 562 | +                                 local_cache_path=self.artifactdir,
 | 
|  | 563 | +                                 available=utils._pretty_size(available_space)))
 | 
|  | 564 | +
 | 
|  | 565 | +        # Place a slight headroom (2e9 (2GB) on the cache_quota) into
 | 
|  | 566 | +        # cache_quota to try and avoid exceptions.
 | 
|  | 567 | +        #
 | 
|  | 568 | +        # Of course, we might still end up running out during a build
 | 
|  | 569 | +        # if we end up writing more than 2G, but hey, this stuff is
 | 
|  | 570 | +        # already really fuzzy.
 | 
|  | 571 | +        #
 | 
|  | 572 | +        self.cache_quota = cache_quota - headroom
 | 
|  | 573 | +        self.cache_lower_threshold = self.cache_quota / 2
 | 
|  | 574 | +
 | 
| 574 | 575 |      # _record_message()
 | 
| 575 | 576 |      #
 | 
| 576 | 577 |      # Records the message if recording is enabled
 |