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
6 changes: 6 additions & 0 deletions apps/workspace-engine/pkg/db/models.go

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

29 changes: 29 additions & 0 deletions apps/workspace-engine/pkg/db/queries/resource_variables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- name: GetResourceVariable :one
SELECT resource_id, key, value
FROM resource_variable
WHERE resource_id = $1 AND key = $2;

-- name: UpsertResourceVariable :exec
INSERT INTO resource_variable (resource_id, key, value)
VALUES ($1, $2, $3)
ON CONFLICT (resource_id, key) DO UPDATE
SET value = EXCLUDED.value;

-- name: DeleteResourceVariable :exec
DELETE FROM resource_variable
WHERE resource_id = $1 AND key = $2;

-- name: ListResourceVariablesByResourceID :many
SELECT resource_id, key, value
FROM resource_variable
WHERE resource_id = $1;

-- name: DeleteResourceVariablesByResourceID :exec
DELETE FROM resource_variable
WHERE resource_id = $1;

-- name: ListResourceVariablesByWorkspaceID :many
SELECT rv.resource_id, rv.key, rv.value
FROM resource_variable rv
INNER JOIN resource r ON r.id = rv.resource_id
WHERE r.workspace_id = $1;
7 changes: 7 additions & 0 deletions apps/workspace-engine/pkg/db/queries/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,11 @@ CREATE TABLE user_approval_record (
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (version_id, user_id, environment_id)
);

CREATE TABLE resource_variable (
resource_id UUID NOT NULL REFERENCES resource(id) ON DELETE CASCADE,
key TEXT NOT NULL,
value JSONB NOT NULL,
PRIMARY KEY (resource_id, key)
);
126 changes: 126 additions & 0 deletions apps/workspace-engine/pkg/db/resource_variables.sql.go

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

6 changes: 6 additions & 0 deletions apps/workspace-engine/pkg/db/sqlc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sql:
- queries/changelog.sql
- queries/policies.sql
- queries/user_approval_records.sql
- queries/resource_variables.sql
database:
uri: "postgresql://ctrlplane:ctrlplane@127.0.0.1:5432/ctrlplane?sslmode=disable"
gen:
Expand Down Expand Up @@ -98,3 +99,8 @@ sql:
- column: "policy_rule_verification.metrics"
go_type:
type: "[]byte"

