Skip to content
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

Implement control-plane placement templating #3154

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manager/dispatcher/assignments.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dispatcher

import (
"fmt"

"github.com/moby/swarmkit/v2/api"
"github.com/moby/swarmkit/v2/api/equality"
"github.com/moby/swarmkit/v2/api/validation"
Expand Down Expand Up @@ -267,6 +266,7 @@ func (a *assignmentSet) addOrUpdateTask(readTx store.ReadTx, t *api.Task) bool {
// agent even if nothing else has changed.
if equality.TasksEqualStable(oldTask, t) && t.Status.State > api.TaskStateAssigned {
// this update should not trigger a task change for the agent

a.tasksMap[t.ID] = t
// If this task got updated to a final state, let's release
// the dependencies that are being used by the task
Expand Down
8 changes: 8 additions & 0 deletions manager/orchestrator/jobs/replicated/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package replicated
import (
"context"
"fmt"
"github.com/moby/swarmkit/v2/template"

"github.com/moby/swarmkit/v2/api"
"github.com/moby/swarmkit/v2/manager/orchestrator"
Expand Down Expand Up @@ -216,6 +217,13 @@ func (r *Reconciler) ReconcileService(id string) error {
}

task := orchestrator.NewTask(cluster, service, slot, "")

newSpec, err := template.ExpandTaskSpec(task)
if err != nil {
return err
}
task.Spec = *newSpec

// when we create the task, we also need to set the
// JobIteration.
task.JobIteration = &api.Version{Index: jobVersion}
Expand Down
51 changes: 51 additions & 0 deletions template/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,57 @@ func (ctx *Context) Expand(s string) (string, error) {
return buf.String(), nil
}

// ControlContext contains field that can be used for templating
// in the control plane.
type ControlContext struct {
Service struct {
ID string
Name string
Labels map[string]string
}
Task struct {
ID string
Name string
Slot string
}
}

// Expand treats the string s as a template and populates it with values from
// the context.
func (ctx *ControlContext) Expand(s string) (string, error) {
tmpl, err := newTemplate(s, nil)
if err != nil {
return s, err
}

var buf bytes.Buffer
if err := tmpl.Execute(&buf, ctx); err != nil {
return s, err
}

return buf.String(), nil
}

// NewControlContext returns template context for templating in the control plane (no node info available)
func NewControlContext(t *api.Task) (ctx Context) {
ctx.Service.ID = t.ServiceID
ctx.Service.Name = t.ServiceAnnotations.Name
ctx.Service.Labels = t.ServiceAnnotations.Labels

ctx.Node.ID = t.NodeID

ctx.Task.ID = t.ID
ctx.Task.Name = naming.Task(t)

if t.Slot != 0 {
ctx.Task.Slot = fmt.Sprint(t.Slot)
} else {
ctx.Task.Slot = ""
}

return
}

// PayloadContext provides a context for expanding a config or secret payload.
// NOTE: Be very careful adding any fields to this structure with types
// that have methods defined on them. The template would be able to
Expand Down
37 changes: 37 additions & 0 deletions template/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@ import (
"github.com/pkg/errors"
)

func ExpandTaskSpec(t *api.Task) (*api.TaskSpec, error) {
spec := t.Spec.Copy()
if spec == nil {
return nil, errors.Errorf("task mising Spec to expand")
}

ctx := NewControlContext(t)

var err error
spec.Placement.Constraints, err = expandPlacement(ctx, spec.Placement.Constraints)

return spec, errors.Wrap(err, "expanding placement failed")
}

func expandPlacement(ctx Context, values []string) ([]string, error) {
var result []string
for _, value := range values {
var (
parts = strings.SplitN(value, "=", 2)
entry = parts[0]
)

if len(parts) > 1 {
expanded, err := ctx.Expand(parts[1])
if err != nil {
return values, errors.Wrapf(err, "expanding placement %q", value)
}

entry = fmt.Sprintf("%s=%s", entry, expanded)
}

result = append(result, entry)
}

return result, nil
}

// ExpandContainerSpec expands templated fields in the runtime using the task
// state and the node where it is scheduled to run.
// Templating is all evaluated on the agent-side, before execution.
Expand Down