-
Notifications
You must be signed in to change notification settings - Fork 3
fix: Run exec agent #11
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
Changes from 2 commits
8223bd0
01bc449
e1a6568
2d98dd4
2d395fb
f01a216
eae19f1
df80fe2
a29b566
ab92d45
6d1fda9
6e6e77c
840e166
3660019
f51d8d4
5160555
c6fdca4
09ccdc6
33ea1ad
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 |
|---|---|---|
|
|
@@ -3,29 +3,60 @@ package exec | |
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/MakeNowJust/heredoc/v2" | ||
| "github.com/charmbracelet/log" | ||
| "github.com/ctrlplanedev/cli/internal/api" | ||
| "github.com/ctrlplanedev/cli/pkg/jobagent" | ||
| "github.com/spf13/cobra" | ||
| "github.com/spf13/viper" | ||
| ) | ||
|
|
||
| type JobAgentType string | ||
|
|
||
| const ( | ||
| JobAgentTypeLinux JobAgentType = "exec-linux" | ||
| JobAgentTypeWindows JobAgentType = "exec-windows" | ||
| ) | ||
|
|
||
| func NewRunExecCmd() *cobra.Command { | ||
| return &cobra.Command{ | ||
| var name string | ||
| var jobAgentType string | ||
|
|
||
| cmd := &cobra.Command{ | ||
| Use: "exec", | ||
| Short: "Execute commands directly when a job is received", | ||
| Example: heredoc.Doc(` | ||
| $ ctrlc run exec --name "my-script-agent" --workspace 123e4567-e89b-12d3-a456-426614174000 | ||
| $ ctrlc run exec --name "my-script-agent" --workspace 123e4567-e89b-12d3-a456-426614174000 --type windows | ||
|
zacharyblasczyk marked this conversation as resolved.
Outdated
zacharyblasczyk marked this conversation as resolved.
Outdated
|
||
| `), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| apiURL := viper.GetString("url") | ||
| apiKey := viper.GetString("api-key") | ||
| workspaceId := viper.GetString("workspace") | ||
| client, err := api.NewAPIKeyClientWithResponses(apiURL, apiKey) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create API client: %w", err) | ||
| } | ||
| if name == "" { | ||
|
Contributor
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. Could move input validation above the client creation? |
||
| return fmt.Errorf("name is required") | ||
| } | ||
| if workspaceId == "" { | ||
| return fmt.Errorf("workspace is required") | ||
| } | ||
| validTypes := map[string]bool{ | ||
| string(JobAgentTypeLinux): true, | ||
| string(JobAgentTypeWindows): true, | ||
| } | ||
| if !validTypes[jobAgentType] { | ||
| return fmt.Errorf("invalid type: %s. Must be one of: linux, windows", jobAgentType) | ||
| } | ||
|
zacharyblasczyk marked this conversation as resolved.
Outdated
|
||
|
|
||
| ja, err := jobagent.NewJobAgent( | ||
| client, | ||
| api.UpsertJobAgentJSONRequestBody{ | ||
| Name: "exec", | ||
| Type: "exec", | ||
| Name: name, | ||
| Type: jobAgentType, | ||
| WorkspaceId: workspaceId, | ||
| }, | ||
| &ExecRunner{}, | ||
| ) | ||
|
|
@@ -41,4 +72,9 @@ func NewRunExecCmd() *cobra.Command { | |
| return nil | ||
| }, | ||
| } | ||
|
|
||
| cmd.Flags().StringVar(&name, "name", "", "Name of the job agent") | ||
| cmd.MarkFlagRequired("name") | ||
| cmd.Flags().StringVar(&jobAgentType, "type", "exec-linux", "Type of the job agent, defaults to linux") | ||
| return cmd | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ package exec | |
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "html/template" | ||
|
|
@@ -13,6 +14,7 @@ import ( | |
|
|
||
| "github.com/ctrlplanedev/cli/internal/api" | ||
| "github.com/ctrlplanedev/cli/pkg/jobagent" | ||
| "github.com/spf13/viper" | ||
| ) | ||
|
|
||
| var _ jobagent.Runner = &ExecRunner{} | ||
|
|
@@ -25,6 +27,10 @@ type ExecConfig struct { | |
| } | ||
|
|
||
| func (r *ExecRunner) Status(job api.Job) (api.JobStatus, string) { | ||
|
Member
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. if the pid is missing is that considered sucessful? what if the pid is now a different process?
Member
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. its not clear how to keep track of these processes, maybe we assum they are not deamon so if the command closes so does the children. This means we won't need this status check and instead create a goroutine that updates the job when it exists |
||
| if job.ExternalId == nil { | ||
| return api.JobStatusExternalRunNotFound, fmt.Sprintf("external ID is nil: %v", job.ExternalId) | ||
| } | ||
|
|
||
| externalId, err := strconv.Atoi(*job.ExternalId) | ||
| if err != nil { | ||
| return api.JobStatusExternalRunNotFound, fmt.Sprintf("invalid process id: %v", err) | ||
|
|
@@ -67,13 +73,34 @@ func (r *ExecRunner) Start(job api.Job) (string, error) { | |
| return "", fmt.Errorf("failed to unmarshal job agent config: %w", err) | ||
| } | ||
|
|
||
| client, err := api.NewAPIKeyClientWithResponses( | ||
| viper.GetString("url"), | ||
| viper.GetString("api-key"), | ||
| ) | ||
|
zacharyblasczyk marked this conversation as resolved.
Outdated
|
||
| if err != nil { | ||
| return "", fmt.Errorf("failed to create API client for job details: %w", err) | ||
| } | ||
|
|
||
| resp, err := client.GetJobWithResponse(context.Background(), job.Id.String()) | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to get job details: %w", err) | ||
| } | ||
|
|
||
| if resp.JSON200 == nil { | ||
| return "", fmt.Errorf("received empty response from job details API") | ||
| } | ||
|
|
||
| var jobDetails map[string]interface{} | ||
| detailsBytes, _ := json.Marshal(resp.JSON200) | ||
| json.Unmarshal(detailsBytes, &jobDetails) | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| templatedScript, err := template.New("script").Parse(config.Script) | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to parse script template: %w", err) | ||
| } | ||
|
|
||
| buf := new(bytes.Buffer) | ||
| if err := templatedScript.Execute(buf, job); err != nil { | ||
| if err := templatedScript.Execute(buf, jobDetails); err != nil { | ||
|
Contributor
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. Avoid potential code injection in the script. |
||
| return "", fmt.Errorf("failed to execute script template: %w", err) | ||
| } | ||
| script := buf.String() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.