mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat(services): traefik routing overrides via allowlisted middleware labels (#64)
services.traefik_labels (jsonb) holds operator-defined Traefik middlewares keyed 'middlewares.<name>.<type>[.<field>]', validated against an allowlist at write time. edgeLabels (extracted from createReplica) scopes each definition to the service router as <router>-<name> and auto-attaches it to the router's middleware chain after builtins. Router/service/provider-level keys are rejected. Surfaces: create/update API fields, services create/update --traefik-label (repeatable, key=value), MCP arg docs, OpenAPI + regenerated types, service-page Access editor (Routing overrides card + textarea). Also marks three stale ROADMAP2 items shipped: notification producers round-out, build-on-node (PR #60), mobile env/domains/scaling (PR #61). Generated with [Devin](https://devin.ai) Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
4071ed2991
commit
a4bb7ad559
14 files changed
+445
-63
No files matched your search
@@ -134,6 +134,11 @@ var servicesCreateCmd = &cobra.Command{
|
||||
}
|
||||
body["placement_tags"] = tags
|
||||
}
|
||||
if labels, err := traefikLabelFlag(cmd); err != nil {
|
||||
return err
|
||||
} else if labels != nil {
|
||||
body["traefik_labels"] = labels
|
||||
}
|
||||
data, err := c.Do("POST", "/projects/"+args[0]+"/services", body)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -148,6 +153,28 @@ var servicesCreateCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// traefikLabelFlag parses --traefik-label key=value pairs into the API's
|
||||
// map shape. Returns nil when the flag was never passed; an empty map when
|
||||
// only empty values were given (clears overrides on update).
|
||||
func traefikLabelFlag(cmd *cobra.Command) (map[string]string, error) {
|
||||
if !cmd.Flags().Changed("traefik-label") {
|
||||
return nil, nil
|
||||
}
|
||||
pairs, _ := cmd.Flags().GetStringArray("traefik-label")
|
||||
labels := map[string]string{}
|
||||
for _, p := range pairs {
|
||||
if strings.TrimSpace(p) == "" {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(p, "=")
|
||||
if !ok || strings.TrimSpace(k) == "" {
|
||||
return nil, &APIError{Message: fmt.Sprintf("invalid --traefik-label %q — want key=value", p), ExitCode: ExitError}
|
||||
}
|
||||
labels[strings.TrimSpace(k)] = v
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
var servicesDeleteCmd = &cobra.Command{
|
||||
Use: "delete <id>",
|
||||
Short: "Delete a service",
|
||||
@@ -241,6 +268,11 @@ var servicesUpdateCmd = &cobra.Command{
|
||||
v, _ := cmd.Flags().GetInt("sleep-idle")
|
||||
body["sleep_idle_minutes"] = v
|
||||
}
|
||||
if labels, err := traefikLabelFlag(cmd); err != nil {
|
||||
return err
|
||||
} else if labels != nil {
|
||||
body["traefik_labels"] = labels
|
||||
}
|
||||
if cmd.Flags().Changed("maintenance") {
|
||||
v, _ := cmd.Flags().GetString("maintenance")
|
||||
body["maintenance_mode"] = v == "on" || v == "true"
|
||||
@@ -533,6 +565,7 @@ func init() {
|
||||
f.Bool("sleep", false, "enable scale-to-zero on idle (sleep mode)")
|
||||
f.Int("sleep-idle", 0, "idle minutes before sleeping (1-1440)")
|
||||
f.String("placement-tags", "", "comma-separated node tags required for remote placement (auto/spread only)")
|
||||
f.StringArray("traefik-label", nil, "Traefik middleware override, key=value (e.g. middlewares.rl.ratelimit.average=100); repeatable")
|
||||
|
||||
uf := servicesUpdateCmd.Flags()
|
||||
uf.String("name", "", "service name")
|
||||
@@ -557,6 +590,7 @@ func init() {
|
||||
uf.String("static-dir", "", "output dir for the static builder (empty clears)")
|
||||
uf.Bool("sleep", false, "enable scale-to-zero on idle (sleep mode)")
|
||||
uf.Int("sleep-idle", 0, "idle minutes before sleeping (1-1440)")
|
||||
uf.StringArray("traefik-label", nil, "Traefik middleware override, key=value; repeatable; empty list clears via --traefik-label ''")
|
||||
uf.String("node", "", "pin to a node agent id, 'auto' for least-loaded, 'local' to clear")
|
||||
uf.Bool("spread", false, "spread replicas across all online schedulable nodes (--spread=false to disable)")
|
||||
uf.String("placement-tags", "", "comma-separated node tags required for remote placement (empty clears)")
|
||||
|
||||
Reference in new issue
Block a user