-
Notifications
You must be signed in to change notification settings - Fork 359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: add checkpoint and max slots config policy enforcements in PATCH experiment #10125
Changes from 1 commit
afe5e43
2ce783a
745bf7e
74f65d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,9 @@ import ( | |
) | ||
|
||
const ( | ||
wkspIDQuery = "workspace_id = ?" | ||
wkspIDGlobalQuery = "workspace_id IS ?" | ||
wkspIDQuery = "workspace_id = ?" | ||
wkspIDGlobalQuery = "workspace_id IS ?" | ||
invalidPolicyTypeErr = "invalid policy type" | ||
// DefaultInvariantConfigStr is the default invariant config val used for tests. | ||
DefaultInvariantConfigStr = `{ | ||
"description": "random description", | ||
|
@@ -115,44 +116,48 @@ func DeleteConfigPolicies(ctx context.Context, | |
return nil | ||
} | ||
|
||
// GetEnforcedConfig gets the fields of the global invariant config or constraint if specified, and | ||
// the workspace invariant config or constraint otherwise. If neither is specified, returns nil. | ||
func GetEnforcedConfig[T any](ctx context.Context, wkspID *int, policyType, field, workloadType string) (*T, | ||
// GetConfigPolicyField fetches the field from an invariant_config or constraints policyType, in order | ||
// of precedence. Global scope has highest precedence, then workspace. Returns nil if none is found. | ||
// **NOTE** The field arguments are wrapped in bun.Safe, so you must specify the "raw" string | ||
// exactly as you wish for it to be accessed in the database. For example, if you want to access | ||
// resources.max_slots, the field argument should be "'resources' -> 'max_slots'" NOT | ||
// "resources -> max_slots". | ||
// **NOTE**When using this function to retrieve an object of Kind Pointer, set T as the Type of | ||
// object that the Pointer wraps. For example, if we want an object of type *int, set T to int, so | ||
// that when its pointer is returned, you get an object of type *int. | ||
func GetConfigPolicyField[T any](ctx context.Context, wkspID *int, policyType, field, workloadType string) (*T, | ||
error, | ||
) { | ||
if policyType != "invariant_config" && policyType != "constraints" { | ||
return nil, fmt.Errorf("invalid policy type :%s", policyType) | ||
return nil, fmt.Errorf("%s :%s", invalidPolicyTypeErr, policyType) | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also validate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems like we actually don't validate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case, don't add it. I was mistaken! |
||
var confBytes []byte | ||
var conf T | ||
err := db.Bun().RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { | ||
globalField := tx.NewSelect(). | ||
ColumnExpr("? -> ? AS globconf", bun.Safe(policyType), bun.Safe(field)). | ||
Table("task_config_policies"). | ||
var globalBytes []byte | ||
err := tx.NewSelect().Table("task_config_policies"). | ||
ColumnExpr("? -> ?", bun.Safe(policyType), bun.Safe(field)). | ||
Where("workspace_id IS NULL"). | ||
Where("workload_type = ?", workloadType) | ||
|
||
wkspField := tx.NewSelect(). | ||
ColumnExpr("? -> ? AS wkspconf", bun.Safe(policyType), bun.Safe(field)). | ||
Table("task_config_policies"). | ||
Where("workspace_id = '?'", wkspID). | ||
Where("workload_type = ?", workloadType) | ||
|
||
both := tx.NewSelect().TableExpr("global_field"). | ||
Join("NATURAL JOIN wksp_field") | ||
|
||
err := tx.NewSelect().ColumnExpr("coalesce(globconf, wkspconf)"). | ||
With("global_field", globalField). | ||
With("wksp_field", wkspField). | ||
Table("both").With("both", both). | ||
Scan(ctx, &confBytes) | ||
if err != nil { | ||
Where("workload_type = ?", workloadType).Scan(ctx, &globalBytes) | ||
if err == nil && len(globalBytes) > 0 { | ||
confBytes = globalBytes | ||
} | ||
if err != nil && err != sql.ErrNoRows { | ||
return err | ||
} | ||
return nil | ||
|
||
var wkspBytes []byte | ||
err = tx.NewSelect().Table("task_config_policies"). | ||
ColumnExpr("? -> ?", bun.Safe(policyType), bun.Safe(field)). | ||
Where("workspace_id = ?", wkspID). | ||
Where("workload_type = ?", workloadType).Scan(ctx, &wkspBytes) | ||
if err == nil && len(globalBytes) == 0 { | ||
confBytes = wkspBytes | ||
} | ||
return err | ||
}) | ||
if err == sql.ErrNoRows { | ||
if err == sql.ErrNoRows || len(confBytes) == 0 { | ||
return nil, nil | ||
} | ||
if err != nil { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package configpolicy | |
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"reflect" | ||
|
@@ -27,6 +28,9 @@ const ( | |
InvalidNTSCConfigPolicyErr = "invalid ntsc config policy" | ||
// NotSupportedConfigPolicyErr is the error reported when admins attempt to set NTSC invariant config. | ||
NotSupportedConfigPolicyErr = "not supported" | ||
// SlotsReqTooHighErr is the error reported when the requested slots violates the max slots | ||
// constraint. | ||
SlotsReqTooHighErr = "requested slots is violates max slots constraint" | ||
) | ||
|
||
// ConfigPolicyWarning logs a warning for the configuration policy component. | ||
|
@@ -298,3 +302,40 @@ func configPolicyOverlap(config1, config2 interface{}) { | |
} | ||
} | ||
} | ||
|
||
// CanSetMaxSlots returns true if the slots requested don't violate a constraint. It returns the | ||
// enforced max slots for the workspace if that's set as an invariant config, and returns the | ||
// requested max slots otherwise. Returns an error when max slots is not set as an invariant config | ||
// and the requested max slots violates the constriant. | ||
func CanSetMaxSlots(slotsReq *int, wkspID int) (bool, *int, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function should return I would simplify it further to just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great point, changed return type to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh wait ok i see your point about int, error! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gah ok actually, I think it's easier to keep this as is since the func takes in an optional *int (so it can return that same optional *int). |
||
if slotsReq == nil { | ||
return true, slotsReq, nil | ||
} | ||
enforcedMaxSlots, err := GetConfigPolicyField[int](context.TODO(), &wkspID, | ||
"invariant_config", | ||
"'resources' -> 'max_slots'", model.ExperimentType) | ||
if err != nil { | ||
return false, nil, err | ||
} | ||
|
||
if enforcedMaxSlots != nil { | ||
return true, enforcedMaxSlots, nil | ||
} | ||
|
||
maxSlotsLimit, err := GetConfigPolicyField[int](context.TODO(), &wkspID, | ||
"constraints", | ||
"'resources' -> 'max_slots'", model.ExperimentType) | ||
if err != nil { | ||
return false, nil, err | ||
} | ||
|
||
var canSetReqSlots bool | ||
if maxSlotsLimit == nil || *slotsReq <= *maxSlotsLimit { | ||
canSetReqSlots = true | ||
} | ||
if !canSetReqSlots { | ||
return false, nil, fmt.Errorf(SlotsReqTooHighErr+": %d > %d", *slotsReq, *maxSlotsLimit) | ||
} | ||
|
||
return true, slotsReq, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetEnforcedConfig
is fine as a name. I think something likeGetConfigPolicyField
is more descriptive. When I first read the function name, I thought it was only for invariant configs. It also wasn't clear that it was fetching a single field, rather than a whole or partial config.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point! Changed this