mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
chore: track terraform modules in telemetry (#15450)
Addresses https://github.com/coder/nexus/issues/35. This PR: - Adds a `workspace_modules` table to track modules used by the Terraform provisioner in provisioner jobs. - Adds a `module_path` column to the `workspace_resources` table, allowing to identify which module a resource originates from. - Starts pushing this new information into telemetry. For the person reviewing this PR, do not fret about the 1,500 new lines - ~1,000 of them are auto-generated.
This commit is contained in:
@@ -2666,6 +2666,20 @@ func (q *querier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspaceApp
|
||||
return fetch(q.log, q.auth, q.db.GetWorkspaceByWorkspaceAppID)(ctx, workspaceAppID)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceModulesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.WorkspaceModule, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceModulesByJobID(ctx, jobID)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceModulesCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceModule, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceModulesCreatedAfter(ctx, createdAt)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceProxies(ctx context.Context) ([]database.WorkspaceProxy, error) {
|
||||
return fetchWithPostFilter(q.auth, policy.ActionRead, func(ctx context.Context, _ interface{}) ([]database.WorkspaceProxy, error) {
|
||||
return q.db.GetWorkspaceProxies(ctx)
|
||||
@@ -3222,6 +3236,13 @@ func (q *querier) InsertWorkspaceBuildParameters(ctx context.Context, arg databa
|
||||
return q.db.InsertWorkspaceBuildParameters(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceModule(ctx context.Context, arg database.InsertWorkspaceModuleParams) (database.WorkspaceModule, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
|
||||
return database.WorkspaceModule{}, err
|
||||
}
|
||||
return q.db.InsertWorkspaceModule(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceProxy(ctx context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceWorkspaceProxy, q.db.InsertWorkspaceProxy)(ctx, arg)
|
||||
}
|
||||
|
||||
@@ -2907,6 +2907,21 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
}
|
||||
check.Args(build.ID).Asserts(rbac.ResourceSystem, policy.ActionRead).Returns(rows)
|
||||
}))
|
||||
s.Run("InsertWorkspaceModule", s.Subtest(func(db database.Store, check *expects) {
|
||||
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
})
|
||||
check.Args(database.InsertWorkspaceModuleParams{
|
||||
JobID: j.ID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("GetWorkspaceModulesByJobID", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(uuid.New()).Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
}))
|
||||
s.Run("GetWorkspaceModulesCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(dbtime.Now()).Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestNotifications() {
|
||||
|
||||
@@ -657,11 +657,29 @@ func WorkspaceResource(t testing.TB, db database.Store, orig database.WorkspaceR
|
||||
Valid: takeFirst(orig.InstanceType.Valid, false),
|
||||
},
|
||||
DailyCost: takeFirst(orig.DailyCost, 0),
|
||||
ModulePath: sql.NullString{
|
||||
String: takeFirst(orig.ModulePath.String, ""),
|
||||
Valid: takeFirst(orig.ModulePath.Valid, true),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err, "insert resource")
|
||||
return resource
|
||||
}
|
||||
|
||||
func WorkspaceModule(t testing.TB, db database.Store, orig database.WorkspaceModule) database.WorkspaceModule {
|
||||
module, err := db.InsertWorkspaceModule(genCtx, database.InsertWorkspaceModuleParams{
|
||||
ID: takeFirst(orig.ID, uuid.New()),
|
||||
JobID: takeFirst(orig.JobID, uuid.New()),
|
||||
Transition: takeFirst(orig.Transition, database.WorkspaceTransitionStart),
|
||||
Source: takeFirst(orig.Source, "test-source"),
|
||||
Version: takeFirst(orig.Version, "v1.0.0"),
|
||||
Key: takeFirst(orig.Key, "test-key"),
|
||||
CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()),
|
||||
})
|
||||
require.NoError(t, err, "insert workspace module")
|
||||
return module
|
||||
}
|
||||
|
||||
func WorkspaceResourceMetadatums(t testing.TB, db database.Store, seed database.WorkspaceResourceMetadatum) []database.WorkspaceResourceMetadatum {
|
||||
meta, err := db.InsertWorkspaceResourceMetadata(genCtx, database.InsertWorkspaceResourceMetadataParams{
|
||||
WorkspaceResourceID: takeFirst(seed.WorkspaceResourceID, uuid.New()),
|
||||
|
||||
@@ -73,6 +73,7 @@ func New() database.Store {
|
||||
workspaceAgents: make([]database.WorkspaceAgent, 0),
|
||||
provisionerJobLogs: make([]database.ProvisionerJobLog, 0),
|
||||
workspaceResources: make([]database.WorkspaceResource, 0),
|
||||
workspaceModules: make([]database.WorkspaceModule, 0),
|
||||
workspaceResourceMetadata: make([]database.WorkspaceResourceMetadatum, 0),
|
||||
provisionerJobs: make([]database.ProvisionerJob, 0),
|
||||
templateVersions: make([]database.TemplateVersionTable, 0),
|
||||
@@ -232,6 +233,7 @@ type data struct {
|
||||
workspaceBuildParameters []database.WorkspaceBuildParameter
|
||||
workspaceResourceMetadata []database.WorkspaceResourceMetadatum
|
||||
workspaceResources []database.WorkspaceResource
|
||||
workspaceModules []database.WorkspaceModule
|
||||
workspaces []database.WorkspaceTable
|
||||
workspaceProxies []database.WorkspaceProxy
|
||||
customRoles []database.CustomRole
|
||||
@@ -6671,6 +6673,32 @@ func (q *FakeQuerier) GetWorkspaceByWorkspaceAppID(_ context.Context, workspaceA
|
||||
return database.Workspace{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceModulesByJobID(_ context.Context, jobID uuid.UUID) ([]database.WorkspaceModule, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
modules := make([]database.WorkspaceModule, 0)
|
||||
for _, module := range q.workspaceModules {
|
||||
if module.JobID == jobID {
|
||||
modules = append(modules, module)
|
||||
}
|
||||
}
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceModulesCreatedAfter(_ context.Context, createdAt time.Time) ([]database.WorkspaceModule, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
modules := make([]database.WorkspaceModule, 0)
|
||||
for _, module := range q.workspaceModules {
|
||||
if module.CreatedAt.After(createdAt) {
|
||||
modules = append(modules, module)
|
||||
}
|
||||
}
|
||||
return modules, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceProxies(_ context.Context) ([]database.WorkspaceProxy, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -8233,6 +8261,20 @@ func (q *FakeQuerier) InsertWorkspaceBuildParameters(_ context.Context, arg data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceModule(_ context.Context, arg database.InsertWorkspaceModuleParams) (database.WorkspaceModule, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.WorkspaceModule{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
workspaceModule := database.WorkspaceModule(arg)
|
||||
q.workspaceModules = append(q.workspaceModules, workspaceModule)
|
||||
return workspaceModule, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceProxy(_ context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@@ -8283,6 +8325,7 @@ func (q *FakeQuerier) InsertWorkspaceResource(_ context.Context, arg database.In
|
||||
Hide: arg.Hide,
|
||||
Icon: arg.Icon,
|
||||
DailyCost: arg.DailyCost,
|
||||
ModulePath: arg.ModulePath,
|
||||
}
|
||||
q.workspaceResources = append(q.workspaceResources, resource)
|
||||
return resource, nil
|
||||
|
||||
@@ -1568,6 +1568,20 @@ func (m queryMetricsStore) GetWorkspaceByWorkspaceAppID(ctx context.Context, wor
|
||||
return workspace, err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceModulesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.WorkspaceModule, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceModulesByJobID(ctx, jobID)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceModulesByJobID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceModulesCreatedAfter(ctx context.Context, createdAt time.Time) ([]database.WorkspaceModule, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceModulesCreatedAfter(ctx, createdAt)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceModulesCreatedAfter").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceProxies(ctx context.Context) ([]database.WorkspaceProxy, error) {
|
||||
start := time.Now()
|
||||
proxies, err := m.s.GetWorkspaceProxies(ctx)
|
||||
@@ -1995,6 +2009,13 @@ func (m queryMetricsStore) InsertWorkspaceBuildParameters(ctx context.Context, a
|
||||
return err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceModule(ctx context.Context, arg database.InsertWorkspaceModuleParams) (database.WorkspaceModule, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertWorkspaceModule(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertWorkspaceModule").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceProxy(ctx context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
|
||||
start := time.Now()
|
||||
proxy, err := m.s.InsertWorkspaceProxy(ctx, arg)
|
||||
|
||||
@@ -3307,6 +3307,36 @@ func (mr *MockStoreMockRecorder) GetWorkspaceByWorkspaceAppID(arg0, arg1 any) *g
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceByWorkspaceAppID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceByWorkspaceAppID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceModulesByJobID mocks base method.
|
||||
func (m *MockStore) GetWorkspaceModulesByJobID(arg0 context.Context, arg1 uuid.UUID) ([]database.WorkspaceModule, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceModulesByJobID", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceModule)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceModulesByJobID indicates an expected call of GetWorkspaceModulesByJobID.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceModulesByJobID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceModulesByJobID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceModulesByJobID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceModulesCreatedAfter mocks base method.
|
||||
func (m *MockStore) GetWorkspaceModulesCreatedAfter(arg0 context.Context, arg1 time.Time) ([]database.WorkspaceModule, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceModulesCreatedAfter", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.WorkspaceModule)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceModulesCreatedAfter indicates an expected call of GetWorkspaceModulesCreatedAfter.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceModulesCreatedAfter(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceModulesCreatedAfter", reflect.TypeOf((*MockStore)(nil).GetWorkspaceModulesCreatedAfter), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetWorkspaceProxies mocks base method.
|
||||
func (m *MockStore) GetWorkspaceProxies(arg0 context.Context) ([]database.WorkspaceProxy, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -4224,6 +4254,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceBuildParameters(arg0, arg1 any)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceBuildParameters", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceBuildParameters), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceModule mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceModule(arg0 context.Context, arg1 database.InsertWorkspaceModuleParams) (database.WorkspaceModule, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertWorkspaceModule", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.WorkspaceModule)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertWorkspaceModule indicates an expected call of InsertWorkspaceModule.
|
||||
func (mr *MockStoreMockRecorder) InsertWorkspaceModule(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceModule", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceModule), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertWorkspaceProxy mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceProxy(arg0 context.Context, arg1 database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+17
-1
@@ -1634,6 +1634,16 @@ CREATE VIEW workspace_build_with_user AS
|
||||
|
||||
COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.';
|
||||
|
||||
CREATE TABLE workspace_modules (
|
||||
id uuid NOT NULL,
|
||||
job_id uuid NOT NULL,
|
||||
transition workspace_transition NOT NULL,
|
||||
source text NOT NULL,
|
||||
version text NOT NULL,
|
||||
key text NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_proxies (
|
||||
id uuid NOT NULL,
|
||||
name text NOT NULL,
|
||||
@@ -1700,7 +1710,8 @@ CREATE TABLE workspace_resources (
|
||||
hide boolean DEFAULT false NOT NULL,
|
||||
icon character varying(256) DEFAULT ''::character varying NOT NULL,
|
||||
instance_type character varying(256),
|
||||
daily_cost integer DEFAULT 0 NOT NULL
|
||||
daily_cost integer DEFAULT 0 NOT NULL,
|
||||
module_path text
|
||||
);
|
||||
|
||||
CREATE TABLE workspaces (
|
||||
@@ -2095,6 +2106,8 @@ CREATE INDEX workspace_agents_resource_id_idx ON workspace_agents USING btree (r
|
||||
|
||||
CREATE INDEX workspace_app_stats_workspace_id_idx ON workspace_app_stats USING btree (workspace_id);
|
||||
|
||||
CREATE INDEX workspace_modules_created_at_idx ON workspace_modules USING btree (created_at);
|
||||
|
||||
CREATE UNIQUE INDEX workspace_proxies_lower_name_idx ON workspace_proxies USING btree (lower(name)) WHERE (deleted = false);
|
||||
|
||||
CREATE INDEX workspace_resources_job_id_idx ON workspace_resources USING btree (job_id);
|
||||
@@ -2360,6 +2373,9 @@ ALTER TABLE ONLY workspace_builds
|
||||
ALTER TABLE ONLY workspace_builds
|
||||
ADD CONSTRAINT workspace_builds_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_modules
|
||||
ADD CONSTRAINT workspace_modules_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY workspace_resource_metadata
|
||||
ADD CONSTRAINT workspace_resource_metadata_workspace_resource_id_fkey FOREIGN KEY (workspace_resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ const (
|
||||
ForeignKeyWorkspaceBuildsJobID ForeignKeyConstraint = "workspace_builds_job_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceBuildsTemplateVersionID ForeignKeyConstraint = "workspace_builds_template_version_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_template_version_id_fkey FOREIGN KEY (template_version_id) REFERENCES template_versions(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceBuildsWorkspaceID ForeignKeyConstraint = "workspace_builds_workspace_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceModulesJobID ForeignKeyConstraint = "workspace_modules_job_id_fkey" // ALTER TABLE ONLY workspace_modules ADD CONSTRAINT workspace_modules_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceResourceMetadataWorkspaceResourceID ForeignKeyConstraint = "workspace_resource_metadata_workspace_resource_id_fkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_workspace_resource_id_fkey FOREIGN KEY (workspace_resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceResourcesJobID ForeignKeyConstraint = "workspace_resources_job_id_fkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspacesOrganizationID ForeignKeyConstraint = "workspaces_organization_id_fkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE RESTRICT;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP TABLE workspace_modules;
|
||||
|
||||
ALTER TABLE
|
||||
workspace_resources
|
||||
DROP COLUMN module_path;
|
||||
@@ -0,0 +1,16 @@
|
||||
ALTER TABLE
|
||||
workspace_resources
|
||||
ADD
|
||||
COLUMN module_path TEXT;
|
||||
|
||||
CREATE TABLE workspace_modules (
|
||||
id uuid NOT NULL,
|
||||
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
|
||||
transition workspace_transition NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX workspace_modules_created_at_idx ON workspace_modules (created_at);
|
||||
@@ -0,0 +1,20 @@
|
||||
INSERT INTO
|
||||
public.workspace_modules (
|
||||
id,
|
||||
job_id,
|
||||
transition,
|
||||
source,
|
||||
version,
|
||||
key,
|
||||
created_at
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'5b1a722c-b8a0-40b0-a3a0-d8078fff9f6c',
|
||||
'424a58cb-61d6-4627-9907-613c396c4a38',
|
||||
'start',
|
||||
'test-source',
|
||||
'v1.0.0',
|
||||
'test-key',
|
||||
'2024-11-08 10:00:00+00'
|
||||
);
|
||||
@@ -3152,6 +3152,16 @@ type WorkspaceBuildTable struct {
|
||||
MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"`
|
||||
}
|
||||
|
||||
type WorkspaceModule struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
JobID uuid.UUID `db:"job_id" json:"job_id"`
|
||||
Transition WorkspaceTransition `db:"transition" json:"transition"`
|
||||
Source string `db:"source" json:"source"`
|
||||
Version string `db:"version" json:"version"`
|
||||
Key string `db:"key" json:"key"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
type WorkspaceProxy struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
@@ -3186,6 +3196,7 @@ type WorkspaceResource struct {
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
InstanceType sql.NullString `db:"instance_type" json:"instance_type"`
|
||||
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
|
||||
ModulePath sql.NullString `db:"module_path" json:"module_path"`
|
||||
}
|
||||
|
||||
type WorkspaceResourceMetadatum struct {
|
||||
|
||||
@@ -323,6 +323,8 @@ type sqlcQuerier interface {
|
||||
GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Workspace, error)
|
||||
GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWorkspaceByOwnerIDAndNameParams) (Workspace, error)
|
||||
GetWorkspaceByWorkspaceAppID(ctx context.Context, workspaceAppID uuid.UUID) (Workspace, error)
|
||||
GetWorkspaceModulesByJobID(ctx context.Context, jobID uuid.UUID) ([]WorkspaceModule, error)
|
||||
GetWorkspaceModulesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceModule, error)
|
||||
GetWorkspaceProxies(ctx context.Context) ([]WorkspaceProxy, error)
|
||||
// Finds a workspace proxy that has an access URL or app hostname that matches
|
||||
// the provided hostname. This is to check if a hostname matches any workspace
|
||||
@@ -404,6 +406,7 @@ type sqlcQuerier interface {
|
||||
InsertWorkspaceAppStats(ctx context.Context, arg InsertWorkspaceAppStatsParams) error
|
||||
InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) error
|
||||
InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error
|
||||
InsertWorkspaceModule(ctx context.Context, arg InsertWorkspaceModuleParams) (WorkspaceModule, error)
|
||||
InsertWorkspaceProxy(ctx context.Context, arg InsertWorkspaceProxyParams) (WorkspaceProxy, error)
|
||||
InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error)
|
||||
InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error)
|
||||
|
||||
@@ -14115,9 +14115,124 @@ func (q *sqlQuerier) UpdateWorkspaceBuildProvisionerStateByID(ctx context.Contex
|
||||
return err
|
||||
}
|
||||
|
||||
const getWorkspaceModulesByJobID = `-- name: GetWorkspaceModulesByJobID :many
|
||||
SELECT
|
||||
id, job_id, transition, source, version, key, created_at
|
||||
FROM
|
||||
workspace_modules
|
||||
WHERE
|
||||
job_id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceModulesByJobID(ctx context.Context, jobID uuid.UUID) ([]WorkspaceModule, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceModulesByJobID, jobID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceModule
|
||||
for rows.Next() {
|
||||
var i WorkspaceModule
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.JobID,
|
||||
&i.Transition,
|
||||
&i.Source,
|
||||
&i.Version,
|
||||
&i.Key,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceModulesCreatedAfter = `-- name: GetWorkspaceModulesCreatedAfter :many
|
||||
SELECT id, job_id, transition, source, version, key, created_at FROM workspace_modules WHERE created_at > $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceModulesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceModule, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceModulesCreatedAfter, createdAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceModule
|
||||
for rows.Next() {
|
||||
var i WorkspaceModule
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.JobID,
|
||||
&i.Transition,
|
||||
&i.Source,
|
||||
&i.Version,
|
||||
&i.Key,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertWorkspaceModule = `-- name: InsertWorkspaceModule :one
|
||||
INSERT INTO
|
||||
workspace_modules (id, job_id, transition, source, version, key, created_at)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7) RETURNING id, job_id, transition, source, version, key, created_at
|
||||
`
|
||||
|
||||
type InsertWorkspaceModuleParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
JobID uuid.UUID `db:"job_id" json:"job_id"`
|
||||
Transition WorkspaceTransition `db:"transition" json:"transition"`
|
||||
Source string `db:"source" json:"source"`
|
||||
Version string `db:"version" json:"version"`
|
||||
Key string `db:"key" json:"key"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceModule(ctx context.Context, arg InsertWorkspaceModuleParams) (WorkspaceModule, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertWorkspaceModule,
|
||||
arg.ID,
|
||||
arg.JobID,
|
||||
arg.Transition,
|
||||
arg.Source,
|
||||
arg.Version,
|
||||
arg.Key,
|
||||
arg.CreatedAt,
|
||||
)
|
||||
var i WorkspaceModule
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.JobID,
|
||||
&i.Transition,
|
||||
&i.Source,
|
||||
&i.Version,
|
||||
&i.Key,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceResourceByID = `-- name: GetWorkspaceResourceByID :one
|
||||
SELECT
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path
|
||||
FROM
|
||||
workspace_resources
|
||||
WHERE
|
||||
@@ -14138,6 +14253,7 @@ func (q *sqlQuerier) GetWorkspaceResourceByID(ctx context.Context, id uuid.UUID)
|
||||
&i.Icon,
|
||||
&i.InstanceType,
|
||||
&i.DailyCost,
|
||||
&i.ModulePath,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -14217,7 +14333,7 @@ func (q *sqlQuerier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Contex
|
||||
|
||||
const getWorkspaceResourcesByJobID = `-- name: GetWorkspaceResourcesByJobID :many
|
||||
SELECT
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path
|
||||
FROM
|
||||
workspace_resources
|
||||
WHERE
|
||||
@@ -14244,6 +14360,7 @@ func (q *sqlQuerier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uui
|
||||
&i.Icon,
|
||||
&i.InstanceType,
|
||||
&i.DailyCost,
|
||||
&i.ModulePath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -14260,7 +14377,7 @@ func (q *sqlQuerier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uui
|
||||
|
||||
const getWorkspaceResourcesByJobIDs = `-- name: GetWorkspaceResourcesByJobIDs :many
|
||||
SELECT
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
|
||||
id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path
|
||||
FROM
|
||||
workspace_resources
|
||||
WHERE
|
||||
@@ -14287,6 +14404,7 @@ func (q *sqlQuerier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uu
|
||||
&i.Icon,
|
||||
&i.InstanceType,
|
||||
&i.DailyCost,
|
||||
&i.ModulePath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -14302,7 +14420,7 @@ func (q *sqlQuerier) GetWorkspaceResourcesByJobIDs(ctx context.Context, ids []uu
|
||||
}
|
||||
|
||||
const getWorkspaceResourcesCreatedAfter = `-- name: GetWorkspaceResourcesCreatedAfter :many
|
||||
SELECT id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost FROM workspace_resources WHERE created_at > $1
|
||||
SELECT id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path FROM workspace_resources WHERE created_at > $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResource, error) {
|
||||
@@ -14325,6 +14443,7 @@ func (q *sqlQuerier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, crea
|
||||
&i.Icon,
|
||||
&i.InstanceType,
|
||||
&i.DailyCost,
|
||||
&i.ModulePath,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -14341,9 +14460,9 @@ func (q *sqlQuerier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, crea
|
||||
|
||||
const insertWorkspaceResource = `-- name: InsertWorkspaceResource :one
|
||||
INSERT INTO
|
||||
workspace_resources (id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost)
|
||||
workspace_resources (id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path
|
||||
`
|
||||
|
||||
type InsertWorkspaceResourceParams struct {
|
||||
@@ -14357,6 +14476,7 @@ type InsertWorkspaceResourceParams struct {
|
||||
Icon string `db:"icon" json:"icon"`
|
||||
InstanceType sql.NullString `db:"instance_type" json:"instance_type"`
|
||||
DailyCost int32 `db:"daily_cost" json:"daily_cost"`
|
||||
ModulePath sql.NullString `db:"module_path" json:"module_path"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) {
|
||||
@@ -14371,6 +14491,7 @@ func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWork
|
||||
arg.Icon,
|
||||
arg.InstanceType,
|
||||
arg.DailyCost,
|
||||
arg.ModulePath,
|
||||
)
|
||||
var i WorkspaceResource
|
||||
err := row.Scan(
|
||||
@@ -14384,6 +14505,7 @@ func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWork
|
||||
&i.Icon,
|
||||
&i.InstanceType,
|
||||
&i.DailyCost,
|
||||
&i.ModulePath,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- name: InsertWorkspaceModule :one
|
||||
INSERT INTO
|
||||
workspace_modules (id, job_id, transition, source, version, key, created_at)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7) RETURNING *;
|
||||
|
||||
-- name: GetWorkspaceModulesByJobID :many
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
workspace_modules
|
||||
WHERE
|
||||
job_id = $1;
|
||||
|
||||
-- name: GetWorkspaceModulesCreatedAfter :many
|
||||
SELECT * FROM workspace_modules WHERE created_at > $1;
|
||||
@@ -27,9 +27,9 @@ SELECT * FROM workspace_resources WHERE created_at > $1;
|
||||
|
||||
-- name: InsertWorkspaceResource :one
|
||||
INSERT INTO
|
||||
workspace_resources (id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost)
|
||||
workspace_resources (id, created_at, job_id, transition, type, name, hide, icon, instance_type, daily_cost, module_path)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *;
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *;
|
||||
|
||||
-- name: GetWorkspaceResourceMetadataByResourceIDs :many
|
||||
SELECT
|
||||
|
||||
@@ -1261,12 +1261,28 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||
slog.F("resource_type", resource.Type),
|
||||
slog.F("transition", transition))
|
||||
|
||||
err = InsertWorkspaceResource(ctx, s.Database, jobID, transition, resource, telemetrySnapshot)
|
||||
if err != nil {
|
||||
if err := InsertWorkspaceResource(ctx, s.Database, jobID, transition, resource, telemetrySnapshot); err != nil {
|
||||
return nil, xerrors.Errorf("insert resource: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
for transition, modules := range map[database.WorkspaceTransition][]*sdkproto.Module{
|
||||
database.WorkspaceTransitionStart: jobType.TemplateImport.StartModules,
|
||||
database.WorkspaceTransitionStop: jobType.TemplateImport.StopModules,
|
||||
} {
|
||||
for _, module := range modules {
|
||||
s.Logger.Info(ctx, "inserting template import job module",
|
||||
slog.F("job_id", job.ID.String()),
|
||||
slog.F("module_source", module.Source),
|
||||
slog.F("module_version", module.Version),
|
||||
slog.F("module_key", module.Key),
|
||||
slog.F("transition", transition))
|
||||
|
||||
if err := InsertWorkspaceModule(ctx, s.Database, jobID, transition, module, telemetrySnapshot); err != nil {
|
||||
return nil, xerrors.Errorf("insert module: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, richParameter := range jobType.TemplateImport.RichParameters {
|
||||
s.Logger.Info(ctx, "inserting template import job parameter",
|
||||
@@ -1472,6 +1488,11 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||
return xerrors.Errorf("insert provisioner job: %w", err)
|
||||
}
|
||||
}
|
||||
for _, module := range jobType.WorkspaceBuild.Modules {
|
||||
if err := InsertWorkspaceModule(ctx, db, job.ID, workspaceBuild.Transition, module, telemetrySnapshot); err != nil {
|
||||
return xerrors.Errorf("insert provisioner job module: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// On start, we want to ensure that workspace agents timeout statuses
|
||||
// are propagated. This method is simple and does not protect against
|
||||
@@ -1653,6 +1674,16 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||
return nil, xerrors.Errorf("insert resource: %w", err)
|
||||
}
|
||||
}
|
||||
for _, module := range jobType.TemplateDryRun.Modules {
|
||||
s.Logger.Info(ctx, "inserting template dry-run job module",
|
||||
slog.F("job_id", job.ID.String()),
|
||||
slog.F("module_source", module.Source),
|
||||
)
|
||||
|
||||
if err := InsertWorkspaceModule(ctx, s.Database, jobID, database.WorkspaceTransitionStart, module, telemetrySnapshot); err != nil {
|
||||
return nil, xerrors.Errorf("insert module: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||
ID: jobID,
|
||||
@@ -1734,6 +1765,23 @@ func (s *server) startTrace(ctx context.Context, name string, opts ...trace.Span
|
||||
))...)
|
||||
}
|
||||
|
||||
func InsertWorkspaceModule(ctx context.Context, db database.Store, jobID uuid.UUID, transition database.WorkspaceTransition, protoModule *sdkproto.Module, snapshot *telemetry.Snapshot) error {
|
||||
module, err := db.InsertWorkspaceModule(ctx, database.InsertWorkspaceModuleParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: dbtime.Now(),
|
||||
JobID: jobID,
|
||||
Transition: transition,
|
||||
Source: protoModule.Source,
|
||||
Version: protoModule.Version,
|
||||
Key: protoModule.Key,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert provisioner job module %q: %w", protoModule.Source, err)
|
||||
}
|
||||
snapshot.WorkspaceModules = append(snapshot.WorkspaceModules, telemetry.ConvertWorkspaceModule(module))
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.UUID, transition database.WorkspaceTransition, protoResource *sdkproto.Resource, snapshot *telemetry.Snapshot) error {
|
||||
resource, err := db.InsertWorkspaceResource(ctx, database.InsertWorkspaceResourceParams{
|
||||
ID: uuid.New(),
|
||||
@@ -1749,6 +1797,11 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
String: protoResource.InstanceType,
|
||||
Valid: protoResource.InstanceType != "",
|
||||
},
|
||||
ModulePath: sql.NullString{
|
||||
String: protoResource.ModulePath,
|
||||
// empty string is root module
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert provisioner job resource %q: %w", protoResource.Name, err)
|
||||
|
||||
@@ -1430,6 +1430,285 @@ func TestCompleteJob(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Modules", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
templateVersionID := uuid.New()
|
||||
workspaceBuildID := uuid.New()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
job *proto.CompletedJob
|
||||
expectedResources []database.WorkspaceResource
|
||||
expectedModules []database.WorkspaceModule
|
||||
provisionerJobParams database.InsertProvisionerJobParams
|
||||
}{
|
||||
{
|
||||
name: "TemplateDryRun",
|
||||
job: &proto.CompletedJob{
|
||||
Type: &proto.CompletedJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.CompletedJob_TemplateDryRun{
|
||||
Resources: []*sdkproto.Resource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "module.test1",
|
||||
}, {
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "",
|
||||
}},
|
||||
Modules: []*sdkproto.Module{
|
||||
{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: []database.WorkspaceResource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "module.test1",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}, {
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}},
|
||||
expectedModules: []database.WorkspaceModule{{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}},
|
||||
provisionerJobParams: database.InsertProvisionerJobParams{
|
||||
Type: database.ProvisionerJobTypeTemplateVersionDryRun,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TemplateImport",
|
||||
job: &proto.CompletedJob{
|
||||
Type: &proto.CompletedJob_TemplateImport_{
|
||||
TemplateImport: &proto.CompletedJob_TemplateImport{
|
||||
StartResources: []*sdkproto.Resource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "module.test1",
|
||||
}},
|
||||
StartModules: []*sdkproto.Module{
|
||||
{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
},
|
||||
},
|
||||
StopResources: []*sdkproto.Resource{{
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "module.test2",
|
||||
}},
|
||||
StopModules: []*sdkproto.Module{
|
||||
{
|
||||
Key: "test2",
|
||||
Version: "2.0.0",
|
||||
Source: "github.com/example2/example",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
provisionerJobParams: database.InsertProvisionerJobParams{
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{
|
||||
TemplateVersionID: templateVersionID,
|
||||
})),
|
||||
},
|
||||
expectedResources: []database.WorkspaceResource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "module.test1",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}, {
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "module.test2",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStop,
|
||||
}},
|
||||
expectedModules: []database.WorkspaceModule{{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}, {
|
||||
Key: "test2",
|
||||
Version: "2.0.0",
|
||||
Source: "github.com/example2/example",
|
||||
Transition: database.WorkspaceTransitionStop,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "WorkspaceBuild",
|
||||
job: &proto.CompletedJob{
|
||||
Type: &proto.CompletedJob_WorkspaceBuild_{
|
||||
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
|
||||
Resources: []*sdkproto.Resource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "module.test1",
|
||||
}, {
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: "",
|
||||
}},
|
||||
Modules: []*sdkproto.Module{
|
||||
{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedResources: []database.WorkspaceResource{{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "module.test1",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}, {
|
||||
Name: "something2",
|
||||
Type: "aws_instance",
|
||||
ModulePath: sql.NullString{
|
||||
String: "",
|
||||
Valid: true,
|
||||
},
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}},
|
||||
expectedModules: []database.WorkspaceModule{{
|
||||
Key: "test1",
|
||||
Version: "1.0.0",
|
||||
Source: "github.com/example/example",
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
}},
|
||||
provisionerJobParams: database.InsertProvisionerJobParams{
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
|
||||
WorkspaceBuildID: workspaceBuildID,
|
||||
})),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
srv, db, _, pd := setup(t, false, &overrides{})
|
||||
jobParams := c.provisionerJobParams
|
||||
if jobParams.ID == uuid.Nil {
|
||||
jobParams.ID = uuid.New()
|
||||
}
|
||||
if jobParams.Provisioner == "" {
|
||||
jobParams.Provisioner = database.ProvisionerTypeEcho
|
||||
}
|
||||
if jobParams.StorageMethod == "" {
|
||||
jobParams.StorageMethod = database.ProvisionerStorageMethodFile
|
||||
}
|
||||
job, err := db.InsertProvisionerJob(ctx, jobParams)
|
||||
|
||||
tpl := dbgen.Template(t, db, database.Template{
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true},
|
||||
JobID: job.ID,
|
||||
})
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
TemplateID: tpl.ID,
|
||||
})
|
||||
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
ID: workspaceBuildID,
|
||||
JobID: job.ID,
|
||||
WorkspaceID: workspace.ID,
|
||||
TemplateVersionID: tv.ID,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
|
||||
WorkerID: uuid.NullUUID{
|
||||
UUID: pd.ID,
|
||||
Valid: true,
|
||||
},
|
||||
Types: []database.ProvisionerType{jobParams.Provisioner},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
completedJob := c.job
|
||||
completedJob.JobId = job.ID.String()
|
||||
|
||||
_, err = srv.CompleteJob(ctx, completedJob)
|
||||
require.NoError(t, err)
|
||||
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, len(c.expectedResources))
|
||||
|
||||
for _, expectedResource := range c.expectedResources {
|
||||
for i, resource := range resources {
|
||||
if resource.Name == expectedResource.Name &&
|
||||
resource.Type == expectedResource.Type &&
|
||||
resource.ModulePath == expectedResource.ModulePath &&
|
||||
resource.Transition == expectedResource.Transition {
|
||||
resources[i] = database.WorkspaceResource{Name: "matched"}
|
||||
}
|
||||
}
|
||||
}
|
||||
// all resources should be matched
|
||||
for _, resource := range resources {
|
||||
require.Equal(t, "matched", resource.Name)
|
||||
}
|
||||
|
||||
modules, err := db.GetWorkspaceModulesByJobID(ctx, job.ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, modules, len(c.expectedModules))
|
||||
|
||||
for _, expectedModule := range c.expectedModules {
|
||||
for i, module := range modules {
|
||||
if module.Key == expectedModule.Key &&
|
||||
module.Version == expectedModule.Version &&
|
||||
module.Source == expectedModule.Source &&
|
||||
module.Transition == expectedModule.Transition {
|
||||
modules[i] = database.WorkspaceModule{Key: "matched"}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, module := range modules {
|
||||
require.Equal(t, "matched", module.Key)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInsertWorkspaceResource(t *testing.T) {
|
||||
|
||||
@@ -456,6 +456,17 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
workspaceModules, err := r.options.Database.GetWorkspaceModulesCreatedAfter(ctx, createdAfter)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get workspace modules: %w", err)
|
||||
}
|
||||
snapshot.WorkspaceModules = make([]WorkspaceModule, 0, len(workspaceModules))
|
||||
for _, module := range workspaceModules {
|
||||
snapshot.WorkspaceModules = append(snapshot.WorkspaceModules, ConvertWorkspaceModule(module))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
licenses, err := r.options.Database.GetUnexpiredLicenses(ctx)
|
||||
if err != nil {
|
||||
@@ -642,7 +653,7 @@ func ConvertWorkspaceApp(app database.WorkspaceApp) WorkspaceApp {
|
||||
|
||||
// ConvertWorkspaceResource anonymizes a workspace resource.
|
||||
func ConvertWorkspaceResource(resource database.WorkspaceResource) WorkspaceResource {
|
||||
return WorkspaceResource{
|
||||
r := WorkspaceResource{
|
||||
ID: resource.ID,
|
||||
JobID: resource.JobID,
|
||||
CreatedAt: resource.CreatedAt,
|
||||
@@ -650,6 +661,10 @@ func ConvertWorkspaceResource(resource database.WorkspaceResource) WorkspaceReso
|
||||
Type: resource.Type,
|
||||
InstanceType: resource.InstanceType.String,
|
||||
}
|
||||
if resource.ModulePath.Valid {
|
||||
r.ModulePath = &resource.ModulePath.String
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ConvertWorkspaceResourceMetadata anonymizes workspace metadata.
|
||||
@@ -661,6 +676,29 @@ func ConvertWorkspaceResourceMetadata(metadata database.WorkspaceResourceMetadat
|
||||
}
|
||||
}
|
||||
|
||||
func shouldSendRawModuleSource(source string) bool {
|
||||
return strings.Contains(source, "registry.coder.com")
|
||||
}
|
||||
|
||||
func ConvertWorkspaceModule(module database.WorkspaceModule) WorkspaceModule {
|
||||
source := module.Source
|
||||
version := module.Version
|
||||
if !shouldSendRawModuleSource(source) {
|
||||
source = fmt.Sprintf("%x", sha256.Sum256([]byte(source)))
|
||||
version = fmt.Sprintf("%x", sha256.Sum256([]byte(version)))
|
||||
}
|
||||
|
||||
return WorkspaceModule{
|
||||
ID: module.ID,
|
||||
JobID: module.JobID,
|
||||
Transition: module.Transition,
|
||||
Source: source,
|
||||
Version: version,
|
||||
Key: module.Key,
|
||||
CreatedAt: module.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertUser anonymizes a user.
|
||||
func ConvertUser(dbUser database.User) User {
|
||||
emailHashed := ""
|
||||
@@ -810,6 +848,7 @@ type Snapshot struct {
|
||||
WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"`
|
||||
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
|
||||
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
|
||||
WorkspaceModules []WorkspaceModule `json:"workspace_modules"`
|
||||
Workspaces []Workspace `json:"workspaces"`
|
||||
NetworkEvents []NetworkEvent `json:"network_events"`
|
||||
}
|
||||
@@ -878,6 +917,11 @@ type WorkspaceResource struct {
|
||||
Transition database.WorkspaceTransition `json:"transition"`
|
||||
Type string `json:"type"`
|
||||
InstanceType string `json:"instance_type"`
|
||||
// ModulePath is nullable because it was added a long time after the
|
||||
// original workspace resource telemetry was added. All new resources
|
||||
// will have a module path, but deployments with older resources still
|
||||
// in the database will not.
|
||||
ModulePath *string `json:"module_path"`
|
||||
}
|
||||
|
||||
type WorkspaceResourceMetadata struct {
|
||||
@@ -886,6 +930,16 @@ type WorkspaceResourceMetadata struct {
|
||||
Sensitive bool `json:"sensitive"`
|
||||
}
|
||||
|
||||
type WorkspaceModule struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
JobID uuid.UUID `json:"job_id"`
|
||||
Transition database.WorkspaceTransition `json:"transition"`
|
||||
Key string `json:"key"`
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type WorkspaceAgent struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbmem"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/telemetry"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
@@ -87,6 +89,8 @@ func TestTelemetry(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
_, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{})
|
||||
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{})
|
||||
|
||||
_, snapshot := collectSnapshot(t, db, nil)
|
||||
require.Len(t, snapshot.ProvisionerJobs, 1)
|
||||
require.Len(t, snapshot.Licenses, 1)
|
||||
@@ -103,6 +107,7 @@ func TestTelemetry(t *testing.T) {
|
||||
require.Len(t, snapshot.WorkspaceResources, 1)
|
||||
require.Len(t, snapshot.WorkspaceAgentStats, 1)
|
||||
require.Len(t, snapshot.WorkspaceProxies, 1)
|
||||
require.Len(t, snapshot.WorkspaceModules, 1)
|
||||
|
||||
wsa := snapshot.WorkspaceAgents[0]
|
||||
require.Len(t, wsa.Subsystems, 2)
|
||||
@@ -119,6 +124,31 @@ func TestTelemetry(t *testing.T) {
|
||||
require.Len(t, snapshot.Users, 1)
|
||||
require.Equal(t, snapshot.Users[0].EmailHashed, "bb44bf07cf9a2db0554bba63a03d822c927deae77df101874496df5a6a3e896d@coder.com")
|
||||
})
|
||||
t.Run("HashedModule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
pj := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{})
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{
|
||||
JobID: pj.ID,
|
||||
Source: "registry.coder.com/terraform/aws",
|
||||
Version: "1.0.0",
|
||||
})
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{
|
||||
JobID: pj.ID,
|
||||
Source: "internal-url.com/some-module",
|
||||
Version: "1.0.0",
|
||||
})
|
||||
_, snapshot := collectSnapshot(t, db, nil)
|
||||
require.Len(t, snapshot.WorkspaceModules, 2)
|
||||
modules := snapshot.WorkspaceModules
|
||||
sort.Slice(modules, func(i, j int) bool {
|
||||
return modules[i].Source < modules[j].Source
|
||||
})
|
||||
require.Equal(t, modules[0].Source, "921c61d6f3eef5118f3cae658d1518b378c5b02a4955a766c791440894d989c5")
|
||||
require.Equal(t, modules[0].Version, "92521fc3cbd964bdc9f584a991b89fddaa5754ed1cc96d6d42445338669c1305")
|
||||
require.Equal(t, modules[1].Source, "registry.coder.com/terraform/aws")
|
||||
require.Equal(t, modules[1].Version, "1.0.0")
|
||||
})
|
||||
}
|
||||
|
||||
// nolint:paralleltest
|
||||
|
||||
@@ -344,6 +344,7 @@ require (
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-terraform-address v0.0.0-20240523040243-ccea9d309e0c
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.22.0
|
||||
|
||||
@@ -562,6 +562,8 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-terraform-address v0.0.0-20240523040243-ccea9d309e0c h1:5v6L/m/HcAZYbrLGYBpPkcCVtDWwIgFxq2+FUmfPxPk=
|
||||
github.com/hashicorp/go-terraform-address v0.0.0-20240523040243-ccea9d309e0c/go.mod h1:xoy1vl2+4YvqSQEkKcFjNYxTk7cll+o1f1t2wxnHIX8=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
|
||||
@@ -356,7 +356,7 @@ func (e *executor) planResources(ctx, killCtx context.Context, planfilePath stri
|
||||
}
|
||||
modules = append(modules, plan.PlannedValues.RootModule)
|
||||
|
||||
state, err := ConvertState(modules, rawGraph)
|
||||
state, err := ConvertState(ctx, modules, rawGraph, e.server.logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -484,9 +484,9 @@ func (e *executor) stateResources(ctx, killCtx context.Context) (*State, error)
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
converted, err = ConvertState([]*tfjson.StateModule{
|
||||
converted, err = ConvertState(ctx, []*tfjson.StateModule{
|
||||
state.Values.RootModule,
|
||||
}, rawGraph)
|
||||
}, rawGraph, e.server.logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
Source string `json:"Source"`
|
||||
Version string `json:"Version"`
|
||||
Key string `json:"Key"`
|
||||
}
|
||||
|
||||
type modulesFile struct {
|
||||
Modules []*module `json:"Modules"`
|
||||
}
|
||||
|
||||
func getModulesFilePath(workdir string) string {
|
||||
return filepath.Join(workdir, ".terraform", "modules", "modules.json")
|
||||
}
|
||||
|
||||
func parseModulesFile(filePath string) ([]*proto.Module, error) {
|
||||
modules := &modulesFile{}
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("read modules file: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(data, modules); err != nil {
|
||||
return nil, xerrors.Errorf("unmarshal modules file: %w", err)
|
||||
}
|
||||
protoModules := make([]*proto.Module, len(modules.Modules))
|
||||
for i, m := range modules.Modules {
|
||||
protoModules[i] = &proto.Module{Source: m.Source, Version: m.Version, Key: m.Key}
|
||||
}
|
||||
return protoModules, nil
|
||||
}
|
||||
|
||||
// getModules returns the modules from the modules file if it exists.
|
||||
// It returns nil if the file does not exist.
|
||||
// Modules become available after terraform init.
|
||||
func getModules(workdir string) ([]*proto.Module, error) {
|
||||
filePath := getModulesFilePath(workdir)
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
modules, err := parseModulesFile(filePath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parse modules file: %w", err)
|
||||
}
|
||||
filteredModules := []*proto.Module{}
|
||||
for _, m := range modules {
|
||||
// Empty string means root module. It's always present, so we skip it.
|
||||
if m.Source == "" {
|
||||
continue
|
||||
}
|
||||
filteredModules = append(filteredModules, m)
|
||||
}
|
||||
return filteredModules, nil
|
||||
}
|
||||
@@ -142,6 +142,13 @@ func (s *server) Plan(
|
||||
return provisionersdk.PlanErrorf("initialize terraform: %s", err)
|
||||
}
|
||||
|
||||
modules, err := getModules(sess.WorkDirectory)
|
||||
if err != nil {
|
||||
// We allow getModules to fail, as the result is used only
|
||||
// for telemetry purposes now.
|
||||
s.logger.Error(ctx, "failed to get modules from disk", slog.Error(err))
|
||||
}
|
||||
|
||||
initTimings.ingest(createInitTimingsEvent(timingInitComplete))
|
||||
|
||||
s.logger.Debug(ctx, "ran initialization")
|
||||
@@ -167,6 +174,7 @@ func (s *server) Plan(
|
||||
// Prepend init timings since they occur prior to plan timings.
|
||||
// Order is irrelevant; this is merely indicative.
|
||||
resp.Timings = append(initTimings.aggregate(), resp.Timings...)
|
||||
resp.Modules = modules
|
||||
return resp
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,21 @@ func makeTar(t *testing.T, files map[string]string) []byte {
|
||||
t.Helper()
|
||||
var buffer bytes.Buffer
|
||||
writer := tar.NewWriter(&buffer)
|
||||
|
||||
addedDirs := make(map[string]bool)
|
||||
for name, content := range files {
|
||||
// Add parent directories if they don't exist
|
||||
dir := filepath.Dir(name)
|
||||
if dir != "." && !addedDirs[dir] {
|
||||
err := writer.WriteHeader(&tar.Header{
|
||||
Name: dir + "/", // Directory names must end with /
|
||||
Mode: 0o755,
|
||||
Typeflag: tar.TypeDir,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
addedDirs[dir] = true
|
||||
}
|
||||
|
||||
err := writer.WriteHeader(&tar.Header{
|
||||
Name: name,
|
||||
Size: int64(len(content)),
|
||||
@@ -745,6 +759,45 @@ func TestProvision(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "returns-modules",
|
||||
Files: map[string]string{
|
||||
"main.tf": `module "hello" {
|
||||
source = "./module"
|
||||
}`,
|
||||
"module/module.tf": `
|
||||
resource "null_resource" "example" {}
|
||||
|
||||
module "there" {
|
||||
source = "./inner_module"
|
||||
}
|
||||
`,
|
||||
"module/inner_module/inner_module.tf": `
|
||||
resource "null_resource" "inner_example" {}
|
||||
`,
|
||||
},
|
||||
Request: &proto.PlanRequest{},
|
||||
Response: &proto.PlanComplete{
|
||||
Resources: []*proto.Resource{{
|
||||
Name: "example",
|
||||
Type: "null_resource",
|
||||
ModulePath: "module.hello",
|
||||
}, {
|
||||
Name: "inner_example",
|
||||
Type: "null_resource",
|
||||
ModulePath: "module.hello.module.there",
|
||||
}},
|
||||
Modules: []*proto.Module{{
|
||||
Key: "hello",
|
||||
Version: "",
|
||||
Source: "./module",
|
||||
}, {
|
||||
Key: "hello.there",
|
||||
Version: "",
|
||||
Source: "./inner_module",
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
@@ -799,7 +852,7 @@ func TestProvision(t *testing.T) {
|
||||
if testCase.Response != nil {
|
||||
require.Equal(t, testCase.Response.Error, planComplete.Error)
|
||||
|
||||
// Remove randomly generated data.
|
||||
// Remove randomly generated data and sort by name.
|
||||
normalizeResources(planComplete.Resources)
|
||||
resourcesGot, err := json.Marshal(planComplete.Resources)
|
||||
require.NoError(t, err)
|
||||
@@ -812,6 +865,12 @@ func TestProvision(t *testing.T) {
|
||||
parametersWant, err := json.Marshal(testCase.Response.Parameters)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(parametersWant), string(parametersGot))
|
||||
|
||||
modulesGot, err := json.Marshal(planComplete.Modules)
|
||||
require.NoError(t, err)
|
||||
modulesWant, err := json.Marshal(testCase.Response.Modules)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(modulesWant), string(modulesGot))
|
||||
}
|
||||
|
||||
if testCase.Apply {
|
||||
@@ -852,6 +911,9 @@ func normalizeResources(resources []*proto.Resource) {
|
||||
agent.Auth = &proto.Agent_Token{}
|
||||
}
|
||||
}
|
||||
sort.Slice(resources, func(i, j int) bool {
|
||||
return resources[i].Name < resources[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// nolint:paralleltest
|
||||
@@ -929,3 +991,20 @@ func TestProvision_SafeEnv(t *testing.T) {
|
||||
require.NotContains(t, log, secretValue)
|
||||
require.Contains(t, log, "CODER_")
|
||||
}
|
||||
|
||||
func TestProvision_MalformedModules(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, api := setupProvisioner(t, nil)
|
||||
sess := configure(ctx, t, api, &proto.Config{
|
||||
TemplateSourceArchive: makeTar(t, map[string]string{
|
||||
"main.tf": `module "hello" { source = "./module" }`,
|
||||
"module/module.tf": `resource "null_`,
|
||||
}),
|
||||
})
|
||||
|
||||
err := sendPlan(sess, proto.WorkspaceTransition_START)
|
||||
require.NoError(t, err)
|
||||
log := readProvisionLog(t, sess)
|
||||
require.Contains(t, log, "Invalid block definition")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -9,8 +10,12 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/terraform-provider-coder/provider"
|
||||
|
||||
tfaddr "github.com/hashicorp/go-terraform-address"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
stringutil "github.com/coder/coder/v2/coderd/util/strings"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
@@ -129,10 +134,12 @@ type State struct {
|
||||
ExternalAuthProviders []*proto.ExternalAuthProviderResource
|
||||
}
|
||||
|
||||
var ErrInvalidTerraformAddr = xerrors.New("invalid terraform address")
|
||||
|
||||
// ConvertState consumes Terraform state and a GraphViz representation
|
||||
// produced by `terraform graph` to produce resources consumable by Coder.
|
||||
// nolint:gocognit // This function makes more sense being large for now, until refactored.
|
||||
func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error) {
|
||||
func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph string, logger slog.Logger) (*State, error) {
|
||||
parsedGraph, err := gographviz.ParseString(rawGraph)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parse graph: %w", err)
|
||||
@@ -593,6 +600,20 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
|
||||
continue
|
||||
}
|
||||
label := convertAddressToLabel(resource.Address)
|
||||
modulePath, err := convertAddressToModulePath(resource.Address)
|
||||
if err != nil {
|
||||
// Module path recording was added primarily to keep track of
|
||||
// modules in telemetry. We're adding this sentinel value so
|
||||
// we can detect if there are any issues with the address
|
||||
// parsing.
|
||||
//
|
||||
// We don't want to set modulePath to null here because, in
|
||||
// the database, a null value in WorkspaceResource's ModulePath
|
||||
// indicates "this resource was created before module paths
|
||||
// were tracked."
|
||||
modulePath = fmt.Sprintf("%s", ErrInvalidTerraformAddr)
|
||||
logger.Error(ctx, "failed to parse Terraform address", slog.F("address", resource.Address))
|
||||
}
|
||||
|
||||
agents, exists := resourceAgents[label]
|
||||
if exists {
|
||||
@@ -608,6 +629,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error
|
||||
Icon: resourceIcon[label],
|
||||
DailyCost: resourceCost[label],
|
||||
InstanceType: applyInstanceType(resource),
|
||||
ModulePath: modulePath,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -752,6 +774,20 @@ func convertAddressToLabel(address string) string {
|
||||
return cut
|
||||
}
|
||||
|
||||
// convertAddressToModulePath returns the module path from a Terraform address.
|
||||
// eg. "module.ec2_dev.ec2_instance.dev[0]" becomes "module.ec2_dev".
|
||||
// Empty string is returned for the root module.
|
||||
//
|
||||
// Module paths are defined in the Terraform spec:
|
||||
// https://github.com/hashicorp/terraform/blob/ef071f3d0e49ba421ae931c65b263827a8af1adb/website/docs/internals/resource-addressing.html.markdown#module-path
|
||||
func convertAddressToModulePath(address string) (string, error) {
|
||||
addr, err := tfaddr.NewAddress(address)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("parse terraform address: %w", err)
|
||||
}
|
||||
return addr.ModulePath.String(), nil
|
||||
}
|
||||
|
||||
func dependsOnAgent(graph *gographviz.Graph, agent *proto.Agent, resourceAgentID string, resource *tfjson.StateResource) bool {
|
||||
// Plan: we need to find if there is edge between the agent and the resource.
|
||||
if agent.Id == "" && resourceAgentID == "" {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package terraform_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -14,11 +15,18 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
protobuf "google.golang.org/protobuf/proto"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
|
||||
"github.com/coder/coder/v2/cryptorand"
|
||||
"github.com/coder/coder/v2/provisioner/terraform"
|
||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||
)
|
||||
|
||||
func ctxAndLogger(t *testing.T) (context.Context, slog.Logger) {
|
||||
return context.Background(), slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
}
|
||||
|
||||
func TestConvertResources(t *testing.T) {
|
||||
t.Parallel()
|
||||
// nolint:dogsled
|
||||
@@ -109,6 +117,7 @@ func TestConvertResources(t *testing.T) {
|
||||
ConnectionTimeoutSeconds: 120,
|
||||
DisplayApps: &displayApps,
|
||||
}},
|
||||
ModulePath: "module.module",
|
||||
}},
|
||||
},
|
||||
// Ensures the attachment of multiple agents to a single
|
||||
@@ -687,6 +696,7 @@ func TestConvertResources(t *testing.T) {
|
||||
dir := filepath.Join(filepath.Dir(filename), "testdata", folderName)
|
||||
t.Run("Plan", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
tfPlanRaw, err := os.ReadFile(filepath.Join(dir, folderName+".tfplan.json"))
|
||||
require.NoError(t, err)
|
||||
@@ -704,7 +714,7 @@ func TestConvertResources(t *testing.T) {
|
||||
// and that no errors occur!
|
||||
modules = append(modules, tfPlan.PlannedValues.RootModule)
|
||||
}
|
||||
state, err := terraform.ConvertState(modules, string(tfPlanGraph))
|
||||
state, err := terraform.ConvertState(ctx, modules, string(tfPlanGraph), logger)
|
||||
require.NoError(t, err)
|
||||
sortResources(state.Resources)
|
||||
sortExternalAuthProviders(state.ExternalAuthProviders)
|
||||
@@ -762,6 +772,7 @@ func TestConvertResources(t *testing.T) {
|
||||
|
||||
t.Run("Provision", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
tfStateRaw, err := os.ReadFile(filepath.Join(dir, folderName+".tfstate.json"))
|
||||
require.NoError(t, err)
|
||||
var tfState tfjson.State
|
||||
@@ -770,7 +781,7 @@ func TestConvertResources(t *testing.T) {
|
||||
tfStateGraph, err := os.ReadFile(filepath.Join(dir, folderName+".tfstate.dot"))
|
||||
require.NoError(t, err)
|
||||
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph))
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfState.Values.RootModule}, string(tfStateGraph), logger)
|
||||
require.NoError(t, err)
|
||||
sortResources(state.Resources)
|
||||
sortExternalAuthProviders(state.ExternalAuthProviders)
|
||||
@@ -805,8 +816,27 @@ func TestConvertResources(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidTerraformAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := context.Background(), slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{{
|
||||
Resources: []*tfjson.StateResource{{
|
||||
Address: "invalid",
|
||||
Type: "invalid",
|
||||
Name: "invalid",
|
||||
Mode: tfjson.ManagedResourceMode,
|
||||
AttributeValues: map[string]interface{}{},
|
||||
}},
|
||||
}}, `digraph {}`, logger)
|
||||
require.Nil(t, err)
|
||||
require.Len(t, state.Resources, 1)
|
||||
require.Equal(t, state.Resources[0].Name, "invalid")
|
||||
require.Equal(t, state.Resources[0].ModulePath, "invalid terraform address")
|
||||
}
|
||||
|
||||
func TestAppSlugValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
// nolint:dogsled
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
@@ -828,7 +858,7 @@ func TestAppSlugValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph))
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "invalid app slug")
|
||||
@@ -840,7 +870,7 @@ func TestAppSlugValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph))
|
||||
state, err = terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "duplicate app slug")
|
||||
@@ -848,6 +878,7 @@ func TestAppSlugValidation(t *testing.T) {
|
||||
|
||||
func TestMetadataResourceDuplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
// Load the multiple-apps state file and edit it.
|
||||
dir := filepath.Join("testdata", "resource-metadata-duplicate")
|
||||
@@ -859,7 +890,7 @@ func TestMetadataResourceDuplicate(t *testing.T) {
|
||||
tfPlanGraph, err := os.ReadFile(filepath.Join(dir, "resource-metadata-duplicate.tfplan.dot"))
|
||||
require.NoError(t, err)
|
||||
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph))
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PlannedValues.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "duplicate metadata resource: null_resource.about")
|
||||
@@ -867,6 +898,7 @@ func TestMetadataResourceDuplicate(t *testing.T) {
|
||||
|
||||
func TestParameterValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
|
||||
// nolint:dogsled
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
@@ -890,7 +922,7 @@ func TestParameterValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical\" appears multiple times")
|
||||
@@ -906,7 +938,7 @@ func TestParameterValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
|
||||
state, err = terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical-0\" and \"identical-1\" appear multiple times")
|
||||
@@ -922,7 +954,7 @@ func TestParameterValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
state, err = terraform.ConvertState([]*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph))
|
||||
state, err = terraform.ConvertState(ctx, []*tfjson.StateModule{tfPlan.PriorState.Values.RootModule}, string(tfPlanGraph), logger)
|
||||
require.Nil(t, state)
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "coder_parameter names must be unique but \"identical-0\", \"identical-1\" and \"identical-2\" appear multiple times")
|
||||
@@ -953,9 +985,10 @@ func TestInstanceTypeAssociation(t *testing.T) {
|
||||
tc := tc
|
||||
t.Run(tc.ResourceType, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
instanceType, err := cryptorand.String(12)
|
||||
require.NoError(t, err)
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{{
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{{
|
||||
Resources: []*tfjson.StateResource{{
|
||||
Address: tc.ResourceType + ".dev",
|
||||
Type: tc.ResourceType,
|
||||
@@ -972,7 +1005,7 @@ func TestInstanceTypeAssociation(t *testing.T) {
|
||||
subgraph "root" {
|
||||
"[root] `+tc.ResourceType+`.dev" [label = "`+tc.ResourceType+`.dev", shape = "box"]
|
||||
}
|
||||
}`)
|
||||
}`, logger)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, state.Resources, 1)
|
||||
require.Equal(t, state.Resources[0].GetInstanceType(), instanceType)
|
||||
@@ -1011,9 +1044,10 @@ func TestInstanceIDAssociation(t *testing.T) {
|
||||
tc := tc
|
||||
t.Run(tc.ResourceType, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, logger := ctxAndLogger(t)
|
||||
instanceID, err := cryptorand.String(12)
|
||||
require.NoError(t, err)
|
||||
state, err := terraform.ConvertState([]*tfjson.StateModule{{
|
||||
state, err := terraform.ConvertState(ctx, []*tfjson.StateModule{{
|
||||
Resources: []*tfjson.StateResource{{
|
||||
Address: "coder_agent.dev",
|
||||
Type: "coder_agent",
|
||||
@@ -1043,7 +1077,7 @@ func TestInstanceIDAssociation(t *testing.T) {
|
||||
"[root] `+tc.ResourceType+`.dev" -> "[root] coder_agent.dev"
|
||||
}
|
||||
}
|
||||
`)
|
||||
`, logger)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, state.Resources, 1)
|
||||
require.Len(t, state.Resources[0].Agents, 1)
|
||||
|
||||
Generated
+104
-54
@@ -1215,6 +1215,7 @@ type CompletedJob_WorkspaceBuild struct {
|
||||
State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
|
||||
Resources []*proto.Resource `protobuf:"bytes,2,rep,name=resources,proto3" json:"resources,omitempty"`
|
||||
Timings []*proto.Timing `protobuf:"bytes,3,rep,name=timings,proto3" json:"timings,omitempty"`
|
||||
Modules []*proto.Module `protobuf:"bytes,4,rep,name=modules,proto3" json:"modules,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CompletedJob_WorkspaceBuild) Reset() {
|
||||
@@ -1270,6 +1271,13 @@ func (x *CompletedJob_WorkspaceBuild) GetTimings() []*proto.Timing {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CompletedJob_WorkspaceBuild) GetModules() []*proto.Module {
|
||||
if x != nil {
|
||||
return x.Modules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CompletedJob_TemplateImport struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1280,6 +1288,8 @@ type CompletedJob_TemplateImport struct {
|
||||
RichParameters []*proto.RichParameter `protobuf:"bytes,3,rep,name=rich_parameters,json=richParameters,proto3" json:"rich_parameters,omitempty"`
|
||||
ExternalAuthProvidersNames []string `protobuf:"bytes,4,rep,name=external_auth_providers_names,json=externalAuthProvidersNames,proto3" json:"external_auth_providers_names,omitempty"`
|
||||
ExternalAuthProviders []*proto.ExternalAuthProviderResource `protobuf:"bytes,5,rep,name=external_auth_providers,json=externalAuthProviders,proto3" json:"external_auth_providers,omitempty"`
|
||||
StartModules []*proto.Module `protobuf:"bytes,6,rep,name=start_modules,json=startModules,proto3" json:"start_modules,omitempty"`
|
||||
StopModules []*proto.Module `protobuf:"bytes,7,rep,name=stop_modules,json=stopModules,proto3" json:"stop_modules,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CompletedJob_TemplateImport) Reset() {
|
||||
@@ -1349,12 +1359,27 @@ func (x *CompletedJob_TemplateImport) GetExternalAuthProviders() []*proto.Extern
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CompletedJob_TemplateImport) GetStartModules() []*proto.Module {
|
||||
if x != nil {
|
||||
return x.StartModules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CompletedJob_TemplateImport) GetStopModules() []*proto.Module {
|
||||
if x != nil {
|
||||
return x.StopModules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CompletedJob_TemplateDryRun struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Resources []*proto.Resource `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"`
|
||||
Modules []*proto.Module `protobuf:"bytes,2,rep,name=modules,proto3" json:"modules,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CompletedJob_TemplateDryRun) Reset() {
|
||||
@@ -1396,6 +1421,13 @@ func (x *CompletedJob_TemplateDryRun) GetResources() []*proto.Resource {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CompletedJob_TemplateDryRun) GetModules() []*proto.Module {
|
||||
if x != nil {
|
||||
return x.Modules
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_provisionerd_proto_provisionerd_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
||||
@@ -1524,7 +1556,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
||||
0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72,
|
||||
0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x80, 0x07, 0x0a,
|
||||
0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd0, 0x08, 0x0a,
|
||||
0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a,
|
||||
0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a,
|
||||
0x6f, 0x62, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
|
||||
@@ -1543,7 +1575,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44,
|
||||
0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x8a, 0x01, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
|
||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0xb9, 0x01, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
|
||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74,
|
||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||
0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20,
|
||||
@@ -1552,35 +1584,48 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x1a, 0xf9, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||
0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74,
|
||||
0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x69, 0x6e, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
|
||||
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75,
|
||||
0x6c, 0x65, 0x73, 0x1a, 0xeb, 0x03, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||
0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61,
|
||||
0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63,
|
||||
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69, 0x63, 0x68,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x65, 0x78,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x64, 0x65, 0x72, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x61, 0x0a,
|
||||
0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
||||
0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73,
|
||||
0x1a, 0x45, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52,
|
||||
0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72,
|
||||
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68,
|
||||
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69, 0x63, 0x68, 0x50,
|
||||
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x1d, 0x65, 0x78, 0x74,
|
||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||
0x64, 0x65, 0x72, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x61, 0x0a, 0x17,
|
||||
0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72,
|
||||
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
||||
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12,
|
||||
0x38, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
|
||||
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x0c, 0x73, 0x74, 0x61,
|
||||
0x72, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0c, 0x73, 0x74, 0x6f,
|
||||
0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x6f,
|
||||
0x64, 0x75, 0x6c, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x70, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
|
||||
0x73, 0x1a, 0x74, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79,
|
||||
0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75,
|
||||
0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07,
|
||||
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22,
|
||||
0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
@@ -1722,8 +1767,9 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
||||
(*proto.Metadata)(nil), // 27: provisioner.Metadata
|
||||
(*proto.Timing)(nil), // 28: provisioner.Timing
|
||||
(*proto.Resource)(nil), // 29: provisioner.Resource
|
||||
(*proto.RichParameter)(nil), // 30: provisioner.RichParameter
|
||||
(*proto.ExternalAuthProviderResource)(nil), // 31: provisioner.ExternalAuthProviderResource
|
||||
(*proto.Module)(nil), // 30: provisioner.Module
|
||||
(*proto.RichParameter)(nil), // 31: provisioner.RichParameter
|
||||
(*proto.ExternalAuthProviderResource)(nil), // 32: provisioner.ExternalAuthProviderResource
|
||||
}
|
||||
var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
|
||||
11, // 0: provisionerd.AcquiredJob.workspace_build:type_name -> provisionerd.AcquiredJob.WorkspaceBuild
|
||||
@@ -1755,28 +1801,32 @@ var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
|
||||
28, // 26: provisionerd.FailedJob.WorkspaceBuild.timings:type_name -> provisioner.Timing
|
||||
29, // 27: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
|
||||
28, // 28: provisionerd.CompletedJob.WorkspaceBuild.timings:type_name -> provisioner.Timing
|
||||
29, // 29: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
|
||||
29, // 30: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
|
||||
30, // 31: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
|
||||
31, // 32: provisionerd.CompletedJob.TemplateImport.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
|
||||
29, // 33: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
|
||||
1, // 34: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
|
||||
10, // 35: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
|
||||
8, // 36: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
|
||||
6, // 37: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
|
||||
3, // 38: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
|
||||
4, // 39: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
|
||||
2, // 40: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
|
||||
2, // 41: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
|
||||
9, // 42: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
|
||||
7, // 43: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
|
||||
1, // 44: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
|
||||
1, // 45: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
|
||||
40, // [40:46] is the sub-list for method output_type
|
||||
34, // [34:40] is the sub-list for method input_type
|
||||
34, // [34:34] is the sub-list for extension type_name
|
||||
34, // [34:34] is the sub-list for extension extendee
|
||||
0, // [0:34] is the sub-list for field type_name
|
||||
30, // 29: provisionerd.CompletedJob.WorkspaceBuild.modules:type_name -> provisioner.Module
|
||||
29, // 30: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
|
||||
29, // 31: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
|
||||
31, // 32: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
|
||||
32, // 33: provisionerd.CompletedJob.TemplateImport.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
|
||||
30, // 34: provisionerd.CompletedJob.TemplateImport.start_modules:type_name -> provisioner.Module
|
||||
30, // 35: provisionerd.CompletedJob.TemplateImport.stop_modules:type_name -> provisioner.Module
|
||||
29, // 36: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
|
||||
30, // 37: provisionerd.CompletedJob.TemplateDryRun.modules:type_name -> provisioner.Module
|
||||
1, // 38: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
|
||||
10, // 39: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
|
||||
8, // 40: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
|
||||
6, // 41: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
|
||||
3, // 42: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
|
||||
4, // 43: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
|
||||
2, // 44: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
|
||||
2, // 45: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
|
||||
9, // 46: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
|
||||
7, // 47: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
|
||||
1, // 48: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
|
||||
1, // 49: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
|
||||
44, // [44:50] is the sub-list for method output_type
|
||||
38, // [38:44] is the sub-list for method input_type
|
||||
38, // [38:38] is the sub-list for extension type_name
|
||||
38, // [38:38] is the sub-list for extension extendee
|
||||
0, // [0:38] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_provisionerd_proto_provisionerd_proto_init() }
|
||||
|
||||
@@ -74,6 +74,7 @@ message CompletedJob {
|
||||
bytes state = 1;
|
||||
repeated provisioner.Resource resources = 2;
|
||||
repeated provisioner.Timing timings = 3;
|
||||
repeated provisioner.Module modules = 4;
|
||||
}
|
||||
message TemplateImport {
|
||||
repeated provisioner.Resource start_resources = 1;
|
||||
@@ -81,9 +82,12 @@ message CompletedJob {
|
||||
repeated provisioner.RichParameter rich_parameters = 3;
|
||||
repeated string external_auth_providers_names = 4;
|
||||
repeated provisioner.ExternalAuthProviderResource external_auth_providers = 5;
|
||||
repeated provisioner.Module start_modules = 6;
|
||||
repeated provisioner.Module stop_modules = 7;
|
||||
}
|
||||
message TemplateDryRun {
|
||||
repeated provisioner.Resource resources = 1;
|
||||
repeated provisioner.Module modules = 2;
|
||||
}
|
||||
|
||||
string job_id = 1;
|
||||
|
||||
@@ -581,6 +581,8 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
||||
RichParameters: startProvision.Parameters,
|
||||
ExternalAuthProvidersNames: externalAuthProviderNames,
|
||||
ExternalAuthProviders: startProvision.ExternalAuthProviders,
|
||||
StartModules: startProvision.Modules,
|
||||
StopModules: stopProvision.Modules,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
@@ -640,6 +642,7 @@ type templateImportProvision struct {
|
||||
Resources []*sdkproto.Resource
|
||||
Parameters []*sdkproto.RichParameter
|
||||
ExternalAuthProviders []*sdkproto.ExternalAuthProviderResource
|
||||
Modules []*sdkproto.Module
|
||||
}
|
||||
|
||||
// Performs a dry-run provision when importing a template.
|
||||
@@ -731,6 +734,7 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(
|
||||
Resources: c.Resources,
|
||||
Parameters: c.Parameters,
|
||||
ExternalAuthProviders: c.ExternalAuthProviders,
|
||||
Modules: c.Modules,
|
||||
}, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("invalid message type %q received from provisioner",
|
||||
@@ -793,6 +797,7 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p
|
||||
Type: &proto.CompletedJob_TemplateDryRun_{
|
||||
TemplateDryRun: &proto.CompletedJob_TemplateDryRun{
|
||||
Resources: provision.Resources,
|
||||
Modules: provision.Modules,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
@@ -1033,6 +1038,10 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
||||
State: applyComplete.State,
|
||||
Resources: applyComplete.Resources,
|
||||
Timings: applyComplete.Timings,
|
||||
// Modules are created on disk by `terraform init`, and that is only
|
||||
// called by `plan`. `apply` does not modify them, so we can use the
|
||||
// modules from the plan response.
|
||||
Modules: planComplete.Modules,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
Generated
+432
-329
File diff suppressed because it is too large
Load Diff
@@ -204,6 +204,13 @@ message Resource {
|
||||
string icon = 6;
|
||||
string instance_type = 7;
|
||||
int32 daily_cost = 8;
|
||||
string module_path = 9;
|
||||
}
|
||||
|
||||
message Module {
|
||||
string source = 1;
|
||||
string version = 2;
|
||||
string key = 3;
|
||||
}
|
||||
|
||||
// WorkspaceTransition is the desired outcome of a build
|
||||
@@ -271,6 +278,7 @@ message PlanComplete {
|
||||
repeated RichParameter parameters = 3;
|
||||
repeated ExternalAuthProviderResource external_auth_providers = 4;
|
||||
repeated Timing timings = 6;
|
||||
repeated Module modules = 7;
|
||||
}
|
||||
|
||||
// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
|
||||
|
||||
@@ -608,6 +608,7 @@ const createTemplateVersionTar = async (
|
||||
metadata: [],
|
||||
name: "dev",
|
||||
type: "echo",
|
||||
modulePath: "",
|
||||
...resource,
|
||||
} as Resource;
|
||||
};
|
||||
@@ -636,6 +637,7 @@ const createTemplateVersionTar = async (
|
||||
parameters: [],
|
||||
externalAuthProviders: [],
|
||||
timings: [],
|
||||
modules: [],
|
||||
...response.plan,
|
||||
} as PlanComplete;
|
||||
response.plan.resources = response.plan.resources?.map(fillResource);
|
||||
|
||||
Generated
+32
@@ -215,6 +215,7 @@ export interface Resource {
|
||||
icon: string;
|
||||
instanceType: string;
|
||||
dailyCost: number;
|
||||
modulePath: string;
|
||||
}
|
||||
|
||||
export interface Resource_Metadata {
|
||||
@@ -224,6 +225,12 @@ export interface Resource_Metadata {
|
||||
isNull: boolean;
|
||||
}
|
||||
|
||||
export interface Module {
|
||||
source: string;
|
||||
version: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
/** Metadata is information about a workspace used in the execution of a build */
|
||||
export interface Metadata {
|
||||
coderUrl: string;
|
||||
@@ -286,6 +293,7 @@ export interface PlanComplete {
|
||||
parameters: RichParameter[];
|
||||
externalAuthProviders: ExternalAuthProviderResource[];
|
||||
timings: Timing[];
|
||||
modules: Module[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,6 +806,9 @@ export const Resource = {
|
||||
if (message.dailyCost !== 0) {
|
||||
writer.uint32(64).int32(message.dailyCost);
|
||||
}
|
||||
if (message.modulePath !== "") {
|
||||
writer.uint32(74).string(message.modulePath);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
@@ -823,6 +834,24 @@ export const Resource_Metadata = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Module = {
|
||||
encode(
|
||||
message: Module,
|
||||
writer: _m0.Writer = _m0.Writer.create(),
|
||||
): _m0.Writer {
|
||||
if (message.source !== "") {
|
||||
writer.uint32(10).string(message.source);
|
||||
}
|
||||
if (message.version !== "") {
|
||||
writer.uint32(18).string(message.version);
|
||||
}
|
||||
if (message.key !== "") {
|
||||
writer.uint32(26).string(message.key);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
|
||||
export const Metadata = {
|
||||
encode(
|
||||
message: Metadata,
|
||||
@@ -996,6 +1025,9 @@ export const PlanComplete = {
|
||||
for (const v of message.timings) {
|
||||
Timing.encode(v!, writer.uint32(50).fork()).ldelim();
|
||||
}
|
||||
for (const v of message.modules) {
|
||||
Module.encode(v!, writer.uint32(58).fork()).ldelim();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user