# ResourceVariable
- column: "resource_variable.value"
go_type:
type: "[]byte"
2 changes: 1 addition & 1 deletion apps/workspace-engine/pkg/persistence/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ func TestPersistence_AllEntityTypes(t *testing.T) {
_, ok = testStore.Repo().ResourceProviders().Get(resourceProvider.Id)
assert.True(t, ok, "ResourceProvider should be restored")

_, ok = testStore.Repo().ResourceVariables.Get(resourceVariable.ID())
_, ok = testStore.Repo().ResourceVariables().Get(resourceVariable.ID())
assert.True(t, ok, "ResourceVariable should be restored")

_, ok = testStore.Repo().Deployments().Get(deploymentId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"workspace-engine/pkg/workspace/store/repository/db/releases"
"workspace-engine/pkg/workspace/store/repository/db/resourceproviders"
"workspace-engine/pkg/workspace/store/repository/db/resources"
"workspace-engine/pkg/workspace/store/repository/db/resourcevariables"
"workspace-engine/pkg/workspace/store/repository/db/systemdeployments"
"workspace-engine/pkg/workspace/store/repository/db/systemenvironments"
"workspace-engine/pkg/workspace/store/repository/db/systems"
Expand All @@ -30,6 +31,7 @@ type DBRepo struct {
systemEnvironments repository.SystemEnvironmentRepo
policies repository.PolicyRepo
userApprovalRecords repository.UserApprovalRecordRepo
resourceVariables repository.ResourceVariableRepo
}

func (d *DBRepo) DeploymentVersions() repository.DeploymentVersionRepo {
Expand Down Expand Up @@ -80,6 +82,10 @@ func (d *DBRepo) UserApprovalRecords() repository.UserApprovalRecordRepo {
return d.userApprovalRecords
}

func (d *DBRepo) ResourceVariables() repository.ResourceVariableRepo {
return d.resourceVariables
}

func NewDBRepo(ctx context.Context, workspaceID string) *DBRepo {
return &DBRepo{
deploymentVersions: deploymentversions.NewRepo(ctx, workspaceID),
Expand All @@ -94,5 +100,6 @@ func NewDBRepo(ctx context.Context, workspaceID string) *DBRepo {
systemEnvironments: systemenvironments.NewRepo(ctx),
policies: policies.NewRepo(ctx, workspaceID),
userApprovalRecords: userapprovalrecords.NewRepo(ctx),
resourceVariables: resourcevariables.NewRepo(ctx, workspaceID),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package resourcevariables

import (
"fmt"
"workspace-engine/pkg/db"
"workspace-engine/pkg/oapi"

"github.com/google/uuid"
)

func ToOapi(row db.ResourceVariable) *oapi.ResourceVariable {
value := oapi.Value{}
_ = value.UnmarshalJSON(row.Value)

return &oapi.ResourceVariable{
ResourceId: row.ResourceID.String(),
Key: row.Key,
Value: value,
}
}
Comment on lines +11 to +20
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the actual file and confirm it exists
fd -t f "mapper.go" apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/

Repository: ctrlplanedev/ctrlplane

Length of output: 149


🏁 Script executed:

# Search for all callers of ToOapi in the repository
rg -n "ToOapi" --type go -C 3

Repository: ctrlplanedev/ctrlplane

Length of output: 27988


🏁 Script executed:

# Also check for ToUpsertParams and other exported functions in mapper.go to understand the pattern
fd -t f "mapper.go" apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/ -x cat {}

Repository: ctrlplanedev/ctrlplane

Length of output: 1260


🏁 Script executed:

# Check if parseKey is used elsewhere in the resourcevariables package
fd -t f "\.go$" apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/ -x rg -l "parseKey"

Repository: ctrlplanedev/ctrlplane

Length of output: 231


🏁 Script executed:

# Examine ToUpsertParams specifically to see how it handles errors
fd -t f "mapper.go" apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/ -x rg -A 10 "func ToUpsertParams"

Repository: ctrlplanedev/ctrlplane

Length of output: 442


Silently discarding the JSON unmarshal error can mask data corruption.

If row.Value contains malformed JSON (manual DB edit, encoding mismatch, or upstream bug), this returns a zero-value Value with no indication of the problem. The inconsistency is notable: ToUpsertParams in the same file properly handles JSON errors, but ToOapi silently discards them. Consider propagating the error like deploymentversions.ToOapi does.

🐛 Proposed fix: return an error from ToOapi
-func ToOapi(row db.ResourceVariable) *oapi.ResourceVariable {
-	value := oapi.Value{}
-	_ = value.UnmarshalJSON(row.Value)
-
-	return &oapi.ResourceVariable{
+func ToOapi(row db.ResourceVariable) (*oapi.ResourceVariable, error) {
+	value := oapi.Value{}
+	if err := value.UnmarshalJSON(row.Value); err != nil {
+		return nil, fmt.Errorf("unmarshal value for key %q: %w", row.Key, err)
+	}
+
+	return &oapi.ResourceVariable{
 		ResourceId: row.ResourceID.String(),
 		Key:        row.Key,
 		Value:      value,
-	}
+	}, nil
 }

This requires updating the two callers in repo.go (lines 36 and 78) to handle the error.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/mapper.go`
around lines 11 - 20, Change ToOapi to return (*oapi.ResourceVariable, error)
and stop discarding JSON unmarshal errors: call value.UnmarshalJSON(row.Value),
check and return the error if non-nil instead of ignoring it; construct and
return the oapi.ResourceVariable only on success. Update the two callers in
repo.go that call ToOapi to handle the new error return (propagate or wrap as
appropriate). Follow the same error-handling pattern used by ToUpsertParams and
deploymentversions.ToOapi so malformed row.Value is reported rather than
silently producing a zero-value oapi.Value.


func ToUpsertParams(e *oapi.ResourceVariable) (db.UpsertResourceVariableParams, error) {
rid, err := uuid.Parse(e.ResourceId)
if err != nil {
return db.UpsertResourceVariableParams{}, fmt.Errorf("parse resource_id: %w", err)
}

valueBytes, err := e.Value.MarshalJSON()
if err != nil {
return db.UpsertResourceVariableParams{}, fmt.Errorf("marshal value: %w", err)
}

return db.UpsertResourceVariableParams{
ResourceID: rid,
Key: e.Key,
Value: valueBytes,
}, nil
}

func parseKey(key string) (uuid.UUID, string, error) {
if len(key) < 38 || key[36] != '-' {
return uuid.Nil, "", fmt.Errorf("invalid resource variable key: %q", key)
}
rid, err := uuid.Parse(key[:36])
if err != nil {
return uuid.Nil, "", fmt.Errorf("parse resource_id from key: %w", err)
}
return rid, key[37:], nil
}
Loading
Loading