Skip to content

Commit

Permalink
Merge pull request #365 from sapcc/commitment-config
Browse files Browse the repository at this point in the history
add config options for commitment API, show CommitmentConfig in project reports
  • Loading branch information
majewsky authored Oct 12, 2023
2 parents d298921 + 29ce77f commit 58c1f7a
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 8 deletions.
4 changes: 4 additions & 0 deletions docs/operators/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ Some special behaviors for resources can be configured in the `resource_behavior
| `resource_behavior[].scales_with` | no | If a resource is given, matching resources scales with this resource. The other resource may be specified by its name (for resources within the same service type), or by a slash-concatenated pair of service type and resource name, e.g. `compute/cores`. |
| `resource_behavior[].scaling_factor` | yes, if `scales_with` is given | The scaling factor that will be reported for these resources' scaling relation. |
| `resource_behavior[].min_nonzero_project_quota` | no | A lower boundary for project quota values that are not zero. |
| `resource_behavior[].commitment_durations` | no | If given, commitments for this resource can be created with any of the given durations. The duration format is the same as in the `commitments[].duration` attribute that appears on the resource API. |
| `resource_behavior[].commitment_min_confirm_date` | no | If given, commitments for this resource will always be created with `confirm_after` no earlier than this timestamp. This can be used to plan the introduction of commitments on a specific date. |
| `resource_behavior[].annotations` | no | A map of extra key-value pairs that will be inserted into matching resources as-is in responses to GET requests, e.g. at `project.services[].resources[].annotations`. |

For example:
Expand All @@ -159,6 +161,8 @@ resource_behavior:
- { resource: .*, scope: foo/.*, max_burst_multiplier: 0 }
# require each project to take at least 100 GB of object storage if they use it at all
- { resource: object-store/capacity, min_nonzero_project_quota: 107374182400 }
# starting in 2024, offer commitments for Cinder storage
- { resource: volumev2/capacity, commitment_durations: [ 1 year, 2 years, 3 years ], commitment_min_confirm_date: 2024-01-01T00:00:00Z }
```

### Quota distribution models
Expand Down
2 changes: 2 additions & 0 deletions docs/users/api-spec-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ The objects at `projects[].services[].resources[]` may contain the following fie
| `category` | string | The category of this resource (only shown when there is one). |
| `contained_in` | string | The name of another resource (if any) within the same service that this resource is [contained in](#contained-resources). |
| `quota_distribution_model` | string | The resource's [quota distribution model](#quota-distribution-model). Possible values are "hierarchical" and "centralized". |
| `commitment_config` | object | Only present if commitments can be created for this resource. |
| `commitment_config.durations` | list of strings | Acceptable durations for commitments on this resource, each expressed as a comma-separated sequence of positive integer multiples of time units like "1 year, 3 months". Acceptable time units include "second", "minute", "hour", "day", "month" and "year". |
| `scales_with` | object | Only present when this resource is [scaling with](#scaling-relations) another resource. |
| `scales_with.resource_name` | string | The name of the resource that this resource is scaling with. |
| `scales_with.service_type` | string | The type name of the service containing the resource that this resource is scaling with. |
Expand Down
36 changes: 28 additions & 8 deletions internal/core/resource_behavior.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package core
import (
"fmt"
"strings"
"time"

limesresources "github.com/sapcc/go-api-declarations/limes/resources"
"github.com/sapcc/go-bits/errext"
Expand All @@ -31,14 +32,16 @@ import (
// ResourceBehavior contains the configuration options for specialized
// behaviors of a single resource (or a set thereof).
type ResourceBehavior struct {
FullResourceNameRx regexpext.BoundedRegexp `yaml:"resource"`
ScopeRx regexpext.BoundedRegexp `yaml:"scope"`
MaxBurstMultiplier *limesresources.BurstingMultiplier `yaml:"max_burst_multiplier"`
OvercommitFactor float64 `yaml:"overcommit_factor"`
ScalesWith ResourceRef `yaml:"scales_with"`
ScalingFactor float64 `yaml:"scaling_factor"`
MinNonZeroProjectQuota uint64 `yaml:"min_nonzero_project_quota"`
Annotations map[string]any `yaml:"annotations"`
FullResourceNameRx regexpext.BoundedRegexp `yaml:"resource"`
ScopeRx regexpext.BoundedRegexp `yaml:"scope"`
MaxBurstMultiplier *limesresources.BurstingMultiplier `yaml:"max_burst_multiplier"`
OvercommitFactor float64 `yaml:"overcommit_factor"`
ScalesWith ResourceRef `yaml:"scales_with"`
ScalingFactor float64 `yaml:"scaling_factor"`
MinNonZeroProjectQuota uint64 `yaml:"min_nonzero_project_quota"`
CommitmentDurations []limesresources.CommitmentDuration `yaml:"commitment_durations"`
CommitmentMinConfirmDate *time.Time `yaml:"commitment_min_confirm_date"`
Annotations map[string]any `yaml:"annotations"`
}

// Validate returns a list of all errors in this behavior configuration. It
Expand Down Expand Up @@ -87,6 +90,17 @@ func (b ResourceBehavior) ToScalingBehavior() *limesresources.ScalingBehavior {
}
}

// ToCommitmentConfig returns the CommitmentConfiguration for this resource,
// or nil if commitments are not allowed on this resource.
func (b ResourceBehavior) ToCommitmentConfig() *limesresources.CommitmentConfiguration {
if len(b.CommitmentDurations) == 0 {
return nil
}
return &limesresources.CommitmentConfiguration{
Durations: b.CommitmentDurations,
}
}

// Merge computes the union of both given resource behaviors.
func (b *ResourceBehavior) Merge(other ResourceBehavior) {
if b.MaxBurstMultiplier == nil || (other.MaxBurstMultiplier != nil && *b.MaxBurstMultiplier > *other.MaxBurstMultiplier) {
Expand All @@ -102,6 +116,12 @@ func (b *ResourceBehavior) Merge(other ResourceBehavior) {
if b.MinNonZeroProjectQuota < other.MinNonZeroProjectQuota {
b.MinNonZeroProjectQuota = other.MinNonZeroProjectQuota
}
b.CommitmentDurations = append(b.CommitmentDurations, other.CommitmentDurations...)
if other.CommitmentMinConfirmDate != nil {
if b.CommitmentMinConfirmDate == nil || b.CommitmentMinConfirmDate.Before(*other.CommitmentMinConfirmDate) {
b.CommitmentMinConfirmDate = other.CommitmentMinConfirmDate
}
}
if len(other.Annotations) > 0 && b.Annotations == nil {
b.Annotations = make(map[string]any)
}
Expand Down
1 change: 1 addition & 0 deletions internal/reports/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func GetProjectResources(cluster *core.Cluster, domain db.Domain, project *db.Pr
if !resReport.NoQuota {
qdConfig := cluster.QuotaDistributionConfigForResource(*serviceType, *resourceName)
resReport.QuotaDistributionModel = qdConfig.Model
resReport.CommitmentConfig = behavior.ToCommitmentConfig()
if quota != nil {
resReport.Quota = quota
resReport.UsableQuota = quota
Expand Down

0 comments on commit 58c1f7a

Please sign in to comment.