mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add provisioner daemon name to provisioner jobs responses (#17877)
# Description This PR adds the `worker_name` field to the provisioner jobs endpoint. To achieve this, the following SQL query was updated: - `GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner` As a result, the `codersdk.ProvisionerJob` type, which represents the provisioner job API response, was modified to include the new field. **Notes:** * As mentioned in [comment](https://github.com/coder/coder/pull/17877#discussion_r2093218206), the `GetProvisionerJobsByIDsWithQueuePosition` query was not changed due to load concerns. This means that for template and template version endpoints, `worker_id` will still be returned, but `worker_name` will not. * Similar to `worker_id`, the `worker_name` is only present once a job is assigned to a provisioner daemon. For jobs in a pending state (not yet assigned), neither `worker_id` nor `worker_name` will be returned. --- # Affected Endpoints - `/organizations/{organization}/provisionerjobs` - `/organizations/{organization}/provisionerjobs/{job}` --- # Testing - Added new tests verifying that both `worker_id` and `worker_name` are returned once a provisioner job reaches the **succeeded** state. - Existing tests covering state transitions and other logic remain unchanged, as they test different scenarios. --- # Front-end Changes Admin provisioner jobs dashboard: <img width="1088" alt="Screenshot 2025-05-16 at 11 51 33" src="https://github.com/user-attachments/assets/0e20e360-c615-4497-84b7-693777c5443e" /> Fixes: https://github.com/coder/coder/issues/16982
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ OPTIONS:
|
|||||||
-O, --org string, $CODER_ORGANIZATION
|
-O, --org string, $CODER_ORGANIZATION
|
||||||
Select which organization (uuid or name) to use.
|
Select which organization (uuid or name) to use.
|
||||||
|
|
||||||
-c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|organization|queue] (default: created at,id,type,template display name,status,queue,tags)
|
-c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|organization|queue] (default: created at,id,type,template display name,status,queue,tags)
|
||||||
Columns to display in table output.
|
Columns to display in table output.
|
||||||
|
|
||||||
-l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50)
|
-l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"completed_at": "====[timestamp]=====",
|
"completed_at": "====[timestamp]=====",
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"worker_id": "====[workspace build worker ID]=====",
|
"worker_id": "====[workspace build worker ID]=====",
|
||||||
|
"worker_name": "test-daemon",
|
||||||
"file_id": "=====[workspace build file ID]======",
|
"file_id": "=====[workspace build file ID]======",
|
||||||
"tags": {
|
"tags": {
|
||||||
"owner": "",
|
"owner": "",
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
"completed_at": "====[timestamp]=====",
|
"completed_at": "====[timestamp]=====",
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"worker_id": "====[workspace build worker ID]=====",
|
"worker_id": "====[workspace build worker ID]=====",
|
||||||
|
"worker_name": "test-daemon",
|
||||||
"file_id": "=====[workspace build file ID]======",
|
"file_id": "=====[workspace build file ID]======",
|
||||||
"tags": {
|
"tags": {
|
||||||
"owner": "",
|
"owner": "",
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
|
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
|
||||||
====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization]
|
====[timestamp]===== ====[timestamp]===== built-in test-daemon v0.0.0-devel idle map[owner: scope:organization]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"key_id": "00000000-0000-0000-0000-000000000001",
|
"key_id": "00000000-0000-0000-0000-000000000001",
|
||||||
"created_at": "====[timestamp]=====",
|
"created_at": "====[timestamp]=====",
|
||||||
"last_seen_at": "====[timestamp]=====",
|
"last_seen_at": "====[timestamp]=====",
|
||||||
"name": "test",
|
"name": "test-daemon",
|
||||||
"version": "v0.0.0-devel",
|
"version": "v0.0.0-devel",
|
||||||
"api_version": "1.6",
|
"api_version": "1.6",
|
||||||
"provisioners": [
|
"provisioners": [
|
||||||
|
|||||||
Generated
+3
@@ -14618,6 +14618,9 @@ const docTemplate = `{
|
|||||||
"worker_id": {
|
"worker_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uuid"
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"worker_name": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+3
@@ -13250,6 +13250,9 @@
|
|||||||
"worker_id": {
|
"worker_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uuid"
|
"format": "uuid"
|
||||||
|
},
|
||||||
|
"worker_name": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ import (
|
|||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultTestDaemonName = "test-daemon"
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// AccessURL denotes a custom access URL. By default we use the httptest
|
// AccessURL denotes a custom access URL. By default we use the httptest
|
||||||
// server's URL. Setting this may result in unexpected behavior (especially
|
// server's URL. Setting this may result in unexpected behavior (especially
|
||||||
@@ -602,7 +604,7 @@ func NewWithAPI(t testing.TB, options *Options) (*codersdk.Client, io.Closer, *c
|
|||||||
setHandler(rootHandler)
|
setHandler(rootHandler)
|
||||||
var provisionerCloser io.Closer = nopcloser{}
|
var provisionerCloser io.Closer = nopcloser{}
|
||||||
if options.IncludeProvisionerDaemon {
|
if options.IncludeProvisionerDaemon {
|
||||||
provisionerCloser = NewTaggedProvisionerDaemon(t, coderAPI, "test", options.ProvisionerDaemonTags, coderd.MemoryProvisionerWithVersionOverride(options.ProvisionerDaemonVersion))
|
provisionerCloser = NewTaggedProvisionerDaemon(t, coderAPI, defaultTestDaemonName, options.ProvisionerDaemonTags, coderd.MemoryProvisionerWithVersionOverride(options.ProvisionerDaemonVersion))
|
||||||
}
|
}
|
||||||
client := codersdk.New(serverURL)
|
client := codersdk.New(serverURL)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
@@ -646,7 +648,7 @@ func (c *ProvisionerdCloser) Close() error {
|
|||||||
// well with coderd testing. It registers the "echo" provisioner for
|
// well with coderd testing. It registers the "echo" provisioner for
|
||||||
// quick testing.
|
// quick testing.
|
||||||
func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
||||||
return NewTaggedProvisionerDaemon(t, coderAPI, "test", nil)
|
return NewTaggedProvisionerDaemon(t, coderAPI, defaultTestDaemonName, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTaggedProvisionerDaemon(t testing.TB, coderAPI *coderd.API, name string, provisionerTags map[string]string, opts ...coderd.MemoryProvisionerDaemonOption) io.Closer {
|
func NewTaggedProvisionerDaemon(t testing.TB, coderAPI *coderd.API, name string, provisionerTags map[string]string, opts ...coderd.MemoryProvisionerDaemonOption) io.Closer {
|
||||||
|
|||||||
@@ -4848,6 +4848,13 @@ func (q *FakeQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePosition
|
|||||||
row.AvailableWorkers = append(row.AvailableWorkers, worker.ID)
|
row.AvailableWorkers = append(row.AvailableWorkers, worker.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add daemon name to provisioner job
|
||||||
|
for _, daemon := range q.provisionerDaemons {
|
||||||
|
if job.WorkerID.Valid && job.WorkerID.UUID == daemon.ID {
|
||||||
|
row.WorkerName = daemon.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
rows = append(rows, row)
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7730,7 +7730,9 @@ SELECT
|
|||||||
COALESCE(t.display_name, '') AS template_display_name,
|
COALESCE(t.display_name, '') AS template_display_name,
|
||||||
COALESCE(t.icon, '') AS template_icon,
|
COALESCE(t.icon, '') AS template_icon,
|
||||||
w.id AS workspace_id,
|
w.id AS workspace_id,
|
||||||
COALESCE(w.name, '') AS workspace_name
|
COALESCE(w.name, '') AS workspace_name,
|
||||||
|
-- Include the name of the provisioner_daemon associated to the job
|
||||||
|
COALESCE(pd.name, '') AS worker_name
|
||||||
FROM
|
FROM
|
||||||
provisioner_jobs pj
|
provisioner_jobs pj
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
@@ -7755,6 +7757,9 @@ LEFT JOIN
|
|||||||
t.id = tv.template_id
|
t.id = tv.template_id
|
||||||
AND t.organization_id = pj.organization_id
|
AND t.organization_id = pj.organization_id
|
||||||
)
|
)
|
||||||
|
LEFT JOIN
|
||||||
|
-- Join to get the daemon name corresponding to the job's worker_id
|
||||||
|
provisioner_daemons pd ON pd.id = pj.worker_id
|
||||||
WHERE
|
WHERE
|
||||||
pj.organization_id = $1::uuid
|
pj.organization_id = $1::uuid
|
||||||
AND (COALESCE(array_length($2::uuid[], 1), 0) = 0 OR pj.id = ANY($2::uuid[]))
|
AND (COALESCE(array_length($2::uuid[], 1), 0) = 0 OR pj.id = ANY($2::uuid[]))
|
||||||
@@ -7770,7 +7775,8 @@ GROUP BY
|
|||||||
t.display_name,
|
t.display_name,
|
||||||
t.icon,
|
t.icon,
|
||||||
w.id,
|
w.id,
|
||||||
w.name
|
w.name,
|
||||||
|
pd.name
|
||||||
ORDER BY
|
ORDER BY
|
||||||
pj.created_at DESC
|
pj.created_at DESC
|
||||||
LIMIT
|
LIMIT
|
||||||
@@ -7797,6 +7803,7 @@ type GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow
|
|||||||
TemplateIcon string `db:"template_icon" json:"template_icon"`
|
TemplateIcon string `db:"template_icon" json:"template_icon"`
|
||||||
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
|
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
|
||||||
WorkspaceName string `db:"workspace_name" json:"workspace_name"`
|
WorkspaceName string `db:"workspace_name" json:"workspace_name"`
|
||||||
|
WorkerName string `db:"worker_name" json:"worker_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *sqlQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(ctx context.Context, arg GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams) ([]GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow, error) {
|
func (q *sqlQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(ctx context.Context, arg GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams) ([]GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow, error) {
|
||||||
@@ -7844,6 +7851,7 @@ func (q *sqlQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionA
|
|||||||
&i.TemplateIcon,
|
&i.TemplateIcon,
|
||||||
&i.WorkspaceID,
|
&i.WorkspaceID,
|
||||||
&i.WorkspaceName,
|
&i.WorkspaceName,
|
||||||
|
&i.WorkerName,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,7 +160,9 @@ SELECT
|
|||||||
COALESCE(t.display_name, '') AS template_display_name,
|
COALESCE(t.display_name, '') AS template_display_name,
|
||||||
COALESCE(t.icon, '') AS template_icon,
|
COALESCE(t.icon, '') AS template_icon,
|
||||||
w.id AS workspace_id,
|
w.id AS workspace_id,
|
||||||
COALESCE(w.name, '') AS workspace_name
|
COALESCE(w.name, '') AS workspace_name,
|
||||||
|
-- Include the name of the provisioner_daemon associated to the job
|
||||||
|
COALESCE(pd.name, '') AS worker_name
|
||||||
FROM
|
FROM
|
||||||
provisioner_jobs pj
|
provisioner_jobs pj
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
@@ -185,6 +187,9 @@ LEFT JOIN
|
|||||||
t.id = tv.template_id
|
t.id = tv.template_id
|
||||||
AND t.organization_id = pj.organization_id
|
AND t.organization_id = pj.organization_id
|
||||||
)
|
)
|
||||||
|
LEFT JOIN
|
||||||
|
-- Join to get the daemon name corresponding to the job's worker_id
|
||||||
|
provisioner_daemons pd ON pd.id = pj.worker_id
|
||||||
WHERE
|
WHERE
|
||||||
pj.organization_id = @organization_id::uuid
|
pj.organization_id = @organization_id::uuid
|
||||||
AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pj.id = ANY(@ids::uuid[]))
|
AND (COALESCE(array_length(@ids::uuid[], 1), 0) = 0 OR pj.id = ANY(@ids::uuid[]))
|
||||||
@@ -200,7 +205,8 @@ GROUP BY
|
|||||||
t.display_name,
|
t.display_name,
|
||||||
t.icon,
|
t.icon,
|
||||||
w.id,
|
w.id,
|
||||||
w.name
|
w.name,
|
||||||
|
pd.name
|
||||||
ORDER BY
|
ORDER BY
|
||||||
pj.created_at DESC
|
pj.created_at DESC
|
||||||
LIMIT
|
LIMIT
|
||||||
|
|||||||
@@ -395,6 +395,7 @@ func convertProvisionerJobWithQueuePosition(pj database.GetProvisionerJobsByOrga
|
|||||||
QueuePosition: pj.QueuePosition,
|
QueuePosition: pj.QueuePosition,
|
||||||
QueueSize: pj.QueueSize,
|
QueueSize: pj.QueueSize,
|
||||||
})
|
})
|
||||||
|
job.WorkerName = pj.WorkerName
|
||||||
job.AvailableWorkers = pj.AvailableWorkers
|
job.AvailableWorkers = pj.AvailableWorkers
|
||||||
job.Metadata = codersdk.ProvisionerJobMetadata{
|
job.Metadata = codersdk.ProvisionerJobMetadata{
|
||||||
TemplateVersionName: pj.TemplateVersionName,
|
TemplateVersionName: pj.TemplateVersionName,
|
||||||
|
|||||||
+231
-130
@@ -27,162 +27,263 @@ import (
|
|||||||
func TestProvisionerJobs(t *testing.T) {
|
func TestProvisionerJobs(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
|
t.Run("ProvisionerJobs", func(t *testing.T) {
|
||||||
client := coderdtest.New(t, &coderdtest.Options{
|
db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
|
||||||
IncludeProvisionerDaemon: true,
|
client := coderdtest.New(t, &coderdtest.Options{
|
||||||
Database: db,
|
IncludeProvisionerDaemon: true,
|
||||||
Pubsub: ps,
|
Database: db,
|
||||||
})
|
Pubsub: ps,
|
||||||
owner := coderdtest.CreateFirstUser(t, client)
|
})
|
||||||
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID))
|
owner := coderdtest.CreateFirstUser(t, client)
|
||||||
memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID))
|
||||||
|
memberClient, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
||||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||||
|
|
||||||
time.Sleep(1500 * time.Millisecond) // Ensure the workspace build job has a different timestamp for sorting.
|
time.Sleep(1500 * time.Millisecond) // Ensure the workspace build job has a different timestamp for sorting.
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
|
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
|
||||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
|
||||||
// Create a pending job.
|
// Create a pending job.
|
||||||
w := dbgen.Workspace(t, db, database.WorkspaceTable{
|
w := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||||
OrganizationID: owner.OrganizationID,
|
|
||||||
OwnerID: member.ID,
|
|
||||||
TemplateID: template.ID,
|
|
||||||
})
|
|
||||||
wbID := uuid.New()
|
|
||||||
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
|
||||||
OrganizationID: w.OrganizationID,
|
|
||||||
StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
|
||||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
|
||||||
Input: json.RawMessage(`{"workspace_build_id":"` + wbID.String() + `"}`),
|
|
||||||
})
|
|
||||||
dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
|
||||||
ID: wbID,
|
|
||||||
JobID: job.ID,
|
|
||||||
WorkspaceID: w.ID,
|
|
||||||
TemplateVersionID: version.ID,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add more jobs than the default limit.
|
|
||||||
for i := range 60 {
|
|
||||||
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
|
||||||
OrganizationID: owner.OrganizationID,
|
OrganizationID: owner.OrganizationID,
|
||||||
Tags: database.StringMap{"count": strconv.Itoa(i)},
|
OwnerID: member.ID,
|
||||||
|
TemplateID: template.ID,
|
||||||
|
})
|
||||||
|
wbID := uuid.New()
|
||||||
|
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
|
OrganizationID: w.OrganizationID,
|
||||||
|
StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
||||||
|
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||||
|
Input: json.RawMessage(`{"workspace_build_id":"` + wbID.String() + `"}`),
|
||||||
|
})
|
||||||
|
dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||||
|
ID: wbID,
|
||||||
|
JobID: job.ID,
|
||||||
|
WorkspaceID: w.ID,
|
||||||
|
TemplateVersionID: version.ID,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Single", func(t *testing.T) {
|
// Add more jobs than the default limit.
|
||||||
t.Parallel()
|
for i := range 60 {
|
||||||
t.Run("Workspace", func(t *testing.T) {
|
dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||||
|
OrganizationID: owner.OrganizationID,
|
||||||
|
Tags: database.StringMap{"count": strconv.Itoa(i)},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Single", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
t.Run("OK", func(t *testing.T) {
|
t.Run("Workspace", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
// Note this calls the single job endpoint.
|
||||||
|
job2, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, job.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, job.ID, job2.ID)
|
||||||
|
|
||||||
|
// Verify that job metadata is correct.
|
||||||
|
assert.Equal(t, job2.Metadata, codersdk.ProvisionerJobMetadata{
|
||||||
|
TemplateVersionName: version.Name,
|
||||||
|
TemplateID: template.ID,
|
||||||
|
TemplateName: template.Name,
|
||||||
|
TemplateDisplayName: template.DisplayName,
|
||||||
|
TemplateIcon: template.Icon,
|
||||||
|
WorkspaceID: &w.ID,
|
||||||
|
WorkspaceName: w.Name,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Template Import", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
// Note this calls the single job endpoint.
|
||||||
|
job2, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, version.Job.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, version.Job.ID, job2.ID)
|
||||||
|
|
||||||
|
// Verify that job metadata is correct.
|
||||||
|
assert.Equal(t, job2.Metadata, codersdk.ProvisionerJobMetadata{
|
||||||
|
TemplateVersionName: version.Name,
|
||||||
|
TemplateID: template.ID,
|
||||||
|
TemplateName: template.Name,
|
||||||
|
TemplateDisplayName: template.DisplayName,
|
||||||
|
TemplateIcon: template.Icon,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Missing", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
// Note this calls the single job endpoint.
|
// Note this calls the single job endpoint.
|
||||||
job2, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, job.ID)
|
_, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, uuid.New())
|
||||||
require.NoError(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, job.ID, job2.ID)
|
|
||||||
|
|
||||||
// Verify that job metadata is correct.
|
|
||||||
assert.Equal(t, job2.Metadata, codersdk.ProvisionerJobMetadata{
|
|
||||||
TemplateVersionName: version.Name,
|
|
||||||
TemplateID: template.ID,
|
|
||||||
TemplateName: template.Name,
|
|
||||||
TemplateDisplayName: template.DisplayName,
|
|
||||||
TemplateIcon: template.Icon,
|
|
||||||
WorkspaceID: &w.ID,
|
|
||||||
WorkspaceName: w.Name,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("Template Import", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
t.Run("OK", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
||||||
// Note this calls the single job endpoint.
|
|
||||||
job2, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, version.Job.ID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, version.Job.ID, job2.ID)
|
|
||||||
|
|
||||||
// Verify that job metadata is correct.
|
t.Run("Default limit", func(t *testing.T) {
|
||||||
assert.Equal(t, job2.Metadata, codersdk.ProvisionerJobMetadata{
|
|
||||||
TemplateVersionName: version.Name,
|
|
||||||
TemplateID: template.ID,
|
|
||||||
TemplateName: template.Name,
|
|
||||||
TemplateDisplayName: template.DisplayName,
|
|
||||||
TemplateIcon: template.Icon,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.Run("Missing", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
// Note this calls the single job endpoint.
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
||||||
_, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, uuid.New())
|
require.NoError(t, err)
|
||||||
|
require.Len(t, jobs, 50)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("IDs", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
||||||
|
IDs: []uuid.UUID{workspace.LatestBuild.Job.ID, version.Job.ID},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, jobs, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Status", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
||||||
|
Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobRunning},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, jobs, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Tags", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
||||||
|
Tags: map[string]string{"count": "1"},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, jobs, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Limit", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
||||||
|
Limit: 1,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, jobs, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
// For now, this is not allowed even though the member has created a
|
||||||
|
// workspace. Once member-level permissions for jobs are supported
|
||||||
|
// by RBAC, this test should be updated.
|
||||||
|
t.Run("MemberDenied", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
jobs, err := memberClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
require.Len(t, jobs, 0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Default limit", func(t *testing.T) {
|
// Ensures that when a provisioner job is in the succeeded state,
|
||||||
|
// the API response includes both worker_id and worker_name fields
|
||||||
|
t.Run("AssignedProvisionerJob", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
||||||
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, jobs, 50)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IDs", func(t *testing.T) {
|
db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
|
||||||
t.Parallel()
|
client, _, coderdAPI := coderdtest.NewWithAPI(t, &coderdtest.Options{
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
IncludeProvisionerDaemon: false,
|
||||||
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
Database: db,
|
||||||
IDs: []uuid.UUID{workspace.LatestBuild.Job.ID, version.Job.ID},
|
Pubsub: ps,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
provisionerDaemonName := "provisioner_daemon_test"
|
||||||
require.Len(t, jobs, 2)
|
provisionerDaemon := coderdtest.NewTaggedProvisionerDaemon(t, coderdAPI, provisionerDaemonName, map[string]string{"owner": "", "scope": "organization"})
|
||||||
})
|
owner := coderdtest.CreateFirstUser(t, client)
|
||||||
|
templateAdminClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID))
|
||||||
|
|
||||||
t.Run("Status", func(t *testing.T) {
|
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
|
||||||
t.Parallel()
|
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||||
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
|
||||||
Status: []codersdk.ProvisionerJobStatus{codersdk.ProvisionerJobRunning},
|
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
|
||||||
|
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||||
|
|
||||||
|
// Stop the provisioner so it doesn't grab any more jobs
|
||||||
|
err := provisionerDaemon.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("List_IncludesWorkerIDAndName", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
|
|
||||||
|
// Get provisioner daemon responsible for executing the provisioner jobs
|
||||||
|
provisionerDaemons, err := db.GetProvisionerDaemons(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(provisionerDaemons))
|
||||||
|
if assert.NotEmpty(t, provisionerDaemons) {
|
||||||
|
require.Equal(t, provisionerDaemonName, provisionerDaemons[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get provisioner jobs
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(jobs))
|
||||||
|
|
||||||
|
for _, job := range jobs {
|
||||||
|
require.Equal(t, owner.OrganizationID, job.OrganizationID)
|
||||||
|
require.Equal(t, database.ProvisionerJobStatusSucceeded, database.ProvisionerJobStatus(job.Status))
|
||||||
|
|
||||||
|
// Guarantee that provisioner jobs contain the provisioner daemon ID and name
|
||||||
|
if assert.NotEmpty(t, provisionerDaemons) {
|
||||||
|
require.Equal(t, &provisionerDaemons[0].ID, job.WorkerID)
|
||||||
|
require.Equal(t, provisionerDaemonName, job.WorkerName)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, jobs, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Tags", func(t *testing.T) {
|
t.Run("Get_IncludesWorkerIDAndName", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
||||||
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||||
Tags: map[string]string{"count": "1"},
|
|
||||||
|
// Get provisioner daemon responsible for executing the provisioner job
|
||||||
|
provisionerDaemons, err := db.GetProvisionerDaemons(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(provisionerDaemons))
|
||||||
|
if assert.NotEmpty(t, provisionerDaemons) {
|
||||||
|
require.Equal(t, provisionerDaemonName, provisionerDaemons[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all provisioner jobs
|
||||||
|
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(jobs))
|
||||||
|
|
||||||
|
// Find workspace_build provisioner job ID
|
||||||
|
var workspaceProvisionerJobID uuid.UUID
|
||||||
|
for _, job := range jobs {
|
||||||
|
if job.Type == codersdk.ProvisionerJobTypeWorkspaceBuild {
|
||||||
|
workspaceProvisionerJobID = job.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.NotNil(t, workspaceProvisionerJobID)
|
||||||
|
|
||||||
|
// Get workspace_build provisioner job by ID
|
||||||
|
workspaceProvisionerJob, err := templateAdminClient.OrganizationProvisionerJob(ctx, owner.OrganizationID, workspaceProvisionerJobID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, owner.OrganizationID, workspaceProvisionerJob.OrganizationID)
|
||||||
|
require.Equal(t, database.ProvisionerJobStatusSucceeded, database.ProvisionerJobStatus(workspaceProvisionerJob.Status))
|
||||||
|
|
||||||
|
// Guarantee that provisioner job contains the provisioner daemon ID and name
|
||||||
|
if assert.NotEmpty(t, provisionerDaemons) {
|
||||||
|
require.Equal(t, &provisionerDaemons[0].ID, workspaceProvisionerJob.WorkerID)
|
||||||
|
require.Equal(t, provisionerDaemonName, workspaceProvisionerJob.WorkerName)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, jobs, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Limit", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
||||||
jobs, err := templateAdminClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, &codersdk.OrganizationProvisionerJobsOptions{
|
|
||||||
Limit: 1,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, jobs, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
// For now, this is not allowed even though the member has created a
|
|
||||||
// workspace. Once member-level permissions for jobs are supported
|
|
||||||
// by RBAC, this test should be updated.
|
|
||||||
t.Run("MemberDenied", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
||||||
jobs, err := memberClient.OrganizationProvisionerJobs(ctx, owner.OrganizationID, nil)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Len(t, jobs, 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ type ProvisionerJob struct {
|
|||||||
ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"REQUIRED_TEMPLATE_VARIABLES" table:"error code"`
|
ErrorCode JobErrorCode `json:"error_code,omitempty" enums:"REQUIRED_TEMPLATE_VARIABLES" table:"error code"`
|
||||||
Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed" table:"status"`
|
Status ProvisionerJobStatus `json:"status" enums:"pending,running,succeeded,canceling,canceled,failed" table:"status"`
|
||||||
WorkerID *uuid.UUID `json:"worker_id,omitempty" format:"uuid" table:"worker id"`
|
WorkerID *uuid.UUID `json:"worker_id,omitempty" format:"uuid" table:"worker id"`
|
||||||
|
WorkerName string `json:"worker_name,omitempty" table:"worker name"`
|
||||||
FileID uuid.UUID `json:"file_id" format:"uuid" table:"file id"`
|
FileID uuid.UUID `json:"file_id" format:"uuid" table:"file id"`
|
||||||
Tags map[string]string `json:"tags" table:"tags"`
|
Tags map[string]string `json:"tags" table:"tags"`
|
||||||
QueuePosition int `json:"queue_position" table:"queue position"`
|
QueuePosition int `json:"queue_position" table:"queue position"`
|
||||||
|
|||||||
Generated
+11
-5
@@ -69,7 +69,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -302,7 +303,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1012,7 +1014,8 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1318,7 +1321,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1527,6 +1531,7 @@ Status Code **200**
|
|||||||
| `»»» [any property]` | string | false | | |
|
| `»»» [any property]` | string | false | | |
|
||||||
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
||||||
| `»» worker_id` | string(uuid) | false | | |
|
| `»» worker_id` | string(uuid) | false | | |
|
||||||
|
| `»» worker_name` | string | false | | |
|
||||||
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
||||||
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
||||||
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
||||||
@@ -1798,7 +1803,8 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
|
|||||||
Generated
+5
-2
@@ -426,7 +426,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@@ -473,6 +474,7 @@ Status Code **200**
|
|||||||
| `»» [any property]` | string | false | | |
|
| `»» [any property]` | string | false | | |
|
||||||
| `» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
| `» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
||||||
| `» worker_id` | string(uuid) | false | | |
|
| `» worker_id` | string(uuid) | false | | |
|
||||||
|
| `» worker_name` | string | false | | |
|
||||||
|
|
||||||
#### Enumerated Values
|
#### Enumerated Values
|
||||||
|
|
||||||
@@ -551,7 +553,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/provisi
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Generated
+11
-5
@@ -5518,7 +5518,8 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -5545,6 +5546,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
|||||||
| » `[any property]` | string | false | | |
|
| » `[any property]` | string | false | | |
|
||||||
| `type` | [codersdk.ProvisionerJobType](#codersdkprovisionerjobtype) | false | | |
|
| `type` | [codersdk.ProvisionerJobType](#codersdkprovisionerjobtype) | false | | |
|
||||||
| `worker_id` | string | false | | |
|
| `worker_id` | string | false | | |
|
||||||
|
| `worker_name` | string | false | | |
|
||||||
|
|
||||||
#### Enumerated Values
|
#### Enumerated Values
|
||||||
|
|
||||||
@@ -7101,7 +7103,8 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -8241,7 +8244,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -9205,7 +9209,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -9928,7 +9933,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
|
|||||||
Generated
+20
-9
@@ -489,7 +489,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -586,7 +587,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -707,7 +709,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1264,7 +1267,8 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1334,6 +1338,7 @@ Status Code **200**
|
|||||||
| `»»» [any property]` | string | false | | |
|
| `»»» [any property]` | string | false | | |
|
||||||
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
||||||
| `»» worker_id` | string(uuid) | false | | |
|
| `»» worker_id` | string(uuid) | false | | |
|
||||||
|
| `»» worker_name` | string | false | | |
|
||||||
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
||||||
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
||||||
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
||||||
@@ -1541,7 +1546,8 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template}/versions/{templ
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1611,6 +1617,7 @@ Status Code **200**
|
|||||||
| `»»» [any property]` | string | false | | |
|
| `»»» [any property]` | string | false | | |
|
||||||
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
| `»» type` | [codersdk.ProvisionerJobType](schemas.md#codersdkprovisionerjobtype) | false | | |
|
||||||
| `»» worker_id` | string(uuid) | false | | |
|
| `»» worker_id` | string(uuid) | false | | |
|
||||||
|
| `»» worker_name` | string | false | | |
|
||||||
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
| `» matched_provisioners` | [codersdk.MatchedProvisioners](schemas.md#codersdkmatchedprovisioners) | false | | |
|
||||||
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
| `»» available` | integer | false | | Available is the number of provisioner daemons that are available to take jobs. This may be less than the count if some provisioners are busy or have been stopped. |
|
||||||
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
| `»» count` | integer | false | | Count is the number of provisioner daemons that matched the given tags. If the count is 0, it means no provisioner daemons matched the requested tags. |
|
||||||
@@ -1708,7 +1715,8 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion} \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1814,7 +1822,8 @@ curl -X PATCH http://coder-server:8080/api/v2/templateversions/{templateversion}
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -2010,7 +2019,8 @@ curl -X POST http://coder-server:8080/api/v2/templateversions/{templateversion}/
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2082,7 +2092,8 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Generated
+12
-6
@@ -124,7 +124,8 @@ of the template will be used.
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -405,7 +406,8 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -712,7 +714,8 @@ of the template will be used.
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -996,7 +999,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1261,7 +1265,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
@@ -1658,7 +1663,8 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \
|
|||||||
"property2": "string"
|
"property2": "string"
|
||||||
},
|
},
|
||||||
"type": "template_version_import",
|
"type": "template_version_import",
|
||||||
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b"
|
"worker_id": "ae5fa6f7-c55b-40c1-b40a-b36ac467652b",
|
||||||
|
"worker_name": "string"
|
||||||
},
|
},
|
||||||
"matched_provisioners": {
|
"matched_provisioners": {
|
||||||
"available": 0,
|
"available": 0,
|
||||||
|
|||||||
+4
-4
@@ -45,10 +45,10 @@ Select which organization (uuid or name) to use.
|
|||||||
|
|
||||||
### -c, --column
|
### -c, --column
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| Type | <code>[id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|file id\|tags\|queue position\|queue size\|organization id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|organization\|queue]</code> |
|
| Type | <code>[id\|created at\|started at\|completed at\|canceled at\|error\|error code\|status\|worker id\|worker name\|file id\|tags\|queue position\|queue size\|organization id\|template version id\|workspace build id\|type\|available workers\|template version name\|template id\|template name\|template display name\|template icon\|workspace id\|workspace name\|organization\|queue]</code> |
|
||||||
| Default | <code>created at,id,type,template display name,status,queue,tags</code> |
|
| Default | <code>created at,id,type,template display name,status,queue,tags</code> |
|
||||||
|
|
||||||
Columns to display in table output.
|
Columns to display in table output.
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ OPTIONS:
|
|||||||
-O, --org string, $CODER_ORGANIZATION
|
-O, --org string, $CODER_ORGANIZATION
|
||||||
Select which organization (uuid or name) to use.
|
Select which organization (uuid or name) to use.
|
||||||
|
|
||||||
-c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|organization|queue] (default: created at,id,type,template display name,status,queue,tags)
|
-c, --column [id|created at|started at|completed at|canceled at|error|error code|status|worker id|worker name|file id|tags|queue position|queue size|organization id|template version id|workspace build id|type|available workers|template version name|template id|template name|template display name|template icon|workspace id|workspace name|organization|queue] (default: created at,id,type,template display name,status,queue,tags)
|
||||||
Columns to display in table output.
|
Columns to display in table output.
|
||||||
|
|
||||||
-l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50)
|
-l, --limit int, $CODER_PROVISIONER_JOB_LIST_LIMIT (default: 50)
|
||||||
|
|||||||
Generated
+1
@@ -1925,6 +1925,7 @@ export interface ProvisionerJob {
|
|||||||
readonly error_code?: JobErrorCode;
|
readonly error_code?: JobErrorCode;
|
||||||
readonly status: ProvisionerJobStatus;
|
readonly status: ProvisionerJobStatus;
|
||||||
readonly worker_id?: string;
|
readonly worker_id?: string;
|
||||||
|
readonly worker_name?: string;
|
||||||
readonly file_id: string;
|
readonly file_id: string;
|
||||||
readonly tags: Record<string, string>;
|
readonly tags: Record<string, string>;
|
||||||
readonly queue_position: number;
|
readonly queue_position: number;
|
||||||
|
|||||||
@@ -120,14 +120,16 @@ export const JobRow: FC<JobRowProps> = ({ job, defaultIsOpen = false }) => {
|
|||||||
<>
|
<>
|
||||||
<dt>Completed by provisioner:</dt>
|
<dt>Completed by provisioner:</dt>
|
||||||
<dd className="flex items-center gap-2">
|
<dd className="flex items-center gap-2">
|
||||||
<span>{job.worker_id}</span>
|
<span>{job.worker_name || "[removed]"}</span>
|
||||||
<Button size="xs" variant="outline" asChild>
|
{job.worker_name && (
|
||||||
<RouterLink
|
<Button size="xs" variant="outline" asChild>
|
||||||
to={`../provisioners?${new URLSearchParams({ ids: job.worker_id })}`}
|
<RouterLink
|
||||||
>
|
to={`../provisioners?${new URLSearchParams({ ids: job.worker_id })}`}
|
||||||
View provisioner
|
>
|
||||||
</RouterLink>
|
View provisioner
|
||||||
</Button>
|
</RouterLink>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</dd>
|
</dd>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user