mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Validate swagger definitions (#5694)
* docs: audit, deploymentconfig, files, parameters * Swagger comments in workspacebuilds.go * structs in workspacebuilds.go * workspaceagents: instance identity * workspaceagents.go in progress * workspaceagents.go in progress * Agents * workspacebuilds.go * /workspaces * templates.go, templateversions.go * templateversion.go in progress * cancel * templateversions * wip * Merge * x-apidocgen * NullTime hack not needed anymore * Fix: x-apidocgen * Members * Fixes * Fix * WIP * WIP * Users * Logout * User profile * Status suspend activate * User roles * User tokens * Keys * SSH key * All * Typo * Fix * Entitlements * Groups * SCIM * Fix * Fix * Clean templates * Sort API pages * Fix: HashedSecret * WIP * WIP * WIP * Fix: cover workspaceagents * Assert: consistent ID and summary * Assert: success or failure defined * Fix: parallel * Refactor * Support enterprise * Go comment goes to top * Security * assertPathParametersDefined * assertUniqueRoutes * assertRequestBody * More fixes * Fix: exceptions * Fix field format * Address PR comments * Refactor
This commit is contained in:
+622
-264
File diff suppressed because it is too large
Load Diff
+542
-222
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -29,6 +29,7 @@ import (
|
||||
// @Summary Create token API key
|
||||
// @ID create-token-api-key
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
// @Param user path string true "User ID, name, or me"
|
||||
@@ -209,9 +210,8 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// @Summary Delete API key
|
||||
// @ID delete-user-tokens
|
||||
// @ID delete-api-key
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
// @Param user path string true "User ID, name, or me"
|
||||
// @Param keyid path string true "Key ID" format(uuid)
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// @Summary Generate fake audit log
|
||||
// @ID generate-fake-audit-logs
|
||||
// @ID generate-fake-audit-log
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Audit
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package coderdtest_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
)
|
||||
|
||||
func TestEndpointsDocumented(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
swaggerComments, err := coderdtest.ParseSwaggerComments("..")
|
||||
require.NoError(t, err, "can't parse swagger comments")
|
||||
|
||||
_, _, api := coderdtest.NewWithAPI(t, nil)
|
||||
coderdtest.VerifySwaggerDefinitions(t, api.APIHandler, swaggerComments)
|
||||
}
|
||||
|
||||
func TestSDKFieldsFormatted(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
nodes, err := parser.ParseDir(fileSet, "../../codersdk", nil, parser.ParseComments)
|
||||
require.NoError(t, err, "parser.ParseDir failed")
|
||||
|
||||
for _, node := range nodes {
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
typeSpec, ok := n.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
structureName := typeSpec.Name
|
||||
|
||||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
return true // not a structure
|
||||
}
|
||||
|
||||
for _, field := range structType.Fields.List {
|
||||
selectorExpr, ok := field.Type.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue // rather a basic, or primitive
|
||||
}
|
||||
|
||||
if field.Tag == nil || !strings.Contains(field.Tag.Value, `json:"`) {
|
||||
continue // not a JSON property
|
||||
}
|
||||
|
||||
switch selectorExpr.Sel.Name {
|
||||
case "UUID":
|
||||
assert.Contains(t, field.Tag.Value, `format:"uuid"`, `Swagger formatting requires to annotate the field with - format:"uuid". Location: %s/%s`, structureName, field.Names)
|
||||
case "Time":
|
||||
assert.Contains(t, field.Tag.Value, `format:"date-time"`, `Swagger formatting requires to annotate the field with - format:"date-time". Location: %s/%s`, structureName, field.Names)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
package coderdtest
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type SwaggerComment struct {
|
||||
summary string
|
||||
id string
|
||||
security string
|
||||
tags string
|
||||
accept string
|
||||
produce string
|
||||
|
||||
method string
|
||||
router string
|
||||
|
||||
successes []response
|
||||
failures []response
|
||||
|
||||
parameters []parameter
|
||||
|
||||
raw []*ast.Comment
|
||||
}
|
||||
|
||||
type parameter struct {
|
||||
name string
|
||||
kind string
|
||||
}
|
||||
|
||||
type response struct {
|
||||
status string
|
||||
kind string // {object} or {array}
|
||||
model string
|
||||
}
|
||||
|
||||
func ParseSwaggerComments(dirs ...string) ([]SwaggerComment, error) {
|
||||
fileSet := token.NewFileSet()
|
||||
|
||||
var swaggerComments []SwaggerComment
|
||||
for _, dir := range dirs {
|
||||
nodes, err := parser.ParseDir(fileSet, dir, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf(`parser.ParseDir failed for "%s": %w`, dir, err)
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
commentGroup, ok := n.(*ast.CommentGroup)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
var isSwaggerComment bool
|
||||
for _, line := range commentGroup.List {
|
||||
text := strings.TrimSpace(line.Text)
|
||||
if strings.HasPrefix(text, "//") && strings.Contains(text, "@Router") {
|
||||
isSwaggerComment = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isSwaggerComment {
|
||||
swaggerComments = append(swaggerComments, parseSwaggerComment(commentGroup))
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
return swaggerComments, nil
|
||||
}
|
||||
|
||||
func parseSwaggerComment(commentGroup *ast.CommentGroup) SwaggerComment {
|
||||
c := SwaggerComment{
|
||||
raw: commentGroup.List,
|
||||
parameters: []parameter{},
|
||||
successes: []response{},
|
||||
failures: []response{},
|
||||
}
|
||||
for _, line := range commentGroup.List {
|
||||
// @<annotationName> [args...]
|
||||
splitN := strings.SplitN(strings.TrimSpace(line.Text), " ", 3)
|
||||
if len(splitN) < 2 {
|
||||
continue // comment prefix without any content
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(splitN[1], "@") {
|
||||
continue // not a swagger annotation
|
||||
}
|
||||
|
||||
annotationName := splitN[1]
|
||||
annotationArgs := splitN[2]
|
||||
args := strings.Split(splitN[2], " ")
|
||||
|
||||
switch annotationName {
|
||||
case "@Router":
|
||||
c.router = args[0]
|
||||
c.method = args[1][1 : len(args[1])-1]
|
||||
case "@Success", "@Failure":
|
||||
var r response
|
||||
if len(args) > 0 {
|
||||
r.status = args[0]
|
||||
}
|
||||
if len(args) > 1 {
|
||||
r.kind = args[1]
|
||||
}
|
||||
if len(args) > 2 {
|
||||
r.model = args[2]
|
||||
}
|
||||
|
||||
if annotationName == "@Success" {
|
||||
c.successes = append(c.successes, r)
|
||||
} else if annotationName == "@Failure" {
|
||||
c.failures = append(c.failures, r)
|
||||
}
|
||||
case "@Param":
|
||||
p := parameter{
|
||||
name: args[0],
|
||||
kind: args[1],
|
||||
}
|
||||
c.parameters = append(c.parameters, p)
|
||||
case "@Summary":
|
||||
c.summary = annotationArgs
|
||||
case "@ID":
|
||||
c.id = annotationArgs
|
||||
case "@Tags":
|
||||
c.tags = annotationArgs
|
||||
case "@Security":
|
||||
c.security = annotationArgs
|
||||
case "@Accept":
|
||||
c.accept = annotationArgs
|
||||
case "@Produce":
|
||||
c.produce = annotationArgs
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments []SwaggerComment) {
|
||||
assertUniqueRoutes(t, swaggerComments)
|
||||
|
||||
err := chi.Walk(router, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||
method = strings.ToLower(method)
|
||||
if route != "/" && strings.HasSuffix(route, "/") {
|
||||
route = route[:len(route)-1]
|
||||
}
|
||||
|
||||
t.Run(method+" "+route, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := findSwaggerCommentByMethodAndRoute(swaggerComments, method, route)
|
||||
assert.NotNil(t, c, "Missing @Router annotation")
|
||||
if c == nil {
|
||||
return // do not fail next assertion for this route
|
||||
}
|
||||
|
||||
assertConsistencyBetweenRouteIDAndSummary(t, *c)
|
||||
assertSuccessOrFailureDefined(t, *c)
|
||||
assertRequiredAnnotations(t, *c)
|
||||
assertGoCommentFirst(t, *c)
|
||||
assertPathParametersDefined(t, *c)
|
||||
assertSecurityDefined(t, *c)
|
||||
assertAccept(t, *c)
|
||||
assertProduce(t, *c)
|
||||
})
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err, "chi.Walk should not fail")
|
||||
}
|
||||
|
||||
func assertUniqueRoutes(t *testing.T, comments []SwaggerComment) {
|
||||
m := map[string]struct{}{}
|
||||
|
||||
for _, c := range comments {
|
||||
key := c.method + " " + c.router
|
||||
_, alreadyDefined := m[key]
|
||||
assert.False(t, alreadyDefined, "defined route must be unique (method: %s, route: %s)", c.method, c.router)
|
||||
if !alreadyDefined {
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findSwaggerCommentByMethodAndRoute(comments []SwaggerComment, method, route string) *SwaggerComment {
|
||||
for _, c := range comments {
|
||||
if c.method == method && c.router == route {
|
||||
return &c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]+`)
|
||||
|
||||
func assertConsistencyBetweenRouteIDAndSummary(t *testing.T, comment SwaggerComment) {
|
||||
exp := strings.ToLower(comment.summary)
|
||||
exp = strings.ReplaceAll(exp, " ", "-")
|
||||
exp = nonAlphanumericRegex.ReplaceAllString(exp, "")
|
||||
|
||||
assert.Equal(t, exp, comment.id, "Router ID must match summary")
|
||||
}
|
||||
|
||||
func assertSuccessOrFailureDefined(t *testing.T, comment SwaggerComment) {
|
||||
assert.True(t, len(comment.successes) > 0 || len(comment.failures) > 0, "At least one @Success or @Failure annotation must be defined")
|
||||
}
|
||||
|
||||
func assertRequiredAnnotations(t *testing.T, comment SwaggerComment) {
|
||||
assert.NotEmpty(t, comment.id, "@ID must be defined")
|
||||
assert.NotEmpty(t, comment.summary, "@Summary must be defined")
|
||||
assert.NotEmpty(t, comment.tags, "@Tags must be defined")
|
||||
}
|
||||
|
||||
func assertGoCommentFirst(t *testing.T, comment SwaggerComment) {
|
||||
var inSwaggerBlock bool
|
||||
|
||||
for _, line := range comment.raw {
|
||||
text := strings.TrimSpace(line.Text)
|
||||
|
||||
if inSwaggerBlock {
|
||||
if !strings.HasPrefix(text, "// @") {
|
||||
assert.Fail(t, "Go function comment must be placed before swagger comments")
|
||||
return
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(text, "// @Summary") {
|
||||
inSwaggerBlock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var urlParameterRegexp = regexp.MustCompile(`{[^{}]*}`)
|
||||
|
||||
func assertPathParametersDefined(t *testing.T, comment SwaggerComment) {
|
||||
matches := urlParameterRegexp.FindAllString(comment.router, -1)
|
||||
if matches == nil {
|
||||
return // router does not require any parameters
|
||||
}
|
||||
|
||||
for _, m := range matches {
|
||||
var matched bool
|
||||
for _, p := range comment.parameters {
|
||||
if p.kind == "path" && "{"+p.name+"}" == m {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
assert.Failf(t, "Missing @Param annotation", "Path parameter: %s", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertSecurityDefined(t *testing.T, comment SwaggerComment) {
|
||||
if comment.router == "/updatecheck" ||
|
||||
comment.router == "/buildinfo" ||
|
||||
comment.router == "/" {
|
||||
return // endpoints do not require authorization
|
||||
}
|
||||
assert.Equal(t, "CoderSessionToken", comment.security, "@Security must be equal CoderSessionToken")
|
||||
}
|
||||
|
||||
func assertAccept(t *testing.T, comment SwaggerComment) {
|
||||
var hasRequestBody bool
|
||||
for _, c := range comment.parameters {
|
||||
if c.name == "request" && c.kind == "body" ||
|
||||
c.name == "file" && c.kind == "formData" {
|
||||
hasRequestBody = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var hasAccept bool
|
||||
if comment.accept != "" {
|
||||
hasAccept = true
|
||||
}
|
||||
|
||||
if comment.method == "get" {
|
||||
assert.Empty(t, comment.accept, "GET route does not require the @Accept annotation")
|
||||
assert.False(t, hasRequestBody, "GET route does not require the request body")
|
||||
} else {
|
||||
assert.False(t, hasRequestBody && !hasAccept, "Route with the request body requires the @Accept annotation")
|
||||
assert.False(t, !hasRequestBody && hasAccept, "Route with @Accept annotation requires the request body or file formData parameter")
|
||||
}
|
||||
}
|
||||
|
||||
func assertProduce(t *testing.T, comment SwaggerComment) {
|
||||
var hasResponseModel bool
|
||||
for _, r := range comment.successes {
|
||||
if r.model != "" {
|
||||
hasResponseModel = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasResponseModel {
|
||||
assert.True(t, comment.produce != "", "Route must have @Produce annotation as it responds with a model structure")
|
||||
} else {
|
||||
if (comment.router == "/workspaceagents/me/app-health" && comment.method == "post") ||
|
||||
(comment.router == "/workspaceagents/me/version" && comment.method == "post") ||
|
||||
(comment.router == "/licenses/{id}" && comment.method == "delete") {
|
||||
return // Exception: HTTP 200 is returned without response entity
|
||||
}
|
||||
|
||||
assert.True(t, comment.produce == "", "Response model is undefined, so we can't predict the content type", comment)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ type cspViolation struct {
|
||||
// @ID report-csp-violations
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce text/plain
|
||||
// @Tags General
|
||||
// @Param request body cspViolation true "Violation report"
|
||||
// @Success 200
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ const (
|
||||
|
||||
// @Summary Upload file
|
||||
// @Description Swagger notice: Swagger 2.0 doesn't support file upload with a `content-type` different than `application/x-www-form-urlencoded`.
|
||||
// @ID update-file
|
||||
// @ID upload-file
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept application/x-tar
|
||||
|
||||
@@ -119,7 +119,6 @@ func (api *API) gitSSHKey(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Summary Get workspace agent Git SSH key
|
||||
// @ID get-workspace-agent-git-ssh-key
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Success 200 {object} codersdk.AgentGitSSHKey
|
||||
|
||||
@@ -40,6 +40,7 @@ func (api *API) organization(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Summary Create organization
|
||||
// @ID create-organization
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Organizations
|
||||
// @Param request body codersdk.CreateOrganizationRequest true "Create organization request"
|
||||
|
||||
+14
-12
@@ -23,15 +23,16 @@ import (
|
||||
"github.com/coder/coder/examples"
|
||||
)
|
||||
|
||||
// Returns a single template.
|
||||
//
|
||||
// @Summary Get template metadata by ID
|
||||
// @ID get-template-metadata-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Template
|
||||
// @Router /templates/{id} [get]
|
||||
// Returns a single template.
|
||||
// @Router /templates/{template} [get]
|
||||
func (api *API) template(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
template := httpmw.TemplateParam(r)
|
||||
@@ -75,9 +76,9 @@ func (api *API) template(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /templates/{id} [delete]
|
||||
// @Router /templates/{template} [delete]
|
||||
func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -131,6 +132,9 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// Create a new template in an organization.
|
||||
// Returns a single template.
|
||||
//
|
||||
// @Summary Create template by organization
|
||||
// @ID create-template-by-organization
|
||||
// @Security CoderSessionToken
|
||||
@@ -141,8 +145,6 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Param organization path string true "Organization ID"
|
||||
// @Success 200 {object} codersdk.Template
|
||||
// @Router /organizations/{organization}/templates [post]
|
||||
// Returns a single template.
|
||||
// Create a new template in an organization.
|
||||
func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -461,13 +463,13 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
|
||||
}
|
||||
|
||||
// @Summary Update template metadata by ID
|
||||
// @ID update-template-metadata
|
||||
// @ID update-template-metadata-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Template
|
||||
// @Router /templates/{id} [patch]
|
||||
// @Router /templates/{template} [patch]
|
||||
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -593,9 +595,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.TemplateDAUsResponse
|
||||
// @Router /templates/{id}/daus [get]
|
||||
// @Router /templates/{template}/daus [get]
|
||||
func (api *API) templateDAUs(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
template := httpmw.TemplateParam(r)
|
||||
|
||||
+29
-29
@@ -353,13 +353,12 @@ func (api *API) postTemplateVersionDryRun(rw http.ResponseWriter, r *http.Reques
|
||||
// @Summary Get template version dry-run by job ID
|
||||
// @ID get-template-version-dry-run-by-job-id
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param templateversion path string true "Template version ID" format(uuid)
|
||||
// @Param jobid path string true "Job ID" format(uuid)
|
||||
// @Param jobID path string true "Job ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.ProvisionerJob
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobid} [get]
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobID} [get]
|
||||
func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
|
||||
@@ -376,9 +375,9 @@ func (api *API) templateVersionDryRun(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param templateversion path string true "Template version ID" format(uuid)
|
||||
// @Param jobid path string true "Job ID" format(uuid)
|
||||
// @Param jobID path string true "Job ID" format(uuid)
|
||||
// @Success 200 {array} codersdk.WorkspaceResource
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobid}/resources [get]
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobID}/resources [get]
|
||||
func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.Request) {
|
||||
job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
|
||||
if !ok {
|
||||
@@ -394,12 +393,12 @@ func (api *API) templateVersionDryRunResources(rw http.ResponseWriter, r *http.R
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param templateversion path string true "Template version ID" format(uuid)
|
||||
// @Param jobid path string true "Job ID" format(uuid)
|
||||
// @Param jobID path string true "Job ID" format(uuid)
|
||||
// @Param before query int false "Before Unix timestamp"
|
||||
// @Param after query int false "After Unix timestamp"
|
||||
// @Param follow query bool false "Follow log stream"
|
||||
// @Success 200 {array} codersdk.ProvisionerJobLog
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobid}/logs [get]
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobID}/logs [get]
|
||||
func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
job, ok := api.fetchTemplateVersionDryRunJob(rw, r)
|
||||
if !ok {
|
||||
@@ -414,9 +413,10 @@ func (api *API) templateVersionDryRunLogs(rw http.ResponseWriter, r *http.Reques
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param jobID path string true "Job ID" format(uuid)
|
||||
// @Param templateversion path string true "Template version ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobid}/cancel [patch]
|
||||
// @Router /templateversions/{templateversion}/dry-run/{jobID}/cancel [patch]
|
||||
func (api *API) patchTemplateVersionDryRunCancel(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
templateVersion := httpmw.TemplateVersionParam(r)
|
||||
@@ -536,16 +536,16 @@ func (api *API) fetchTemplateVersionDryRunJob(rw http.ResponseWriter, r *http.Re
|
||||
}
|
||||
|
||||
// @Summary List template versions by template ID
|
||||
// @ID list-template-versions-by-template-ID
|
||||
// @ID list-template-versions-by-template-id
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Param after_id query string false "After ID" format(uuid)
|
||||
// @Param limit query int false "Page limit"
|
||||
// @Param offset query int false "Page offset"
|
||||
// @Success 200 {array} codersdk.TemplateVersion
|
||||
// @Router /templates/{id}/versions [get]
|
||||
// @Router /templates/{template}/versions [get]
|
||||
func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
template := httpmw.TemplateParam(r)
|
||||
@@ -648,10 +648,10 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Param templateversionname path string true "Template version name"
|
||||
// @Success 200 {array} codersdk.TemplateVersion
|
||||
// @Router /templates/{id}/versions/{templateversionname} [get]
|
||||
// @Router /templates/{template}/versions/{templateversionname} [get]
|
||||
func (api *API) templateVersionByName(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
template := httpmw.TemplateParam(r)
|
||||
@@ -828,15 +828,15 @@ func (api *API) previousTemplateVersionByOrganizationAndName(rw http.ResponseWri
|
||||
}
|
||||
|
||||
// @Summary Update active template version by template ID
|
||||
// @ID update-active-template-version-by-template-ID
|
||||
// @ID update-active-template-version-by-template-id
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Templates
|
||||
// @Param request body codersdk.UpdateActiveTemplateVersion true "Modified template version"
|
||||
// @Param id path string true "Template ID" format(uuid)
|
||||
// @Param template path string true "Template ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /templates/{id}/versions [patch]
|
||||
// @Router /templates/{template}/versions [patch]
|
||||
func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -911,6 +911,8 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
||||
})
|
||||
}
|
||||
|
||||
// postTemplateVersionsByOrganization creates a new version of a template. An import job is queued to parse the storage method provided.
|
||||
//
|
||||
// @Summary Create template version by organization
|
||||
// @ID create-template-version-by-organization
|
||||
// @Security CoderSessionToken
|
||||
@@ -921,8 +923,6 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
|
||||
// @Param request body codersdk.CreateTemplateVersionDryRunRequest true "Create template version request"
|
||||
// @Success 201 {object} codersdk.TemplateVersion
|
||||
// @Router /organizations/{organization}/templateversions [post]
|
||||
//
|
||||
// postTemplateVersionsByOrganization creates a new version of a template. An import job is queued to parse the storage method provided.
|
||||
func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -1207,6 +1207,12 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
||||
httpapi.Write(ctx, rw, http.StatusCreated, convertTemplateVersion(templateVersion, convertProvisionerJob(provisionerJob), user))
|
||||
}
|
||||
|
||||
// templateVersionResources returns the workspace agent resources associated
|
||||
// with a template version. A template can specify more than one resource to be
|
||||
// provisioned, each resource can have an agent that dials back to coderd. The
|
||||
// agents returned are informative of the template version, and do not return
|
||||
// agents associated with any particular workspace.
|
||||
//
|
||||
// @Summary Get resources by template version
|
||||
// @ID get-resources-by-template-version
|
||||
// @Security CoderSessionToken
|
||||
@@ -1215,12 +1221,6 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
|
||||
// @Param templateversion path string true "Template version ID" format(uuid)
|
||||
// @Success 200 {array} codersdk.WorkspaceResource
|
||||
// @Router /templateversions/{templateversion}/resources [get]
|
||||
//
|
||||
// templateVersionResources returns the workspace agent resources associated
|
||||
// with a template version. A template can specify more than one resource to be
|
||||
// provisioned, each resource can have an agent that dials back to coderd. The
|
||||
// agents returned are informative of the template version, and do not return
|
||||
// agents associated with any particular workspace.
|
||||
func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -1244,6 +1244,11 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
|
||||
api.provisionerJobResources(rw, r, job)
|
||||
}
|
||||
|
||||
// templateVersionLogs returns the logs returned by the provisioner for the given
|
||||
// template version. These logs are only associated with the template version,
|
||||
// and not any build logs for a workspace.
|
||||
// Eg: Logs returned from 'terraform plan' when uploading a new terraform file.
|
||||
//
|
||||
// @Summary Get logs by template version
|
||||
// @ID get-logs-by-template-version
|
||||
// @Security CoderSessionToken
|
||||
@@ -1255,11 +1260,6 @@ func (api *API) templateVersionResources(rw http.ResponseWriter, r *http.Request
|
||||
// @Param follow query bool false "Follow log stream"
|
||||
// @Success 200 {array} codersdk.ProvisionerJobLog
|
||||
// @Router /templateversions/{templateversion}/logs [get]
|
||||
//
|
||||
// templateVersionLogs returns the logs returned by the provisioner for the given
|
||||
// template version. These logs are only associated with the template version,
|
||||
// and not any build logs for a workspace.
|
||||
// Eg: Logs returned from 'terraform plan' when uploading a new terraform file.
|
||||
func (api *API) templateVersionLogs(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
|
||||
+2
-4
@@ -59,9 +59,8 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// @Summary OAuth 2.0 GitHub Callback
|
||||
// @ID oauth2-github-callback
|
||||
// @ID oauth-20-github-callback
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
// @Success 307
|
||||
// @Router /users/oauth2/github/callback [get]
|
||||
@@ -218,9 +217,8 @@ type OIDCConfig struct {
|
||||
}
|
||||
|
||||
// @Summary OpenID Connect Callback
|
||||
// @ID oidc-callback
|
||||
// @ID openid-connect-callback
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
// @Success 307
|
||||
// @Router /users/oidc/callback [get]
|
||||
|
||||
+4
-2
@@ -435,6 +435,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Summary Update user profile
|
||||
// @ID update-user-profile
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
// @Param user path string true "User ID, name, or me"
|
||||
@@ -617,7 +618,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
|
||||
// @Summary Update user password
|
||||
// @ID update-user-password
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Accept json
|
||||
// @Tags Users
|
||||
// @Param user path string true "User ID, name, or me"
|
||||
// @Param request body codersdk.UpdateUserPasswordRequest true "Update password request"
|
||||
@@ -908,7 +909,7 @@ func (api *API) updateSiteUserRoles(ctx context.Context, args database.UpdateUse
|
||||
// Returns organizations the parameterized user has access to.
|
||||
//
|
||||
// @Summary Get organizations by user
|
||||
// @ID get-organizations-by-users
|
||||
// @ID get-organizations-by-user
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Users
|
||||
@@ -990,6 +991,7 @@ func (api *API) organizationByUserAndName(rw http.ResponseWriter, r *http.Reques
|
||||
// @Summary Log in user
|
||||
// @ID log-in-user
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Authorization
|
||||
// @Param request body codersdk.LoginWithPasswordRequest true "Login request"
|
||||
|
||||
@@ -35,6 +35,14 @@ import (
|
||||
"github.com/coder/coder/tailnet"
|
||||
)
|
||||
|
||||
// @Summary Get workspace agent by ID
|
||||
// @ID get-workspace-agent-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.WorkspaceAgent
|
||||
// @Router /workspaceagents/{workspaceagent} [get]
|
||||
func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgentParam(r)
|
||||
@@ -66,7 +74,6 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Summary Get authorized workspace agent metadata
|
||||
// @ID get-authorized-workspace-agent-metadata
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Success 200 {object} codersdk.WorkspaceAgentMetadata
|
||||
@@ -147,9 +154,10 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent version
|
||||
// @ID submit-workspace-workspace-agent-version
|
||||
// @ID submit-workspace-agent-version
|
||||
// @Security CoderSessionToken
|
||||
// @Produce application/json
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.PostWorkspaceAgentVersionRequest true "Version request"
|
||||
// @Success 200
|
||||
@@ -198,6 +206,14 @@ func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Reques
|
||||
|
||||
// workspaceAgentPTY spawns a PTY and pipes it over a WebSocket.
|
||||
// This is used for the web terminal.
|
||||
//
|
||||
// @Summary Open PTY to workspace agent
|
||||
// @ID open-pty-to-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Success 101
|
||||
// @Router /workspaceagents/{workspaceagent}/pty [get]
|
||||
func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -276,6 +292,14 @@ func (api *API) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
||||
agent.Bicopy(ctx, wsNetConn, ptNetConn)
|
||||
}
|
||||
|
||||
// @Summary Get listening ports for workspace agent
|
||||
// @ID get-listening-ports-for-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.ListeningPortsResponse
|
||||
// @Router /workspaceagents/{workspaceagent}/listening-ports [get]
|
||||
func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
@@ -443,6 +467,14 @@ func (api *API) dialWorkspaceAgentTailnet(r *http.Request, agentID uuid.UUID) (*
|
||||
}, nil
|
||||
}
|
||||
|
||||
// @Summary Get connection info for workspace agent
|
||||
// @ID get-connection-info-for-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.WorkspaceAgentConnectionInfo
|
||||
// @Router /workspaceagents/{workspaceagent}/connection [get]
|
||||
func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
@@ -458,9 +490,8 @@ func (api *API) workspaceAgentConnection(rw http.ResponseWriter, r *http.Request
|
||||
// @Summary Coordinate workspace agent via Tailnet
|
||||
// @Description It accepts a WebSocket connection to an agent that listens to
|
||||
// @Description incoming connections and publishes node updates.
|
||||
// @ID get-workspace-agent-git-ssh-key-via-tailnet
|
||||
// @ID coordinate-workspace-agent-via-tailnet
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Success 101
|
||||
// @Router /workspaceagents/me/coordinate [get]
|
||||
@@ -622,6 +653,14 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
|
||||
// workspaceAgentClientCoordinate accepts a WebSocket that reads node network updates.
|
||||
// After accept a PubSub starts listening for new connection node updates
|
||||
// which are written to the WebSocket.
|
||||
//
|
||||
// @Summary Coordinate workspace agent
|
||||
// @ID coordinate-workspace-agent
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Agents
|
||||
// @Param workspaceagent path string true "Workspace agent ID" format(uuid)
|
||||
// @Success 101
|
||||
// @Router /workspaceagents/{workspaceagent}/coordinate [get]
|
||||
func (api *API) workspaceAgentClientCoordinate(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -784,8 +823,9 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent stats
|
||||
// @ID submit-workspace-workspace-agent-stats
|
||||
// @ID submit-workspace-agent-stats
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce application/json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.AgentStats true "Stats request"
|
||||
@@ -860,9 +900,10 @@ func (api *API) workspaceAgentReportStats(rw http.ResponseWriter, r *http.Reques
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Submit workspace application health
|
||||
// @ID submit-workspace-workspace-agent-health
|
||||
// @Summary Submit workspace agent application health
|
||||
// @ID submit-workspace-agent-application-health
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce application/json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.PostWorkspaceAppHealthsRequest true "Application health request"
|
||||
@@ -989,7 +1030,6 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request)
|
||||
// @Summary Get workspace agent Git auth
|
||||
// @ID get-workspace-agent-git-auth
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param url query string true "Git URL" format(uri)
|
||||
|
||||
@@ -66,7 +66,7 @@ var nonCanonicalHeaders = map[string]string{
|
||||
}
|
||||
|
||||
// @Summary Get applications host
|
||||
// @ID get-app-host
|
||||
// @ID get-applications-host
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Applications
|
||||
@@ -614,6 +614,13 @@ func (api *API) setWorkspaceAppCookie(rw http.ResponseWriter, r *http.Request, t
|
||||
return true
|
||||
}
|
||||
|
||||
// workspaceApplicationAuth is an endpoint on the main router that handles
|
||||
// redirects from the subdomain handler.
|
||||
//
|
||||
// This endpoint is under /api so we don't return the friendly error page here.
|
||||
// Any errors on this endpoint should be errors that are unlikely to happen
|
||||
// in production unless the user messes with the URL.
|
||||
//
|
||||
// @Summary Redirect to URI with encrypted API key
|
||||
// @ID redirect-to-uri-with-encrypted-api-key
|
||||
// @Security CoderSessionToken
|
||||
@@ -621,13 +628,6 @@ func (api *API) setWorkspaceAppCookie(rw http.ResponseWriter, r *http.Request, t
|
||||
// @Param redirect_uri query string false "Redirect destination"
|
||||
// @Success 307
|
||||
// @Router /applications/auth-redirect [get]
|
||||
//
|
||||
// workspaceApplicationAuth is an endpoint on the main router that handles
|
||||
// redirects from the subdomain handler.
|
||||
//
|
||||
// This endpoint is under /api so we don't return the friendly error page here.
|
||||
// Any errors on this endpoint should be errors that are unlikely to happen
|
||||
// in production unless the user messes with the URL.
|
||||
func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
if api.AppHostname == "" {
|
||||
|
||||
@@ -77,13 +77,13 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Builds
|
||||
// @Param id path string true "Workspace ID" format(uuid)
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param after_id query string false "After ID" format(uuid)
|
||||
// @Param limit query int false "Page limit"
|
||||
// @Param offset query int false "Page offset"
|
||||
// @Param since query string false "Since timestamp" format(date-time)
|
||||
// @Success 200 {array} codersdk.WorkspaceBuild
|
||||
// @Router /workspaces/{id}/builds [get]
|
||||
// @Router /workspaces/{workspace}/builds [get]
|
||||
func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
@@ -290,13 +290,13 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
|
||||
// @Summary Create workspace build
|
||||
// @ID create-workspace-build
|
||||
// @Security CoderSessionToken
|
||||
// @Accepts json
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Builds
|
||||
// @Param id path string true "Workspace ID" format(uuid)
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param request body codersdk.CreateWorkspaceBuildRequest true "Create workspace build request"
|
||||
// @Success 200 {object} codersdk.WorkspaceBuild
|
||||
// @Router /workspaces/{id}/builds [post]
|
||||
// @Router /workspaces/{workspace}/builds [post]
|
||||
func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
// @Summary Authenticate agent on Azure instance
|
||||
// @ID authenticate-agent-on-azure-instance
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.AzureInstanceIdentityToken true "Instance identity token"
|
||||
@@ -45,18 +46,19 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r
|
||||
api.handleAuthInstanceID(rw, r, instanceID)
|
||||
}
|
||||
|
||||
// AWS supports instance identity verification:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
//
|
||||
// @Summary Authenticate agent on AWS instance
|
||||
// @ID authenticate-agent-on-aws-instance
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.AWSInstanceIdentityToken true "Instance identity token"
|
||||
// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse
|
||||
// @Router /workspaceagents/aws-instance-identity [post]
|
||||
//
|
||||
// AWS supports instance identity verification:
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var req codersdk.AWSInstanceIdentityToken
|
||||
@@ -74,18 +76,19 @@ func (api *API) postWorkspaceAuthAWSInstanceIdentity(rw http.ResponseWriter, r *
|
||||
api.handleAuthInstanceID(rw, r, identity.InstanceID)
|
||||
}
|
||||
|
||||
// Google Compute Engine supports instance identity verification:
|
||||
// https://cloud.google.com/compute/docs/instances/verifying-instance-identity
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
//
|
||||
// @Summary Authenticate agent on Google Cloud instance
|
||||
// @ID authenticate-agent-on-google-cloud-instance
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body codersdk.GoogleInstanceIdentityToken true "Instance identity token"
|
||||
// @Success 200 {object} codersdk.WorkspaceAgentAuthenticateResponse
|
||||
// @Router /workspaceagents/google-instance-identity [post]
|
||||
//
|
||||
// Google Compute Engine supports instance identity verification:
|
||||
// https://cloud.google.com/compute/docs/instances/verifying-instance-identity
|
||||
// Using this, we can exchange a signed instance payload for an agent token.
|
||||
func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var req codersdk.GoogleInstanceIdentityToken
|
||||
|
||||
+9
-12
@@ -48,10 +48,10 @@ var (
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
// @Param id path string true "Workspace ID" format(uuid)
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param include_deleted query bool false "Return data instead of HTTP 404 if the workspace is deleted"
|
||||
// @Success 200 {object} codersdk.Workspace
|
||||
// @Router /workspaces/{id} [get]
|
||||
// @Router /workspaces/{workspace} [get]
|
||||
func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspace := httpmw.WorkspaceParam(r)
|
||||
@@ -101,8 +101,11 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
))
|
||||
}
|
||||
|
||||
// workspaces returns all workspaces a user can read.
|
||||
// Optional filters with query params
|
||||
//
|
||||
// @Summary List workspaces
|
||||
// @ID get-workspaces
|
||||
// @ID list-workspaces
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
@@ -113,9 +116,6 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Param has_agent query string false "Filter by agent status" Enums(connected,connecting,disconnected,timeout)
|
||||
// @Success 200 {object} codersdk.WorkspacesResponse
|
||||
// @Router /workspaces [get]
|
||||
//
|
||||
// workspaces returns all workspaces a user can read.
|
||||
// Optional filters with query params
|
||||
func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -266,6 +266,8 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
))
|
||||
}
|
||||
|
||||
// Create a new workspace for the currently authenticated user.
|
||||
//
|
||||
// @Summary Create user workspace by organization
|
||||
// @ID create-user-workspace-by-organization
|
||||
// @Security CoderSessionToken
|
||||
@@ -275,8 +277,6 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
// @Param user path string true "Username, UUID, or me"
|
||||
// @Success 200 {object} codersdk.Workspace
|
||||
// @Router /organizations/{organization}/members/{user}/workspaces [post]
|
||||
//
|
||||
// Create a new workspace for the currently authenticated user.
|
||||
func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
@@ -558,7 +558,6 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
|
||||
// @ID update-workspace-metadata-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param request body codersdk.UpdateWorkspaceRequest true "Metadata update request"
|
||||
@@ -648,7 +647,6 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) {
|
||||
// @ID update-workspace-autostart-schedule-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param request body codersdk.UpdateWorkspaceAutostartRequest true "Schedule update request"
|
||||
@@ -711,7 +709,6 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
|
||||
// @ID update-workspace-ttl-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Workspaces
|
||||
// @Param workspace path string true "Workspace ID" format(uuid)
|
||||
// @Param request body codersdk.UpdateWorkspaceTTLRequest true "Workspace TTL update request"
|
||||
@@ -875,7 +872,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// @Summary Watch workspace by ID
|
||||
// @ID watch-workspace-id
|
||||
// @ID watch-workspace-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Produce text/event-stream
|
||||
// @Tags Workspaces
|
||||
|
||||
Reference in New Issue
Block a user