feat: add provisioning timings to understand slow build times (#14274)

This commit is contained in:
Danny Kopping
2024-08-21 14:18:58 +02:00
committed by GitHub
parent 9c8c6a952d
commit 6960d194ae
42 changed files with 2653 additions and 701 deletions
+8
View File
@@ -2793,6 +2793,14 @@ func (q *querier) InsertProvisionerJobLogs(ctx context.Context, arg database.Ins
return q.db.InsertProvisionerJobLogs(ctx, arg)
}
// TODO: We need to create a ProvisionerJob resource type
func (q *querier) InsertProvisionerJobTimings(ctx context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
// if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
// return nil, err
// }
return q.db.InsertProvisionerJobTimings(ctx, arg)
}
func (q *querier) InsertProvisionerKey(ctx context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
return insert(q.log, q.auth, rbac.ResourceProvisionerKeys.InOrg(arg.OrganizationID).WithID(arg.ID), q.db.InsertProvisionerKey)(ctx, arg)
}
+7
View File
@@ -2470,6 +2470,13 @@ func (s *MethodTestSuite) TestSystemFunctions() {
JobID: j.ID,
}).Asserts( /*rbac.ResourceSystem, policy.ActionCreate*/ )
}))
s.Run("InsertProvisionerJobTimings", s.Subtest(func(db database.Store, check *expects) {
// TODO: we need to create a ProvisionerJob resource
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
check.Args(database.InsertProvisionerJobTimingsParams{
JobID: j.ID,
}).Asserts( /*rbac.ResourceSystem, policy.ActionCreate*/ )
}))
s.Run("UpsertProvisionerDaemon", s.Subtest(func(db database.Store, check *expects) {
org := dbgen.Organization(s.T(), db, database.Organization{})
pd := rbac.ResourceProvisionerDaemon.InOrg(org.ID)
+9
View File
@@ -6652,6 +6652,15 @@ func (q *FakeQuerier) InsertProvisionerJobLogs(_ context.Context, arg database.I
return logs, nil
}
func (*FakeQuerier) InsertProvisionerJobTimings(_ context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
err := validateDatabaseType(arg)
if err != nil {
return nil, err
}
return nil, nil
}
func (q *FakeQuerier) InsertProvisionerKey(_ context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
err := validateDatabaseType(arg)
if err != nil {
+7
View File
@@ -1712,6 +1712,13 @@ func (m metricsStore) InsertProvisionerJobLogs(ctx context.Context, arg database
return logs, err
}
func (m metricsStore) InsertProvisionerJobTimings(ctx context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
start := time.Now()
r0, r1 := m.s.InsertProvisionerJobTimings(ctx, arg)
m.queryLatencies.WithLabelValues("InsertProvisionerJobTimings").Observe(time.Since(start).Seconds())
return r0, r1
}
func (m metricsStore) InsertProvisionerKey(ctx context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
start := time.Now()
r0, r1 := m.s.InsertProvisionerKey(ctx, arg)
+15
View File
@@ -3604,6 +3604,21 @@ func (mr *MockStoreMockRecorder) InsertProvisionerJobLogs(arg0, arg1 any) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertProvisionerJobLogs", reflect.TypeOf((*MockStore)(nil).InsertProvisionerJobLogs), arg0, arg1)
}
// InsertProvisionerJobTimings mocks base method.
func (m *MockStore) InsertProvisionerJobTimings(arg0 context.Context, arg1 database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertProvisionerJobTimings", arg0, arg1)
ret0, _ := ret[0].([]database.ProvisionerJobTiming)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertProvisionerJobTimings indicates an expected call of InsertProvisionerJobTimings.
func (mr *MockStoreMockRecorder) InsertProvisionerJobTimings(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertProvisionerJobTimings", reflect.TypeOf((*MockStore)(nil).InsertProvisionerJobTimings), arg0, arg1)
}
// InsertProvisionerKey mocks base method.
func (m *MockStore) InsertProvisionerKey(arg0 context.Context, arg1 database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
m.ctrl.T.Helper()
+4
View File
@@ -9,6 +9,10 @@ func Now() time.Time {
// Time returns a time compatible with Postgres. Postgres only stores dates with
// microsecond precision.
// FIXME(dannyk): refactor all calls to Time() to expect the input time to be modified to UTC; there are currently a
//
// few calls whose behavior would change subtly.
// See https://github.com/coder/coder/pull/14274#discussion_r1718427461
func Time(t time.Time) time.Time {
return t.Round(time.Microsecond)
}
+89
View File
@@ -136,6 +136,13 @@ CREATE TYPE provisioner_job_status AS ENUM (
COMMENT ON TYPE provisioner_job_status IS 'Computed status of a provisioner job. Jobs could be stuck in a hung state, these states do not guarantee any transition to another state.';
CREATE TYPE provisioner_job_timing_stage AS ENUM (
'init',
'plan',
'graph',
'apply'
);
CREATE TYPE provisioner_job_type AS ENUM (
'template_version_import',
'workspace_build',
@@ -849,6 +856,33 @@ CREATE SEQUENCE provisioner_job_logs_id_seq
ALTER SEQUENCE provisioner_job_logs_id_seq OWNED BY provisioner_job_logs.id;
CREATE VIEW provisioner_job_stats AS
SELECT
NULL::uuid AS job_id,
NULL::provisioner_job_status AS job_status,
NULL::uuid AS workspace_id,
NULL::uuid AS worker_id,
NULL::text AS error,
NULL::text AS error_code,
NULL::timestamp with time zone AS updated_at,
NULL::double precision AS queued_secs,
NULL::double precision AS completion_secs,
NULL::double precision AS canceled_secs,
NULL::double precision AS init_secs,
NULL::double precision AS plan_secs,
NULL::double precision AS graph_secs,
NULL::double precision AS apply_secs;
CREATE TABLE provisioner_job_timings (
job_id uuid NOT NULL,
started_at timestamp with time zone NOT NULL,
ended_at timestamp with time zone NOT NULL,
stage provisioner_job_timing_stage NOT NULL,
source text NOT NULL,
action text NOT NULL,
resource text NOT NULL
);
CREATE TABLE provisioner_jobs (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -1915,6 +1949,58 @@ CREATE INDEX workspace_resources_job_id_idx ON workspace_resources USING btree (
CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false);
CREATE OR REPLACE VIEW provisioner_job_stats AS
SELECT pj.id AS job_id,
pj.job_status,
wb.workspace_id,
pj.worker_id,
pj.error,
pj.error_code,
pj.updated_at,
GREATEST(date_part('epoch'::text, (pj.started_at - pj.created_at)), (0)::double precision) AS queued_secs,
GREATEST(date_part('epoch'::text, (pj.completed_at - pj.started_at)), (0)::double precision) AS completion_secs,
GREATEST(date_part('epoch'::text, (pj.canceled_at - pj.started_at)), (0)::double precision) AS canceled_secs,
GREATEST(date_part('epoch'::text, (max(
CASE
WHEN (pjt.stage = 'init'::provisioner_job_timing_stage) THEN pjt.ended_at
ELSE NULL::timestamp with time zone
END) - min(
CASE
WHEN (pjt.stage = 'init'::provisioner_job_timing_stage) THEN pjt.started_at
ELSE NULL::timestamp with time zone
END))), (0)::double precision) AS init_secs,
GREATEST(date_part('epoch'::text, (max(
CASE
WHEN (pjt.stage = 'plan'::provisioner_job_timing_stage) THEN pjt.ended_at
ELSE NULL::timestamp with time zone
END) - min(
CASE
WHEN (pjt.stage = 'plan'::provisioner_job_timing_stage) THEN pjt.started_at
ELSE NULL::timestamp with time zone
END))), (0)::double precision) AS plan_secs,
GREATEST(date_part('epoch'::text, (max(
CASE
WHEN (pjt.stage = 'graph'::provisioner_job_timing_stage) THEN pjt.ended_at
ELSE NULL::timestamp with time zone
END) - min(
CASE
WHEN (pjt.stage = 'graph'::provisioner_job_timing_stage) THEN pjt.started_at
ELSE NULL::timestamp with time zone
END))), (0)::double precision) AS graph_secs,
GREATEST(date_part('epoch'::text, (max(
CASE
WHEN (pjt.stage = 'apply'::provisioner_job_timing_stage) THEN pjt.ended_at
ELSE NULL::timestamp with time zone
END) - min(
CASE
WHEN (pjt.stage = 'apply'::provisioner_job_timing_stage) THEN pjt.started_at
ELSE NULL::timestamp with time zone
END))), (0)::double precision) AS apply_secs
FROM ((provisioner_jobs pj
JOIN workspace_builds wb ON ((wb.job_id = pj.id)))
LEFT JOIN provisioner_job_timings pjt ON ((pjt.job_id = pj.id)))
GROUP BY pj.id, wb.workspace_id;
CREATE TRIGGER inhibit_enqueue_if_disabled BEFORE INSERT ON notification_messages FOR EACH ROW EXECUTE FUNCTION inhibit_enqueue_if_disabled();
CREATE TRIGGER remove_organization_member_custom_role BEFORE DELETE ON custom_roles FOR EACH ROW EXECUTE FUNCTION remove_organization_member_role();
@@ -2012,6 +2098,9 @@ ALTER TABLE ONLY provisioner_daemons
ALTER TABLE ONLY provisioner_job_logs
ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_job_timings
ADD CONSTRAINT provisioner_job_timings_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_jobs
ADD CONSTRAINT provisioner_jobs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
@@ -29,6 +29,7 @@ const (
ForeignKeyParameterSchemasJobID ForeignKeyConstraint = "parameter_schemas_job_id_fkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyProvisionerDaemonsOrganizationID ForeignKeyConstraint = "provisioner_daemons_organization_id_fkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyProvisionerJobLogsJobID ForeignKeyConstraint = "provisioner_job_logs_job_id_fkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyProvisionerJobTimingsJobID ForeignKeyConstraint = "provisioner_job_timings_job_id_fkey" // ALTER TABLE ONLY provisioner_job_timings ADD CONSTRAINT provisioner_job_timings_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyProvisionerJobsOrganizationID ForeignKeyConstraint = "provisioner_jobs_organization_id_fkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyProvisionerKeysOrganizationID ForeignKeyConstraint = "provisioner_keys_organization_id_fkey" // ALTER TABLE ONLY provisioner_keys ADD CONSTRAINT provisioner_keys_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyTailnetAgentsCoordinatorID ForeignKeyConstraint = "tailnet_agents_coordinator_id_fkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_coordinator_id_fkey FOREIGN KEY (coordinator_id) REFERENCES tailnet_coordinators(id) ON DELETE CASCADE;
@@ -0,0 +1,5 @@
DROP VIEW IF EXISTS provisioner_job_stats;
DROP TYPE IF EXISTS provisioner_job_timing_stage CASCADE;
DROP TABLE IF EXISTS provisioner_job_timings;
@@ -0,0 +1,45 @@
CREATE TYPE provisioner_job_timing_stage AS ENUM (
'init',
'plan',
'graph',
'apply'
);
CREATE TABLE provisioner_job_timings
(
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
started_at timestamp with time zone not null,
ended_at timestamp with time zone not null,
stage provisioner_job_timing_stage not null,
source text not null,
action text not null,
resource text not null
);
CREATE VIEW provisioner_job_stats AS
SELECT pj.id AS job_id,
pj.job_status,
wb.workspace_id,
pj.worker_id,
pj.error,
pj.error_code,
pj.updated_at,
GREATEST(EXTRACT(EPOCH FROM (pj.started_at - pj.created_at)), 0) AS queued_secs,
GREATEST(EXTRACT(EPOCH FROM (pj.completed_at - pj.started_at)), 0) AS completion_secs,
GREATEST(EXTRACT(EPOCH FROM (pj.canceled_at - pj.started_at)), 0) AS canceled_secs,
GREATEST(EXTRACT(EPOCH FROM (
MAX(CASE WHEN pjt.stage = 'init'::provisioner_job_timing_stage THEN pjt.ended_at END) -
MIN(CASE WHEN pjt.stage = 'init'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS init_secs,
GREATEST(EXTRACT(EPOCH FROM (
MAX(CASE WHEN pjt.stage = 'plan'::provisioner_job_timing_stage THEN pjt.ended_at END) -
MIN(CASE WHEN pjt.stage = 'plan'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS plan_secs,
GREATEST(EXTRACT(EPOCH FROM (
MAX(CASE WHEN pjt.stage = 'graph'::provisioner_job_timing_stage THEN pjt.ended_at END) -
MIN(CASE WHEN pjt.stage = 'graph'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS graph_secs,
GREATEST(EXTRACT(EPOCH FROM (
MAX(CASE WHEN pjt.stage = 'apply'::provisioner_job_timing_stage THEN pjt.ended_at END) -
MIN(CASE WHEN pjt.stage = 'apply'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS apply_secs
FROM provisioner_jobs pj
JOIN workspace_builds wb ON wb.job_id = pj.id
LEFT JOIN provisioner_job_timings pjt ON pjt.job_id = pj.id
GROUP BY pj.id, wb.workspace_id;
@@ -0,0 +1,13 @@
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
VALUES
-- Job 1 - init stage
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 55 minutes', NOW() - INTERVAL '1 hour 50 minutes', 'init', 'source1', 'action1', 'resource1'),
-- Job 1 - plan stage
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 50 minutes', NOW() - INTERVAL '1 hour 40 minutes', 'plan', 'source2', 'action2', 'resource2'),
-- Job 1 - graph stage
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 40 minutes', NOW() - INTERVAL '1 hour 30 minutes', 'graph', 'source3', 'action3', 'resource3'),
-- Job 1 - apply stage
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 30 minutes', NOW() - INTERVAL '1 hour 20 minutes', 'apply', 'source4', 'action4', 'resource4');
+91
View File
@@ -1216,6 +1216,70 @@ func AllProvisionerJobStatusValues() []ProvisionerJobStatus {
}
}
type ProvisionerJobTimingStage string
const (
ProvisionerJobTimingStageInit ProvisionerJobTimingStage = "init"
ProvisionerJobTimingStagePlan ProvisionerJobTimingStage = "plan"
ProvisionerJobTimingStageGraph ProvisionerJobTimingStage = "graph"
ProvisionerJobTimingStageApply ProvisionerJobTimingStage = "apply"
)
func (e *ProvisionerJobTimingStage) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = ProvisionerJobTimingStage(s)
case string:
*e = ProvisionerJobTimingStage(s)
default:
return fmt.Errorf("unsupported scan type for ProvisionerJobTimingStage: %T", src)
}
return nil
}
type NullProvisionerJobTimingStage struct {
ProvisionerJobTimingStage ProvisionerJobTimingStage `json:"provisioner_job_timing_stage"`
Valid bool `json:"valid"` // Valid is true if ProvisionerJobTimingStage is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullProvisionerJobTimingStage) Scan(value interface{}) error {
if value == nil {
ns.ProvisionerJobTimingStage, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.ProvisionerJobTimingStage.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullProvisionerJobTimingStage) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.ProvisionerJobTimingStage), nil
}
func (e ProvisionerJobTimingStage) Valid() bool {
switch e {
case ProvisionerJobTimingStageInit,
ProvisionerJobTimingStagePlan,
ProvisionerJobTimingStageGraph,
ProvisionerJobTimingStageApply:
return true
}
return false
}
func AllProvisionerJobTimingStageValues() []ProvisionerJobTimingStage {
return []ProvisionerJobTimingStage{
ProvisionerJobTimingStageInit,
ProvisionerJobTimingStagePlan,
ProvisionerJobTimingStageGraph,
ProvisionerJobTimingStageApply,
}
}
type ProvisionerJobType string
const (
@@ -2282,6 +2346,33 @@ type ProvisionerJobLog struct {
ID int64 `db:"id" json:"id"`
}
type ProvisionerJobStat struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
JobStatus ProvisionerJobStatus `db:"job_status" json:"job_status"`
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
Error sql.NullString `db:"error" json:"error"`
ErrorCode sql.NullString `db:"error_code" json:"error_code"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
QueuedSecs float64 `db:"queued_secs" json:"queued_secs"`
CompletionSecs float64 `db:"completion_secs" json:"completion_secs"`
CanceledSecs float64 `db:"canceled_secs" json:"canceled_secs"`
InitSecs float64 `db:"init_secs" json:"init_secs"`
PlanSecs float64 `db:"plan_secs" json:"plan_secs"`
GraphSecs float64 `db:"graph_secs" json:"graph_secs"`
ApplySecs float64 `db:"apply_secs" json:"apply_secs"`
}
type ProvisionerJobTiming struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
StartedAt time.Time `db:"started_at" json:"started_at"`
EndedAt time.Time `db:"ended_at" json:"ended_at"`
Stage ProvisionerJobTimingStage `db:"stage" json:"stage"`
Source string `db:"source" json:"source"`
Action string `db:"action" json:"action"`
Resource string `db:"resource" json:"resource"`
}
type ProvisionerKey struct {
ID uuid.UUID `db:"id" json:"id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
+1
View File
@@ -357,6 +357,7 @@ type sqlcQuerier interface {
InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error)
InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error)
InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error)
InsertProvisionerJobTimings(ctx context.Context, arg InsertProvisionerJobTimingsParams) ([]ProvisionerJobTiming, error)
InsertProvisionerKey(ctx context.Context, arg InsertProvisionerKeyParams) (ProvisionerKey, error)
InsertReplica(ctx context.Context, arg InsertReplicaParams) (Replica, error)
InsertTemplate(ctx context.Context, arg InsertTemplateParams) error
+62
View File
@@ -5545,6 +5545,68 @@ func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisi
return i, err
}
const insertProvisionerJobTimings = `-- name: InsertProvisionerJobTimings :many
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
SELECT
$1::uuid AS provisioner_job_id,
unnest($2::timestamptz[]),
unnest($3::timestamptz[]),
unnest($4::provisioner_job_timing_stage[]),
unnest($5::text[]),
unnest($6::text[]),
unnest($7::text[])
RETURNING job_id, started_at, ended_at, stage, source, action, resource
`
type InsertProvisionerJobTimingsParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
StartedAt []time.Time `db:"started_at" json:"started_at"`
EndedAt []time.Time `db:"ended_at" json:"ended_at"`
Stage []ProvisionerJobTimingStage `db:"stage" json:"stage"`
Source []string `db:"source" json:"source"`
Action []string `db:"action" json:"action"`
Resource []string `db:"resource" json:"resource"`
}
func (q *sqlQuerier) InsertProvisionerJobTimings(ctx context.Context, arg InsertProvisionerJobTimingsParams) ([]ProvisionerJobTiming, error) {
rows, err := q.db.QueryContext(ctx, insertProvisionerJobTimings,
arg.JobID,
pq.Array(arg.StartedAt),
pq.Array(arg.EndedAt),
pq.Array(arg.Stage),
pq.Array(arg.Source),
pq.Array(arg.Action),
pq.Array(arg.Resource),
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerJobTiming
for rows.Next() {
var i ProvisionerJobTiming
if err := rows.Scan(
&i.JobID,
&i.StartedAt,
&i.EndedAt,
&i.Stage,
&i.Source,
&i.Action,
&i.Resource,
); 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 updateProvisionerJobByID = `-- name: UpdateProvisionerJobByID :exec
UPDATE
provisioner_jobs
@@ -144,3 +144,15 @@ WHERE
updated_at < $1
AND started_at IS NOT NULL
AND completed_at IS NULL;
-- name: InsertProvisionerJobTimings :many
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
SELECT
@job_id::uuid AS provisioner_job_id,
unnest(@started_at::timestamptz[]),
unnest(@ended_at::timestamptz[]),
unnest(@stage::provisioner_job_timing_stage[]),
unnest(@source::text[]),
unnest(@action::text[]),
unnest(@resource::text[])
RETURNING *;
+3
View File
@@ -73,6 +73,9 @@ sql:
- column: "notification_messages.payload"
go_type:
type: "[]byte"
- column: "provisioner_job_stats.*_secs"
go_type:
type: "float64"
rename:
group_member: GroupMemberTable
group_members_expanded: GroupMember
@@ -1441,6 +1441,36 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
return nil, xerrors.Errorf("complete job: %w", err)
}
// Insert timings outside transaction since it is metadata.
// nolint:exhaustruct // The other fields are set further down.
params := database.InsertProvisionerJobTimingsParams{
JobID: jobID,
}
for _, t := range completed.GetWorkspaceBuild().GetTimings() {
if t.Start == nil || t.End == nil {
s.Logger.Warn(ctx, "timings entry has nil start or end time", slog.F("entry", t.String()))
continue
}
var stg database.ProvisionerJobTimingStage
if err := stg.Scan(t.Stage); err != nil {
s.Logger.Warn(ctx, "failed to parse timings stage, skipping", slog.F("value", t.Stage))
continue
}
params.Stage = append(params.Stage, stg)
params.Source = append(params.Source, t.Source)
params.Resource = append(params.Resource, t.Resource)
params.Action = append(params.Action, t.Action)
params.StartedAt = append(params.StartedAt, t.Start.AsTime())
params.EndedAt = append(params.EndedAt, t.End.AsTime())
}
_, err = s.Database.InsertProvisionerJobTimings(ctx, params)
if err != nil {
// Don't fail the transaction for non-critical data.
s.Logger.Warn(ctx, "failed to update provisioner job timings", slog.F("job_id", jobID), slog.Error(err))
}
// audit the outcome of the workspace build
if getWorkspaceError == nil {
// If the workspace has been deleted, notify the owner about it.
+1 -1
View File
@@ -117,7 +117,7 @@
name = "coder-${osArch}";
# Updated with ./scripts/update-flake.sh`.
# This should be updated whenever go.mod changes!
vendorHash = "sha256-6K1Y61RaSXITD0tr6iW8PHGyf82lkgVQOSCs3K8YcsY=";
vendorHash = "sha256-I2YMiYXq8XrJd6jw8JZnOL5wugPTLsip9JGELEy5Uao=";
proxyVendor = true;
src = ./.;
nativeBuildInputs = with pkgs; [ getopt openssl zstd ];
+1
View File
@@ -196,6 +196,7 @@ require (
require go.uber.org/mock v0.4.0
require (
github.com/cespare/xxhash v1.1.0
github.com/coder/serpent v0.7.1-0.20240815055535-d46fb20fa158
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.21.2
+2
View File
@@ -51,6 +51,7 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
@@ -856,6 +857,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+111 -31
View File
@@ -13,6 +13,7 @@ import (
"runtime"
"strings"
"sync"
"time"
"github.com/hashicorp/go-version"
tfjson "github.com/hashicorp/terraform-json"
@@ -20,6 +21,8 @@ import (
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/provisionersdk/proto"
)
@@ -34,6 +37,8 @@ type executor struct {
// cachePath and workdir must not be used by multiple processes at once.
cachePath string
workdir string
// used to capture execution times at various stages
timings *timingAggregator
}
func (e *executor) basicEnv() []string {
@@ -252,7 +257,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
args = append(args, "-var", variable)
}
outWriter, doneOut := provisionLogWriter(logr)
outWriter, doneOut := e.provisionLogWriter(logr)
errWriter, doneErr := logWriter(logr, proto.LogLevel_ERROR)
defer func() {
_ = outWriter.Close()
@@ -265,14 +270,24 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
if err != nil {
return nil, xerrors.Errorf("terraform plan: %w", err)
}
// Capture the duration of the call to `terraform graph`.
graphTimings := newTimingAggregator(database.ProvisionerJobTimingStageGraph)
graphTimings.ingest(createGraphTimingsEvent(timingGraphStart))
state, err := e.planResources(ctx, killCtx, planfilePath)
if err != nil {
graphTimings.ingest(createGraphTimingsEvent(timingGraphErrored))
return nil, err
}
graphTimings.ingest(createGraphTimingsEvent(timingGraphComplete))
return &proto.PlanComplete{
Parameters: state.Parameters,
Resources: state.Resources,
ExternalAuthProviders: state.ExternalAuthProviders,
Timings: append(e.timings.aggregate(), graphTimings.aggregate()...),
}, nil
}
@@ -399,7 +414,7 @@ func (e *executor) apply(
getPlanFilePath(e.workdir),
}
outWriter, doneOut := provisionLogWriter(logr)
outWriter, doneOut := e.provisionLogWriter(logr)
errWriter, doneErr := logWriter(logr, proto.LogLevel_ERROR)
defer func() {
_ = outWriter.Close()
@@ -421,11 +436,13 @@ func (e *executor) apply(
if err != nil {
return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err)
}
return &proto.ApplyComplete{
Parameters: state.Parameters,
Resources: state.Resources,
ExternalAuthProviders: state.ExternalAuthProviders,
State: stateContent,
Timings: e.timings.aggregate(),
}, nil
}
@@ -539,46 +556,42 @@ func readAndLog(sink logSink, r io.Reader, done chan<- any, level proto.LogLevel
// provisionLogWriter creates a WriteCloser that will log each JSON formatted terraform log. The WriteCloser must be
// closed by the caller to end logging, after which the returned channel will be closed to indicate that logging of the
// written data has finished. Failure to close the WriteCloser will leak a goroutine.
func provisionLogWriter(sink logSink) (io.WriteCloser, <-chan any) {
func (e *executor) provisionLogWriter(sink logSink) (io.WriteCloser, <-chan any) {
r, w := io.Pipe()
done := make(chan any)
go provisionReadAndLog(sink, r, done)
go e.provisionReadAndLog(sink, r, done)
return w, done
}
func provisionReadAndLog(sink logSink, r io.Reader, done chan<- any) {
func (e *executor) provisionReadAndLog(sink logSink, r io.Reader, done chan<- any) {
defer close(done)
errCount := 0
scanner := bufio.NewScanner(r)
for scanner.Scan() {
var log terraformProvisionLog
err := json.Unmarshal(scanner.Bytes(), &log)
if err != nil {
// Sometimes terraform doesn't log JSON, even though we asked it to.
// The terraform maintainers have said on the issue tracker that
// they don't guarantee that non-JSON lines won't get printed.
// https://github.com/hashicorp/terraform/issues/29252#issuecomment-887710001
//
// > I think as a practical matter it isn't possible for us to
// > promise that the output will always be entirely JSON, because
// > there's plenty of code that runs before command line arguments
// > are parsed and thus before we even know we're in JSON mode.
// > Given that, I'd suggest writing code that consumes streaming
// > JSON output from Terraform in such a way that it can tolerate
// > the output not having JSON in it at all.
//
// Log lines such as:
// - Acquiring state lock. This may take a few moments...
// - Releasing state lock. This may take a few moments...
if strings.TrimSpace(scanner.Text()) == "" {
continue
}
log.Level = "info"
log.Message = scanner.Text()
log := parseTerraformLogLine(scanner.Bytes())
if log == nil {
continue
}
logLevel := convertTerraformLogLevel(log.Level, sink)
sink.ProvisionLog(logLevel, log.Message)
ts, span, err := extractTimingSpan(log)
if err != nil {
// It's too noisy to log all of these as timings are not an essential feature, but we do need to log *some*.
if errCount%10 == 0 {
e.logger.Warn(context.Background(), "(sampled) failed to extract timings entry from log line",
slog.F("line", log.Message), slog.Error(err))
}
errCount++
} else {
// Only ingest valid timings.
e.timings.ingest(ts, span)
}
// If the diagnostic is provided, let's provide a bit more info!
if log.Diagnostic == nil {
continue
@@ -590,6 +603,60 @@ func provisionReadAndLog(sink logSink, r io.Reader, done chan<- any) {
}
}
func parseTerraformLogLine(line []byte) *terraformProvisionLog {
var log terraformProvisionLog
err := json.Unmarshal(line, &log)
if err != nil {
// Sometimes terraform doesn't log JSON, even though we asked it to.
// The terraform maintainers have said on the issue tracker that
// they don't guarantee that non-JSON lines won't get printed.
// https://github.com/hashicorp/terraform/issues/29252#issuecomment-887710001
//
// > I think as a practical matter it isn't possible for us to
// > promise that the output will always be entirely JSON, because
// > there's plenty of code that runs before command line arguments
// > are parsed and thus before we even know we're in JSON mode.
// > Given that, I'd suggest writing code that consumes streaming
// > JSON output from Terraform in such a way that it can tolerate
// > the output not having JSON in it at all.
//
// Log lines such as:
// - Acquiring state lock. This may take a few moments...
// - Releasing state lock. This may take a few moments...
if len(bytes.TrimSpace(line)) == 0 {
return nil
}
log.Level = "info"
log.Message = string(line)
}
return &log
}
func extractTimingSpan(log *terraformProvisionLog) (time.Time, *timingSpan, error) {
// Input is not well-formed, bail out.
if log.Type == "" {
return time.Time{}, nil, xerrors.Errorf("invalid timing kind: %q", log.Type)
}
typ := timingKind(log.Type)
if !typ.Valid() {
return time.Time{}, nil, xerrors.Errorf("unexpected timing kind: %q", log.Type)
}
ts, err := time.Parse("2006-01-02T15:04:05.000000Z07:00", log.Timestamp)
if err != nil {
// TODO: log
ts = time.Now()
}
return ts, &timingSpan{
kind: typ,
action: log.Hook.Action,
provider: log.Hook.Resource.Provider,
resource: log.Hook.Resource.Addr,
}, nil
}
func convertTerraformLogLevel(logLevel string, sink logSink) proto.LogLevel {
switch strings.ToLower(logLevel) {
case "trace":
@@ -609,12 +676,25 @@ func convertTerraformLogLevel(logLevel string, sink logSink) proto.LogLevel {
}
type terraformProvisionLog struct {
Level string `json:"@level"`
Message string `json:"@message"`
Level string `json:"@level"`
Message string `json:"@message"`
Timestamp string `json:"@timestamp"`
Type string `json:"type"`
Hook terraformProvisionLogHook `json:"hook"`
Diagnostic *tfjson.Diagnostic `json:"diagnostic,omitempty"`
}
type terraformProvisionLogHook struct {
Action string `json:"action"`
Resource terraformProvisionLogHookResource `json:"resource"`
}
type terraformProvisionLogHookResource struct {
Addr string `json:"addr"`
Provider string `json:"implied_provider"`
}
// syncWriter wraps an io.Writer in a sync.Mutex.
type syncWriter struct {
mut *sync.Mutex
@@ -0,0 +1,114 @@
package terraform
import (
"bufio"
"bytes"
"slices"
"testing"
"github.com/cespare/xxhash"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
protobuf "google.golang.org/protobuf/proto"
"github.com/coder/coder/v2/provisionersdk/proto"
)
func ParseTimingLines(t *testing.T, input []byte) []*proto.Timing {
t.Helper()
// Parse the input into *proto.Timing structs.
var expected []*proto.Timing
scanner := bufio.NewScanner(bytes.NewBuffer(input))
for scanner.Scan() {
line := scanner.Bytes()
var msg proto.Timing
require.NoError(t, protojson.Unmarshal(line, &msg))
expected = append(expected, &msg)
}
require.NoError(t, scanner.Err())
StableSortTimings(t, expected) // To reduce flakiness.
return expected
}
func TimingsAreEqual(t *testing.T, expected []*proto.Timing, actual []*proto.Timing) bool {
t.Helper()
// Shortcut check.
if len(expected)+len(actual) == 0 {
t.Logf("both timings are empty")
return true
}
// Shortcut check.
if len(expected) != len(actual) {
t.Logf("timings lengths are not equal: %d != %d", len(expected), len(actual))
return false
}
// Compare each element; both are expected to be sorted in a stable manner.
for i := 0; i < len(expected); i++ {
ex := expected[i]
ac := actual[i]
if !protobuf.Equal(ex, ac) {
t.Logf("timings are not equivalent: %q != %q", ex.String(), ac.String())
return false
}
}
return true
}
func PrintTiming(t *testing.T, timing *proto.Timing) {
t.Helper()
marshaler := protojson.MarshalOptions{
Multiline: false, // Ensure it's set to false for single-line JSON
Indent: "", // No indentation
}
out, err := marshaler.Marshal(timing)
assert.NoError(t, err)
t.Logf("%s", out)
}
func StableSortTimings(t *testing.T, timings []*proto.Timing) {
t.Helper()
slices.SortStableFunc(timings, func(a, b *proto.Timing) int {
if a == nil || b == nil || a.Start == nil || b.Start == nil {
return 0
}
if a.Start.AsTime().Equal(b.Start.AsTime()) {
// Special case: when start times are equal, we need to keep the ordering stable, so we hash both entries
// and sort based on that (since end times could be equal too, in principle).
ah := xxhash.Sum64String(a.String())
bh := xxhash.Sum64String(b.String())
if ah == bh {
// WTF.
t.Logf("identical timings detected!")
PrintTiming(t, a)
PrintTiming(t, b)
return 0
}
if ah < bh {
return -1
}
return 1
}
if a.Start.AsTime().Before(b.Start.AsTime()) {
return -1
}
return 1
})
}
+18 -2
View File
@@ -14,6 +14,7 @@ import (
"cdr.dev/slog"
"github.com/coder/terraform-provider-coder/provider"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/provisionersdk"
"github.com/coder/coder/v2/provisionersdk/proto"
@@ -71,7 +72,7 @@ func (s *server) Plan(
defer cancel()
defer kill()
e := s.executor(sess.WorkDirectory)
e := s.executor(sess.WorkDirectory, database.ProvisionerJobTimingStagePlan)
if err := e.checkMinVersion(ctx); err != nil {
return provisionersdk.PlanErrorf(err.Error())
}
@@ -101,11 +102,22 @@ func (s *server) Plan(
}
s.logger.Debug(ctx, "running initialization")
// The JSON output of `terraform init` doesn't include discrete fields for capturing timings of each plugin,
// so we capture the whole init process.
initTimings := newTimingAggregator(database.ProvisionerJobTimingStageInit)
initTimings.ingest(createInitTimingsEvent(timingInitStart))
err = e.init(ctx, killCtx, sess)
if err != nil {
initTimings.ingest(createInitTimingsEvent(timingInitErrored))
s.logger.Debug(ctx, "init failed", slog.Error(err))
return provisionersdk.PlanErrorf("initialize terraform: %s", err)
}
initTimings.ingest(createInitTimingsEvent(timingInitComplete))
s.logger.Debug(ctx, "ran initialization")
env, err := provisionEnv(sess.Config, request.Metadata, request.RichParameterValues, request.ExternalAuthProviders)
@@ -125,6 +137,10 @@ func (s *server) Plan(
if err != nil {
return provisionersdk.PlanErrorf(err.Error())
}
// Prepend init timings since they occur prior to plan timings.
// Order is irrelevant; this is merely indicative.
resp.Timings = append(initTimings.aggregate(), resp.Timings...)
return resp
}
@@ -137,7 +153,7 @@ func (s *server) Apply(
defer cancel()
defer kill()
e := s.executor(sess.WorkDirectory)
e := s.executor(sess.WorkDirectory, database.ProvisionerJobTimingStageApply)
if err := e.checkMinVersion(ctx); err != nil {
return provisionersdk.ApplyErrorf(err.Error())
}
+4 -1
View File
@@ -12,6 +12,8 @@ import (
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/unhanger"
"github.com/coder/coder/v2/provisionersdk"
)
@@ -138,7 +140,7 @@ func (s *server) startTrace(ctx context.Context, name string, opts ...trace.Span
))...)
}
func (s *server) executor(workdir string) *executor {
func (s *server) executor(workdir string, stage database.ProvisionerJobTimingStage) *executor {
return &executor{
server: s,
mut: s.execMut,
@@ -146,5 +148,6 @@ func (s *server) executor(workdir string) *executor {
cachePath: s.cachePath,
workdir: workdir,
logger: s.logger.Named("executor"),
timings: newTimingAggregator(stage),
}
}
@@ -0,0 +1,39 @@
A successful build which results in successful plan and apply timings.
-- plan --
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:38.097648+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.194726+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_parameter.memory_size: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.194726+02:00","hook":{"resource":{"addr":"data.coder_parameter.memory_size","module":"","resource":"data.coder_parameter.memory_size","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"memory_size","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_provisioner.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.194726+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_provisioner.me: Refresh complete after 0s [id=2470b3d2-32f4-4f95-ac70-0971efdb8338]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.195712+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"2470b3d2-32f4-4f95-ac70-0971efdb8338","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_workspace.me: Refresh complete after 0s [id=feb06d32-3252-4cd8-b7db-ea0c5145747f]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.195820+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"feb06d32-3252-4cd8-b7db-ea0c5145747f","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_parameter.memory_size: Refresh complete after 0s [id=b136c86c-1be0-43b4-9d78-e492918c5de0]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.195836+02:00","hook":{"resource":{"addr":"data.coder_parameter.memory_size","module":"","resource":"data.coder_parameter.memory_size","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"memory_size","resource_key":null},"action":"read","id_key":"id","id_value":"b136c86c-1be0-43b4-9d78-e492918c5de0","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"coder_agent.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.221555+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_image.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.221574+02:00","change":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_volume.home_volume: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.221580+02:00","change":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_container.workspace[0]: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.221584+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"Plan: 4 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.221589+02:00","changes":{"add":4,"change":0,"import":0,"remove":0,"operation":"plan"},"type":"change_summary"}
-- apply --
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.507006+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"coder_agent.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.572335+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_image.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.572411+02:00","change":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_volume.home_volume: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.572416+02:00","change":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_container.workspace[0]: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.572424+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_agent.main: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.616546+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"coder_agent.main: Creation complete after 0s [id=a23083da-4679-4396-a306-f7b466237883]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.618045+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"a23083da-4679-4396-a306-f7b466237883","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_image.main: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.626722+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"docker_volume.home_volume: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.627335+02:00","hook":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"docker_volume.home_volume: Creation complete after 0s [id=coder-feb06d32-3252-4cd8-b7db-ea0c5145747f-home]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.660616+02:00","hook":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"action":"create","id_key":"id","id_value":"coder-feb06d32-3252-4cd8-b7db-ea0c5145747f-home","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_image.main: Creation complete after 0s [id=sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.669954+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_container.workspace[0]: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.682223+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"docker_container.workspace[0]: Creation complete after 0s [id=e39f34233fe1f6d18a33eaed8ad47ef1ae19ccf8cf6841858d5f2dafe4e3c8c9]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:40.186482+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create","id_key":"id","id_value":"e39f34233fe1f6d18a33eaed8ad47ef1ae19ccf8cf6841858d5f2dafe4e3c8c9","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"Apply complete! Resources: 4 added, 0 changed, 0 destroyed.","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:40.204593+02:00","changes":{"add":4,"change":0,"import":0,"remove":0,"operation":"apply"},"type":"change_summary"}
{"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:40.205051+02:00","outputs":{},"type":"outputs"}
-- timings --
{"start":"2024-08-15T08:26:39.194726Z","end":"2024-08-15T08:26:39.195820Z","action":"read","source":"coder","resource":"data.coder_workspace.me","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.194726Z","end":"2024-08-15T08:26:39.195712Z","action":"read","source":"coder","resource":"data.coder_provisioner.me","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.194726Z","end":"2024-08-15T08:26:39.195836Z","action":"read","source":"coder","resource":"data.coder_parameter.memory_size","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.616546Z","end":"2024-08-15T08:26:39.618045Z","action":"create","source":"coder","resource":"coder_agent.main","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.626722Z","end":"2024-08-15T08:26:39.669954Z","action":"create","source":"docker","resource":"docker_image.main","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.627335Z","end":"2024-08-15T08:26:39.660616Z","action":"create","source":"docker","resource":"docker_volume.home_volume","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.682223Z","end":"2024-08-15T08:26:40.186482Z","action":"create","source":"docker","resource":"docker_container.workspace[0]","stage":"apply","state":"COMPLETED"}
@@ -0,0 +1,113 @@
Logs of an attempt to apply a resource which encounters an error.
-- plan --
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:22.823175+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"data.coder_provisioner.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.209992+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_parameter.argument: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.210000+02:00","hook":{"resource":{"addr":"data.coder_parameter.argument","module":"","resource":"data.coder_parameter.argument","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"argument","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.210004+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.210006+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.210008+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_workspace.me","module":"module.jetbrains_gateway","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_provisioner.me: Refresh complete after 0s [id=3893952e-e5f2-4a98-a65d-3ee06e0e2f12]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.211454+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"3893952e-e5f2-4a98-a65d-3ee06e0e2f12","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_workspace.me: Refresh complete after 0s [id=82090a02-f4e3-46bd-9c84-7a1b2bd7f8c8]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.211697+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_workspace.me","module":"module.jetbrains_gateway","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"82090a02-f4e3-46bd-9c84-7a1b2bd7f8c8","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_parameter.argument: Refresh complete after 0s [id=f2a0c8f2-527f-4b2a-b388-0c490d37e728]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.211745+02:00","hook":{"resource":{"addr":"data.coder_parameter.argument","module":"","resource":"data.coder_parameter.argument","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"argument","resource_key":null},"action":"read","id_key":"id","id_value":"f2a0c8f2-527f-4b2a-b388-0c490d37e728","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refresh complete after 0s [id=228fd650-0c83-4b1a-82a7-d110e5ffa141]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.212438+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read","id_key":"id","id_value":"228fd650-0c83-4b1a-82a7-d110e5ffa141","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_workspace.me: Refresh complete after 0s [id=82090a02-f4e3-46bd-9c84-7a1b2bd7f8c8]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.212607+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"82090a02-f4e3-46bd-9c84-7a1b2bd7f8c8","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"null_resource.force_apply: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227631+02:00","change":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_agent.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227652+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_script.oops: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227655+02:00","change":{"resource":{"addr":"coder_script.oops","module":"","resource":"coder_script.oops","implied_provider":"coder","resource_type":"coder_script","resource_name":"oops","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"module.jetbrains_gateway.coder_app.gateway: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227659+02:00","change":{"resource":{"addr":"module.jetbrains_gateway.coder_app.gateway","module":"module.jetbrains_gateway","resource":"coder_app.gateway","implied_provider":"coder","resource_type":"coder_app","resource_name":"gateway","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_image.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227662+02:00","change":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_container.workspace[0]: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227674+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"Plan: 6 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.227684+02:00","changes":{"add":6,"change":0,"import":0,"remove":0,"operation":"plan"},"type":"change_summary"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.228625+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.229002+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.229350+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.229687+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.230027+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:24.230363+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:34.917480+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"data.coder_parameter.argument: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.198205+02:00","hook":{"resource":{"addr":"data.coder_parameter.argument","module":"","resource":"data.coder_parameter.argument","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"argument","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.198207+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_parameter.argument: Refresh complete after 0s [id=271583ea-f59b-4a41-81e7-81e4285de037]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.199241+02:00","hook":{"resource":{"addr":"data.coder_parameter.argument","module":"","resource":"data.coder_parameter.argument","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"argument","resource_key":null},"action":"read","id_key":"id","id_value":"271583ea-f59b-4a41-81e7-81e4285de037","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_workspace.me: Refresh complete after 0s [id=e1a65799-9978-43e8-a752-bea95d15a68e]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.200058+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"e1a65799-9978-43e8-a752-bea95d15a68e","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"data.coder_provisioner.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.200671+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.200993+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_workspace.me","module":"module.jetbrains_gateway","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"data.coder_provisioner.me: Refresh complete after 0s [id=0465a592-aa80-49e9-b511-88ef42937f19]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.201032+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"0465a592-aa80-49e9-b511-88ef42937f19","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.201143+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_workspace.me: Refresh complete after 0s [id=e1a65799-9978-43e8-a752-bea95d15a68e]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.201434+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_workspace.me","module":"module.jetbrains_gateway","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"e1a65799-9978-43e8-a752-bea95d15a68e","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refresh complete after 0s [id=ab94c14a-e63e-40f9-9ac2-da9dd32dba69]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.201824+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read","id_key":"id","id_value":"ab94c14a-e63e-40f9-9ac2-da9dd32dba69","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_image.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212508+02:00","change":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"null_resource.force_apply: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212529+02:00","change":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_agent.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212533+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_script.oops: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212539+02:00","change":{"resource":{"addr":"coder_script.oops","module":"","resource":"coder_script.oops","implied_provider":"coder","resource_type":"coder_script","resource_name":"oops","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"module.jetbrains_gateway.coder_app.gateway: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212543+02:00","change":{"resource":{"addr":"module.jetbrains_gateway.coder_app.gateway","module":"module.jetbrains_gateway","resource":"coder_app.gateway","implied_provider":"coder","resource_type":"coder_app","resource_name":"gateway","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_container.workspace[0]: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212548+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"Plan: 6 to add, 0 to change, 0 to destroy.","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212554+02:00","changes":{"add":6,"change":0,"import":0,"remove":0,"operation":"plan"},"type":"change_summary"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.212995+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.213403+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.213773+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.214120+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.214471+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.215417+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
-- apply --
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.551400+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"docker_image.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657040+02:00","change":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"null_resource.force_apply: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657084+02:00","change":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_agent.main: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657091+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"coder_script.oops: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657102+02:00","change":{"resource":{"addr":"coder_script.oops","module":"","resource":"coder_script.oops","implied_provider":"coder","resource_type":"coder_script","resource_name":"oops","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"module.jetbrains_gateway.coder_app.gateway: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657112+02:00","change":{"resource":{"addr":"module.jetbrains_gateway.coder_app.gateway","module":"module.jetbrains_gateway","resource":"coder_app.gateway","implied_provider":"coder","resource_type":"coder_app","resource_name":"gateway","resource_key":null},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"docker_container.workspace[0]: Plan to create","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.657117+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"planned_change"}
{"@level":"info","@message":"null_resource.force_apply: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.733209+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"coder_agent.main: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.733249+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"null_resource.force_apply: Provisioning with 'local-exec'...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.734017+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec"},"type":"provision_start"}
{"@level":"info","@message":"coder_agent.main: Creation complete after 0s [id=ddf1ac02-0871-4ce6-8586-2ae40dedd108]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.734361+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"ddf1ac02-0871-4ce6-8586-2ae40dedd108","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): Executing: [\"/bin/sh\" \"-c\" \"terraform refresh -target=data.http.example\"]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.734443+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"Executing: [\"/bin/sh\" \"-c\" \"terraform refresh -target=data.http.example\"]"},"type":"provision_progress"}
{"@level":"info","@message":"docker_image.main: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.743474+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"coder_script.oops: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.743860+02:00","hook":{"resource":{"addr":"coder_script.oops","module":"","resource":"coder_script.oops","implied_provider":"coder","resource_type":"coder_script","resource_name":"oops","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.coder_app.gateway: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.744322+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.coder_app.gateway","module":"module.jetbrains_gateway","resource":"coder_app.gateway","implied_provider":"coder","resource_type":"coder_app","resource_name":"gateway","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"coder_script.oops: Creation complete after 0s [id=fbb33fcb-70b8-4c35-b8d9-0f861a4d94dc]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.744685+02:00","hook":{"resource":{"addr":"coder_script.oops","module":"","resource":"coder_script.oops","implied_provider":"coder","resource_type":"coder_script","resource_name":"oops","resource_key":null},"action":"create","id_key":"id","id_value":"fbb33fcb-70b8-4c35-b8d9-0f861a4d94dc","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"module.jetbrains_gateway.coder_app.gateway: Creation complete after 0s [id=b18ccb68-1b14-4b79-9711-2fd5231f0ea0]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.749897+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.coder_app.gateway","module":"module.jetbrains_gateway","resource":"coder_app.gateway","implied_provider":"coder","resource_type":"coder_app","resource_name":"gateway","resource_key":null},"action":"create","id_key":"id","id_value":"b18ccb68-1b14-4b79-9711-2fd5231f0ea0","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_image.main: Creation complete after 0s [id=sha256:3fba0c87fcc8ba126bf99e4ee205b43c91ffc6b15bb052315312e71bc6296551busybox]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.764011+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"sha256:3fba0c87fcc8ba126bf99e4ee205b43c91ffc6b15bb052315312e71bc6296551busybox","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"info","@message":"docker_container.workspace[0]: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.772885+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m╷\u001b[0m\u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810643+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m╷\u001b[0m\u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m\u001b[1m\u001b[31mError: \u001b[0m\u001b[0m\u001b[1mError acquiring the state lock\u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810824+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m\u001b[1m\u001b[31mError: \u001b[0m\u001b[0m\u001b[1mError acquiring the state lock\u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810884+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m\u001b[0mError message: resource temporarily unavailable","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810897+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m\u001b[0mError message: resource temporarily unavailable"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0mLock Info:","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810931+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0mLock Info:"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m ID: ee05d38d-92ba-31b5-549c-9fb1da816f40","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.810988+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m ID: ee05d38d-92ba-31b5-549c-9fb1da816f40"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Path: terraform.tfstate","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811118+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Path: terraform.tfstate"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Operation: OperationTypeApply","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811150+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Operation: OperationTypeApply"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Who: danny@Dannys-MBP.Dlink","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811189+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Who: danny@Dannys-MBP.Dlink"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Version: 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811205+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Version: 1.9.2"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Created: 2024-08-15 08:09:36.581953 +0000 UTC","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811244+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Created: 2024-08-15 08:09:36.581953 +0000 UTC"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m Info:","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811270+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m Info:"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811372+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811438+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0mTerraform acquires a state lock to protect the state from being written","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811526+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0mTerraform acquires a state lock to protect the state from being written"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0mby multiple users at the same time. Please resolve the issue above and try","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811660+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0mby multiple users at the same time. Please resolve the issue above and try"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0magain. For most commands, you can disable locking with the \"-lock=false\"","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811756+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0magain. For most commands, you can disable locking with the \"-lock=false\""},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m│\u001b[0m \u001b[0mflag, but this is not recommended.","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811808+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m│\u001b[0m \u001b[0mflag, but this is not recommended."},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec): \u001b[31m╵\u001b[0m\u001b[0m","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811859+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec","output":"\u001b[31m╵\u001b[0m\u001b[0m"},"type":"provision_progress"}
{"@level":"info","@message":"null_resource.force_apply: (local-exec) Provisioning errored","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.811901+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"provisioner":"local-exec"},"type":"provision_errored"}
{"@level":"info","@message":"null_resource.force_apply: Creation errored after 0s","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:36.812108+02:00","hook":{"resource":{"addr":"null_resource.force_apply","module":"","resource":"null_resource.force_apply","implied_provider":"null","resource_type":"null_resource","resource_name":"force_apply","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_errored"}
{"@level":"info","@message":"docker_container.workspace[0]: Creation complete after 0s [id=d1b7a49ed5999b9d04b9ccf399988906f39e8c760c9dd853f9bd0aac9c1c7676]","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.307787+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create","id_key":"id","id_value":"d1b7a49ed5999b9d04b9ccf399988906f39e8c760c9dd853f9bd0aac9c1c7676","elapsed_seconds":0},"type":"apply_complete"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.321624+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.322400+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.322919+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.323286+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":80,"column":42,"byte":1659},"end":{"line":80,"column":48,"byte":1665}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" name = \"coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}\"","start_line":80,"highlight_start_offset":41,"highlight_end_offset":47,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.323631+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":99,"column":36,"byte":2293},"end":{"line":99,"column":42,"byte":2299}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner","start_line":99,"highlight_start_offset":35,"highlight_end_offset":41,"values":[]}},"type":"diagnostic"}
{"@level":"warn","@message":"Warning: Deprecated attribute","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.323976+02:00","diagnostic":{"severity":"warning","summary":"Deprecated attribute","detail":"The attribute \"owner_id\" is deprecated. Refer to the provider documentation for details.","range":{"filename":"main.tf","start":{"line":103,"column":36,"byte":2379},"end":{"line":103,"column":45,"byte":2388}},"snippet":{"context":"resource \"docker_container\" \"workspace\"","code":" value = data.coder_workspace.me.owner_id","start_line":103,"highlight_start_offset":35,"highlight_end_offset":44,"values":[]}},"type":"diagnostic"}
{"@level":"error","@message":"Error: local-exec provisioner error","@module":"terraform.ui","@timestamp":"2024-08-15T10:09:37.324321+02:00","diagnostic":{"severity":"error","summary":"local-exec provisioner error","detail":"Error running command 'terraform refresh -target=data.http.example': exit status 1. Output: \u001b[31m╷\u001b[0m\u001b[0m\n\u001b[31m│\u001b[0m \u001b[0m\u001b[1m\u001b[31mError: \u001b[0m\u001b[0m\u001b[1mError acquiring the state lock\u001b[0m\n\u001b[31m│\u001b[0m \u001b[0m\n\u001b[31m│\u001b[0m \u001b[0m\u001b[0mError message: resource temporarily unavailable\n\u001b[31m│\u001b[0m \u001b[0mLock Info:\n\u001b[31m│\u001b[0m \u001b[0m ID: ee05d38d-92ba-31b5-549c-9fb1da816f40\n\u001b[31m│\u001b[0m \u001b[0m Path: terraform.tfstate\n\u001b[31m│\u001b[0m \u001b[0m Operation: OperationTypeApply\n\u001b[31m│\u001b[0m \u001b[0m Who: danny@Dannys-MBP.Dlink\n\u001b[31m│\u001b[0m \u001b[0m Version: 1.9.2\n\u001b[31m│\u001b[0m \u001b[0m Created: 2024-08-15 08:09:36.581953 +0000 UTC\n\u001b[31m│\u001b[0m \u001b[0m Info: \n\u001b[31m│\u001b[0m \u001b[0m\n\u001b[31m│\u001b[0m \u001b[0m\n\u001b[31m│\u001b[0m \u001b[0mTerraform acquires a state lock to protect the state from being written\n\u001b[31m│\u001b[0m \u001b[0mby multiple users at the same time. Please resolve the issue above and try\n\u001b[31m│\u001b[0m \u001b[0magain. For most commands, you can disable locking with the \"-lock=false\"\n\u001b[31m│\u001b[0m \u001b[0mflag, but this is not recommended.\n\u001b[31m╵\u001b[0m\u001b[0m\n","address":"null_resource.force_apply","range":{"filename":"main.tf","start":{"line":55,"column":28,"byte":1074},"end":{"line":55,"column":29,"byte":1075}},"snippet":{"context":"resource \"null_resource\" \"force_apply\"","code":" provisioner \"local-exec\" {","start_line":55,"highlight_start_offset":27,"highlight_end_offset":28,"values":[]}},"type":"diagnostic"}
-- timings --
{"start":"2024-08-15T08:09:36.198205Z","end":"2024-08-15T08:09:36.199241Z","action":"read","source":"coder","resource":"data.coder_parameter.argument","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.198207Z","end":"2024-08-15T08:09:36.200058Z","action":"read","source":"coder","resource":"data.coder_workspace.me","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.200671Z","end":"2024-08-15T08:09:36.201032Z","action":"read","source":"coder","resource":"data.coder_provisioner.me","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.200993Z","end":"2024-08-15T08:09:36.201434Z","action":"read","source":"coder","resource":"module.jetbrains_gateway.data.coder_workspace.me","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.201143Z","end":"2024-08-15T08:09:36.201824Z","action":"read","source":"coder","resource":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","stage":"plan","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.733209Z","end":"2024-08-15T08:09:36.812108Z","action":"create","source":"null","resource":"null_resource.force_apply","stage":"apply","state":"FAILED"}
{"start":"2024-08-15T08:09:36.733249Z","end":"2024-08-15T08:09:36.734361Z","action":"create","source":"coder","resource":"coder_agent.main","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.734017Z","end":"2024-08-15T08:09:36.811901Z","action":"provision","source":"null","resource":"null_resource.force_apply","stage":"apply","state":"FAILED"}
{"start":"2024-08-15T08:09:36.743474Z","end":"2024-08-15T08:09:36.764011Z","action":"create","source":"docker","resource":"docker_image.main","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.743860Z","end":"2024-08-15T08:09:36.744685Z","action":"create","source":"coder","resource":"coder_script.oops","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.744322Z","end":"2024-08-15T08:09:36.749897Z","action":"create","source":"coder","resource":"module.jetbrains_gateway.coder_app.gateway","stage":"apply","state":"COMPLETED"}
{"start":"2024-08-15T08:09:36.772885Z","end":"2024-08-15T08:09:37.307787Z","action":"create","source":"docker","resource":"docker_container.workspace[0]","stage":"apply","state":"COMPLETED"}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
A provisioning which appears to complete before it started has its start and end times aligned.
-- apply --
{"@level":"info","@message":"coder_agent.main: Creating...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.616546+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create"},"type":"apply_start"}
{"@level":"info","@message":"coder_agent.main: Creation complete after 0s [id=a23083da-4679-4396-a306-f7b466237883]","@module":"terraform.ui","@timestamp":"2024-08-15T10:21:39.618045+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"a23083da-4679-4396-a306-f7b466237883","elapsed_seconds":0},"type":"apply_complete"}
-- timings --
{"start":"2024-08-15T08:21:39.618045Z","end":"2024-08-15T08:21:39.618045Z","action":"create","source":"coder","resource":"coder_agent.main","stage":"apply","state":"COMPLETED"}
@@ -0,0 +1,7 @@
An apply_start without a corresponding apply_complete will not produce a timing (and vice-versa).
-- plan --
{"@level":"info","@message":"data.coder_provisioner.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.194726+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"}
-- apply --
{"@level":"info","@message":"coder_agent.main: Creation complete after 0s [id=a23083da-4679-4396-a306-f7b466237883]","@module":"terraform.ui","@timestamp":"2024-08-15T10:26:39.618045+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"create","id_key":"id","id_value":"a23083da-4679-4396-a306-f7b466237883","elapsed_seconds":0},"type":"apply_complete"}
-- timings --
@@ -0,0 +1,38 @@
Init produces JSON logs, but not with discrete fields which we can parse.
It only gained the ability to output JSON logs in v1.9.0 (https://github.com/hashicorp/terraform/blob/v1.9/CHANGELOG.md#190-june-26-2024),
so I've included the non-JSON logs as well.
Neither one produces any timings.
-- init --
# Before v1.9.0
Initializing the backend...
Initializing modules...
Initializing provider plugins...
- Reusing previous version of hashicorp/http from the dependency lock file
- Reusing previous version of coder/coder from the dependency lock file
- Using previously-installed hashicorp/http v3.4.4
- Using previously-installed coder/coder v1.0.1
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
# After v1.9.0
{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-15T09:19:30.835464+02:00","terraform":"1.9.2","type":"version","ui":"1.2"}
{"@level":"info","@message":"Initializing the backend...","@module":"terraform.ui","@timestamp":"2024-08-15T07:19:30Z","message_code":"initializing_backend_message","type":"init_output"}
{"@level":"info","@message":"Initializing modules...","@module":"terraform.ui","@timestamp":"2024-08-15T07:19:30Z","message_code":"initializing_modules_message","type":"init_output"}
{"@level":"info","@message":"Initializing provider plugins...","@module":"terraform.ui","@timestamp":"2024-08-15T07:19:30Z","message_code":"initializing_provider_plugin_message","type":"init_output"}
{"@level":"info","@message":"coder/coder: Reusing previous version from the dependency lock file","@module":"terraform.ui","@timestamp":"2024-08-15T09:19:30.870861+02:00","type":"log"}
{"@level":"info","@message":"hashicorp/http: Reusing previous version from the dependency lock file","@module":"terraform.ui","@timestamp":"2024-08-15T09:19:31.282247+02:00","type":"log"}
{"@level":"info","@message":"coder/coder v1.0.1: Using previously-installed provider version","@module":"terraform.ui","@timestamp":"2024-08-15T09:19:31.466355+02:00","type":"log"}
{"@level":"info","@message":"hashicorp/http v3.4.4: Using previously-installed provider version","@module":"terraform.ui","@timestamp":"2024-08-15T09:19:31.479221+02:00","type":"log"}
{"@level":"info","@message":"Terraform has been successfully initialized!","@module":"terraform.ui","@timestamp":"2024-08-15T07:19:31Z","message_code":"output_init_success_message","type":"init_output"}
{"@level":"info","@message":"You may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.","@module":"terraform.ui","@timestamp":"2024-08-15T07:19:31Z","message_code":"output_init_success_cli_message","type":"init_output"}
-- timings --
@@ -0,0 +1,7 @@
The presence of an apply_start and apply_complete on the same resource results in a complete timing.
-- plan --
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-14T16:29:29.953727+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read"},"type":"apply_start"}
{"@level":"info","@message":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide: Refresh complete after 0s [id=60e62a98-97e4-459b-9af2-617ea9ccc385]","@module":"terraform.ui","@timestamp":"2024-08-14T16:29:29.955272+02:00","hook":{"resource":{"addr":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide","module":"module.jetbrains_gateway","resource":"data.coder_parameter.jetbrains_ide","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"jetbrains_ide","resource_key":null},"action":"read","id_key":"id","id_value":"60e62a98-97e4-459b-9af2-617ea9ccc385","elapsed_seconds":0},"type":"apply_complete"}
-- timings --
{"start":"2024-08-14T14:29:29.953727Z", "end":"2024-08-14T14:29:29.955272Z", "action":"read", "source":"coder", "resource":"module.jetbrains_gateway.data.coder_parameter.jetbrains_ide", "stage":"plan", "state":"COMPLETED"}
+240
View File
@@ -0,0 +1,240 @@
package terraform
import (
"fmt"
"slices"
"sync"
"time"
"github.com/cespare/xxhash"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/provisionersdk/proto"
)
type timingKind string
// Copied from https://github.com/hashicorp/terraform/blob/01c0480e77263933b2b086dc8d600a69f80fad2d/internal/command/jsonformat/renderer.go
// We cannot reference these because they're in an internal package.
const (
timingApplyStart timingKind = "apply_start"
timingApplyProgress timingKind = "apply_progress"
timingApplyComplete timingKind = "apply_complete"
timingApplyErrored timingKind = "apply_errored"
timingProvisionStart timingKind = "provision_start"
timingProvisionProgress timingKind = "provision_progress"
timingProvisionComplete timingKind = "provision_complete"
timingProvisionErrored timingKind = "provision_errored"
timingRefreshStart timingKind = "refresh_start"
timingRefreshComplete timingKind = "refresh_complete"
// Ignored.
timingChangeSummary timingKind = "change_summary"
timingDiagnostic timingKind = "diagnostic"
timingPlannedChange timingKind = "planned_change"
timingOutputs timingKind = "outputs"
timingResourceDrift timingKind = "resource_drift"
timingVersion timingKind = "version"
// These are not part of message_types, but we want to track init/graph timings as well.
timingInitStart timingKind = "init_start"
timingInitComplete timingKind = "init_complete"
timingInitErrored timingKind = "init_errored"
timingGraphStart timingKind = "graph_start"
timingGraphComplete timingKind = "graph_complete"
timingGraphErrored timingKind = "graph_errored"
// Other terraform log types which we ignore.
timingLog timingKind = "log"
timingInitOutput timingKind = "init_output"
)
type timingAggregator struct {
stage database.ProvisionerJobTimingStage
// Protects the stateLookup map.
lookupMu sync.Mutex
stateLookup map[uint64]*timingSpan
}
type timingSpan struct {
kind timingKind
start, end time.Time
stage database.ProvisionerJobTimingStage
action, provider, resource string
state proto.TimingState
}
// newTimingAggregator creates a new aggregator which measures the duration of resource init/plan/apply actions; stage
// represents the stage of provisioning the timings are occurring within.
func newTimingAggregator(stage database.ProvisionerJobTimingStage) *timingAggregator {
return &timingAggregator{
stage: stage,
stateLookup: make(map[uint64]*timingSpan),
}
}
// ingest accepts a timing span at a certain timestamp and assigns it a state according to the kind of timing event.
// We memoize start & completion events, and then calculate their total duration in aggregate.
// We ignore progress events because we only care about the full duration of the action (delta between *_start and *_complete events).
func (t *timingAggregator) ingest(ts time.Time, s *timingSpan) {
if s == nil {
return
}
s.stage = t.stage
ts = dbtime.Time(ts.UTC())
switch s.kind {
case timingApplyStart, timingProvisionStart, timingRefreshStart, timingInitStart, timingGraphStart:
s.start = ts
s.state = proto.TimingState_STARTED
case timingApplyComplete, timingProvisionComplete, timingRefreshComplete, timingInitComplete, timingGraphComplete:
s.end = ts
s.state = proto.TimingState_COMPLETED
case timingApplyErrored, timingProvisionErrored, timingInitErrored, timingGraphErrored:
s.end = ts
s.state = proto.TimingState_FAILED
default:
// We just want start/end timings, ignore all other events.
return
}
t.lookupMu.Lock()
// Memoize this span by its unique attributes and the determined state.
// This will be used in aggregate() to determine the duration of the resource action.
t.stateLookup[s.hashByState(s.state)] = s
t.lookupMu.Unlock()
}
// aggregate performs a pass through all memoized events to build up a slice of *proto.Timing instances which represent
// the total time taken to perform a certain action.
// The resulting slice of *proto.Timing is NOT sorted.
func (t *timingAggregator) aggregate() []*proto.Timing {
t.lookupMu.Lock()
defer t.lookupMu.Unlock()
// Pre-allocate len(measurements)/2 since each timing will have one STARTED and one FAILED/COMPLETED entry.
out := make([]*proto.Timing, 0, len(t.stateLookup)/2)
for _, s := range t.stateLookup {
// We are only concerned here with failed or completed events.
if s.state != proto.TimingState_FAILED && s.state != proto.TimingState_COMPLETED {
continue
}
// Look for a corresponding span for the STARTED state.
startSpan, ok := t.stateLookup[s.hashByState(proto.TimingState_STARTED)]
if !ok {
// Not found, we'll ignore this span.
continue
}
s.start = startSpan.start
// Until faster-than-light travel is a possibility, let's prevent this.
// Better to capture a zero delta than a negative one.
if s.start.After(s.end) {
s.start = s.end
}
// Let's only aggregate valid entries.
// Later we can add support for partial / failed applies, perhaps.
if s.start.IsZero() || s.end.IsZero() {
continue
}
out = append(out, s.toProto())
}
return out
}
func (l timingKind) Valid() bool {
return slices.Contains([]timingKind{
timingApplyStart,
timingApplyProgress,
timingApplyComplete,
timingApplyErrored,
timingProvisionStart,
timingProvisionProgress,
timingProvisionComplete,
timingProvisionErrored,
timingRefreshStart,
timingRefreshComplete,
timingChangeSummary,
timingDiagnostic,
timingPlannedChange,
timingOutputs,
timingResourceDrift,
timingVersion,
timingInitStart,
timingInitComplete,
timingInitErrored,
timingGraphStart,
timingGraphComplete,
timingGraphErrored,
timingLog,
timingInitOutput,
}, l)
}
// Category returns the category for a giving timing state so that timings can be aggregated by this category.
// We can't use the state itself because we need an `apply_start` and an `apply_complete` to both hash to the same entry
// if all other attributes are identical.
func (l timingKind) Category() string {
switch l {
case timingInitStart, timingInitComplete, timingInitErrored:
return "init"
case timingGraphStart, timingGraphComplete, timingGraphErrored:
return "graph"
case timingApplyStart, timingApplyProgress, timingApplyComplete, timingApplyErrored:
return "apply"
case timingProvisionStart, timingProvisionProgress, timingProvisionComplete, timingProvisionErrored:
return "provision"
case timingRefreshStart, timingRefreshComplete:
return "state refresh"
default:
return "?"
}
}
// hashState computes a hash based on a timingSpan's unique properties and state.
// The combination of resource and provider names MUST be unique across entries.
func (e *timingSpan) hashByState(state proto.TimingState) uint64 {
id := fmt.Sprintf("%s:%s:%s:%s", e.kind.Category(), state.String(), e.resource, e.provider)
return xxhash.Sum64String(id)
}
func (e *timingSpan) toProto() *proto.Timing {
// Some log entries, like state refreshes, don't have any "action" logged.
if e.action == "" {
e.action = e.kind.Category()
}
return &proto.Timing{
Start: timestamppb.New(e.start),
End: timestamppb.New(e.end),
Action: e.action,
Stage: string(e.stage),
Source: e.provider,
Resource: e.resource,
State: e.state,
}
}
func createInitTimingsEvent(event timingKind) (time.Time, *timingSpan) {
return dbtime.Now(), &timingSpan{
kind: event,
action: "initializing terraform",
provider: "terraform",
resource: "state file",
}
}
func createGraphTimingsEvent(event timingKind) (time.Time, *timingSpan) {
return dbtime.Now(), &timingSpan{
kind: event,
action: "building terraform dependency graph",
provider: "terraform",
resource: "state file",
}
}
@@ -0,0 +1,143 @@
//go:build linux || darwin
package terraform
import (
"bufio"
"bytes"
_ "embed"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/tools/txtar"
"github.com/coder/coder/v2/coderd/database"
terraform_internal "github.com/coder/coder/v2/provisioner/terraform/internal"
"github.com/coder/coder/v2/provisionersdk/proto"
)
var (
//go:embed testdata/timings-aggregation/simple.txtar
inputSimple []byte
//go:embed testdata/timings-aggregation/init.txtar
inputInit []byte
//go:embed testdata/timings-aggregation/error.txtar
inputError []byte
//go:embed testdata/timings-aggregation/complete.txtar
inputComplete []byte
//go:embed testdata/timings-aggregation/incomplete.txtar
inputIncomplete []byte
//go:embed testdata/timings-aggregation/faster-than-light.txtar
inputFasterThanLight []byte
)
func TestAggregation(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input []byte
}{
{
name: "init",
input: inputInit,
},
{
name: "simple",
input: inputSimple,
},
{
name: "error",
input: inputError,
},
{
name: "complete",
input: inputComplete,
},
{
name: "incomplete",
input: inputIncomplete,
},
{
name: "faster-than-light",
input: inputFasterThanLight,
},
}
// nolint:paralleltest // Not since go v1.22.
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// txtar is a text-based archive format used in the stdlib for simple and elegant tests.
//
// We ALWAYS expect that the archive contains two or more "files":
// 1. JSON logs generated by a terraform execution, one per line, *one file per stage*
// N. Expected resulting timings in JSON form, one per line
arc := txtar.Parse(tc.input)
require.GreaterOrEqual(t, len(arc.Files), 2)
t.Logf("%s: %s", t.Name(), arc.Comment)
var actualTimings []*proto.Timing
// The last "file" MUST contain the expected timings.
expectedTimings := arc.Files[len(arc.Files)-1]
// Iterate over the initial "files" and extract their timings according to their stage.
for i := 0; i < len(arc.Files)-1; i++ {
file := arc.Files[i]
stage := database.ProvisionerJobTimingStage(file.Name)
require.Truef(t, stage.Valid(), "%q is not a valid stage name; acceptable values: %v",
file.Name, database.AllProvisionerJobTimingStageValues())
agg := newTimingAggregator(stage)
ingestAllSpans(t, file.Data, agg)
actualTimings = append(actualTimings, agg.aggregate()...)
}
// Ensure that the expected timings were produced.
expected := terraform_internal.ParseTimingLines(t, expectedTimings.Data)
terraform_internal.StableSortTimings(t, actualTimings) // To reduce flakiness.
if !assert.True(t, terraform_internal.TimingsAreEqual(t, expected, actualTimings)) {
printExpectation(t, expected)
}
})
}
}
func ingestAllSpans(t *testing.T, input []byte, aggregator *timingAggregator) {
t.Helper()
scanner := bufio.NewScanner(bytes.NewBuffer(input))
for scanner.Scan() {
line := scanner.Bytes()
log := parseTerraformLogLine(line)
if log == nil {
continue
}
ts, span, err := extractTimingSpan(log)
if err != nil {
// t.Logf("%s: failed span extraction on line: %q", err, line)
continue
}
require.NotZerof(t, ts, "failed on line: %q", line)
require.NotNilf(t, span, "failed on line: %q", line)
aggregator.ingest(ts, span)
}
require.NoError(t, scanner.Err())
}
func printExpectation(t *testing.T, actual []*proto.Timing) {
t.Helper()
t.Log("expected:")
for _, a := range actual {
terraform_internal.PrintTiming(t, a)
}
}
+136
View File
@@ -0,0 +1,136 @@
//go:build linux || darwin
package terraform_test
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database"
terraform_internal "github.com/coder/coder/v2/provisioner/terraform/internal"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
)
// TestTimingsFromProvision uses a fake terraform binary which spits out expected log content.
// This log content is then used to usher the provisioning process along as if terraform has run, and consequently
// the timing data is extracted from the log content and validated against the expected values.
func TestTimingsFromProvision(t *testing.T) {
t.Parallel()
cwd, err := os.Getwd()
require.NoError(t, err)
// Given: a fake terraform bin that behaves as we expect it to.
fakeBin := filepath.Join(cwd, "testdata", "timings-aggregation/fake-terraform.sh")
t.Logf(fakeBin)
ctx, api := setupProvisioner(t, &provisionerServeOptions{
binaryPath: fakeBin,
})
sess := configure(ctx, t, api, &proto.Config{
TemplateSourceArchive: makeTar(t, nil),
})
ctx, cancel := context.WithTimeout(ctx, testutil.WaitLong)
t.Cleanup(cancel)
// When: a plan is executed in the provisioner, our fake terraform will be executed and will produce a
// state file and some log content.
err = sendPlan(sess, proto.WorkspaceTransition_START)
require.NoError(t, err)
var timings []*proto.Timing
for {
select {
case <-ctx.Done():
t.Fatal(ctx.Err())
default:
}
msg, err := sess.Recv()
require.NoError(t, err)
if log := msg.GetLog(); log != nil {
t.Logf("%s: %s: %s", "plan", log.Level.String(), log.Output)
}
if c := msg.GetPlan(); c != nil {
require.Empty(t, c.Error)
// Capture the timing information returned by the plan process.
timings = append(timings, c.GetTimings()...)
break
}
}
// When: the plan has completed, let's trigger an apply.
err = sendApply(sess, proto.WorkspaceTransition_START)
require.NoError(t, err)
for {
select {
case <-ctx.Done():
t.Fatal(ctx.Err())
default:
}
msg, err := sess.Recv()
require.NoError(t, err)
if log := msg.GetLog(); log != nil {
t.Logf("%s: %s: %s", "apply", log.Level.String(), log.Output)
}
if c := msg.GetApply(); c != nil {
require.Empty(t, c.Error)
// Capture the timing information returned by the apply process.
timings = append(timings, c.GetTimings()...)
break
}
}
// Sort the timings stably to keep reduce flakiness.
terraform_internal.StableSortTimings(t, timings)
// Then: the received timings should match the expected values below.
// NOTE: These timings have been encoded to JSON format to make the tests more readable.
planTimings := terraform_internal.ParseTimingLines(t, []byte(`{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195836Z", "action":"read", "source":"coder", "resource":"data.coder_parameter.memory_size", "stage":"plan", "state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195712Z", "action":"read", "source":"coder", "resource":"data.coder_provisioner.me", "stage":"plan", "state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.194726Z", "end":"2024-08-15T08:26:39.195820Z", "action":"read", "source":"coder", "resource":"data.coder_workspace.me", "stage":"plan", "state":"COMPLETED"}`))
applyTimings := terraform_internal.ParseTimingLines(t, []byte(`{"start":"2024-08-15T08:26:39.616546Z", "end":"2024-08-15T08:26:39.618045Z", "action":"create", "source":"coder", "resource":"coder_agent.main", "stage":"apply", "state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.626722Z", "end":"2024-08-15T08:26:39.669954Z", "action":"create", "source":"docker", "resource":"docker_image.main", "stage":"apply", "state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.627335Z", "end":"2024-08-15T08:26:39.660616Z", "action":"create", "source":"docker", "resource":"docker_volume.home_volume", "stage":"apply", "state":"COMPLETED"}
{"start":"2024-08-15T08:26:39.682223Z", "end":"2024-08-15T08:26:40.186482Z", "action":"create", "source":"docker", "resource":"docker_container.workspace[0]", "stage":"apply", "state":"COMPLETED"}`))
initTiming := terraform_internal.ParseTimingLines(t, []byte(`{"start":"2000-01-01T01:01:01.123456Z", "end":"2000-01-01T01:01:01.123456Z", "action":"initializing terraform", "source":"terraform", "resource":"state file", "stage":"init", "state":"COMPLETED"}`))[0]
graphTiming := terraform_internal.ParseTimingLines(t, []byte(`{"start":"2000-01-01T01:01:01.123456Z", "end":"2000-01-01T01:01:01.123456Z", "action":"building terraform dependency graph", "source":"terraform", "resource":"state file", "stage":"graph", "state":"COMPLETED"}`))[0]
require.Len(t, timings, len(planTimings)+len(applyTimings)+2)
// init/graph timings are computed dynamically during provisioning whereas plan/apply come from the logs (fixtures) in
// provisioner/terraform/testdata/timings-aggregation/fake-terraform.sh.
//
// This walks the timings, keeping separate cursors for plan and apply.
// We manually override the init/graph timings' timestamps so that the equality check works (all other fields should be as expected).
pCursor := 0
aCursor := 0
for _, tim := range timings {
switch tim.Stage {
case string(database.ProvisionerJobTimingStageInit):
tim.Start, tim.End = initTiming.Start, initTiming.End
require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{initTiming}, []*proto.Timing{tim}))
case string(database.ProvisionerJobTimingStageGraph):
tim.Start, tim.End = graphTiming.Start, graphTiming.End
require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{graphTiming}, []*proto.Timing{tim}))
case string(database.ProvisionerJobTimingStagePlan):
require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{planTimings[pCursor]}, []*proto.Timing{tim}))
pCursor++
case string(database.ProvisionerJobTimingStageApply):
require.True(t, terraform_internal.TimingsAreEqual(t, []*proto.Timing{applyTimings[aCursor]}, []*proto.Timing{tim}))
aCursor++
}
}
}
+197 -172
View File
@@ -1081,7 +1081,8 @@ type FailedJob_WorkspaceBuild struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
State []byte `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
Timings []*proto.Timing `protobuf:"bytes,2,rep,name=timings,proto3" json:"timings,omitempty"`
}
func (x *FailedJob_WorkspaceBuild) Reset() {
@@ -1123,6 +1124,13 @@ func (x *FailedJob_WorkspaceBuild) GetState() []byte {
return nil
}
func (x *FailedJob_WorkspaceBuild) GetTimings() []*proto.Timing {
if x != nil {
return x.Timings
}
return nil
}
type FailedJob_TemplateImport struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1206,6 +1214,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"`
}
func (x *CompletedJob_WorkspaceBuild) Reset() {
@@ -1254,6 +1263,13 @@ func (x *CompletedJob_WorkspaceBuild) GetResources() []*proto.Resource {
return nil
}
func (x *CompletedJob_WorkspaceBuild) GetTimings() []*proto.Timing {
if x != nil {
return x.Timings
}
return nil
}
type CompletedJob_TemplateImport struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1479,7 +1495,7 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a,
0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64,
0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd4, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 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, 0x14, 0x0a, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
@@ -1500,159 +1516,165 @@ var file_provisionerd_proto_provisionerd_proto_rawDesc = []byte{
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, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f, 0x72,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x55, 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, 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, 0xd0, 0x06,
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, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f,
0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x74,
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 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, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48,
0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72,
0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72,
0x6f, 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, 0x5b, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
0x65, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 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, 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,
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,
0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b,
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x74, 0x65,
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 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, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00,
0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79,
0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f,
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,
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,
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, 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,
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, 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, 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, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76,
0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x22, 0xa6, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f,
0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12,
0x25, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67,
0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61,
0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72,
0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12,
0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e, 0x77, 0x6f,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
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,
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,
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,
0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65,
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05,
0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x22, 0xa6, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x25,
0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52,
0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62,
0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69,
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75,
0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e, 0x77, 0x6f, 0x72,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54,
0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11, 0x55,
0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0f,
0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a,
0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a,
0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f,
0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43,
0x6f, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f,
0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72,
0x65, 0x64, 0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e,
0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x22, 0x0f, 0x0a,
0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2a, 0x34,
0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50,
0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f,
0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e,
0x45, 0x52, 0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0a, 0x41, 0x63,
0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71,
0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x52, 0x0a,
0x14, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x57, 0x69, 0x74, 0x68, 0x43,
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69,
0x72, 0x65, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x28, 0x01, 0x30,
0x01, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61,
0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a,
0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x54, 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a,
0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73,
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d,
0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15,
0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63,
0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79,
0x43, 0x6f, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75,
0x6f, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f,
0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63,
0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f,
0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x22, 0x0f,
0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x2a,
0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12,
0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d,
0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f,
0x4e, 0x45, 0x52, 0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0a, 0x41,
0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19,
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63,
0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x52,
0x0a, 0x14, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x4a, 0x6f, 0x62, 0x57, 0x69, 0x74, 0x68,
0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x63, 0x71, 0x75,
0x69, 0x72, 0x65, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x28, 0x01,
0x30, 0x01, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74,
0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12,
0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46,
0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a,
0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e, 0x5a,
0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65,
0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x17,
0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61,
0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b,
0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e, 0x70, 0x72,
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c,
0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e, 0x5a, 0x2c,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72,
0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1698,9 +1720,10 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
(*proto.RichParameterValue)(nil), // 25: provisioner.RichParameterValue
(*proto.ExternalAuthProvider)(nil), // 26: provisioner.ExternalAuthProvider
(*proto.Metadata)(nil), // 27: provisioner.Metadata
(*proto.Resource)(nil), // 28: provisioner.Resource
(*proto.RichParameter)(nil), // 29: provisioner.RichParameter
(*proto.ExternalAuthProviderResource)(nil), // 30: provisioner.ExternalAuthProviderResource
(*proto.Timing)(nil), // 28: provisioner.Timing
(*proto.Resource)(nil), // 29: provisioner.Resource
(*proto.RichParameter)(nil), // 30: provisioner.RichParameter
(*proto.ExternalAuthProviderResource)(nil), // 31: provisioner.ExternalAuthProviderResource
}
var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
11, // 0: provisionerd.AcquiredJob.workspace_build:type_name -> provisionerd.AcquiredJob.WorkspaceBuild
@@ -1729,29 +1752,31 @@ var file_provisionerd_proto_provisionerd_proto_depIdxs = []int32{
25, // 23: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
24, // 24: provisionerd.AcquiredJob.TemplateDryRun.variable_values:type_name -> provisioner.VariableValue
27, // 25: provisionerd.AcquiredJob.TemplateDryRun.metadata:type_name -> provisioner.Metadata
28, // 26: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
28, // 27: provisionerd.CompletedJob.TemplateImport.start_resources:type_name -> provisioner.Resource
28, // 28: provisionerd.CompletedJob.TemplateImport.stop_resources:type_name -> provisioner.Resource
29, // 29: provisionerd.CompletedJob.TemplateImport.rich_parameters:type_name -> provisioner.RichParameter
30, // 30: provisionerd.CompletedJob.TemplateImport.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
28, // 31: provisionerd.CompletedJob.TemplateDryRun.resources:type_name -> provisioner.Resource
1, // 32: provisionerd.ProvisionerDaemon.AcquireJob:input_type -> provisionerd.Empty
10, // 33: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:input_type -> provisionerd.CancelAcquire
8, // 34: provisionerd.ProvisionerDaemon.CommitQuota:input_type -> provisionerd.CommitQuotaRequest
6, // 35: provisionerd.ProvisionerDaemon.UpdateJob:input_type -> provisionerd.UpdateJobRequest
3, // 36: provisionerd.ProvisionerDaemon.FailJob:input_type -> provisionerd.FailedJob
4, // 37: provisionerd.ProvisionerDaemon.CompleteJob:input_type -> provisionerd.CompletedJob
2, // 38: provisionerd.ProvisionerDaemon.AcquireJob:output_type -> provisionerd.AcquiredJob
2, // 39: provisionerd.ProvisionerDaemon.AcquireJobWithCancel:output_type -> provisionerd.AcquiredJob
9, // 40: provisionerd.ProvisionerDaemon.CommitQuota:output_type -> provisionerd.CommitQuotaResponse
7, // 41: provisionerd.ProvisionerDaemon.UpdateJob:output_type -> provisionerd.UpdateJobResponse
1, // 42: provisionerd.ProvisionerDaemon.FailJob:output_type -> provisionerd.Empty
1, // 43: provisionerd.ProvisionerDaemon.CompleteJob:output_type -> provisionerd.Empty
38, // [38:44] is the sub-list for method output_type
32, // [32:38] is the sub-list for method input_type
32, // [32:32] is the sub-list for extension type_name
32, // [32:32] is the sub-list for extension extendee
0, // [0:32] is the sub-list for field type_name
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
}
func init() { file_provisionerd_proto_provisionerd_proto_init() }
+2
View File
@@ -53,6 +53,7 @@ message AcquiredJob {
message FailedJob {
message WorkspaceBuild {
bytes state = 1;
repeated provisioner.Timing timings = 2;
}
message TemplateImport {}
message TemplateDryRun {}
@@ -72,6 +73,7 @@ message CompletedJob {
message WorkspaceBuild {
bytes state = 1;
repeated provisioner.Resource resources = 2;
repeated provisioner.Timing timings = 3;
}
message TemplateImport {
repeated provisioner.Resource start_resources = 1;
+8 -1
View File
@@ -19,6 +19,7 @@ import (
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/provisionerd/proto"
@@ -996,6 +997,10 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
if applyComplete == nil {
return nil, r.failedWorkspaceBuildf("invalid message type %T received from provisioner", resp.Type)
}
// Prepend the plan timings (since they occurred first).
applyComplete.Timings = append(planComplete.Timings, applyComplete.Timings...)
if applyComplete.Error != "" {
r.logger.Warn(context.Background(), "apply failed; updating state",
slog.F("error", applyComplete.Error),
@@ -1007,7 +1012,8 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
Error: applyComplete.Error,
Type: &proto.FailedJob_WorkspaceBuild_{
WorkspaceBuild: &proto.FailedJob_WorkspaceBuild{
State: applyComplete.State,
State: applyComplete.State,
Timings: applyComplete.Timings,
},
},
}
@@ -1026,6 +1032,7 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
State: applyComplete.State,
Resources: applyComplete.Resources,
Timings: applyComplete.Timings,
},
},
}, nil
+701 -493
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -2,6 +2,8 @@
syntax = "proto3";
option go_package = "github.com/coder/coder/v2/provisionersdk/proto";
import "google/protobuf/timestamp.proto";
package provisioner;
// Empty indicates a successful request/response.
@@ -266,6 +268,7 @@ message PlanComplete {
repeated Resource resources = 2;
repeated RichParameter parameters = 3;
repeated ExternalAuthProviderResource external_auth_providers = 4;
repeated Timing timings = 6;
}
// ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
@@ -281,6 +284,23 @@ message ApplyComplete {
repeated Resource resources = 3;
repeated RichParameter parameters = 4;
repeated ExternalAuthProviderResource external_auth_providers = 5;
repeated Timing timings = 6;
}
message Timing {
google.protobuf.Timestamp start = 1;
google.protobuf.Timestamp end = 2;
string action = 3;
string source = 4;
string resource = 5;
string stage = 6;
TimingState state = 7;
}
enum TimingState {
STARTED = 0;
COMPLETED = 1;
FAILED = 2;
}
// CancelRequest requests that the previous request be canceled gracefully.
@@ -0,0 +1,123 @@
/* eslint-disable */
import * as _m0 from "protobufjs/minimal";
export const protobufPackage = "google.protobuf";
/**
* A Timestamp represents a point in time independent of any time zone or local
* calendar, encoded as a count of seconds and fractions of seconds at
* nanosecond resolution. The count is relative to an epoch at UTC midnight on
* January 1, 1970, in the proleptic Gregorian calendar which extends the
* Gregorian calendar backwards to year one.
*
* All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
* second table is needed for interpretation, using a [24-hour linear
* smear](https://developers.google.com/time/smear).
*
* The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
* restricting to that range, we ensure that we can convert to and from [RFC
* 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
*
* # Examples
*
* Example 1: Compute Timestamp from POSIX `time()`.
*
* Timestamp timestamp;
* timestamp.set_seconds(time(NULL));
* timestamp.set_nanos(0);
*
* Example 2: Compute Timestamp from POSIX `gettimeofday()`.
*
* struct timeval tv;
* gettimeofday(&tv, NULL);
*
* Timestamp timestamp;
* timestamp.set_seconds(tv.tv_sec);
* timestamp.set_nanos(tv.tv_usec * 1000);
*
* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
*
* FILETIME ft;
* GetSystemTimeAsFileTime(&ft);
* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
*
* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
* Timestamp timestamp;
* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
*
* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
*
* long millis = System.currentTimeMillis();
*
* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
* .setNanos((int) ((millis % 1000) * 1000000)).build();
*
* Example 5: Compute Timestamp from Java `Instant.now()`.
*
* Instant now = Instant.now();
*
* Timestamp timestamp =
* Timestamp.newBuilder().setSeconds(now.getEpochSecond())
* .setNanos(now.getNano()).build();
*
* Example 6: Compute Timestamp from current time in Python.
*
* timestamp = Timestamp()
* timestamp.GetCurrentTime()
*
* # JSON Mapping
*
* In JSON format, the Timestamp type is encoded as a string in the
* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
* where {year} is always expressed using four digits while {month}, {day},
* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
* is required. A proto3 JSON serializer should always use UTC (as indicated by
* "Z") when printing the Timestamp type and a proto3 JSON parser should be
* able to accept both UTC and other timezones (as indicated by an offset).
*
* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
* 01:30 UTC on January 15, 2017.
*
* In JavaScript, one can convert a Date object to this format using the
* standard
* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
* method. In Python, a standard `datetime.datetime` object can be converted
* to this format using
* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
* the Joda Time's [`ISODateTimeFormat.dateTime()`](
* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
* ) to obtain a formatter capable of generating timestamps in this format.
*/
export interface Timestamp {
/**
* Represents seconds of UTC time since Unix epoch
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59Z inclusive.
*/
seconds: number;
/**
* Non-negative fractions of a second at nanosecond resolution. Negative
* second values with fractions must still have non-negative nanos values
* that count forward in time. Must be from 0 to 999,999,999
* inclusive.
*/
nanos: number;
}
export const Timestamp = {
encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.seconds !== 0) {
writer.uint32(8).int64(message.seconds);
}
if (message.nanos !== 0) {
writer.uint32(16).int32(message.nanos);
}
return writer;
},
};
+3
View File
@@ -481,6 +481,7 @@ const createTemplateVersionTar = async (
resources: response.apply?.resources ?? [],
parameters: response.apply?.parameters ?? [],
externalAuthProviders: response.apply?.externalAuthProviders ?? [],
timings: response.apply?.timings ?? [],
},
};
});
@@ -582,6 +583,7 @@ const createTemplateVersionTar = async (
resources: [],
parameters: [],
externalAuthProviders: [],
timings: [],
...response.apply,
} as ApplyComplete;
response.apply.resources = response.apply.resources?.map(fillResource);
@@ -597,6 +599,7 @@ const createTemplateVersionTar = async (
resources: [],
parameters: [],
externalAuthProviders: [],
timings: [],
...response.plan,
} as PlanComplete;
response.plan.resources = response.plan.resources?.map(fillResource);
+68
View File
@@ -1,6 +1,7 @@
/* eslint-disable */
import * as _m0 from "protobufjs/minimal";
import { Observable } from "rxjs";
import { Timestamp } from "./google/protobuf/timestampGenerated";
export const protobufPackage = "provisioner";
@@ -29,6 +30,13 @@ export enum WorkspaceTransition {
UNRECOGNIZED = -1,
}
export enum TimingState {
STARTED = 0,
COMPLETED = 1,
FAILED = 2,
UNRECOGNIZED = -1,
}
/** Empty indicates a successful request/response. */
export interface Empty {}
@@ -275,6 +283,7 @@ export interface PlanComplete {
resources: Resource[];
parameters: RichParameter[];
externalAuthProviders: ExternalAuthProviderResource[];
timings: Timing[];
}
/**
@@ -292,6 +301,17 @@ export interface ApplyComplete {
resources: Resource[];
parameters: RichParameter[];
externalAuthProviders: ExternalAuthProviderResource[];
timings: Timing[];
}
export interface Timing {
start: Date | undefined;
end: Date | undefined;
action: string;
source: string;
resource: string;
stage: string;
state: TimingState;
}
/** CancelRequest requests that the previous request be canceled gracefully. */
@@ -965,6 +985,9 @@ export const PlanComplete = {
writer.uint32(34).fork(),
).ldelim();
}
for (const v of message.timings) {
Timing.encode(v!, writer.uint32(50).fork()).ldelim();
}
return writer;
},
};
@@ -1004,6 +1027,45 @@ export const ApplyComplete = {
writer.uint32(42).fork(),
).ldelim();
}
for (const v of message.timings) {
Timing.encode(v!, writer.uint32(50).fork()).ldelim();
}
return writer;
},
};
export const Timing = {
encode(
message: Timing,
writer: _m0.Writer = _m0.Writer.create(),
): _m0.Writer {
if (message.start !== undefined) {
Timestamp.encode(
toTimestamp(message.start),
writer.uint32(10).fork(),
).ldelim();
}
if (message.end !== undefined) {
Timestamp.encode(
toTimestamp(message.end),
writer.uint32(18).fork(),
).ldelim();
}
if (message.action !== "") {
writer.uint32(26).string(message.action);
}
if (message.source !== "") {
writer.uint32(34).string(message.source);
}
if (message.resource !== "") {
writer.uint32(42).string(message.resource);
}
if (message.stage !== "") {
writer.uint32(50).string(message.stage);
}
if (message.state !== 0) {
writer.uint32(56).int32(message.state);
}
return writer;
},
};
@@ -1077,3 +1139,9 @@ export interface Provisioner {
*/
Session(request: Observable<Request>): Observable<Response>;
}
function toTimestamp(date: Date): Timestamp {
const seconds = date.getTime() / 1_000;
const nanos = (date.getTime() % 1_000) * 1_000_000;
return { seconds, nanos };
}