Skip to content

Commit 7167fa0

Browse files
committed
Refactor parameter parsing to return nil values if none computed
1 parent 07fe5ce commit 7167fa0

16 files changed

+441
-485
lines changed

cli/projectcreate.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cli
33
import (
44
"archive/tar"
55
"bytes"
6-
"context"
76
"fmt"
87
"io"
98
"os"
@@ -91,7 +90,7 @@ func projectCreate() *cobra.Command {
9190
}
9291
spin.Stop()
9392

94-
logs, err := client.FollowProvisionerJobLogsAfter(context.Background(), organization.Name, job.ID, time.Time{})
93+
logs, err := client.FollowProvisionerJobLogsAfter(cmd.Context(), organization.Name, job.ID, time.Time{})
9594
if err != nil {
9695
return err
9796
}
@@ -103,6 +102,15 @@ func projectCreate() *cobra.Command {
103102
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", color.HiGreenString("[parse]"), log.Output)
104103
}
105104

105+
schemas, err := client.ProvisionerJobParameterSchemas(cmd.Context(), organization.Name, job.ID)
106+
if err != nil {
107+
return err
108+
}
109+
110+
for _, schema := range schemas {
111+
fmt.Printf("Schema: %+v\n", schema)
112+
}
113+
106114
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Create project %q!\n", name)
107115
return nil
108116
},

coderd/coderd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ func New(options *Options) http.Handler {
7373
r.Route("/{projectversion}", func(r chi.Router) {
7474
r.Use(httpmw.ExtractProjectVersionParam(api.Database))
7575
r.Get("/", api.projectVersionByOrganizationAndName)
76-
r.Get("/parameters", api.projectVersionParametersByOrganizationAndName)
7776
})
7877
})
7978
})
@@ -122,6 +121,7 @@ func New(options *Options) http.Handler {
122121
r.Route("/{provisionerjob}", func(r chi.Router) {
123122
r.Use(httpmw.ExtractProvisionerJobParam(options.Database))
124123
r.Get("/", api.provisionerJobByOrganization)
124+
r.Get("/parameters", api.provisionerJobParametersByID)
125125
r.Get("/logs", api.provisionerJobLogsByID)
126126
})
127127
})

