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

Scaffolding actions #18639

Merged
merged 11 commits into from
Oct 11, 2023
8 changes: 8 additions & 0 deletions api/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,8 @@ type Task struct {

// Workload Identities
Identities []*WorkloadIdentity `hcl:"identity,block"`

Actions []*Action `hcl:"action,block"`
}

func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
Expand Down Expand Up @@ -1167,3 +1169,9 @@ type WorkloadIdentity struct {
ServiceName string `hcl:"service_name,optional"`
TTL time.Duration `mapstructure:"ttl" hcl:"ttl,optional"`
}

type Action struct {
Name string `hcl:"name,label"`
Command string `mapstructure:"command" hcl:"command"`
Args []string `mapstructure:"args" hcl:"args,optional"`
}
14 changes: 13 additions & 1 deletion command/agent/alloc_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,19 @@ func (s *HTTPServer) allocExec(allocID string, resp http.ResponseWriter, req *ht
}
s.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions)

conn, err := s.wsUpgrader.Upgrade(resp, req, nil)
// conn, err := s.wsUpgrader.Upgrade(resp, req, nil)
// FIXME: this is an open checkOrigin here that allows :4200 to make requests to :4646,
// freeing local ember up from not having to proxy.
// This is like three workarounds in a trenchcoat and I dno't feel good about it but it unblocks me

var upgrader = websocket.Upgrader{
// Allow all origins
CheckOrigin: func(r *http.Request) bool { return true },
}

// Then when you upgrade the connection:
conn, err := upgrader.Upgrade(resp, req, nil)

if err != nil {
return nil, fmt.Errorf("failed to upgrade connection: %v", err)
}
Expand Down
13 changes: 13 additions & 0 deletions command/agent/job_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,11 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup,
Sidecar: apiTask.Lifecycle.Sidecar,
}
}

for _, action := range apiTask.Actions {
act := ApiActionToStructsAction(job, action, &structs.Action{})
structsTask.Actions = append(structsTask.Actions, act)
}
}

// apiWaitConfigToStructsWaitConfig is a copy and type conversion between the API
Expand Down Expand Up @@ -1385,6 +1390,14 @@ func ApiCSIPluginConfigToStructsCSIPluginConfig(apiConfig *api.TaskCSIPluginConf
return sc
}

func ApiActionToStructsAction(job *structs.Job, action *api.Action, act *structs.Action) *structs.Action {
philrenaud marked this conversation as resolved.
Show resolved Hide resolved
return &structs.Action{
Name: action.Name,
Args: slices.Clone(action.Args),
Command: action.Command,
}
}

func ApiResourcesToStructs(in *api.Resources) *structs.Resources {
if in == nil {
return nil
Expand Down
13 changes: 13 additions & 0 deletions command/agent/testingutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ func MockJob() *api.Job {
PortLabel: "admin",
},
},
// actions
Actions: []*api.Action{
{
Name: "date test",
Command: "/bin/date",
Args: []string{"-u"},
},
{
Name: "echo test",
Command: "/bin/echo",
Args: []string{"hello world"},
},
},
LogConfig: api.DefaultLogConfig(),
Resources: &api.Resources{
CPU: pointer.Of(500),
Expand Down
1 change: 1 addition & 0 deletions jobspec/parse_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var (
"kind",
"volume_mount",
"csi_plugin",
"actions",
)

sidecarTaskKeys = append(commonTaskKeys,
Expand Down
12 changes: 12 additions & 0 deletions nomad/mock/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ func Job() *structs.Job {
Env: map[string]string{
"FOO": "bar",
},
Actions: []*structs.Action{
{
Name: "date test",
Command: "/bin/date",
Args: []string{"-u"},
},
{
Name: "echo test",
Command: "/bin/echo",
Args: []string{"hello world"},
},
},
Services: []*structs.Service{
{
Name: "${TASK}-frontend",
Expand Down
38 changes: 38 additions & 0 deletions nomad/structs/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

// Actions are executable commands that can be run on an allocation within
// the context of a task. They are left open-ended enough to be applied to
// other Nomad concepts like Nodes in the future.

package structs

import "slices"

type Action struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum...I feel like this name may be too generic 🤔

The structs package deal with all sorts of stuff and a struct called Action may not be obvious what it relates to. Perhaps TaskAction would be a better name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not opposed to this (they started out with that name!) but lately we've been talking about Node Actions and similar concepts, which I think would borrow from this a bit. I'm torn!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they similar or the same?

Reusing structs for similar concepts may result in weird interfaces, like attributes that are nil in some cases but not in others. And sometimes that's OK. So there's not right answer, just feels at this point.

My vote would be for TaskAction, but I will leave it up to you 😄

Name string
Command string
Args []string
}

func (a *Action) Copy() *Action {
if a == nil {
return nil
}
na := new(Action)
*na = *a
na.Args = slices.Clone(a.Args)
return na
}

func (a *Action) Equal(o *Action) bool {
if a == o {
return true
}
if a == nil || o == nil {
return false
}
return a.Name == o.Name &&
a.Command == o.Command &&
slices.Equal(a.Args, o.Args)
}
67 changes: 67 additions & 0 deletions nomad/structs/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,76 @@ func (t *Task) Diff(other *Task, contextual bool) (*TaskDiff, error) {
diff.Objects = append(diff.Objects, altIDDiffs...)
}

// Actions diff
if aDiffs := actionDiffs(t.Actions, other.Actions, contextual); aDiffs != nil {
diff.Objects = append(diff.Objects, aDiffs...)
}

return diff, nil
}

func actionDiff(old, new *Action, contextual bool) *ObjectDiff {
diff := &ObjectDiff{Type: DiffTypeNone, Name: "Action"}
var oldPrimitiveFlat, newPrimitiveFlat map[string]string

if reflect.DeepEqual(old, new) {
return nil
} else if old == nil {
old = &Action{}
diff.Type = DiffTypeAdded
newPrimitiveFlat = flatmap.Flatten(new, nil, true)
} else if new == nil {
new = &Action{}
diff.Type = DiffTypeDeleted
oldPrimitiveFlat = flatmap.Flatten(old, nil, true)
} else {
diff.Type = DiffTypeEdited
oldPrimitiveFlat = flatmap.Flatten(old, nil, true)
newPrimitiveFlat = flatmap.Flatten(new, nil, true)
}

// Diff the primitive fields
diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual)

// Diff the Args field using stringSetDiff
if setDiff := stringSetDiff(old.Args, new.Args, "Args", contextual); setDiff != nil {
diff.Objects = append(diff.Objects, setDiff)
}

return diff
}

// actionDiffs diffs a set of actions. If contextual diff is enabled, unchanged
// fields within objects nested in the actions will be returned.
func actionDiffs(old, new []*Action, contextual bool) []*ObjectDiff {
var diffs []*ObjectDiff

for i := 0; i < len(old) && i < len(new); i++ {
oldAction := old[i]
newAction := new[i]

if diff := actionDiff(oldAction, newAction, contextual); diff != nil {
diffs = append(diffs, diff)
}
}

for i := len(new); i < len(old); i++ {
if diff := actionDiff(old[i], nil, contextual); diff != nil {
diffs = append(diffs, diff)
}
}

for i := len(old); i < len(new); i++ {
if diff := actionDiff(nil, new[i], contextual); diff != nil {
diffs = append(diffs, diff)
}
}

sort.Sort(ObjectDiffs(diffs))

return diffs
}

func (t *TaskDiff) GoString() string {
var out string
if len(t.Annotations) == 0 {
Expand Down
Loading
Loading