Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/wsbuilder"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/coderd/wsrelated"
"github.com/coder/coder/v2/codersdk"
)

Expand All @@ -51,7 +52,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
workspaceBuild := httpmw.WorkspaceBuildParam(r)
workspace := httpmw.WorkspaceParam(r)

data, err := api.workspaceBuildsData(ctx, []database.WorkspaceBuild{workspaceBuild}, allLatestBuildRelated())
data, err := api.workspaceBuildsData(ctx, []database.WorkspaceBuild{workspaceBuild}, wsrelated.AllLatestBuild())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error getting workspace build data.",
Expand Down Expand Up @@ -190,7 +191,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}

data, err := api.workspaceBuildsData(ctx, workspaceBuilds, allLatestBuildRelated())
data, err := api.workspaceBuildsData(ctx, workspaceBuilds, wsrelated.AllLatestBuild())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error getting workspace build data.",
Expand Down Expand Up @@ -281,7 +282,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
return
}

data, err := api.workspaceBuildsData(ctx, []database.WorkspaceBuild{workspaceBuild}, allLatestBuildRelated())
data, err := api.workspaceBuildsData(ctx, []database.WorkspaceBuild{workspaceBuild}, wsrelated.AllLatestBuild())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error getting workspace build data.",
Expand Down Expand Up @@ -1117,7 +1118,7 @@ type workspaceBuildsData struct {
// computed with window functions over pending jobs and provisioner daemons and
// are comparatively expensive. Otherwise, it uses the cheaper
// GetProvisionerJobsByIDs and leaves QueuePosition and QueueSize zero.
func (api *API) provisionerJobsByIDs(ctx context.Context, jobIDs []uuid.UUID, cfg jobRelated) ([]database.GetProvisionerJobsByIDsWithQueuePositionRow, error) {
func (api *API) provisionerJobsByIDs(ctx context.Context, jobIDs []uuid.UUID, cfg wsrelated.Job) ([]database.GetProvisionerJobsByIDsWithQueuePositionRow, error) {
if cfg.QueuePosition {
return api.Database.GetProvisionerJobsByIDsWithQueuePosition(ctx, database.GetProvisionerJobsByIDsWithQueuePositionParams{
IDs: jobIDs,
Expand All @@ -1140,7 +1141,7 @@ func (api *API) provisionerJobsByIDs(ctx context.Context, jobIDs []uuid.UUID, cf
return jobs, nil
}

func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []database.WorkspaceBuild, cfg latestBuildRelated) (workspaceBuildsData, error) {
func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []database.WorkspaceBuild, cfg wsrelated.LatestBuild) (workspaceBuildsData, error) {
jobIDs := make([]uuid.UUID, 0, len(workspaceBuilds))
for _, build := range workspaceBuilds {
jobIDs = append(jobIDs, build.JobID)
Expand Down Expand Up @@ -1290,7 +1291,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []datab
}

var statuses []database.WorkspaceAppStatus
if cfg.appStatuses() {
if cfg.AppStatuses() {
appIDs := make([]uuid.UUID, 0)
for _, app := range apps {
appIDs = append(appIDs, app.ID)
Expand Down
15 changes: 8 additions & 7 deletions coderd/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/coderd/wsbuilder"
"github.com/coder/coder/v2/coderd/wspubsub"
"github.com/coder/coder/v2/coderd/wsrelated"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/codersdk/wsjson"
Expand Down Expand Up @@ -96,7 +97,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
return
}

data, err := api.workspaceData(ctx, []database.Workspace{workspace}, allWorkspaceRelated())
data, err := api.workspaceData(ctx, []database.Workspace{workspace}, wsrelated.All())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace resources.",
Expand Down Expand Up @@ -224,7 +225,7 @@ func (api *API) workspaces(rw http.ResponseWriter, r *http.Request) {
return
}

data, err := api.workspaceData(ctx, workspaces, allWorkspaceRelated())
data, err := api.workspaceData(ctx, workspaces, wsrelated.All())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace resources.",
Expand Down Expand Up @@ -313,7 +314,7 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
return
}

data, err := api.workspaceData(ctx, []database.Workspace{workspace}, allWorkspaceRelated())
data, err := api.workspaceData(ctx, []database.Workspace{workspace}, wsrelated.All())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace resources.",
Expand Down Expand Up @@ -1570,7 +1571,7 @@ func (api *API) putWorkspaceDormant(rw http.ResponseWriter, r *http.Request) {
return
}

data, err := api.workspaceData(ctx, []database.Workspace{workspace}, allWorkspaceRelated())
data, err := api.workspaceData(ctx, []database.Workspace{workspace}, wsrelated.All())
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace resources.",
Expand Down Expand Up @@ -2148,7 +2149,7 @@ func (api *API) watchWorkspace(
return
}

data, err := api.workspaceData(ctx, []database.Workspace{workspace}, allWorkspaceRelated())
data, err := api.workspaceData(ctx, []database.Workspace{workspace}, wsrelated.All())
if err != nil {
_ = sendEvent(codersdk.ServerSentEvent{
Type: codersdk.ServerSentEventTypeError,
Expand Down Expand Up @@ -2687,7 +2688,7 @@ func (api *API) allowWorkspaceSharing(ctx context.Context, rw http.ResponseWrite
// does not have the correct perms to read a given template, the template will
// not be returned.
// So the caller must check the templates & users exist before using them.
func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspace, cfg workspaceRelated) (workspaceData, error) {
func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspace, cfg wsrelated.Config) (workspaceData, error) {
workspaceIDs := make([]uuid.UUID, 0, len(workspaces))
templateIDs := make([]uuid.UUID, 0, len(workspaces))
for _, workspace := range workspaces {
Expand Down Expand Up @@ -2723,7 +2724,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa
return nil
})
}
if cfg.LatestBuild.appStatuses() {
if cfg.LatestBuild.AppStatuses() {
eg.Go(func() (err error) {
// This query must be run as system restricted to be efficient.
// nolint:gocritic
Expand Down
179 changes: 179 additions & 0 deletions coderd/wsrelated/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package wsrelated

import (
"strings"

"golang.org/x/xerrors"
)

// schema holds the root nodes of the include_related hierarchy. It is built once
// in init rather than on every Parse call, since it is immutable.
var schema []relatedNode

func init() {
schema = buildSchema()
}

// relatedNode describes one node in the related-data hierarchy for the purpose
// of parsing the include_related query parameter. selectInto marks this node as
// selected in the target tree; via the ensure* helpers it also creates the
// node's ancestors, so selecting a node implicitly selects its ancestors.
type relatedNode struct {
name string
selectInto func(*Config)
children []relatedNode
}

// buildSchema constructs the include_related hierarchy. Each node's name matches
// a segment in a dotted path, and its selectInto closure mirrors the
// corresponding node in Config.
func buildSchema() []relatedNode {
return []relatedNode{
{
name: "template",
selectInto: func(r *Config) { r.Template = true },
},
{
name: "latest_build",
selectInto: func(r *Config) { r.ensureLatestBuild() },
children: []relatedNode{
{
name: "job",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureJob() },
children: []relatedNode{
{
name: "queue_position",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureJob().QueuePosition = true },
},
},
},
{
name: "resources",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources() },
children: []relatedNode{
{
name: "metadata",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().Metadata = true },
},
{
name: "agents",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().ensureAgents() },
children: []relatedNode{
{
name: "apps",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().ensureAgents().ensureApps() },
children: []relatedNode{
{
name: "statuses",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().ensureAgents().ensureApps().Statuses = true },
},
},
},
{
name: "scripts",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().ensureAgents().Scripts = true },
},
{
name: "log_sources",
selectInto: func(r *Config) { r.ensureLatestBuild().ensureResources().ensureAgents().LogSources = true },
},
},
},
},
},
{
name: "template_version",
selectInto: func(r *Config) { r.ensureLatestBuild().TemplateVersion = true },
},
},
},
}
}

// Parse parses the include_related query parameter: a comma-separated list of
// dotted hierarchy paths, e.g. "template,latest_build.resources.agents.*". Each
// path may end in a single wildcard "*" that selects the node and all of its
// descendants; "*" on its own selects everything. Selecting a node implicitly
// selects its ancestors.
//
// Surrounding whitespace and empty entries are ignored. An empty string selects
// nothing but the workspace itself; callers that want the RFC's "absent
// parameter means everything" behavior should use All when the parameter is not
// present. An unknown path or a wildcard that is not the final segment returns
// an error.
func Parse(includeRelated string) (Config, error) {
var result Config

tokens := strings.Split(includeRelated, ",")
for i, raw := range tokens {
token := strings.TrimSpace(raw)
if token == "" {
// Reject empty entries such as "a,,b" or ", ,", but allow a single
// optional trailing comma (an empty final element).
if i == len(tokens)-1 {
continue
}
return Config{}, xerrors.New("empty include_related path")
}

if token == "*" {
for _, n := range schema {
selectSubtree(n, &result)
}
continue
}

path := token
wildcard := false
if strings.HasSuffix(path, ".*") {
wildcard = true
path = strings.TrimSuffix(path, ".*")
}

segments := strings.Split(path, ".")
for _, s := range segments {
if s == "" || s == "*" {
return Config{}, xerrors.Errorf("invalid include_related path %q", token)
}
}

node, ok := findRelatedNode(schema, segments)
if !ok {
return Config{}, xerrors.Errorf("unknown include_related path %q", token)
}

if wildcard {
selectSubtree(node, &result)
} else {
node.selectInto(&result)
}
}

return result, nil
}

// findRelatedNode walks the schema following the dotted path segments, returning
// the matching node.
func findRelatedNode(nodes []relatedNode, segments []string) (relatedNode, bool) {
if len(segments) == 0 {
return relatedNode{}, false
}
for _, n := range nodes {
if n.name != segments[0] {
continue
}
if len(segments) == 1 {
return n, true
}
return findRelatedNode(n.children, segments[1:])
}
return relatedNode{}, false
}

// selectSubtree selects a node and all of its descendants.
func selectSubtree(n relatedNode, r *Config) {
n.selectInto(r)
for _, c := range n.children {
selectSubtree(c, r)
}
}
Loading
Loading