Skip to content
Merged
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 apps/workspace-engine/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func main() {

allServices := []svc.Service{
pprof.New(pprof.DefaultAddr(config.Global.PprofPort)),
httpsvc.New(config.Global),
httpsvc.New(config.Global, db.GetPool(ctx)),
claimcleanup.New(db.GetPool(ctx), 30*time.Second),

deploymentplan.New(WorkerID, db.GetPool(ctx)),
Expand Down
4 changes: 0 additions & 4 deletions apps/workspace-engine/oapi/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2933,9 +2933,6 @@
"description": "Configuration for the job agent",
"type": "object"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
Expand All @@ -2949,7 +2946,6 @@
}
},
"required": [
"id",
"name",
"ref",
"config",
Expand Down
3 changes: 1 addition & 2 deletions apps/workspace-engine/oapi/spec/schemas/workflows.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ local openapi = import '../lib/openapi.libsonnet';
{
WorkflowJobAgent: {
type: 'object',
required: ['id', 'name', 'ref', 'config', 'selector'],
required: ['name', 'ref', 'config', 'selector'],
properties: {
name: { type: 'string' },
id: { type: 'string' },
ref: { type: 'string', description: 'Reference to the job agent' },
config: { type: 'object', additionalProperties: true, description: 'Configuration for the job agent' },
selector: { type: 'string', description: 'CEL expression to determine if the job agent should dispatch a job' },
Expand Down
1 change: 0 additions & 1 deletion apps/workspace-engine/pkg/oapi/oapi.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/workspace-engine/pkg/oapi/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (wt *Workflow) CompactionKey() (string, string) {
}

func (wtt *WorkflowJobAgent) CompactionKey() (string, string) {
return "workflow_job_agent", wtt.Id
return "workflow_job_agent", wtt.Ref
}

func (w *WorkflowRun) CompactionKey() (string, string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@ func (s *PostgresSetter) UpdateJob(
}

releaseID := existingJob.ReleaseId
if releaseID != "" {
if err := dispatchProgressionTargets(ctx, s.Queue, jobIDUUID); err != nil {
return fmt.Errorf("dispatch progression targets: %w", err)
}
if releaseID == "" || releaseID == "00000000-0000-0000-0000-000000000000" {
return nil
}

if err := dispatchProgressionTargets(ctx, s.Queue, jobIDUUID); err != nil {
return fmt.Errorf("dispatch progression targets: %w", err)
}

return nil
Expand Down
8 changes: 5 additions & 3 deletions apps/workspace-engine/svc/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"

"github.com/charmbracelet/log"
"github.com/jackc/pgx/v5/pgxpool"
"workspace-engine/pkg/config"
"workspace-engine/svc"
"workspace-engine/svc/http/server"
Expand All @@ -16,17 +17,18 @@ var _ svc.Service = (*Service)(nil)
// Service wraps the workspace-engine HTTP server as a service.Service.
type Service struct {
cfg config.Config
pool *pgxpool.Pool
httpServer *http.Server
}

func New(cfg config.Config) *Service {
return &Service{cfg: cfg}
func New(cfg config.Config, pool *pgxpool.Pool) *Service {
return &Service{cfg: cfg, pool: pool}
}

func (s *Service) Name() string { return "http" }

func (s *Service) Start(_ context.Context) error {
srv := server.New()
srv := server.New(s.pool)
router := srv.SetupRouter()

addr := fmt.Sprintf("%s:%d", s.cfg.Host, s.cfg.Port)
Expand Down
5 changes: 3 additions & 2 deletions apps/workspace-engine/svc/http/server/openapi/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package openapi

import (
"github.com/jackc/pgx/v5/pgxpool"
"workspace-engine/pkg/oapi"
release_targets "workspace-engine/svc/http/server/openapi/release_targets"
"workspace-engine/svc/http/server/openapi/resources"
Expand All @@ -9,9 +10,9 @@ import (
"workspace-engine/svc/http/server/openapi/workflows"
)

func New() *Server {
func New(pool *pgxpool.Pool) *Server {
return &Server{
Workflows: workflows.NewWorkflows(),
Workflows: workflows.NewWorkflows(pool),
ReleaseTargets: release_targets.New(),
Verifications: verifications.New(),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (s *PostgresSetter) dispatchJobForAgent(
dispatchContext *oapi.DispatchContext,
workspaceID string,
) error {
jobAgentIDUUID, err := uuid.Parse(jobAgent.Id)
jobAgentIDUUID, err := uuid.Parse(jobAgent.Ref)
if err != nil {
return fmt.Errorf("parse job agent id: %w", err)
}
Comment on lines +106 to 109
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update error message to match the field being parsed.

The error message references "job agent id" but the code now parses jobAgent.Ref. This inconsistency could confuse debugging efforts.

Suggested fix
 	jobAgentIDUUID, err := uuid.Parse(jobAgent.Ref)
 	if err != nil {
-		return fmt.Errorf("parse job agent id: %w", err)
+		return fmt.Errorf("parse job agent ref: %w", err)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jobAgentIDUUID, err := uuid.Parse(jobAgent.Ref)
if err != nil {
return fmt.Errorf("parse job agent id: %w", err)
}
jobAgentIDUUID, err := uuid.Parse(jobAgent.Ref)
if err != nil {
return fmt.Errorf("parse job agent ref: %w", err)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/svc/http/server/openapi/workflows/setters.go` around
lines 106 - 109, The error message for the uuid.Parse call should reference the
actual field being parsed: change the fmt.Errorf in the uuid.Parse(jobAgent.Ref)
error branch so it mentions "job agent ref" (or jobAgent.Ref) instead of "job
agent id"; update the error creation where uuid.Parse(jobAgent.Ref) is used (the
block that assigns jobAgentIDUUID) to return a matching message that includes
the underlying error.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@ import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5/pgxpool"
"workspace-engine/pkg/oapi"
"workspace-engine/pkg/reconcile/postgres"
)

type Workflows struct {
getter Getter
setter Setter
}

func NewWorkflows() Workflows {
return Workflows{getter: &PostgresGetter{}}
func NewWorkflows(pool *pgxpool.Pool) Workflows {
queue := postgres.New(pool)
return Workflows{
getter: &PostgresGetter{},
setter: NewPostgresSetter(queue),
}
}

func getInputs(c *gin.Context) (map[string]any, error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package workflows

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"workspace-engine/pkg/oapi"
)

func stringInput(key string, def *string) oapi.WorkflowInput {
var input oapi.WorkflowInput
_ = input.FromWorkflowStringInput(oapi.WorkflowStringInput{
Key: key,
Type: "string",
Default: def,
})
return input
}

func numberInput(key string, def *float32) oapi.WorkflowInput {
var input oapi.WorkflowInput
_ = input.FromWorkflowNumberInput(oapi.WorkflowNumberInput{
Key: key,
Type: "number",
Default: def,
})
return input
}

func booleanInput(key string, def *bool) oapi.WorkflowInput {
var input oapi.WorkflowInput
_ = input.FromWorkflowBooleanInput(oapi.WorkflowBooleanInput{
Key: key,
Type: "boolean",
Default: def,
})
return input
}

func ptr[T any](v T) *T { return &v }

func TestResolveInputs_ProvidedInputsPassThrough(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
},
}
provided := map[string]any{"env": "production"}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "production", resolved["env"])
}

func TestResolveInputs_MissingInputsGetDefaults(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
numberInput("retries", ptr(float32(3))),
booleanInput("dryRun", ptr(true)),
},
}
provided := map[string]any{}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "staging", resolved["env"])
assert.Equal(t, float32(3), resolved["retries"])
assert.Equal(t, true, resolved["dryRun"])
}

func TestResolveInputs_InputsWithoutDefaultsStayAbsent(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", nil),
numberInput("retries", nil),
booleanInput("dryRun", nil),
},
}
provided := map[string]any{}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.NotContains(t, resolved, "env")
assert.NotContains(t, resolved, "retries")
assert.NotContains(t, resolved, "dryRun")
}

func TestResolveInputs_ProvidedOverridesDefault(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
numberInput("retries", ptr(float32(3))),
booleanInput("dryRun", ptr(true)),
},
}
provided := map[string]any{
"env": "production",
"retries": 10,
"dryRun": false,
}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "production", resolved["env"])
assert.Equal(t, 10, resolved["retries"])
assert.Equal(t, false, resolved["dryRun"])
}

func TestResolveInputs_MixedProvidedAndDefaults(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
numberInput("retries", ptr(float32(3))),
booleanInput("verbose", nil),
},
}
provided := map[string]any{"env": "production"}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "production", resolved["env"])
assert.Equal(t, float32(3), resolved["retries"])
assert.NotContains(t, resolved, "verbose")
}

func TestResolveInputs_DoesNotMutateProvidedMap(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
},
}
provided := map[string]any{"existing": "value"}

_, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Len(t, provided, 1)
assert.Equal(t, "value", provided["existing"])
}

func TestResolveInputs_EmptyWorkflowInputs(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{},
}
provided := map[string]any{"extra": "value"}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "value", resolved["extra"])
}

func TestResolveInputs_ExtraProvidedInputsPassThrough(t *testing.T) {
workflow := &oapi.Workflow{
Inputs: []oapi.WorkflowInput{
stringInput("env", ptr("staging")),
},
}
provided := map[string]any{
"env": "production",
"extra": "unexpected",
}

resolved, err := resolveInputs(workflow, provided)

require.NoError(t, err)
assert.Equal(t, "production", resolved["env"])
assert.Equal(t, "unexpected", resolved["extra"])
}
11 changes: 7 additions & 4 deletions apps/workspace-engine/svc/http/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/charmbracelet/log"
"github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5/pgxpool"
swaggerfiles "github.com/swaggo/files"
ginswagger "github.com/swaggo/gin-swagger"
"go.opentelemetry.io/otel"
Expand All @@ -20,11 +21,13 @@ import (
var tracer = otel.Tracer("server")

// Server implements the OpenAPI ServerInterface for the workspace engine.
type Server struct{}
type Server struct {
pool *pgxpool.Pool
}

// New creates a new Server instance.
func New() *Server {
return &Server{}
func New(pool *pgxpool.Pool) *Server {
return &Server{pool: pool}
}

// SetupRouter configures and returns a Gin router with all routes and middleware.
Expand Down Expand Up @@ -52,7 +55,7 @@ func (s *Server) SetupRouter() *gin.Engine {
)

// Register OpenAPI handlers
oapi.RegisterHandlers(router, openapi.New())
oapi.RegisterHandlers(router, openapi.New(s.pool))

return router
}
Expand Down
Loading