-
Notifications
You must be signed in to change notification settings - Fork 881
feat: add provisioner daemon and jobs endpoints and commands #15940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
b776b1b
to
af6789a
Compare
==========[version job ID]========== ====[timestamp]===== succeeded map[owner: scope:organization] template_version_import Coder | ||
======[workspace build job ID]====== ====[timestamp]===== succeeded map[owner: scope:organization] workspace_build Coder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if tags were listed in k1=v1,k2=v2
format.
We can also probably "lie" a bit about the case {"owner": "", "scope": "organization"}
and just pretend it's empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestions, and I agree we should hide default owner/scope. We should probably update table formatter to change how maps are output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move "Queue" column to be the first one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move "Queue" column to be the first one.
@mtojek I'm going to create a follow-up issue for this as ordering columns is not supported in our table formatter currently. I would like to avoid us having to craft custom structs every time we want to re-order columns.
cli/provisionerjobs.go
Outdated
func convertSlice[D, S ~string](dstType []D, src []S) []D { | ||
for _, item := range src { | ||
dstType = append(dstType, D(item)) | ||
} | ||
return dstType | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe throw this into slice
as ToEnum
🤷♂️
Similar to ToStrings
coder/coderd/util/slice/slice.go
Lines 7 to 14 in dc70114
// ToStrings works for any type where the base type is a string. | |
func ToStrings[T ~string](a []T) []string { | |
tmp := make([]string, 0, len(a)) | |
for _, v := range a { | |
tmp = append(tmp, string(v)) | |
} | |
return tmp | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can do 👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, any good reason these utils under coderd
? I wouldn't move it in this PR but could make it more accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no good reason at all. I think it just grew over time
Version string `json:"version" table:"version"` | ||
APIVersion string `json:"api_version" table:"api version"` | ||
Provisioners []ProvisionerType `json:"provisioners" table:"-"` | ||
Tags map[string]string `json:"tags" table:"tags"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not love this output: map[owner: scope:organization]
, but I'm ok with it.
We could have the table formatter know how to handle some basic types like map[string]string
. Just leave it for this PR though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a similar thought, something human-readable and copy/paste friendly:
owner = (none)
scope = organization
cmd := r.RootCmd.Provisioners() | ||
cmd.AddSubcommands( | ||
r.provisionerDaemonStart(), | ||
r.provisionerKeys(), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a crafty
enterprise/cli/proxyserver.go
Outdated
@@ -114,6 +114,8 @@ func (r *RootCmd) proxyServer() *serpent.Command { | |||
), | |||
Handler: func(inv *serpent.Invocation) error { | |||
var closers closers | |||
defer closers.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol, closers
was a nice thought, but never closing the closers is quite the funny bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var losers closers
defer losers.Close()
:)
r.Route("/provisionerdaemons", func(r chi.Router) { | ||
r.Get("/", api.provisionerDaemons) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we add filtering by key
, can we merge this with /organizations/{organization}/provisionerkeys/daemons
? Or at least merge the code they both invoke? If possible, just to make sure we are consistent.
coder/enterprise/coderd/provisionerkeys.go
Line 129 in dc70114
func (api *API) provisionerKeyDaemons(rw http.ResponseWriter, r *http.Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea, I wasn't sure how much to move to AGPL vs enterprise but none of this code is critical to keep in enterprise 👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally I think GET
endpoints can be made AGPL, and POST
/PATCH
should be enterprise.
cli/cliui/table.go
Outdated
@@ -195,6 +197,10 @@ func renderTable(out any, sort string, headers table.Row, filterColumns []string | |||
if val != nil { | |||
v = val.Format(time.RFC3339) | |||
} | |||
case codersdk.NullTime: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: shouldn't we extract it to a separate PR?
|
||
jobs, err := client.OrganizationProvisionerJobs(ctx, org.ID, &codersdk.OrganizationProvisionerJobsOptions{ | ||
Status: slice.ToStringEnums[codersdk.ProvisionerJobStatus](status), | ||
Limit: int(limit), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: paging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to keep it simple for now, I realized we don't have a precedent on the CLI for paging so I didn't want to introduce it here. Granted --limit
is also new, so I could remove that too but I was worried about how many entries there will be.
The original issue #15084 mentioned we should have "created at" (or rather "created after"), and I added that initially, but then I realized we don't have a precedent for filtering on time on the CLI either. (There's even a lack of serpent support there.) For this reason both pagination and created at filter have been omitted and should IMO be part of a grander plan for how to support these in a unified way on the CLI.
==========[version job ID]========== ====[timestamp]===== succeeded map[owner: scope:organization] template_version_import Coder | ||
======[workspace build job ID]====== ====[timestamp]===== succeeded map[owner: scope:organization] workspace_build Coder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move "Queue" column to be the first one.
if err != nil { | ||
return "", nil, xerrors.Errorf("open container: %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: double-checking, is it intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in some cases this will cause a nil pointer dereference otherwise.
// @Param tags query object false "Provisioner tags to filter by (JSON of the form {'tag1':'value1','tag2':'value2'})" | ||
// @Success 200 {array} codersdk.ProvisionerDaemonWithStatus | ||
// @Router /organizations/{organization}/provisionerdaemons [get] | ||
func (api *API) provisionerDaemons(rw http.ResponseWriter, r *http.Request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why endpoint changes and CLI go together? Alternatively, you could split the PR in half, and keep all golden files aside. Large PRs are typically a problem when somebody needs to revert them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and no, for development I'm working on them in tandem, CLI requirements have and keep resulted in API changes, etc. Stacking could be a better approach though.
I don't think the golden files should be separate as that would make the feature harder to revert, but API and CLI can definitely be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be possible to have the DB stuff in its own PR and the CLI stuff then in a separate PR based off that. No need to have a really deep stack here IMO.
I'm fine with reviewing initially here "all-in-one".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Version string `json:"version" table:"version"` | ||
APIVersion string `json:"api_version" table:"api version"` | ||
Provisioners []ProvisionerType `json:"provisioners" table:"-"` | ||
Tags map[string]string `json:"tags" table:"tags"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a similar thought, something human-readable and copy/paste friendly:
owner = (none)
scope = organization
docs/manifest.json
Outdated
"path": "reference/cli/provisioner_keys.md" | ||
"path": "reference/cli/provisioners_keys.md" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: renaming could go in a separate PR, right?
enterprise/cli/proxyserver.go
Outdated
@@ -114,6 +114,8 @@ func (r *RootCmd) proxyServer() *serpent.Command { | |||
), | |||
Handler: func(inv *serpent.Invocation) error { | |||
var closers closers | |||
defer closers.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var losers closers
defer losers.Close()
:)
@@ -377,7 +377,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { | |||
// | |||
// We may in future decide to scope provisioner daemons to organizations, so we'll keep the API | |||
// route as is. | |||
r.Route("/organizations/{organization}/provisionerdaemons", func(r chi.Router) { | |||
r.Route("/organizations/{organization}/provisionerdaemons/serve", func(r chi.Router) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: does this change give any special benefits? Most likely I'm missing something...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We removed the other endpoints from here that were under /organizations/{organization}/provisionerdaemons
and only /organizations/{organization}/provisionerdaemons/serve
remains. There's no reason to keep the broader one and IIRC it also interfered with the AGPL endpoint.
if err != nil { | ||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ | ||
Message: "Internal error fetching provisioner daemons.", | ||
Detail: err.Error(), | ||
}) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: does sql.ErrNoRows
need to be handled separately?
5b54ed6
to
720b715
Compare
Co-authored-by: Steven Masley <[email protected]>
720b715
to
24d49da
Compare
Fixes #15190
Fixes #15191
Updates #15084