coderd/parameter/parameter.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package parameter
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"errors"
7+
8+
"github.com/google/uuid"
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/database"
12+
)
13+
14+
// Scope targets identifiers to pull parameters from.
15+
type Scope struct {
16+
ImportJobID uuid.UUID
17+
OrganizationID string
18+
UserID string
19+
ProjectID uuid.NullUUID
20+
WorkspaceID uuid.NullUUID
21+
}
22+
23+
type Value struct {
24+
DestinationScheme database.ParameterDestinationScheme
25+
Name string
26+
Value string
27+
// Default is whether the default value from the schema
28+
// was consumed.
29+
Default bool
30+
Scope database.ParameterScope
31+
ScopeID string
32+
}
33+
34+
type Computed struct {
35+
Schema database.ParameterSchema
36+
// Value is nil when no value was computed for the schema.
37+
Value *Value
38+
}
39+
40+
// Compute accepts a scope in which parameter values are sourced.
41+
// These sources are iterated in a hierarchical fashion to determine
42+
// the runtime parameter values for a project.
43+
func Compute(ctx context.Context, db database.Store, scope Scope, additional ...database.ParameterValue) ([]Computed, error) {
44+
compute := &compute{
45+
db: db,
46+
parametersByName: map[string]Computed{},
47+
}
48+
49+
// All parameters for the import job ID!
50+
parameterSchemas, err := db.GetParameterSchemasByJobID(ctx, scope.ImportJobID)
51+
if errors.Is(err, sql.ErrNoRows) {
52+
err = nil
53+
}
54+
if err != nil {
55+
return nil, xerrors.Errorf("get project parameters: %w", err)
56+
}
57+
for _, parameterSchema := range parameterSchemas {
58+
compute.parametersByName[parameterSchema.Name] = Computed{
59+
Schema: parameterSchema,
60+
}
61+
}
62+
63+
// Organization parameters come first!
64+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
65+
Scope: database.ParameterScopeOrganization,
66+
ScopeID: scope.OrganizationID,
67+
})
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
// Default project parameter values come second!
73+
for _, parameterSchema := range parameterSchemas {
74+
if !parameterSchema.DefaultSourceValue.Valid {
75+
continue
76+
}
77+
if !parameterSchema.DefaultDestinationValue.Valid {
78+
continue
79+
}
80+
81+
switch parameterSchema.DefaultSourceScheme {
82+
case database.ParameterSourceSchemeData:
83+
compute.parametersByName[parameterSchema.Name] = Computed{
84+
Value: &Value{
85+
DestinationScheme: parameterSchema.DefaultDestinationScheme,
86+
Name: parameterSchema.DefaultDestinationValue.String,
87+
Value: parameterSchema.DefaultSourceValue.String,
88+
Default: true,
89+
Scope: database.ParameterScopeProject,
90+
ScopeID: scope.ProjectID.UUID.String(),
91+
},
92+
Schema: parameterSchema,
93+
}
94+
default:
95+
return nil, xerrors.Errorf("unsupported source scheme for project version parameter %q: %q", parameterSchema.Name, string(parameterSchema.DefaultSourceScheme))
96+
}
97+
}
98+
99+
if scope.ProjectID.Valid {
100+
// Project parameters come third!
101+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
102+
Scope: database.ParameterScopeProject,
103+
ScopeID: scope.ProjectID.UUID.String(),
104+
})
105+
if err != nil {
106+
return nil, err
107+
}
108+
}
109+
110+
// User parameters come fourth!
111+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
112+
Scope: database.ParameterScopeUser,
113+
ScopeID: scope.UserID,
114+
})
115+
if err != nil {
116+
return nil, err
117+
}
118+
119+
if scope.WorkspaceID.Valid {
120+
// Workspace parameters come last!
121+
err = compute.injectScope(ctx, database.GetParameterValuesByScopeParams{
122+
Scope: database.ParameterScopeWorkspace,
123+
ScopeID: scope.WorkspaceID.UUID.String(),
124+
})
125+
if err != nil {
126+
return nil, err
127+
}
128+
}
129+
130+
for _, parameterValue := range additional {
131+
err = compute.injectSingle(parameterValue)
132+
if err != nil {
133+
return nil, xerrors.Errorf("inject %q: %w", parameterValue.Name, err)
134+
}
135+
}
136+
137+
values := make([]Computed, 0, len(compute.parametersByName))
138+
for _, value := range compute.parametersByName {
139+
values = append(values, value)
140+
}
141+
return values, nil
142+
}
143+
144+
type compute struct {
145+
db database.Store
146+
parametersByName map[string]Computed
147+
}
148+
149+
// Validates and computes the value for parameters; setting the value on "parameterByName".
150+
func (c *compute) injectScope(ctx context.Context, scopeParams database.GetParameterValuesByScopeParams) error {
151+
scopedParameters, err := c.db.GetParameterValuesByScope(ctx, scopeParams)
152+
if errors.Is(err, sql.ErrNoRows) {
153+
err = nil
154+
}
155+
if err != nil {
156+
return xerrors.Errorf("get %s parameters: %w", scopeParams.Scope, err)
157+
}
158+
159+
for _, scopedParameter := range scopedParameters {
160+
err = c.injectSingle(scopedParameter)
161+
if err != nil {
162+
return xerrors.Errorf("inject single %q: %w", scopedParameter.Name, err)
163+
}
164+
}
165+
return nil
166+
}
167+
168+
func (c *compute) injectSingle(scopedParameter database.ParameterValue) error {
169+
computed, hasComputed := c.parametersByName[scopedParameter.Name]
170+
if !hasComputed {
171+
// Don't inject parameters that aren't defined by the project.
172+
return nil
173+
}
174+
175+
if computed.Value != nil {
176+
// If a parameter already exists, check if this variable can override it.
177+
// Injection hierarchy is the responsibility of the caller. This check ensures
178+
// project parameters cannot be overridden if already set.
179+
if !computed.Schema.AllowOverrideSource && scopedParameter.Scope != database.ParameterScopeProject {
180+
return nil
181+
}
182+
}
183+
184+
switch scopedParameter.SourceScheme {
185+
case database.ParameterSourceSchemeData:
186+
computed.Value = &Value{
187+
DestinationScheme: scopedParameter.DestinationScheme,
188+
Name: scopedParameter.SourceValue,
189+
Value: scopedParameter.DestinationValue,
190+
Scope: scopedParameter.Scope,
191+
ScopeID: scopedParameter.ScopeID,
192+
Default: false,
193+
}
194+
c.parametersByName[scopedParameter.Name] = computed
195+
default:
196+
return xerrors.Errorf("unsupported source scheme: %q", string(computed.Schema.DefaultSourceScheme))
197+
}
198+
return nil
199+
}

0 commit comments

Comments
 (0)