feat: support custom order of agent metadata (#12066)

This commit is contained in:
Marcin Tojek
2024-02-08 17:29:34 +01:00
committed by GitHub
parent e659957b65
commit c0e169ebf9
23 changed files with 438 additions and 316 deletions
+1
View File
@@ -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)
+4 -1
View File
@@ -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.';
+2
View File
@@ -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 {
+7 -3
View File
@@ -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
}
+3 -2
View File
@@ -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 {
+12 -8
View File
@@ -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
}
+86
View File
@@ -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]