mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: support custom order of agent metadata (#12066)
This commit is contained in:
@@ -5687,6 +5687,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg databa
|
||||
Key: arg.Key,
|
||||
Timeout: arg.Timeout,
|
||||
Interval: arg.Interval,
|
||||
DisplayOrder: arg.DisplayOrder,
|
||||
}
|
||||
|
||||
q.workspaceAgentMetadata = append(q.workspaceAgentMetadata, metadatum)
|
||||
|
||||
Generated
+4
-1
@@ -950,9 +950,12 @@ CREATE UNLOGGED TABLE workspace_agent_metadata (
|
||||
error character varying(65535) DEFAULT ''::character varying NOT NULL,
|
||||
timeout bigint NOT NULL,
|
||||
"interval" bigint NOT NULL,
|
||||
collected_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL
|
||||
collected_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL,
|
||||
display_order integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_metadata.display_order IS 'Specifies the order in which to display agent metadata in user interfaces.';
|
||||
|
||||
CREATE TABLE workspace_agent_scripts (
|
||||
workspace_agent_id uuid NOT NULL,
|
||||
log_source_id uuid NOT NULL,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE workspace_agent_metadata DROP COLUMN display_order;
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE workspace_agent_metadata ADD COLUMN display_order integer NOT NULL DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN workspace_agent_metadata.display_order
|
||||
IS 'Specifies the order in which to display agent metadata in user interfaces.';
|
||||
@@ -2268,6 +2268,8 @@ type WorkspaceAgentMetadatum struct {
|
||||
Timeout int64 `db:"timeout" json:"timeout"`
|
||||
Interval int64 `db:"interval" json:"interval"`
|
||||
CollectedAt time.Time `db:"collected_at" json:"collected_at"`
|
||||
// Specifies the order in which to display agent metadata in user interfaces.
|
||||
DisplayOrder int32 `db:"display_order" json:"display_order"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentScript struct {
|
||||
|
||||
@@ -8457,7 +8457,7 @@ func (q *sqlQuerier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWork
|
||||
|
||||
const getWorkspaceAgentMetadata = `-- name: GetWorkspaceAgentMetadata :many
|
||||
SELECT
|
||||
workspace_agent_id, display_name, key, script, value, error, timeout, interval, collected_at
|
||||
workspace_agent_id, display_name, key, script, value, error, timeout, interval, collected_at, display_order
|
||||
FROM
|
||||
workspace_agent_metadata
|
||||
WHERE
|
||||
@@ -8489,6 +8489,7 @@ func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, arg GetWorks
|
||||
&i.Timeout,
|
||||
&i.Interval,
|
||||
&i.CollectedAt,
|
||||
&i.DisplayOrder,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -8925,10 +8926,11 @@ INSERT INTO
|
||||
key,
|
||||
script,
|
||||
timeout,
|
||||
interval
|
||||
interval,
|
||||
display_order
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6)
|
||||
($1, $2, $3, $4, $5, $6, $7)
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentMetadataParams struct {
|
||||
@@ -8938,6 +8940,7 @@ type InsertWorkspaceAgentMetadataParams struct {
|
||||
Script string `db:"script" json:"script"`
|
||||
Timeout int64 `db:"timeout" json:"timeout"`
|
||||
Interval int64 `db:"interval" json:"interval"`
|
||||
DisplayOrder int32 `db:"display_order" json:"display_order"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error {
|
||||
@@ -8948,6 +8951,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentMetadata(ctx context.Context, arg Inser
|
||||
arg.Script,
|
||||
arg.Timeout,
|
||||
arg.Interval,
|
||||
arg.DisplayOrder,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -103,10 +103,11 @@ INSERT INTO
|
||||
key,
|
||||
script,
|
||||
timeout,
|
||||
interval
|
||||
interval,
|
||||
display_order
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6);
|
||||
($1, $2, $3, $4, $5, $6, $7);
|
||||
|
||||
-- name: UpdateWorkspaceAgentMetadata :exec
|
||||
WITH metadata AS (
|
||||
|
||||
@@ -1538,6 +1538,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
Key: md.Key,
|
||||
Timeout: md.Timeout,
|
||||
Interval: md.Interval,
|
||||
DisplayOrder: int32(md.Order),
|
||||
}
|
||||
err := db.InsertWorkspaceAgentMetadata(ctx, p)
|
||||
if err != nil {
|
||||
|
||||
@@ -1590,10 +1590,18 @@ func appendUnique[T comparable](dst, src []T) []T {
|
||||
}
|
||||
|
||||
func convertWorkspaceAgentMetadata(db []database.WorkspaceAgentMetadatum) []codersdk.WorkspaceAgentMetadata {
|
||||
// Sort the input database slice by DisplayOrder and then by Key before processing
|
||||
sort.Slice(db, func(i, j int) bool {
|
||||
if db[i].DisplayOrder == db[j].DisplayOrder {
|
||||
return db[i].Key < db[j].Key
|
||||
}
|
||||
return db[i].DisplayOrder < db[j].DisplayOrder
|
||||
})
|
||||
|
||||
// An empty array is easier for clients to handle than a null.
|
||||
result := make([]codersdk.WorkspaceAgentMetadata, 0, len(db))
|
||||
for _, datum := range db {
|
||||
result = append(result, codersdk.WorkspaceAgentMetadata{
|
||||
result := make([]codersdk.WorkspaceAgentMetadata, len(db))
|
||||
for i, datum := range db {
|
||||
result[i] = codersdk.WorkspaceAgentMetadata{
|
||||
Result: codersdk.WorkspaceAgentMetadataResult{
|
||||
Value: datum.Value,
|
||||
Error: datum.Error,
|
||||
@@ -1607,12 +1615,8 @@ func convertWorkspaceAgentMetadata(db []database.WorkspaceAgentMetadatum) []code
|
||||
Interval: datum.Interval,
|
||||
Timeout: datum.Timeout,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
// Sorting prevents the metadata from jumping around in the frontend.
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Description.Key < result[j].Description.Key
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -1248,6 +1248,92 @@ func TestWorkspaceAgent_Metadata(t *testing.T) {
|
||||
post(ctx, "unknown", unknownKeyMetadata)
|
||||
}
|
||||
|
||||
func TestWorkspaceAgent_Metadata_DisplayOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
r := dbfake.WorkspaceBuild(t, db, database.Workspace{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).WithAgent(func(agents []*proto.Agent) []*proto.Agent {
|
||||
agents[0].Metadata = []*proto.Agent_Metadata{
|
||||
{
|
||||
DisplayName: "First Meta",
|
||||
Key: "foo1",
|
||||
Script: "echo hi",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
Order: 2,
|
||||
},
|
||||
{
|
||||
DisplayName: "Second Meta",
|
||||
Key: "foo2",
|
||||
Script: "echo howdy",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
Order: 1,
|
||||
},
|
||||
{
|
||||
DisplayName: "Third Meta",
|
||||
Key: "foo3",
|
||||
Script: "echo howdy",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
Order: 2,
|
||||
},
|
||||
{
|
||||
DisplayName: "Fourth Meta",
|
||||
Key: "foo4",
|
||||
Script: "echo howdy",
|
||||
Interval: 10,
|
||||
Timeout: 3,
|
||||
Order: 3,
|
||||
},
|
||||
}
|
||||
return agents
|
||||
}).Do()
|
||||
|
||||
workspace, err := client.Workspace(context.Background(), r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
for _, res := range workspace.LatestBuild.Resources {
|
||||
for _, a := range res.Agents {
|
||||
require.Equal(t, codersdk.WorkspaceAgentLifecycleCreated, a.LifecycleState)
|
||||
}
|
||||
}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
workspace, err = client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err, "get workspace")
|
||||
|
||||
agentID := workspace.LatestBuild.Resources[0].Agents[0].ID
|
||||
|
||||
var update []codersdk.WorkspaceAgentMetadata
|
||||
|
||||
// Setup is complete, reset the context.
|
||||
ctx = testutil.Context(t, testutil.WaitMedium)
|
||||
updates, errors := client.WatchWorkspaceAgentMetadata(ctx, agentID)
|
||||
|
||||
recvUpdate := func() []codersdk.WorkspaceAgentMetadata {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("context done: %v", ctx.Err())
|
||||
case err := <-errors:
|
||||
t.Fatalf("error watching metadata: %v", err)
|
||||
case update := <-updates:
|
||||
return update
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
update = recvUpdate()
|
||||
require.Len(t, update, 4)
|
||||
require.Equal(t, "Second Meta", update[0].Description.DisplayName)
|
||||
require.Equal(t, "First Meta", update[1].Description.DisplayName)
|
||||
require.Equal(t, "Third Meta", update[2].Description.DisplayName)
|
||||
require.Equal(t, "Fourth Meta", update[3].Description.DisplayName)
|
||||
}
|
||||
|
||||
type testWAMErrorStore struct {
|
||||
database.Store
|
||||
err atomic.Pointer[error]
|
||||
|
||||
Reference in New Issue
Block a user