Skip to content

Commit

Permalink
(choria-io#2209) Add basic agent
Browse files Browse the repository at this point in the history
Signed-off-by: R.I.Pienaar <[email protected]>
  • Loading branch information
ripienaar committed Jan 22, 2025
1 parent 22056a8 commit b38cf01
Show file tree
Hide file tree
Showing 9 changed files with 450 additions and 24 deletions.
3 changes: 2 additions & 1 deletion config/choria.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ type ChoriaPluginConfig struct {
RPCAuditLogfileGroup string `confkey:"plugin.rpcaudit.logfile.group"` // User group to set file ownership to
RPCAuditLogFileMode string `confkey:"plugin.rpcaudit.logfile.mode" default:"0600"` // File mode to apply to the file

ExecutorSpool string `confkey:"plugin.choria.executor.spool" type:"path_string"` // Path where the command executor writes state
ExecutorEnabled bool `confkey:"plugin.choria.executor.enabled" default:"false"` // Enables the long running command executor
ExecutorSpool string `confkey:"plugin.choria.executor.spool" type:"path_string"` // Path where the command executor writes state

AutonomousAgentsDownload bool `confkey:"plugin.machines.download"` // Activate run-time installation of Autonomous Agents
AutonomousAgentsBucket string `confkey:"plugin.machines.bucket" default:"CHORIA_PLUGINS"` // The KV bucket to query for plugins to install
Expand Down
86 changes: 86 additions & 0 deletions internal/fs/ddl/cache/agent/executor.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
metadata :name => "executor",
:description => "Choria Process Executor Management",
:author => "R.I.Pienaar <[email protected]>",
:license => "Apache-2.0",
:version => "0.29.4",
:url => "https://choria.io",
:provider => "golang",
:timeout => 20


action "status", :description => "Requests the status of a job by ID" do
display :always

input :id,
:prompt => "Job ID",
:description => "The unique ID for the job",
:type => :string,
:validation => '.',
:maxlength => 20,
:optional => false




output :action,
:description => "The RPC Action that started the process",
:type => "string",
:display_as => "Action"

output :agent,
:description => "The RPC Agent that started the process",
:type => "string",
:display_as => "Agent"

output :caller,
:description => "The Caller ID who started the process",
:type => "string",
:display_as => "Caller"

output :exit_code,
:description => "The exit code the process terminated with",
:type => "integer",
:display_as => "Exit Code"

output :pid,
:description => "The OS Process ID",
:type => "integer",
:display_as => "Pid"

output :requestid,
:description => "The Request ID that started the process",
:type => "string",
:display_as => "Request ID"

output :running,
:description => "Indicates if the process is still running",
:type => "boolean",
:display_as => "Running"

output :start_time,
:description => "Time that the process started",
:type => "string",
:display_as => "Started"

output :started,
:description => "Indicates if the process was started",
:type => "boolean",
:display_as => "Started"

output :stderr_bytes,
:description => "The number of bytes of STDERR output available",
:type => "integer",
:display_as => "STDERR Bytes"

output :stdout_bytes,
:description => "The number of bytes of STDOUT output available",
:type => "integer",
:display_as => "STDOUT Bytes"

output :terminate_time,
:description => "Time that the process terminated",
:type => "string",
:display_as => "Terminated"

end

92 changes: 92 additions & 0 deletions internal/fs/ddl/cache/agent/executor.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{
"$schema": "https://choria.io/schemas/mcorpc/ddl/v1/agent.json",
"metadata": {
"license": "Apache-2.0",
"author": "R.I.Pienaar \u003c[email protected]\u003e",
"timeout": 20,
"name": "executor",
"version": "0.29.4",
"url": "https://choria.io",
"description": "Choria Process Executor Management",
"provider": "golang"
},
"actions": [
{
"action": "status",
"input": {
"id": {
"prompt": "Job ID",
"description": "The unique ID for the job",
"type": "string",
"maxlength": 20,
"validation": ".",
"optional": false
}
},
"output": {
"action": {
"description": "The RPC Action that started the process",
"display_as": "Action",
"type": "string"
},
"agent": {
"description": "The RPC Agent that started the process",
"display_as": "Agent",
"type": "string"
},
"caller": {
"description": "The Caller ID who started the process",
"display_as": "Caller",
"type": "string"
},
"exit_code": {
"description": "The exit code the process terminated with",
"display_as": "Exit Code",
"type": "integer"
},
"pid": {
"description": "The OS Process ID",
"display_as": "Pid",
"type": "integer"
},
"requestid": {
"description": "The Request ID that started the process",
"display_as": "Request ID",
"type": "string"
},
"running": {
"description": "Indicates if the process is still running",
"display_as": "Running",
"type": "boolean"
},
"started": {
"description": "Indicates if the process was started",
"display_as": "Started",
"type": "boolean"
},
"start_time": {
"description": "Time that the process started",
"display_as": "Started",
"type": "string"
},
"terminate_time": {
"description": "Time that the process terminated",
"display_as": "Terminated",
"type": "string"
},
"stdout_bytes": {
"description": "The number of bytes of STDOUT output available",
"display_as": "STDOUT Bytes",
"type": "integer"
},
"stderr_bytes": {
"description": "The number of bytes of STDERR output available",
"display_as": "STDERR Bytes",
"type": "integer"
}
},
"display": "always",
"description": "Requests the status of a job by ID"
}
]
}
35 changes: 35 additions & 0 deletions providers/agent/mcorpc/golang/executor/executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2025, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0

package executor

import (
"github.com/choria-io/go-choria/build"
"github.com/choria-io/go-choria/providers/agent/mcorpc"
"github.com/choria-io/go-choria/server"
"github.com/choria-io/go-choria/server/agents"
)

var metadata = &agents.Metadata{
Name: "executor",
Description: "Choria Process Executor Management",
Author: "R.I.Pienaar <[email protected]>",
Version: build.Version,
License: build.License,
Timeout: 20,
URL: "https://choria.io",
}

func New(mgr server.AgentManager) (*mcorpc.Agent, error) {
log := mgr.Logger()
agent := mcorpc.New("executor", metadata, mgr.Choria(), log)

agent.SetActivationChecker(func() bool {
return mgr.Choria().Configuration().Choria.ExecutorEnabled
})

agent.MustRegisterAction("status", statusAction)

return agent, nil
}
96 changes: 96 additions & 0 deletions providers/agent/mcorpc/golang/executor/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) 2025, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0

package executor

import (
"context"
"github.com/choria-io/go-choria/inter"
"github.com/choria-io/go-choria/providers/agent/mcorpc"
"github.com/choria-io/go-choria/providers/execution"
"time"
)

type StatusRequest struct {
JobID string `json:"id"`
}

type StatusResponse struct {
Started bool `json:"started"`
StartTime time.Time `json:"start_time"`
TerminateTime time.Time `json:"terminate_time"`
ExitCode int `json:"exit_code"`
Running bool `json:"running"`
Agent string `json:"agent"`
Action string `json:"action"`
RequestID string `json:"requestid"`
Caller string `json:"caller"`
Pid int `json:"pid"`
StdoutBytes int `json:"stdout_bytes"`
StderrBytes int `json:"stderr_bytes"`
}

func statusAction(ctx context.Context, req *mcorpc.Request, reply *mcorpc.Reply, agent *mcorpc.Agent, conn inter.ConnectorInfo) {
spool := agent.Config.Choria.ExecutorSpool

if spool == "" {
abort(reply, "Executor spool is not configured")
return
}

args := &StatusRequest{}
if !mcorpc.ParseRequestData(args, req, reply) {
return
}

p, err := execution.Load(spool, args.JobID)
if err != nil {
reply.Statuscode = mcorpc.Aborted
reply.Statusmsg = err.Error()
abort(reply, "Could not load job: %v", err)
return
}

resp := &StatusResponse{
Running: p.IsRunning(),
StartTime: p.StartTime,
TerminateTime: p.TerminateTime,
Agent: p.Agent,
Action: p.Action,
RequestID: p.RequestID,
Caller: p.Caller,
Pid: -1,
ExitCode: -1,
}

resp.Started, err = p.HasStarted()
if err != nil {
abort(reply, "Could not check if job is started: %v", err)
return
}

if resp.Started {
resp.Pid, err = p.ParsePid()
if err != nil {
abort(reply, "Could not parse pid: %v", err)
return
}
}

if !resp.Running && resp.Started {
resp.ExitCode, err = p.ParseExitCode()
if err != nil {
abort(reply, "Could not parse exit code: %v", err)
return
}

b, _ := p.Stderr()
resp.StderrBytes = len(b)

b, _ = p.Stdout()
resp.StdoutBytes = len(b)
}

reply.Data = resp
}
15 changes: 15 additions & 0 deletions providers/agent/mcorpc/golang/executor/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2025, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0

package executor

import (
"fmt"
"github.com/choria-io/go-choria/providers/agent/mcorpc"
)

func abort(reply *mcorpc.Reply, format string, a ...any) {
reply.Statuscode = mcorpc.Aborted
reply.Statusmsg = fmt.Sprintf(format, a...)
}
12 changes: 8 additions & 4 deletions providers/agent/mcorpc/golang/provider.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020-2021, R.I. Pienaar and the Choria Project contributors
// Copyright (c) 2020-2025, R.I. Pienaar and the Choria Project contributors
//
// SPDX-License-Identifier: Apache-2.0

Expand All @@ -12,6 +12,7 @@ import (
"github.com/choria-io/go-choria/inter"
"github.com/choria-io/go-choria/providers/agent/mcorpc/golang/choriautil"
"github.com/choria-io/go-choria/providers/agent/mcorpc/golang/discovery"
"github.com/choria-io/go-choria/providers/agent/mcorpc/golang/executor"
"github.com/choria-io/go-choria/providers/agent/mcorpc/golang/rpcutil"
"github.com/choria-io/go-choria/server"
"github.com/choria-io/go-choria/server/agents"
Expand All @@ -38,7 +39,6 @@ func (p *Provider) RegisterAgents(ctx context.Context, mgr server.AgentManager,
if err != nil {
return err
}

err = mgr.RegisterAgent(ctx, "discovery", agent, connector)
if err != nil {
return err
Expand All @@ -48,7 +48,6 @@ func (p *Provider) RegisterAgents(ctx context.Context, mgr server.AgentManager,
if err != nil {
return err
}

err = mgr.RegisterAgent(ctx, "rpcutil", agent, connector)
if err != nil {
return err
Expand All @@ -58,11 +57,16 @@ func (p *Provider) RegisterAgents(ctx context.Context, mgr server.AgentManager,
if err != nil {
return err
}

err = mgr.RegisterAgent(ctx, "choria_util", agent, connector)
if err != nil {
return err
}

agent, err = executor.New(mgr)
err = mgr.RegisterAgent(ctx, "executor", agent, connector)
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit b38cf01

Please sign in to comment.