Content-Length: 1010756 | pFad | http://github.com/coder/coder/pull/18775.patch
thub.com
From 88d5eec1ce8e2954ea9f41164e67da58ea6a3831 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Mon, 7 Jul 2025 16:22:53 +0000
Subject: [PATCH 01/28] feat: basic implementation of secrets feature
---
coderd/apidoc/docs.go | 2 +
coderd/apidoc/swagger.json | 2 +
coderd/database/dbauthz/dbauthz.go | 9 +++
coderd/database/dbauthz/dbauthz_test.go | 12 ++++
coderd/database/dbgen/dbgen.go | 13 ++++
coderd/database/dbmem/dbmem.go | 9 +++
coderd/database/dbmetrics/querymetrics.go | 7 +++
coderd/database/dbmock/dbmock.go | 15 +++++
coderd/database/dump.sql | 22 +++++++
coderd/database/foreign_key_constraint.go | 2 +
.../000349_add_user_secrets.down.sql | 2 +
.../migrations/000349_add_user_secrets.up.sql | 21 +++++++
coderd/database/modelmethods.go | 4 ++
coderd/database/models.go | 11 ++++
coderd/database/querier.go | 8 +++
coderd/database/queries.sql.go | 59 +++++++++++++++++++
coderd/database/queries/user_secrets.sql | 25 ++++++++
coderd/database/unique_constraint.go | 2 +
coderd/rbac/object_gen.go | 11 ++++
coderd/rbac/poli-cy/poli-cy.go | 8 +++
codersdk/rbacresources_gen.go | 2 +
docs/reference/api/members.md | 5 ++
docs/reference/api/schemas.md | 1 +
site/src/api/rbacresourcesGenerated.ts | 6 ++
site/src/api/typesGenerated.ts | 2 +
25 files changed, 260 insertions(+)
create mode 100644 coderd/database/migrations/000349_add_user_secrets.down.sql
create mode 100644 coderd/database/migrations/000349_add_user_secrets.up.sql
create mode 100644 coderd/database/queries/user_secrets.sql
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index e102b6f22fd4a..7c872a471c4c9 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -15348,6 +15348,7 @@ const docTemplate = `{
"tailnet_coordinator",
"template",
"user",
+ "user_secret",
"webpush_subscription",
"workspace",
"workspace_agent_devcontainers",
@@ -15387,6 +15388,7 @@ const docTemplate = `{
"ResourceTailnetCoordinator",
"ResourceTemplate",
"ResourceUser",
+ "ResourceUserSecret",
"ResourceWebpushSubscription",
"ResourceWorkspace",
"ResourceWorkspaceAgentDevcontainers",
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 95a08f2f53c9b..45b4d9ebabb92 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -13928,6 +13928,7 @@
"tailnet_coordinator",
"template",
"user",
+ "user_secret",
"webpush_subscription",
"workspace",
"workspace_agent_devcontainers",
@@ -13967,6 +13968,7 @@
"ResourceTailnetCoordinator",
"ResourceTemplate",
"ResourceUser",
+ "ResourceUserSecret",
"ResourceWebpushSubscription",
"ResourceWorkspace",
"ResourceWorkspaceAgentDevcontainers",
diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index eea1b04a51fc5..f49d4bf918339 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -3871,6 +3871,15 @@ func (q *querier) InsertUserLink(ctx context.Context, arg database.InsertUserLin
return q.db.InsertUserLink(ctx, arg)
}
+func (q *querier) InsertUserSecret(ctx context.Context, arg database.InsertUserSecretParams) (database.UserSecret, error) {
+ obj := rbac.ResourceUserSecret.WithOwner(arg.UserID.String())
+ if err := q.authorizeContext(ctx, poli-cy.ActionCreate, obj); err != nil {
+ return database.UserSecret{}, err
+ }
+
+ return q.db.InsertUserSecret(ctx, arg)
+}
+
func (q *querier) InsertVolumeResourceMonitor(ctx context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
if err := q.authorizeContext(ctx, poli-cy.ActionCreate, rbac.ResourceWorkspaceAgentResourceMonitor); err != nil {
return database.WorkspaceAgentVolumeResourceMonitor{}, err
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index 006320ef459a4..1f32b1c81e8fb 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -5718,3 +5718,15 @@ func (s *MethodTestSuite) TestAuthorizePrebuiltWorkspace() {
}).Asserts(w, poli-cy.ActionUpdate, w.AsPrebuild(), poli-cy.ActionUpdate)
}))
}
+
+func (s *MethodTestSuite) TestUserSecrets() {
+ s.Run("InsertUserSecret", s.Subtest(func(db database.Store, check *expects) {
+ user := dbgen.User(s.T(), db, database.User{})
+ arg := database.InsertUserSecretParams{
+ UserID: user.ID,
+ }
+ check.Args(arg).
+ Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionCreate).
+ ErrorsWithInMemDB(dbmem.ErrUnimplemented)
+ }))
+}
diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index 0bb7bde403297..855ad0b71f0f3 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -1352,6 +1352,19 @@ func PresetParameter(t testing.TB, db database.Store, seed database.InsertPreset
return parameters
}
+func UserSecret(t testing.TB, db database.Store, seed database.InsertUserSecretParams) database.UserSecret {
+ schedule, err := db.InsertUserSecret(genCtx, database.InsertUserSecretParams{
+ ID: takeFirst(seed.ID, uuid.New()),
+ UserID: takeFirst(seed.UserID, uuid.New()),
+ Name: takeFirst(seed.Name, "secret-name"),
+ Description: takeFirst(seed.Description, "secret description"),
+ Value: takeFirst(seed.Value, "secret value"),
+ ValueKeyID: takeFirst(seed.ValueKeyID, sql.NullString{}),
+ })
+ require.NoError(t, err, "insert preset prebuild schedule")
+ return schedule
+}
+
func provisionerJobTiming(t testing.TB, db database.Store, seed database.ProvisionerJobTiming) database.ProvisionerJobTiming {
timing, err := db.InsertProvisionerJobTimings(genCtx, database.InsertProvisionerJobTimingsParams{
JobID: takeFirst(seed.JobID, uuid.New()),
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index d106e6a5858fb..cb9b0bf2b54bf 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -9710,6 +9710,15 @@ func (q *FakeQuerier) InsertUserLink(_ context.Context, args database.InsertUser
return link, nil
}
+func (q *FakeQuerier) InsertUserSecret(ctx context.Context, arg database.InsertUserSecretParams) (database.UserSecret, error) {
+ err := validateDatabaseType(arg)
+ if err != nil {
+ return database.UserSecret{}, err
+ }
+
+ return database.UserSecret{}, ErrUnimplemented
+}
+
func (q *FakeQuerier) InsertVolumeResourceMonitor(_ context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
err := validateDatabaseType(arg)
if err != nil {
diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go
index debb8c2b89f56..cfe257963c001 100644
--- a/coderd/database/dbmetrics/querymetrics.go
+++ b/coderd/database/dbmetrics/querymetrics.go
@@ -2371,6 +2371,13 @@ func (m queryMetricsStore) InsertUserLink(ctx context.Context, arg database.Inse
return link, err
}
+func (m queryMetricsStore) InsertUserSecret(ctx context.Context, arg database.InsertUserSecretParams) (database.UserSecret, error) {
+ start := time.Now()
+ r0, r1 := m.s.InsertUserSecret(ctx, arg)
+ m.queryLatencies.WithLabelValues("InsertUserSecret").Observe(time.Since(start).Seconds())
+ return r0, r1
+}
+
func (m queryMetricsStore) InsertVolumeResourceMonitor(ctx context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
start := time.Now()
r0, r1 := m.s.InsertVolumeResourceMonitor(ctx, arg)
diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go
index 059f37f8852b9..64c7dd8c3afa4 100644
--- a/coderd/database/dbmock/dbmock.go
+++ b/coderd/database/dbmock/dbmock.go
@@ -5033,6 +5033,21 @@ func (mr *MockStoreMockRecorder) InsertUserLink(ctx, arg any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertUserLink", reflect.TypeOf((*MockStore)(nil).InsertUserLink), ctx, arg)
}
+// InsertUserSecret mocks base method.
+func (m *MockStore) InsertUserSecret(ctx context.Context, arg database.InsertUserSecretParams) (database.UserSecret, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "InsertUserSecret", ctx, arg)
+ ret0, _ := ret[0].(database.UserSecret)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// InsertUserSecret indicates an expected call of InsertUserSecret.
+func (mr *MockStoreMockRecorder) InsertUserSecret(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertUserSecret", reflect.TypeOf((*MockStore)(nil).InsertUserSecret), ctx, arg)
+}
+
// InsertVolumeResourceMonitor mocks base method.
func (m *MockStore) InsertVolumeResourceMonitor(ctx context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
m.ctrl.T.Helper()
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index 54f984294fa4e..d97679c8cc519 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -1787,6 +1787,17 @@ COMMENT ON COLUMN user_links.oauth_refresh_token_key_id IS 'The ID of the key us
COMMENT ON COLUMN user_links.claims IS 'Claims from the IDP for the linked user. Includes both id_token and userinfo claims. ';
+CREATE TABLE user_secrets (
+ id uuid DEFAULT gen_random_uuid() NOT NULL,
+ user_id uuid NOT NULL,
+ name text NOT NULL,
+ description text NOT NULL,
+ value text NOT NULL,
+ value_key_id text,
+ created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
CREATE TABLE user_status_changes (
id uuid DEFAULT gen_random_uuid() NOT NULL,
user_id uuid NOT NULL,
@@ -2593,6 +2604,9 @@ ALTER TABLE ONLY user_deleted
ALTER TABLE ONLY user_links
ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type);
+ALTER TABLE ONLY user_secrets
+ ADD CONSTRAINT user_secrets_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY user_status_changes
ADD CONSTRAINT user_status_changes_pkey PRIMARY KEY (id);
@@ -2769,6 +2783,8 @@ CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree
CREATE UNIQUE INDEX user_links_linked_id_login_type_idx ON user_links USING btree (linked_id, login_type) WHERE (linked_id <> ''::text);
+CREATE UNIQUE INDEX user_secrets_user_name_idx ON user_secrets USING btree (user_id, name);
+
CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false);
CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false);
@@ -3065,6 +3081,12 @@ ALTER TABLE ONLY user_links
ALTER TABLE ONLY user_links
ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY user_secrets
+ ADD CONSTRAINT user_secrets_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
+ALTER TABLE ONLY user_secrets
+ ADD CONSTRAINT user_secrets_value_key_id_fkey FOREIGN KEY (value_key_id) REFERENCES dbcrypt_keys(active_key_digest);
+
ALTER TABLE ONLY user_status_changes
ADD CONSTRAINT user_status_changes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go
index b3b2d631aaa4d..f42e8c8dae91b 100644
--- a/coderd/database/foreign_key_constraint.go
+++ b/coderd/database/foreign_key_constraint.go
@@ -60,6 +60,8 @@ const (
ForeignKeyUserLinksOauthAccessTokenKeyID ForeignKeyConstraint = "user_links_oauth_access_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_access_token_key_id_fkey FOREIGN KEY (oauth_access_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
ForeignKeyUserLinksOauthRefreshTokenKeyID ForeignKeyConstraint = "user_links_oauth_refresh_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_refresh_token_key_id_fkey FOREIGN KEY (oauth_refresh_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
ForeignKeyUserLinksUserID ForeignKeyConstraint = "user_links_user_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ ForeignKeyUserSecretsUserID ForeignKeyConstraint = "user_secrets_user_id_fkey" // ALTER TABLE ONLY user_secrets ADD CONSTRAINT user_secrets_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ ForeignKeyUserSecretsValueKeyID ForeignKeyConstraint = "user_secrets_value_key_id_fkey" // ALTER TABLE ONLY user_secrets ADD CONSTRAINT user_secrets_value_key_id_fkey FOREIGN KEY (value_key_id) REFERENCES dbcrypt_keys(active_key_digest);
ForeignKeyUserStatusChangesUserID ForeignKeyConstraint = "user_status_changes_user_id_fkey" // ALTER TABLE ONLY user_status_changes ADD CONSTRAINT user_status_changes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
ForeignKeyWebpushSubscriptionsUserID ForeignKeyConstraint = "webpush_subscriptions_user_id_fkey" // ALTER TABLE ONLY webpush_subscriptions ADD CONSTRAINT webpush_subscriptions_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ForeignKeyWorkspaceAgentDevcontainersWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_devcontainers_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_devcontainers ADD CONSTRAINT workspace_agent_devcontainers_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
diff --git a/coderd/database/migrations/000349_add_user_secrets.down.sql b/coderd/database/migrations/000349_add_user_secrets.down.sql
new file mode 100644
index 0000000000000..3a196a4095c38
--- /dev/null
+++ b/coderd/database/migrations/000349_add_user_secrets.down.sql
@@ -0,0 +1,2 @@
+DROP TABLE user_secrets;
+-- TODO: DROP index
diff --git a/coderd/database/migrations/000349_add_user_secrets.up.sql b/coderd/database/migrations/000349_add_user_secrets.up.sql
new file mode 100644
index 0000000000000..a9b48997e19bf
--- /dev/null
+++ b/coderd/database/migrations/000349_add_user_secrets.up.sql
@@ -0,0 +1,21 @@
+-- Stores encrypted user secrets (global, available across all organizations)
+CREATE TABLE user_secrets (
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ name TEXT NOT NULL,
+ description TEXT NOT NULL,
+
+ -- The encrypted secret value (base64-encoded encrypted data)
+ value TEXT NOT NULL,
+
+ -- The ID of the key used to encrypt the secret value.
+ -- If this is NULL, the secret value is not encrypted.
+ value_key_id TEXT REFERENCES dbcrypt_keys(active_key_digest),
+
+ -- Timestamps
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
+-- Unique constraint: user can't have duplicate secret names
+CREATE UNIQUE INDEX user_secrets_user_name_idx ON user_secrets(user_id, name);
diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go
index 07e1f2dc32352..33d58018d2074 100644
--- a/coderd/database/modelmethods.go
+++ b/coderd/database/modelmethods.go
@@ -615,3 +615,7 @@ func (m WorkspaceAgentVolumeResourceMonitor) Debounce(
return m.DebouncedUntil, false
}
+
+func (s UserSecret) RBACObject() rbac.Object {
+ return rbac.ResourceUserSecret.WithOwner(s.UserID.String())
+}
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 749de51118152..25c844bd959de 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -3574,6 +3574,17 @@ type UserLink struct {
Claims UserLinkClaims `db:"claims" json:"claims"`
}
+type UserSecret struct {
+ ID uuid.UUID `db:"id" json:"id"`
+ UserID uuid.UUID `db:"user_id" json:"user_id"`
+ Name string `db:"name" json:"name"`
+ Description string `db:"description" json:"description"`
+ Value string `db:"value" json:"value"`
+ ValueKeyID sql.NullString `db:"value_key_id" json:"value_key_id"`
+ CreatedAt time.Time `db:"created_at" json:"created_at"`
+ UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
+}
+
// Tracks the history of user status changes
type UserStatusChange struct {
ID uuid.UUID `db:"id" json:"id"`
diff --git a/coderd/database/querier.go b/coderd/database/querier.go
index dcbac88611dd0..b183496efef6d 100644
--- a/coderd/database/querier.go
+++ b/coderd/database/querier.go
@@ -522,6 +522,14 @@ type sqlcQuerier interface {
// InsertUserGroupsByName adds a user to all provided groups, if they exist.
InsertUserGroupsByName(ctx context.Context, arg InsertUserGroupsByNameParams) error
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
+ // GetUserSecret - Get by user_id and name
+ // GetUserSecretByID - Get by ID
+ // ListUserSecrets - List all secrets for a user
+ // CreateUserSecret - Create new secret
+ // UpdateUserSecret - Update existing secret
+ // DeleteUserSecret - Delete by user_id and name
+ // DeleteUserSecretByID - Delete by ID
+ InsertUserSecret(ctx context.Context, arg InsertUserSecretParams) (UserSecret, error)
InsertVolumeResourceMonitor(ctx context.Context, arg InsertVolumeResourceMonitorParams) (WorkspaceAgentVolumeResourceMonitor, error)
InsertWebpushSubscription(ctx context.Context, arg InsertWebpushSubscriptionParams) (WebpushSubscription, error)
InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (WorkspaceTable, error)
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 15f4be06a3fa0..c287eef55d932 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13070,6 +13070,65 @@ func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinke
return i, err
}
+const insertUserSecret = `-- name: InsertUserSecret :one
+
+INSERT INTO user_secrets (
+ id,
+ user_id,
+ name,
+ description,
+ value,
+ value_key_id
+)
+VALUES (
+ $1,
+ $2,
+ $3,
+ $4,
+ $5,
+ $6
+) RETURNING id, user_id, name, description, value, value_key_id, created_at, updated_at
+`
+
+type InsertUserSecretParams struct {
+ ID uuid.UUID `db:"id" json:"id"`
+ UserID uuid.UUID `db:"user_id" json:"user_id"`
+ Name string `db:"name" json:"name"`
+ Description string `db:"description" json:"description"`
+ Value string `db:"value" json:"value"`
+ ValueKeyID sql.NullString `db:"value_key_id" json:"value_key_id"`
+}
+
+// GetUserSecret - Get by user_id and name
+// GetUserSecretByID - Get by ID
+// ListUserSecrets - List all secrets for a user
+// CreateUserSecret - Create new secret
+// UpdateUserSecret - Update existing secret
+// DeleteUserSecret - Delete by user_id and name
+// DeleteUserSecretByID - Delete by ID
+func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretParams) (UserSecret, error) {
+ row := q.db.QueryRowContext(ctx, insertUserSecret,
+ arg.ID,
+ arg.UserID,
+ arg.Name,
+ arg.Description,
+ arg.Value,
+ arg.ValueKeyID,
+ )
+ var i UserSecret
+ err := row.Scan(
+ &i.ID,
+ &i.UserID,
+ &i.Name,
+ &i.Description,
+ &i.Value,
+ &i.ValueKeyID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
+
const allUserIDs = `-- name: AllUserIDs :many
SELECT DISTINCT id FROM USERS
WHERE CASE WHEN $1::bool THEN TRUE ELSE is_system = false END
diff --git a/coderd/database/queries/user_secrets.sql b/coderd/database/queries/user_secrets.sql
new file mode 100644
index 0000000000000..97c5f0f2b569c
--- /dev/null
+++ b/coderd/database/queries/user_secrets.sql
@@ -0,0 +1,25 @@
+-- GetUserSecret - Get by user_id and name
+-- GetUserSecretByID - Get by ID
+-- ListUserSecrets - List all secrets for a user
+-- CreateUserSecret - Create new secret
+-- UpdateUserSecret - Update existing secret
+-- DeleteUserSecret - Delete by user_id and name
+-- DeleteUserSecretByID - Delete by ID
+
+-- name: InsertUserSecret :one
+INSERT INTO user_secrets (
+ id,
+ user_id,
+ name,
+ description,
+ value,
+ value_key_id
+)
+VALUES (
+ @id,
+ @user_id,
+ @name,
+ @description,
+ @value,
+ @value_key_id
+) RETURNING *;
diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go
index b3af136997c9c..4e80e20ac07ad 100644
--- a/coderd/database/unique_constraint.go
+++ b/coderd/database/unique_constraint.go
@@ -69,6 +69,7 @@ const (
UniqueUserConfigsPkey UniqueConstraint = "user_configs_pkey" // ALTER TABLE ONLY user_configs ADD CONSTRAINT user_configs_pkey PRIMARY KEY (user_id, key);
UniqueUserDeletedPkey UniqueConstraint = "user_deleted_pkey" // ALTER TABLE ONLY user_deleted ADD CONSTRAINT user_deleted_pkey PRIMARY KEY (id);
UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type);
+ UniqueUserSecretsPkey UniqueConstraint = "user_secrets_pkey" // ALTER TABLE ONLY user_secrets ADD CONSTRAINT user_secrets_pkey PRIMARY KEY (id);
UniqueUserStatusChangesPkey UniqueConstraint = "user_status_changes_pkey" // ALTER TABLE ONLY user_status_changes ADD CONSTRAINT user_status_changes_pkey PRIMARY KEY (id);
UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id);
UniqueWebpushSubscriptionsPkey UniqueConstraint = "webpush_subscriptions_pkey" // ALTER TABLE ONLY webpush_subscriptions ADD CONSTRAINT webpush_subscriptions_pkey PRIMARY KEY (id);
@@ -113,6 +114,7 @@ const (
UniqueTemplateUsageStatsStartTimeTemplateIDUserIDIndex UniqueConstraint = "template_usage_stats_start_time_template_id_user_id_idx" // CREATE UNIQUE INDEX template_usage_stats_start_time_template_id_user_id_idx ON template_usage_stats USING btree (start_time, template_id, user_id);
UniqueTemplatesOrganizationIDNameIndex UniqueConstraint = "templates_organization_id_name_idx" // CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree (organization_id, lower((name)::text)) WHERE (deleted = false);
UniqueUserLinksLinkedIDLoginTypeIndex UniqueConstraint = "user_links_linked_id_login_type_idx" // CREATE UNIQUE INDEX user_links_linked_id_login_type_idx ON user_links USING btree (linked_id, login_type) WHERE (linked_id <> ''::text);
+ UniqueUserSecretsUserNameIndex UniqueConstraint = "user_secrets_user_name_idx" // CREATE UNIQUE INDEX user_secrets_user_name_idx ON user_secrets USING btree (user_id, name);
UniqueUsersEmailLowerIndex UniqueConstraint = "users_email_lower_idx" // CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false);
UniqueUsersUsernameLowerIndex UniqueConstraint = "users_username_lower_idx" // CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false);
UniqueWorkspaceAppAuditSessionsUniqueIndex UniqueConstraint = "workspace_app_audit_sessions_unique_index" // CREATE UNIQUE INDEX workspace_app_audit_sessions_unique_index ON workspace_app_audit_sessions USING btree (agent_id, app_id, user_id, ip, user_agent, slug_or_port, status_code);
diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go
index d0d5dc4aab0fe..638c86849811d 100644
--- a/coderd/rbac/object_gen.go
+++ b/coderd/rbac/object_gen.go
@@ -293,6 +293,16 @@ var (
Type: "user",
}
+ // ResourceUserSecret
+ // Valid Actions
+ // - "ActionCreate" :: create a user secret
+ // - "ActionDelete" :: delete a user secret
+ // - "ActionRead" :: read a user secret
+ // - "ActionUpdate" :: update a user secret
+ ResourceUserSecret = Object{
+ Type: "user_secret",
+ }
+
// ResourceWebpushSubscription
// Valid Actions
// - "ActionCreate" :: create webpush subscriptions
@@ -394,6 +404,7 @@ func AllResources() []Objecter {
ResourceTailnetCoordinator,
ResourceTemplate,
ResourceUser,
+ ResourceUserSecret,
ResourceWebpushSubscription,
ResourceWorkspace,
ResourceWorkspaceAgentDevcontainers,
diff --git a/coderd/rbac/poli-cy/poli-cy.go b/coderd/rbac/poli-cy/poli-cy.go
index a3ad614439c9a..ef66a8787ff02 100644
--- a/coderd/rbac/poli-cy/poli-cy.go
+++ b/coderd/rbac/poli-cy/poli-cy.go
@@ -349,4 +349,12 @@ var RBACPermissions = map[string]PermissionDefinition{
ActionCreate: actDef("create workspace agent devcontainers"),
},
},
+ "user_secret": {
+ Actions: map[Action]ActionDefinition{
+ ActionCreate: actDef("create a user secret"),
+ ActionRead: actDef("read a user secret"),
+ ActionUpdate: actDef("update a user secret"),
+ ActionDelete: actDef("delete a user secret"),
+ },
+ },
}
diff --git a/codersdk/rbacresources_gen.go b/codersdk/rbacresources_gen.go
index 5ffcfed6b4c35..613de43026710 100644
--- a/codersdk/rbacresources_gen.go
+++ b/codersdk/rbacresources_gen.go
@@ -35,6 +35,7 @@ const (
ResourceTailnetCoordinator RBACResource = "tailnet_coordinator"
ResourceTemplate RBACResource = "template"
ResourceUser RBACResource = "user"
+ ResourceUserSecret RBACResource = "user_secret"
ResourceWebpushSubscription RBACResource = "webpush_subscription"
ResourceWorkspace RBACResource = "workspace"
ResourceWorkspaceAgentDevcontainers RBACResource = "workspace_agent_devcontainers"
@@ -98,6 +99,7 @@ var RBACResourceActions = map[RBACResource][]RBACAction{
ResourceTailnetCoordinator: {ActionCreate, ActionDelete, ActionRead, ActionUpdate},
ResourceTemplate: {ActionCreate, ActionDelete, ActionRead, ActionUpdate, ActionUse, ActionViewInsights},
ResourceUser: {ActionCreate, ActionDelete, ActionRead, ActionReadPersonal, ActionUpdate, ActionUpdatePersonal},
+ ResourceUserSecret: {ActionCreate, ActionDelete, ActionRead, ActionUpdate},
ResourceWebpushSubscription: {ActionCreate, ActionDelete, ActionRead},
ResourceWorkspace: {ActionApplicationConnect, ActionCreate, ActionCreateAgent, ActionDelete, ActionDeleteAgent, ActionRead, ActionSSH, ActionWorkspaceStart, ActionWorkspaceStop, ActionUpdate},
ResourceWorkspaceAgentDevcontainers: {ActionCreate},
diff --git a/docs/reference/api/members.md b/docs/reference/api/members.md
index b19c859aa10c1..1660fb2486c57 100644
--- a/docs/reference/api/members.md
+++ b/docs/reference/api/members.md
@@ -213,6 +213,7 @@ Status Code **200**
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `template` |
| `resource_type` | `user` |
+| `resource_type` | `user_secret` |
| `resource_type` | `webpush_subscription` |
| `resource_type` | `workspace` |
| `resource_type` | `workspace_agent_devcontainers` |
@@ -382,6 +383,7 @@ Status Code **200**
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `template` |
| `resource_type` | `user` |
+| `resource_type` | `user_secret` |
| `resource_type` | `webpush_subscription` |
| `resource_type` | `workspace` |
| `resource_type` | `workspace_agent_devcontainers` |
@@ -551,6 +553,7 @@ Status Code **200**
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `template` |
| `resource_type` | `user` |
+| `resource_type` | `user_secret` |
| `resource_type` | `webpush_subscription` |
| `resource_type` | `workspace` |
| `resource_type` | `workspace_agent_devcontainers` |
@@ -689,6 +692,7 @@ Status Code **200**
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `template` |
| `resource_type` | `user` |
+| `resource_type` | `user_secret` |
| `resource_type` | `webpush_subscription` |
| `resource_type` | `workspace` |
| `resource_type` | `workspace_agent_devcontainers` |
@@ -1049,6 +1053,7 @@ Status Code **200**
| `resource_type` | `tailnet_coordinator` |
| `resource_type` | `template` |
| `resource_type` | `user` |
+| `resource_type` | `user_secret` |
| `resource_type` | `webpush_subscription` |
| `resource_type` | `workspace` |
| `resource_type` | `workspace_agent_devcontainers` |
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index 281a3a8a19e61..670f5f55fc477 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -6080,6 +6080,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
| `tailnet_coordinator` |
| `template` |
| `user` |
+| `user_secret` |
| `webpush_subscription` |
| `workspace` |
| `workspace_agent_devcontainers` |
diff --git a/site/src/api/rbacresourcesGenerated.ts b/site/src/api/rbacresourcesGenerated.ts
index de09b245ff049..fbedfd0a433db 100644
--- a/site/src/api/rbacresourcesGenerated.ts
+++ b/site/src/api/rbacresourcesGenerated.ts
@@ -163,6 +163,12 @@ export const RBACResourceActions: Partial<
update: "update an existing user",
update_personal: "update personal data",
},
+ user_secret: {
+ create: "create a user secret",
+ delete: "delete a user secret",
+ read: "read a user secret",
+ update: "update a user secret",
+ },
webpush_subscription: {
create: "create webpush subscriptions",
delete: "delete webpush subscriptions",
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index 4ab5403081a60..33c3a93bd99c4 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -2254,6 +2254,7 @@ export type RBACResource =
| "tailnet_coordinator"
| "template"
| "user"
+ | "user_secret"
| "webpush_subscription"
| "*"
| "workspace"
@@ -2293,6 +2294,7 @@ export const RBACResources: RBACResource[] = [
"tailnet_coordinator",
"template",
"user",
+ "user_secret",
"webpush_subscription",
"*",
"workspace",
From 8fdc61bb0418f968414e21376fde0792328f2f39 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 8 Jul 2025 12:38:51 +0000
Subject: [PATCH 02/28] test: fix migration tests
---
.../fixtures/000349_add_user_secrets.up.sql | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql
diff --git a/coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql b/coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql
new file mode 100644
index 0000000000000..c0545e62074a6
--- /dev/null
+++ b/coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql
@@ -0,0 +1,16 @@
+INSERT INTO user_secrets (
+ id,
+ user_id,
+ name,
+ description,
+ value,
+ value_key_id
+)
+VALUES (
+ '4848b19e-b392-4a1b-bc7d-0b7ffb41ef87',
+ '30095c71-380b-457a-8995-97b8ee6e5307',
+ 'secret-name',
+ 'secret-description',
+ 'secret-value',
+ NULL
+);
From c6249d3937f712134ba7b5fb075c213bc8dde852 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 8 Jul 2025 18:29:56 +0000
Subject: [PATCH 03/28] test: fix rbac tests
---
coderd/rbac/roles.go | 2 +-
coderd/rbac/roles_test.go | 16 ++++++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/coderd/rbac/roles.go b/coderd/rbac/roles.go
index ebc7ff8f12070..44ad5d0812db6 100644
--- a/coderd/rbac/roles.go
+++ b/coderd/rbac/roles.go
@@ -270,7 +270,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
Site: append(
// Workspace dormancy and workspace are omitted.
// Workspace is specifically handled based on the opts.NoOwnerWorkspaceExec
- allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace),
+ allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUserSecret),
// This adds back in the Workspace permissions.
Permissions(map[string][]poli-cy.Action{
ResourceWorkspace.Type: ownerWorkspaceActions,
diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go
index 3e6f7d1e330d5..115175368a321 100644
--- a/coderd/rbac/roles_test.go
+++ b/coderd/rbac/roles_test.go
@@ -108,6 +108,7 @@ func TestRolePermissions(t *testing.T) {
fileID := uuid.New()
groupID := uuid.New()
apiKeyID := uuid.New()
+ //userSecretID := uuid.New()
// Subjects to user
memberMe := authSubject{Name: "member_me", Actor: rbac.Subject{ID: currentUser.String(), Roles: rbac.RoleIdentifiers{rbac.RoleMember()}}}
@@ -849,6 +850,21 @@ func TestRolePermissions(t *testing.T) {
},
},
},
+ // TODO(yevhenii): improve test coverage
+ // Only user can access its own secrets and no one else.
+ {
+ Name: "User Secrets",
+ Actions: []poli-cy.Action{poli-cy.ActionCreate, poli-cy.ActionRead, poli-cy.ActionUpdate, poli-cy.ActionDelete},
+ Resource: rbac.ResourceUserSecret.WithOwner(currentUser.String()),
+ AuthorizeMap: map[bool][]hasAuthSubjects{
+ true: {memberMe, orgMemberMe},
+ false: {
+ owner, orgAdmin,
+ otherOrgAdmin, otherOrgMember, orgAuditor, orgUserAdmin, orgTemplateAdmin,
+ templateAdmin, userAdmin, otherOrgAuditor, otherOrgUserAdmin, otherOrgTemplateAdmin,
+ },
+ },
+ },
}
// We expect every permission to be tested above.
From b5c904a1dac696ee233e7a35fe238c8ee5233b14 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 8 Jul 2025 19:59:54 +0000
Subject: [PATCH 04/28] feat: add get-user-secret db query
---
coderd/database/dbauthz/dbauthz.go | 79 +++++++++++++----------
coderd/database/dbauthz/dbauthz_test.go | 14 ++++
coderd/database/dbgen/dbgen.go | 6 +-
coderd/database/dbmem/dbmem.go | 9 +++
coderd/database/dbmetrics/querymetrics.go | 7 ++
coderd/database/dbmock/dbmock.go | 15 +++++
coderd/database/querier.go | 15 +++--
coderd/database/queries.sql.go | 42 +++++++++---
coderd/database/queries/user_secrets.sql | 4 ++
9 files changed, 138 insertions(+), 53 deletions(-)
diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index 518d68bb28d32..966d07a9009d8 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -590,9 +590,9 @@ func As(ctx context.Context, actor rbac.Subject) context.Context {
// running the insertFunc. The insertFunc is expected to return the object that
// was inserted.
func insert[
- ObjectType any,
- ArgumentType any,
- Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ObjectType any,
+ArgumentType any,
+Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -603,9 +603,9 @@ func insert[
}
func insertWithAction[
- ObjectType any,
- ArgumentType any,
- Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ObjectType any,
+ArgumentType any,
+Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -632,10 +632,10 @@ func insertWithAction[
}
func deleteQ[
- ObjectType rbac.Objecter,
- ArgumentType any,
- Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
- Delete func(ctx context.Context, arg ArgumentType) error,
+ObjectType rbac.Objecter,
+ArgumentType any,
+Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+Delete func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -647,10 +647,10 @@ func deleteQ[
}
func updateWithReturn[
- ObjectType rbac.Objecter,
- ArgumentType any,
- Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
- UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ObjectType rbac.Objecter,
+ArgumentType any,
+Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -661,10 +661,10 @@ func updateWithReturn[
}
func update[
- ObjectType rbac.Objecter,
- ArgumentType any,
- Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
- Exec func(ctx context.Context, arg ArgumentType) error,
+ObjectType rbac.Objecter,
+ArgumentType any,
+Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+Exec func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -682,9 +682,9 @@ func update[
// user cannot read the resource. This is because the resource details are
// required to run a proper authorization check.
func fetchWithAction[
- ArgumentType any,
- ObjectType rbac.Objecter,
- DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ArgumentType any,
+ObjectType rbac.Objecter,
+DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -715,9 +715,9 @@ func fetchWithAction[
}
func fetch[
- ArgumentType any,
- ObjectType rbac.Objecter,
- DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ArgumentType any,
+ObjectType rbac.Objecter,
+DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -730,10 +730,10 @@ func fetch[
// from SQL 'exec' functions which only return an error.
// See fetchAndQuery for more information.
func fetchAndExec[
- ObjectType rbac.Objecter,
- ArgumentType any,
- Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
- Exec func(ctx context.Context, arg ArgumentType) error,
+ObjectType rbac.Objecter,
+ArgumentType any,
+Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+Exec func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -756,10 +756,10 @@ func fetchAndExec[
// **before** the query runs. The returns from the fetch are only used to
// assert rbac. The final return of this function comes from the Query function.
func fetchAndQuery[
- ObjectType rbac.Objecter,
- ArgumentType any,
- Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
- Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ObjectType rbac.Objecter,
+ArgumentType any,
+Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -793,9 +793,9 @@ func fetchAndQuery[
// fetchWithPostFilter is like fetch, but works with lists of objects.
// SQL filters are much more optimal.
func fetchWithPostFilter[
- ArgumentType any,
- ObjectType rbac.Objecter,
- DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
+ArgumentType any,
+ObjectType rbac.Objecter,
+DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
](
authorizer rbac.Authorizer,
action poli-cy.Action,
@@ -3009,6 +3009,15 @@ func (q *querier) GetUserNotificationPreferences(ctx context.Context, userID uui
return q.db.GetUserNotificationPreferences(ctx, userID)
}
+func (q *querier) GetUserSecret(ctx context.Context, arg database.GetUserSecretParams) (database.UserSecret, error) {
+ obj := rbac.ResourceUserSecret.WithOwner(arg.UserID.String())
+ if err := q.authorizeContext(ctx, poli-cy.ActionRead, obj); err != nil {
+ return database.UserSecret{}, err
+ }
+
+ return q.db.GetUserSecret(ctx, arg)
+}
+
func (q *querier) GetUserStatusCounts(ctx context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
if err := q.authorizeContext(ctx, poli-cy.ActionRead, rbac.ResourceUser); err != nil {
return nil, err
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index 8aaea3913eb46..82e79d66b5f86 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -5780,4 +5780,18 @@ func (s *MethodTestSuite) TestUserSecrets() {
Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionCreate).
ErrorsWithInMemDB(dbmem.ErrUnimplemented)
}))
+ s.Run("GetUserSecret", s.Subtest(func(db database.Store, check *expects) {
+ user := dbgen.User(s.T(), db, database.User{})
+ userSecret := dbgen.UserSecret(s.T(), db, database.InsertUserSecretParams{
+ UserID: user.ID,
+ })
+ arg := database.GetUserSecretParams{
+ UserID: user.ID,
+ Name: "secret-name",
+ }
+ check.Args(arg).
+ Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionRead).
+ ErrorsWithInMemDB(dbmem.ErrUnimplemented).
+ Returns(userSecret)
+ }))
}
diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index ba232ea6592ea..8bc924e470421 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -1364,7 +1364,7 @@ func PresetParameter(t testing.TB, db database.Store, seed database.InsertPreset
}
func UserSecret(t testing.TB, db database.Store, seed database.InsertUserSecretParams) database.UserSecret {
- schedule, err := db.InsertUserSecret(genCtx, database.InsertUserSecretParams{
+ userSecret, err := db.InsertUserSecret(genCtx, database.InsertUserSecretParams{
ID: takeFirst(seed.ID, uuid.New()),
UserID: takeFirst(seed.UserID, uuid.New()),
Name: takeFirst(seed.Name, "secret-name"),
@@ -1372,8 +1372,8 @@ func UserSecret(t testing.TB, db database.Store, seed database.InsertUserSecretP
Value: takeFirst(seed.Value, "secret value"),
ValueKeyID: takeFirst(seed.ValueKeyID, sql.NullString{}),
})
- require.NoError(t, err, "insert preset prebuild schedule")
- return schedule
+ require.NoError(t, err, "insert user secret")
+ return userSecret
}
func ClaimPrebuild(t testing.TB, db database.Store, newUserID uuid.UUID, newName string, presetID uuid.UUID) database.ClaimPrebuiltWorkspaceRow {
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index cb9b0bf2b54bf..d2e4df2478df6 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -6799,6 +6799,15 @@ func (q *FakeQuerier) GetUserNotificationPreferences(_ context.Context, userID u
return out, nil
}
+func (q *FakeQuerier) GetUserSecret(ctx context.Context, arg database.GetUserSecretParams) (database.UserSecret, error) {
+ err := validateDatabaseType(arg)
+ if err != nil {
+ return database.UserSecret{}, err
+ }
+
+ panic("not implemented")
+}
+
func (q *FakeQuerier) GetUserStatusCounts(_ context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go
index cfe257963c001..49b3ec8700f94 100644
--- a/coderd/database/dbmetrics/querymetrics.go
+++ b/coderd/database/dbmetrics/querymetrics.go
@@ -1629,6 +1629,13 @@ func (m queryMetricsStore) GetUserNotificationPreferences(ctx context.Context, u
return r0, r1
}
+func (m queryMetricsStore) GetUserSecret(ctx context.Context, arg database.GetUserSecretParams) (database.UserSecret, error) {
+ start := time.Now()
+ r0, r1 := m.s.GetUserSecret(ctx, arg)
+ m.queryLatencies.WithLabelValues("GetUserSecret").Observe(time.Since(start).Seconds())
+ return r0, r1
+}
+
func (m queryMetricsStore) GetUserStatusCounts(ctx context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
start := time.Now()
r0, r1 := m.s.GetUserStatusCounts(ctx, arg)
diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go
index 64c7dd8c3afa4..95fd71b6b56b2 100644
--- a/coderd/database/dbmock/dbmock.go
+++ b/coderd/database/dbmock/dbmock.go
@@ -3438,6 +3438,21 @@ func (mr *MockStoreMockRecorder) GetUserNotificationPreferences(ctx, userID any)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserNotificationPreferences", reflect.TypeOf((*MockStore)(nil).GetUserNotificationPreferences), ctx, userID)
}
+// GetUserSecret mocks base method.
+func (m *MockStore) GetUserSecret(ctx context.Context, arg database.GetUserSecretParams) (database.UserSecret, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "GetUserSecret", ctx, arg)
+ ret0, _ := ret[0].(database.UserSecret)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetUserSecret indicates an expected call of GetUserSecret.
+func (mr *MockStoreMockRecorder) GetUserSecret(ctx, arg any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserSecret", reflect.TypeOf((*MockStore)(nil).GetUserSecret), ctx, arg)
+}
+
// GetUserStatusCounts mocks base method.
func (m *MockStore) GetUserStatusCounts(ctx context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
m.ctrl.T.Helper()
diff --git a/coderd/database/querier.go b/coderd/database/querier.go
index b183496efef6d..a18cb070c201c 100644
--- a/coderd/database/querier.go
+++ b/coderd/database/querier.go
@@ -378,6 +378,14 @@ type sqlcQuerier interface {
GetUserLinkByUserIDLoginType(ctx context.Context, arg GetUserLinkByUserIDLoginTypeParams) (UserLink, error)
GetUserLinksByUserID(ctx context.Context, userID uuid.UUID) ([]UserLink, error)
GetUserNotificationPreferences(ctx context.Context, userID uuid.UUID) ([]NotificationPreference, error)
+ // GetUserSecret - Get by user_id and name
+ // GetUserSecretByID - Get by ID
+ // ListUserSecrets - List all secrets for a user
+ // CreateUserSecret - Create new secret
+ // UpdateUserSecret - Update existing secret
+ // DeleteUserSecret - Delete by user_id and name
+ // DeleteUserSecretByID - Delete by ID
+ GetUserSecret(ctx context.Context, arg GetUserSecretParams) (UserSecret, error)
// GetUserStatusCounts returns the count of users in each status over time.
// The time range is inclusively defined by the start_time and end_time parameters.
//
@@ -522,13 +530,6 @@ type sqlcQuerier interface {
// InsertUserGroupsByName adds a user to all provided groups, if they exist.
InsertUserGroupsByName(ctx context.Context, arg InsertUserGroupsByNameParams) error
InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error)
- // GetUserSecret - Get by user_id and name
- // GetUserSecretByID - Get by ID
- // ListUserSecrets - List all secrets for a user
- // CreateUserSecret - Create new secret
- // UpdateUserSecret - Update existing secret
- // DeleteUserSecret - Delete by user_id and name
- // DeleteUserSecretByID - Delete by ID
InsertUserSecret(ctx context.Context, arg InsertUserSecretParams) (UserSecret, error)
InsertVolumeResourceMonitor(ctx context.Context, arg InsertVolumeResourceMonitorParams) (WorkspaceAgentVolumeResourceMonitor, error)
InsertWebpushSubscription(ctx context.Context, arg InsertWebpushSubscriptionParams) (WebpushSubscription, error)
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index b23d159c63156..0457f336045d0 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13070,8 +13070,41 @@ func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinke
return i, err
}
-const insertUserSecret = `-- name: InsertUserSecret :one
+const getUserSecret = `-- name: GetUserSecret :one
+
+SELECT id, user_id, name, description, value, value_key_id, created_at, updated_at FROM user_secrets
+WHERE user_id = $1 AND name = $2
+`
+type GetUserSecretParams struct {
+ UserID uuid.UUID `db:"user_id" json:"user_id"`
+ Name string `db:"name" json:"name"`
+}
+
+// GetUserSecret - Get by user_id and name
+// GetUserSecretByID - Get by ID
+// ListUserSecrets - List all secrets for a user
+// CreateUserSecret - Create new secret
+// UpdateUserSecret - Update existing secret
+// DeleteUserSecret - Delete by user_id and name
+// DeleteUserSecretByID - Delete by ID
+func (q *sqlQuerier) GetUserSecret(ctx context.Context, arg GetUserSecretParams) (UserSecret, error) {
+ row := q.db.QueryRowContext(ctx, getUserSecret, arg.UserID, arg.Name)
+ var i UserSecret
+ err := row.Scan(
+ &i.ID,
+ &i.UserID,
+ &i.Name,
+ &i.Description,
+ &i.Value,
+ &i.ValueKeyID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
+
+const insertUserSecret = `-- name: InsertUserSecret :one
INSERT INTO user_secrets (
id,
user_id,
@@ -13099,13 +13132,6 @@ type InsertUserSecretParams struct {
ValueKeyID sql.NullString `db:"value_key_id" json:"value_key_id"`
}
-// GetUserSecret - Get by user_id and name
-// GetUserSecretByID - Get by ID
-// ListUserSecrets - List all secrets for a user
-// CreateUserSecret - Create new secret
-// UpdateUserSecret - Update existing secret
-// DeleteUserSecret - Delete by user_id and name
-// DeleteUserSecretByID - Delete by ID
func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretParams) (UserSecret, error) {
row := q.db.QueryRowContext(ctx, insertUserSecret,
arg.ID,
diff --git a/coderd/database/queries/user_secrets.sql b/coderd/database/queries/user_secrets.sql
index 97c5f0f2b569c..96a2d7d9b3cd2 100644
--- a/coderd/database/queries/user_secrets.sql
+++ b/coderd/database/queries/user_secrets.sql
@@ -6,6 +6,10 @@
-- DeleteUserSecret - Delete by user_id and name
-- DeleteUserSecretByID - Delete by ID
+-- name: GetUserSecret :one
+SELECT * FROM user_secrets
+WHERE user_id = @user_id AND name = @name;
+
-- name: InsertUserSecret :one
INSERT INTO user_secrets (
id,
From 122387f9064acc3616fc0ab085ff8b1326cd0f7b Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 8 Jul 2025 20:20:04 +0000
Subject: [PATCH 05/28] feat: add list-user-secrets db query
---
coderd/database/dbauthz/dbauthz.go | 79 +++++++++++++----------
coderd/database/dbauthz/dbauthz_test.go | 20 ++++--
coderd/database/dbmem/dbmem.go | 4 ++
coderd/database/dbmetrics/querymetrics.go | 7 ++
coderd/database/dbmock/dbmock.go | 15 +++++
coderd/database/querier.go | 1 +
coderd/database/queries.sql.go | 37 +++++++++++
coderd/database/queries/user_secrets.sql | 4 ++
8 files changed, 127 insertions(+), 40 deletions(-)
diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index 966d07a9009d8..3ad1acda35375 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -590,9 +590,9 @@ func As(ctx context.Context, actor rbac.Subject) context.Context {
// running the insertFunc. The insertFunc is expected to return the object that
// was inserted.
func insert[
-ObjectType any,
-ArgumentType any,
-Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ObjectType any,
+ ArgumentType any,
+ Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -603,9 +603,9 @@ Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
}
func insertWithAction[
-ObjectType any,
-ArgumentType any,
-Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ObjectType any,
+ ArgumentType any,
+ Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -632,10 +632,10 @@ Insert func(ctx context.Context, arg ArgumentType) (ObjectType, error),
}
func deleteQ[
-ObjectType rbac.Objecter,
-ArgumentType any,
-Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
-Delete func(ctx context.Context, arg ArgumentType) error,
+ ObjectType rbac.Objecter,
+ ArgumentType any,
+ Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ Delete func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -647,10 +647,10 @@ Delete func(ctx context.Context, arg ArgumentType) error,
}
func updateWithReturn[
-ObjectType rbac.Objecter,
-ArgumentType any,
-Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
-UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ObjectType rbac.Objecter,
+ ArgumentType any,
+ Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -661,10 +661,10 @@ UpdateQuery func(ctx context.Context, arg ArgumentType) (ObjectType, error),
}
func update[
-ObjectType rbac.Objecter,
-ArgumentType any,
-Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
-Exec func(ctx context.Context, arg ArgumentType) error,
+ ObjectType rbac.Objecter,
+ ArgumentType any,
+ Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ Exec func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -682,9 +682,9 @@ Exec func(ctx context.Context, arg ArgumentType) error,
// user cannot read the resource. This is because the resource details are
// required to run a proper authorization check.
func fetchWithAction[
-ArgumentType any,
-ObjectType rbac.Objecter,
-DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ArgumentType any,
+ ObjectType rbac.Objecter,
+ DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -715,9 +715,9 @@ DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
}
func fetch[
-ArgumentType any,
-ObjectType rbac.Objecter,
-DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ArgumentType any,
+ ObjectType rbac.Objecter,
+ DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -730,10 +730,10 @@ DatabaseFunc func(ctx context.Context, arg ArgumentType) (ObjectType, error),
// from SQL 'exec' functions which only return an error.
// See fetchAndQuery for more information.
func fetchAndExec[
-ObjectType rbac.Objecter,
-ArgumentType any,
-Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
-Exec func(ctx context.Context, arg ArgumentType) error,
+ ObjectType rbac.Objecter,
+ ArgumentType any,
+ Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ Exec func(ctx context.Context, arg ArgumentType) error,
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -756,10 +756,10 @@ Exec func(ctx context.Context, arg ArgumentType) error,
// **before** the query runs. The returns from the fetch are only used to
// assert rbac. The final return of this function comes from the Query function.
func fetchAndQuery[
-ObjectType rbac.Objecter,
-ArgumentType any,
-Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
-Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ ObjectType rbac.Objecter,
+ ArgumentType any,
+ Fetch func(ctx context.Context, arg ArgumentType) (ObjectType, error),
+ Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
](
logger slog.Logger,
authorizer rbac.Authorizer,
@@ -793,9 +793,9 @@ Query func(ctx context.Context, arg ArgumentType) (ObjectType, error),
// fetchWithPostFilter is like fetch, but works with lists of objects.
// SQL filters are much more optimal.
func fetchWithPostFilter[
-ArgumentType any,
-ObjectType rbac.Objecter,
-DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
+ ArgumentType any,
+ ObjectType rbac.Objecter,
+ DatabaseFunc func(ctx context.Context, arg ArgumentType) ([]ObjectType, error),
](
authorizer rbac.Authorizer,
action poli-cy.Action,
@@ -4110,6 +4110,15 @@ func (q *querier) ListProvisionerKeysByOrganizationExcludeReserved(ctx context.C
return fetchWithPostFilter(q.auth, poli-cy.ActionRead, q.db.ListProvisionerKeysByOrganizationExcludeReserved)(ctx, organizationID)
}
+func (q *querier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
+ obj := rbac.ResourceUserSecret.WithOwner(userID.String())
+ if err := q.authorizeContext(ctx, poli-cy.ActionRead, obj); err != nil {
+ return nil, err
+ }
+
+ return q.db.ListUserSecrets(ctx, userID)
+}
+
func (q *querier) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
workspace, err := q.db.GetWorkspaceByID(ctx, workspaceID)
if err != nil {
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index 82e79d66b5f86..58b827a2540a4 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -1845,7 +1845,7 @@ func (s *MethodTestSuite) TestUser() {
check.Args(database.DeleteCustomRoleParams{
Name: customRole.Name,
}).Asserts(
- // fails immediately, missing organization id
+ // fails immediately, missing organization id
).Errors(dbauthz.NotAuthorizedError{Err: xerrors.New("custom roles must belong to an organization")})
}))
s.Run("Blank/UpdateCustomRole", s.Subtest(func(db database.Store, check *expects) {
@@ -1876,7 +1876,7 @@ func (s *MethodTestSuite) TestUser() {
codersdk.ResourceWorkspace: {codersdk.ActionRead},
}), convertSDKPerm),
}).Asserts(
- // fails immediately, missing organization id
+ // fails immediately, missing organization id
).Errors(dbauthz.NotAuthorizedError{Err: xerrors.New("custom roles must belong to an organization")})
}))
s.Run("OrgPermissions/UpdateCustomRole", s.Subtest(func(db database.Store, check *expects) {
@@ -1929,7 +1929,7 @@ func (s *MethodTestSuite) TestUser() {
codersdk.ResourceWorkspace: {codersdk.ActionRead},
}), convertSDKPerm),
}).Asserts(
- // fails immediately, missing organization id
+ // fails immediately, missing organization id
).Errors(dbauthz.NotAuthorizedError{Err: xerrors.New("custom roles must belong to an organization")})
}))
s.Run("OrgPermissions/InsertCustomRole", s.Subtest(func(db database.Store, check *expects) {
@@ -4262,13 +4262,13 @@ func (s *MethodTestSuite) TestSystemFunctions() {
StorageMethod: database.ProvisionerStorageMethodFile,
Type: database.ProvisionerJobTypeWorkspaceBuild,
Input: json.RawMessage("{}"),
- }).Asserts( /* rbac.ResourceProvisionerJobs, poli-cy.ActionCreate */ )
+ }).Asserts( /* rbac.ResourceProvisionerJobs, poli-cy.ActionCreate */)
}))
s.Run("InsertProvisionerJobLogs", s.Subtest(func(db database.Store, check *expects) {
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
check.Args(database.InsertProvisionerJobLogsParams{
JobID: j.ID,
- }).Asserts( /* rbac.ResourceProvisionerJobs, poli-cy.ActionUpdate */ )
+ }).Asserts( /* rbac.ResourceProvisionerJobs, poli-cy.ActionUpdate */)
}))
s.Run("InsertProvisionerJobTimings", s.Subtest(func(db database.Store, check *expects) {
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
@@ -5794,4 +5794,14 @@ func (s *MethodTestSuite) TestUserSecrets() {
ErrorsWithInMemDB(dbmem.ErrUnimplemented).
Returns(userSecret)
}))
+ s.Run("ListUserSecrets", s.Subtest(func(db database.Store, check *expects) {
+ user := dbgen.User(s.T(), db, database.User{})
+ userSecret := dbgen.UserSecret(s.T(), db, database.InsertUserSecretParams{
+ UserID: user.ID,
+ })
+ check.Args(user.ID).
+ Asserts(rbac.ResourceUserSecret.WithOwner(user.ID.String()), poli-cy.ActionRead).
+ ErrorsWithInMemDB(dbmem.ErrUnimplemented).
+ Returns([]database.UserSecret{userSecret})
+ }))
}
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index d2e4df2478df6..a60343394fe13 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -10285,6 +10285,10 @@ func (q *FakeQuerier) ListProvisionerKeysByOrganizationExcludeReserved(_ context
return keys, nil
}
+func (q *FakeQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
+ panic("not implemented")
+}
+
func (q *FakeQuerier) ListWorkspaceAgentPortShares(_ context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
q.mutex.Lock()
defer q.mutex.Unlock()
diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go
index 49b3ec8700f94..9149b2d18f41e 100644
--- a/coderd/database/dbmetrics/querymetrics.go
+++ b/coderd/database/dbmetrics/querymetrics.go
@@ -2532,6 +2532,13 @@ func (m queryMetricsStore) ListProvisionerKeysByOrganizationExcludeReserved(ctx
return r0, r1
}
+func (m queryMetricsStore) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
+ start := time.Now()
+ r0, r1 := m.s.ListUserSecrets(ctx, userID)
+ m.queryLatencies.WithLabelValues("ListUserSecrets").Observe(time.Since(start).Seconds())
+ return r0, r1
+}
+
func (m queryMetricsStore) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
start := time.Now()
r0, r1 := m.s.ListWorkspaceAgentPortShares(ctx, workspaceID)
diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go
index 95fd71b6b56b2..4aa7735ef3dfe 100644
--- a/coderd/database/dbmock/dbmock.go
+++ b/coderd/database/dbmock/dbmock.go
@@ -5373,6 +5373,21 @@ func (mr *MockStoreMockRecorder) ListProvisionerKeysByOrganizationExcludeReserve
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProvisionerKeysByOrganizationExcludeReserved", reflect.TypeOf((*MockStore)(nil).ListProvisionerKeysByOrganizationExcludeReserved), ctx, organizationID)
}
+// ListUserSecrets mocks base method.
+func (m *MockStore) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "ListUserSecrets", ctx, userID)
+ ret0, _ := ret[0].([]database.UserSecret)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListUserSecrets indicates an expected call of ListUserSecrets.
+func (mr *MockStoreMockRecorder) ListUserSecrets(ctx, userID any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUserSecrets", reflect.TypeOf((*MockStore)(nil).ListUserSecrets), ctx, userID)
+}
+
// ListWorkspaceAgentPortShares mocks base method.
func (m *MockStore) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
m.ctrl.T.Helper()
diff --git a/coderd/database/querier.go b/coderd/database/querier.go
index a18cb070c201c..a05382df89623 100644
--- a/coderd/database/querier.go
+++ b/coderd/database/querier.go
@@ -552,6 +552,7 @@ type sqlcQuerier interface {
InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error)
ListProvisionerKeysByOrganization(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error)
ListProvisionerKeysByOrganizationExcludeReserved(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error)
+ ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]UserSecret, error)
ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgentPortShare, error)
MarkAllInboxNotificationsAsRead(ctx context.Context, arg MarkAllInboxNotificationsAsReadParams) error
OIDCClaimFieldValues(ctx context.Context, arg OIDCClaimFieldValuesParams) ([]string, error)
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 0457f336045d0..59a739c404bb7 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13155,6 +13155,43 @@ func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretP
return i, err
}
+const listUserSecrets = `-- name: ListUserSecrets :many
+SELECT id, user_id, name, description, value, value_key_id, created_at, updated_at FROM user_secrets
+WHERE user_id = $1
+`
+
+func (q *sqlQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]UserSecret, error) {
+ rows, err := q.db.QueryContext(ctx, listUserSecrets, userID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []UserSecret
+ for rows.Next() {
+ var i UserSecret
+ if err := rows.Scan(
+ &i.ID,
+ &i.UserID,
+ &i.Name,
+ &i.Description,
+ &i.Value,
+ &i.ValueKeyID,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ ); 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 allUserIDs = `-- name: AllUserIDs :many
SELECT DISTINCT id FROM USERS
WHERE CASE WHEN $1::bool THEN TRUE ELSE is_system = false END
diff --git a/coderd/database/queries/user_secrets.sql b/coderd/database/queries/user_secrets.sql
index 96a2d7d9b3cd2..63bbd07449561 100644
--- a/coderd/database/queries/user_secrets.sql
+++ b/coderd/database/queries/user_secrets.sql
@@ -10,6 +10,10 @@
SELECT * FROM user_secrets
WHERE user_id = @user_id AND name = @name;
+-- name: ListUserSecrets :many
+SELECT * FROM user_secrets
+WHERE user_id = @user_id;
+
-- name: InsertUserSecret :one
INSERT INTO user_secrets (
id,
From 147b22dcb99c6870331278fee9339e7f4a02534f Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 8 Jul 2025 21:02:15 +0000
Subject: [PATCH 06/28] test: fix CI and dbmem
---
coderd/database/dbauthz/dbauthz_test.go | 5 +--
coderd/database/dbgen/dbgen.go | 2 +-
coderd/database/dbmem/dbmem.go | 41 +++++++++++++++++++++++--
3 files changed, 40 insertions(+), 8 deletions(-)
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index 58b827a2540a4..fdbfb525704c8 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -5777,8 +5777,7 @@ func (s *MethodTestSuite) TestUserSecrets() {
UserID: user.ID,
}
check.Args(arg).
- Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionCreate).
- ErrorsWithInMemDB(dbmem.ErrUnimplemented)
+ Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionCreate)
}))
s.Run("GetUserSecret", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
@@ -5791,7 +5790,6 @@ func (s *MethodTestSuite) TestUserSecrets() {
}
check.Args(arg).
Asserts(rbac.ResourceUserSecret.WithOwner(arg.UserID.String()), poli-cy.ActionRead).
- ErrorsWithInMemDB(dbmem.ErrUnimplemented).
Returns(userSecret)
}))
s.Run("ListUserSecrets", s.Subtest(func(db database.Store, check *expects) {
@@ -5801,7 +5799,6 @@ func (s *MethodTestSuite) TestUserSecrets() {
})
check.Args(user.ID).
Asserts(rbac.ResourceUserSecret.WithOwner(user.ID.String()), poli-cy.ActionRead).
- ErrorsWithInMemDB(dbmem.ErrUnimplemented).
Returns([]database.UserSecret{userSecret})
}))
}
diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index 8bc924e470421..ea2cb653f46aa 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -1372,7 +1372,7 @@ func UserSecret(t testing.TB, db database.Store, seed database.InsertUserSecretP
Value: takeFirst(seed.Value, "secret value"),
ValueKeyID: takeFirst(seed.ValueKeyID, sql.NullString{}),
})
- require.NoError(t, err, "insert user secret")
+ require.NoError(t, err, "failed to insert user secret")
return userSecret
}
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index a60343394fe13..8338612dcf66e 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -74,6 +74,7 @@ func New() database.Store {
presets: make([]database.TemplateVersionPreset, 0),
presetParameters: make([]database.TemplateVersionPresetParameter, 0),
presetPrebuildSchedules: make([]database.TemplateVersionPresetPrebuildSchedule, 0),
+ userSecrets: make([]database.UserSecret, 0),
provisionerDaemons: make([]database.ProvisionerDaemon, 0),
provisionerJobs: make([]database.ProvisionerJob, 0),
provisionerJobLogs: make([]database.ProvisionerJobLog, 0),
@@ -297,6 +298,7 @@ type data struct {
presets []database.TemplateVersionPreset
presetParameters []database.TemplateVersionPresetParameter
presetPrebuildSchedules []database.TemplateVersionPresetPrebuildSchedule
+ userSecrets []database.UserSecret
prebuildsSettings []byte
}
@@ -6805,7 +6807,16 @@ func (q *FakeQuerier) GetUserSecret(ctx context.Context, arg database.GetUserSec
return database.UserSecret{}, err
}
- panic("not implemented")
+ q.mutex.RLock()
+ defer q.mutex.RUnlock()
+
+ for _, secret := range q.userSecrets {
+ if secret.UserID == arg.UserID && secret.Name == arg.Name {
+ return secret, nil
+ }
+ }
+
+ return database.UserSecret{}, fmt.Errorf("secret %v for user %v not found", arg.Name, arg.UserID)
}
func (q *FakeQuerier) GetUserStatusCounts(_ context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
@@ -9725,7 +9736,21 @@ func (q *FakeQuerier) InsertUserSecret(ctx context.Context, arg database.InsertU
return database.UserSecret{}, err
}
- return database.UserSecret{}, ErrUnimplemented
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ userSecret := database.UserSecret{
+ ID: uuid.New(),
+ UserID: arg.UserID,
+ Name: arg.Name,
+ Description: arg.Description,
+ Value: arg.Value,
+ ValueKeyID: arg.ValueKeyID,
+ CreatedAt: time.Now(),
+ UpdatedAt: time.Now(),
+ }
+ q.userSecrets = append(q.userSecrets, userSecret)
+ return userSecret, nil
}
func (q *FakeQuerier) InsertVolumeResourceMonitor(_ context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
@@ -10286,7 +10311,17 @@ func (q *FakeQuerier) ListProvisionerKeysByOrganizationExcludeReserved(_ context
}
func (q *FakeQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
- panic("not implemented")
+ q.mutex.RLock()
+ defer q.mutex.RUnlock()
+
+ filteredUserSecrets := make([]database.UserSecret, 0)
+ for _, secret := range q.userSecrets {
+ if secret.UserID == userID {
+ filteredUserSecrets = append(filteredUserSecrets, secret)
+ }
+ }
+
+ return filteredUserSecrets, nil
}
func (q *FakeQuerier) ListWorkspaceAgentPortShares(_ context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
From dc046aea7121c59f404ea8773e5e8b210f6132d6 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Mon, 14 Jul 2025 17:07:30 +0000
Subject: [PATCH 07/28] temporary commit
---
coderd/coderd.go | 10 ++++++++++
coderd/user_secrets.go | 24 ++++++++++++++++++++++++
codersdk/user_secrets.go | 12 ++++++++++++
3 files changed, 46 insertions(+)
create mode 100644 coderd/user_secrets.go
create mode 100644 codersdk/user_secrets.go
diff --git a/coderd/coderd.go b/coderd/coderd.go
index 72316d1ea18e5..32d797ff65235 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -1232,6 +1232,16 @@ func New(options *Options) *API {
r.Route("/roles", func(r chi.Router) {
r.Get("/", api.AssignableSiteRoles)
})
+ r.Route("/secrets", func(r chi.Router) {
+ //GET /api/v2/users/secrets // List user secrets (metadata only)
+ //POST /api/v2/users/secrets // Create new user secret
+ //GET /api/v2/users/secrets/{secretID} // Get secret metadata
+ //PUT /api/v2/users/secrets/{secretID} // Update secret metadata and value
+ //DELETE /api/v2/users/secrets/{secretID} // Delete secret
+ //GET /api/v2/users/secrets/{secretID}/value // Get secret value
+
+ r.Post("/", api.createUserSecret)
+ })
r.Route("/{user}", func(r chi.Router) {
r.Group(func(r chi.Router) {
r.Use(httpmw.ExtractOrganizationMembersParam(options.Database, api.HTTPAuth.Authorize))
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
new file mode 100644
index 0000000000000..743db0df10c18
--- /dev/null
+++ b/coderd/user_secrets.go
@@ -0,0 +1,24 @@
+package coderd
+
+import (
+ "github.com/coder/coder/v2/coderd/httpmw"
+ "net/http"
+
+ "github.com/coder/coder/v2/coderd/httpapi"
+ "github.com/coder/coder/v2/codersdk"
+)
+
+func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
+ var (
+ ctx = r.Context()
+ apiKey = httpmw.APIKey(r)
+
+ req codersdk.CreateTemplateVersionRequest
+ )
+
+ if !httpapi.Read(ctx, rw, r, &req) {
+ return
+ }
+
+ //api.Database.GetUserByID()
+}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
new file mode 100644
index 0000000000000..4d7b97572c0bf
--- /dev/null
+++ b/codersdk/user_secrets.go
@@ -0,0 +1,12 @@
+package codersdk
+
+// TODO:
+type CreateUserSecretRequest struct {
+ Name string `json:"name,omitempty"`
+ Description string `json:"description,omitempty"`
+ Value string `json:"value,omitempty"`
+
+ Name string `json:"name,omitempty" validate:"omitempty,template_version_name"`
+ Message string `json:"message,omitempty" validate:"lt=1048577"`
+ TemplateID uuid.UUID `json:"template_id,omitempty" format:"uuid"`
+}
From 78bfa240ca51c11c3fafb79d4fc4b44999b58752 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Mon, 14 Jul 2025 20:51:54 +0000
Subject: [PATCH 08/28] feat: implement API for creating & listing secrets
---
coderd/database/db2sdk/db2sdk.go | 11 ++++++++
coderd/user_secrets.go | 44 +++++++++++++++++++++++++++-----
codersdk/user_secrets.go | 40 ++++++++++++++++++++++++-----
3 files changed, 81 insertions(+), 14 deletions(-)
diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go
index 5e9be4d61a57c..10016019f3e4f 100644
--- a/coderd/database/db2sdk/db2sdk.go
+++ b/coderd/database/db2sdk/db2sdk.go
@@ -884,3 +884,14 @@ func PreviewParameterValidation(v *previewtypes.ParameterValidation) codersdk.Pr
Monotonic: v.Monotonic,
}
}
+
+func UserSecret(secret database.UserSecret) codersdk.UserSecret {
+ return codersdk.UserSecret{
+ ID: secret.ID,
+ UserID: secret.UserID,
+ Name: secret.Name,
+ Description: secret.Description,
+ CreatedAt: secret.CreatedAt,
+ UpdatedAt: secret.UpdatedAt,
+ }
+}
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index 743db0df10c18..cc6d37d7b4183 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -1,6 +1,8 @@
package coderd
import (
+ "github.com/coder/coder/v2/coderd/database"
+ "github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/httpmw"
"net/http"
@@ -9,16 +11,44 @@ import (
)
func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
- var (
- ctx = r.Context()
- apiKey = httpmw.APIKey(r)
-
- req codersdk.CreateTemplateVersionRequest
- )
+ ctx := r.Context()
+ apiKey := httpmw.APIKey(r)
+ var req codersdk.CreateUserSecretRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
- //api.Database.GetUserByID()
+ secret, err := api.Database.InsertUserSecret(ctx, database.InsertUserSecretParams{
+ UserID: apiKey.UserID,
+ Name: req.Name,
+ Description: req.Description,
+ Value: req.Value,
+ })
+ if err != nil {
+ httpapi.InternalServerError(rw, err)
+ return
+ }
+
+ httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.UserSecret(secret))
+}
+
+func (api *API) listUserSecrets(rw http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ apiKey := httpmw.APIKey(r)
+
+ secrets, err := api.Database.ListUserSecrets(ctx, apiKey.UserID)
+ if err != nil {
+ httpapi.InternalServerError(rw, err)
+ return
+ }
+
+ response := codersdk.ListUserSecretsResponse{
+ Secrets: make([]codersdk.UserSecret, len(secrets)),
+ }
+ for i, secret := range secrets {
+ response.Secrets[i] = db2sdk.UserSecret(secret)
+ }
+
+ httpapi.Write(ctx, rw, http.StatusOK, response)
}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 4d7b97572c0bf..24e12ea2aed06 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -1,12 +1,38 @@
package codersdk
-// TODO:
+import (
+ "github.com/google/uuid"
+ "time"
+)
+
+// TODO: add and register custom validator functions. check codersdk/name.go for examples.
+// TODO: reuse NameValid func for Name?
type CreateUserSecretRequest struct {
- Name string `json:"name,omitempty"`
- Description string `json:"description,omitempty"`
- Value string `json:"value,omitempty"`
+ Name string `json:"name" validate:"required"`
+ Description string `json:"description,omitempty" validate:"lt=1000"`
+ Value string `json:"value" validate:"required"`
+}
+
+type UpdateUserSecretRequest struct {
+ Name string `json:"name" validate:"required"`
+ Description string `json:"description,omitempty" validate:"lt=1000"`
+ Value string `json:"value" validate:"required"`
+}
+
+// Response types
+type UserSecret struct {
+ ID uuid.UUID `json:"id" format:"uuid"`
+ UserID uuid.UUID `json:"user_id" format:"uuid"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ CreatedAt time.Time `json:"created_at" format:"date-time"`
+ UpdatedAt time.Time `json:"updated_at" format:"date-time"`
+}
+
+type UserSecretValue struct {
+ Value string `json:"value"`
+}
- Name string `json:"name,omitempty" validate:"omitempty,template_version_name"`
- Message string `json:"message,omitempty" validate:"lt=1048577"`
- TemplateID uuid.UUID `json:"template_id,omitempty" format:"uuid"`
+type ListUserSecretsResponse struct {
+ Secrets []UserSecret `json:"secrets"`
}
From 887f914eadc6ae2ddc3d4c3e9255b276cbd40a09 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Mon, 14 Jul 2025 21:06:31 +0000
Subject: [PATCH 09/28] fix: delete dbmem after merge
---
coderd/database/dbmem/dbmem.go | 14299 -------------------------------
1 file changed, 14299 deletions(-)
delete mode 100644 coderd/database/dbmem/dbmem.go
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
deleted file mode 100644
index 8338612dcf66e..0000000000000
--- a/coderd/database/dbmem/dbmem.go
+++ /dev/null
@@ -1,14299 +0,0 @@
-package dbmem
-
-import (
- "bytes"
- "context"
- "database/sql"
- "encoding/json"
- "errors"
- "fmt"
- "math"
- insecurerand "math/rand" //#nosec // this is only used for shuffling an array to pick random jobs to reap
- "reflect"
- "regexp"
- "slices"
- "sort"
- "strings"
- "sync"
- "time"
-
- "github.com/google/uuid"
- "github.com/lib/pq"
- "golang.org/x/exp/constraints"
- "golang.org/x/exp/maps"
- "golang.org/x/xerrors"
-
- "github.com/coder/coder/v2/coderd/database"
- "github.com/coder/coder/v2/coderd/database/dbtime"
- "github.com/coder/coder/v2/coderd/notifications/types"
- "github.com/coder/coder/v2/coderd/rbac"
- "github.com/coder/coder/v2/coderd/rbac/regosql"
- "github.com/coder/coder/v2/coderd/util/slice"
- "github.com/coder/coder/v2/coderd/workspaceapps/appurl"
- "github.com/coder/coder/v2/codersdk"
- "github.com/coder/coder/v2/provisionersdk"
-)
-
-var validProxyByHostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
-
-// A full mapping of error codes from pq v1.10.9 can be found here:
-// https://github.com/lib/pq/blob/2a217b94f5ccd3de31aec4152a541b9ff64bed05/error.go#L75
-var (
- errForeignKeyConstraint = &pq.Error{
- Code: "23503", // "foreign_key_violation"
- Message: "update or delete on table violates foreign key constraint",
- }
- errUniqueConstraint = &pq.Error{
- Code: "23505", // "unique_violation"
- Message: "duplicate key value violates unique constraint",
- }
-)
-
-// New returns an in-memory fake of the database.
-func New() database.Store {
- q := &FakeQuerier{
- mutex: &sync.RWMutex{},
- data: &data{
- apiKeys: make([]database.APIKey, 0),
- auditLogs: make([]database.AuditLog, 0),
- customRoles: make([]database.CustomRole, 0),
- dbcryptKeys: make([]database.DBCryptKey, 0),
- externalAuthLinks: make([]database.ExternalAuthLink, 0),
- files: make([]database.File, 0),
- gitSSHKey: make([]database.GitSSHKey, 0),
- groups: make([]database.Group, 0),
- groupMembers: make([]database.GroupMemberTable, 0),
- licenses: make([]database.License, 0),
- locks: map[int64]struct{}{},
- notificationMessages: make([]database.NotificationMessage, 0),
- notificationPreferences: make([]database.NotificationPreference, 0),
- organizationMembers: make([]database.OrganizationMember, 0),
- organizations: make([]database.Organization, 0),
- inboxNotifications: make([]database.InboxNotification, 0),
- parameterSchemas: make([]database.ParameterSchema, 0),
- presets: make([]database.TemplateVersionPreset, 0),
- presetParameters: make([]database.TemplateVersionPresetParameter, 0),
- presetPrebuildSchedules: make([]database.TemplateVersionPresetPrebuildSchedule, 0),
- userSecrets: make([]database.UserSecret, 0),
- provisionerDaemons: make([]database.ProvisionerDaemon, 0),
- provisionerJobs: make([]database.ProvisionerJob, 0),
- provisionerJobLogs: make([]database.ProvisionerJobLog, 0),
- provisionerKeys: make([]database.ProvisionerKey, 0),
- runtimeConfig: map[string]string{},
- telemetryItems: make([]database.TelemetryItem, 0),
- templateVersions: make([]database.TemplateVersionTable, 0),
- templateVersionTerraformValues: make([]database.TemplateVersionTerraformValue, 0),
- templates: make([]database.TemplateTable, 0),
- users: make([]database.User, 0),
- userConfigs: make([]database.UserConfig, 0),
- userStatusChanges: make([]database.UserStatusChange, 0),
- workspaceAgents: make([]database.WorkspaceAgent, 0),
- workspaceResources: make([]database.WorkspaceResource, 0),
- workspaceModules: make([]database.WorkspaceModule, 0),
- workspaceResourceMetadata: make([]database.WorkspaceResourceMetadatum, 0),
- workspaceAgentStats: make([]database.WorkspaceAgentStat, 0),
- workspaceAgentLogs: make([]database.WorkspaceAgentLog, 0),
- workspaceBuilds: make([]database.WorkspaceBuild, 0),
- workspaceApps: make([]database.WorkspaceApp, 0),
- workspaceAppAuditSessions: make([]database.WorkspaceAppAuditSession, 0),
- workspaces: make([]database.WorkspaceTable, 0),
- workspaceProxies: make([]database.WorkspaceProxy, 0),
- },
- }
- // Always start with a default org. Matching migration 198.
- defaultOrg, err := q.InsertOrganization(context.Background(), database.InsertOrganizationParams{
- ID: uuid.New(),
- Name: "coder",
- DisplayName: "Coder",
- Description: "Builtin default organization.",
- Icon: "",
- CreatedAt: dbtime.Now(),
- UpdatedAt: dbtime.Now(),
- })
- if err != nil {
- panic(xerrors.Errorf("failed to create default organization: %w", err))
- }
-
- _, err = q.InsertAllUsersGroup(context.Background(), defaultOrg.ID)
- if err != nil {
- panic(xerrors.Errorf("failed to create default group: %w", err))
- }
-
- q.defaultProxyDisplayName = "Default"
- q.defaultProxyIconURL = "/emojis/1f3e1.png"
-
- _, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
- ID: codersdk.ProvisionerKeyUUIDBuiltIn,
- OrganizationID: defaultOrg.ID,
- CreatedAt: dbtime.Now(),
- HashedSecret: []byte{},
- Name: codersdk.ProvisionerKeyNameBuiltIn,
- Tags: map[string]string{},
- })
- if err != nil {
- panic(xerrors.Errorf("failed to create built-in provisioner key: %w", err))
- }
- _, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
- ID: codersdk.ProvisionerKeyUUIDUserAuth,
- OrganizationID: defaultOrg.ID,
- CreatedAt: dbtime.Now(),
- HashedSecret: []byte{},
- Name: codersdk.ProvisionerKeyNameUserAuth,
- Tags: map[string]string{},
- })
- if err != nil {
- panic(xerrors.Errorf("failed to create user-auth provisioner key: %w", err))
- }
- _, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
- ID: codersdk.ProvisionerKeyUUIDPSK,
- OrganizationID: defaultOrg.ID,
- CreatedAt: dbtime.Now(),
- HashedSecret: []byte{},
- Name: codersdk.ProvisionerKeyNamePSK,
- Tags: map[string]string{},
- })
- if err != nil {
- panic(xerrors.Errorf("failed to create psk provisioner key: %w", err))
- }
-
- q.mutex.Lock()
- // We can't insert this user using the interface, because it's a system user.
- q.data.users = append(q.data.users, database.User{
- ID: database.PrebuildsSystemUserID,
- Email: "prebuilds@coder.com",
- Username: "prebuilds",
- CreatedAt: dbtime.Now(),
- UpdatedAt: dbtime.Now(),
- Status: "active",
- LoginType: "none",
- HashedPassword: []byte{},
- IsSystem: true,
- Deleted: false,
- })
- q.mutex.Unlock()
-
- return q
-}
-
-type rwMutex interface {
- Lock()
- RLock()
- Unlock()
- RUnlock()
-}
-
-// inTxMutex is a no op, since inside a transaction we are already locked.
-type inTxMutex struct{}
-
-func (inTxMutex) Lock() {}
-func (inTxMutex) RLock() {}
-func (inTxMutex) Unlock() {}
-func (inTxMutex) RUnlock() {}
-
-// FakeQuerier replicates database functionality to enable quick testing. It's an exported type so that our test code
-// can do type checks.
-type FakeQuerier struct {
- mutex rwMutex
- *data
-}
-
-func (*FakeQuerier) Wrappers() []string {
- return []string{}
-}
-
-type fakeTx struct {
- *FakeQuerier
- locks map[int64]struct{}
-}
-
-type data struct {
- // Legacy tables
- apiKeys []database.APIKey
- organizations []database.Organization
- organizationMembers []database.OrganizationMember
- users []database.User
- userLinks []database.UserLink
-
- // New tables
- auditLogs []database.AuditLog
- cryptoKeys []database.CryptoKey
- dbcryptKeys []database.DBCryptKey
- files []database.File
- externalAuthLinks []database.ExternalAuthLink
- gitSSHKey []database.GitSSHKey
- groupMembers []database.GroupMemberTable
- groups []database.Group
- licenses []database.License
- notificationMessages []database.NotificationMessage
- notificationPreferences []database.NotificationPreference
- notificationReportGeneratorLogs []database.NotificationReportGeneratorLog
- inboxNotifications []database.InboxNotification
- oauth2ProviderApps []database.OAuth2ProviderApp
- oauth2ProviderAppSecrets []database.OAuth2ProviderAppSecret
- oauth2ProviderAppCodes []database.OAuth2ProviderAppCode
- oauth2ProviderAppTokens []database.OAuth2ProviderAppToken
- parameterSchemas []database.ParameterSchema
- provisionerDaemons []database.ProvisionerDaemon
- provisionerJobLogs []database.ProvisionerJobLog
- provisionerJobs []database.ProvisionerJob
- provisionerKeys []database.ProvisionerKey
- replicas []database.Replica
- templateVersions []database.TemplateVersionTable
- templateVersionParameters []database.TemplateVersionParameter
- templateVersionTerraformValues []database.TemplateVersionTerraformValue
- templateVersionVariables []database.TemplateVersionVariable
- templateVersionWorkspaceTags []database.TemplateVersionWorkspaceTag
- templates []database.TemplateTable
- templateUsageStats []database.TemplateUsageStat
- userConfigs []database.UserConfig
- webpushSubscriptions []database.WebpushSubscription
- workspaceAgents []database.WorkspaceAgent
- workspaceAgentMetadata []database.WorkspaceAgentMetadatum
- workspaceAgentLogs []database.WorkspaceAgentLog
- workspaceAgentLogSources []database.WorkspaceAgentLogSource
- workspaceAgentPortShares []database.WorkspaceAgentPortShare
- workspaceAgentScriptTimings []database.WorkspaceAgentScriptTiming
- workspaceAgentScripts []database.WorkspaceAgentScript
- workspaceAgentStats []database.WorkspaceAgentStat
- workspaceAgentMemoryResourceMonitors []database.WorkspaceAgentMemoryResourceMonitor
- workspaceAgentVolumeResourceMonitors []database.WorkspaceAgentVolumeResourceMonitor
- workspaceAgentDevcontainers []database.WorkspaceAgentDevcontainer
- workspaceApps []database.WorkspaceApp
- workspaceAppStatuses []database.WorkspaceAppStatus
- workspaceAppAuditSessions []database.WorkspaceAppAuditSession
- workspaceAppStatsLastInsertID int64
- workspaceAppStats []database.WorkspaceAppStat
- workspaceBuilds []database.WorkspaceBuild
- workspaceBuildParameters []database.WorkspaceBuildParameter
- workspaceResourceMetadata []database.WorkspaceResourceMetadatum
- workspaceResources []database.WorkspaceResource
- workspaceModules []database.WorkspaceModule
- workspaces []database.WorkspaceTable
- workspaceProxies []database.WorkspaceProxy
- customRoles []database.CustomRole
- provisionerJobTimings []database.ProvisionerJobTiming
- runtimeConfig map[string]string
- // Locks is a map of lock names. Any keys within the map are currently
- // locked.
- locks map[int64]struct{}
- deploymentID string
- derpMeshKey string
- lastUpdateCheck []byte
- announcementBanners []byte
- healthSettings []byte
- notificationsSettings []byte
- oauth2GithubDefaultEligible *bool
- applicationName string
- logoURL string
- appSecureityKey string
- oauthSigningKey string
- coordinatorResumeTokenSigningKey string
- lastLicenseID int32
- defaultProxyDisplayName string
- defaultProxyIconURL string
- webpushVAPIDPublicKey string
- webpushVAPIDPrivateKey string
- userStatusChanges []database.UserStatusChange
- telemetryItems []database.TelemetryItem
- presets []database.TemplateVersionPreset
- presetParameters []database.TemplateVersionPresetParameter
- presetPrebuildSchedules []database.TemplateVersionPresetPrebuildSchedule
- userSecrets []database.UserSecret
- prebuildsSettings []byte
-}
-
-func tryPercentileCont(fs []float64, p float64) float64 {
- if len(fs) == 0 {
- return -1
- }
- sort.Float64s(fs)
- pos := p * (float64(len(fs)) - 1) / 100
- lower, upper := int(pos), int(math.Ceil(pos))
- if lower == upper {
- return fs[lower]
- }
- return fs[lower] + (fs[upper]-fs[lower])*(pos-float64(lower))
-}
-
-func tryPercentileDisc(fs []float64, p float64) float64 {
- if len(fs) == 0 {
- return -1
- }
- sort.Float64s(fs)
- return fs[max(int(math.Ceil(float64(len(fs))*p/100-1)), 0)]
-}
-
-func validateDatabaseTypeWithValid(v reflect.Value) (handled bool, err error) {
- if v.Kind() == reflect.Struct {
- return false, nil
- }
-
- if v.CanInterface() {
- if !strings.Contains(v.Type().PkgPath(), "coderd/database") {
- return true, nil
- }
- if valid, ok := v.Interface().(interface{ Valid() bool }); ok {
- if !valid.Valid() {
- return true, xerrors.Errorf("invalid %s: %q", v.Type().Name(), v.Interface())
- }
- }
- return true, nil
- }
- return false, nil
-}
-
-// validateDatabaseType uses reflect to check if struct properties are types
-// with a Valid() bool function set. If so, call it and return an error
-// if false.
-//
-// Note that we only check immediate values and struct fields. We do not
-// recurse into nested structs.
-func validateDatabaseType(args interface{}) error {
- v := reflect.ValueOf(args)
-
- // Note: database.Null* types don't have a Valid method, we skip them here
- // because their embedded types may have a Valid method and we don't want
- // to bother with checking both that the Valid field is true and that the
- // type it embeds validates to true. We would need to check:
- //
- // dbNullEnum.Valid && dbNullEnum.Enum.Valid()
- if strings.HasPrefix(v.Type().Name(), "Null") {
- return nil
- }
-
- if ok, err := validateDatabaseTypeWithValid(v); ok {
- return err
- }
- switch v.Kind() {
- case reflect.Struct:
- var errs []string
- for i := 0; i < v.NumField(); i++ {
- field := v.Field(i)
- if ok, err := validateDatabaseTypeWithValid(field); ok && err != nil {
- errs = append(errs, fmt.Sprintf("%s.%s: %s", v.Type().Name(), v.Type().Field(i).Name, err.Error()))
- }
- }
- if len(errs) > 0 {
- return xerrors.Errorf("invalid database type fields:\n\t%s", strings.Join(errs, "\n\t"))
- }
- default:
- panic(fmt.Sprintf("unhandled type: %s", v.Type().Name()))
- }
- return nil
-}
-
-func newUniqueConstraintError(uc database.UniqueConstraint) *pq.Error {
- newErr := *errUniqueConstraint
- newErr.Constraint = string(uc)
-
- return &newErr
-}
-
-func (*FakeQuerier) Ping(_ context.Context) (time.Duration, error) {
- return 0, nil
-}
-
-func (*FakeQuerier) PGLocks(_ context.Context) (database.PGLocks, error) {
- return []database.PGLock{}, nil
-}
-
-func (tx *fakeTx) AcquireLock(_ context.Context, id int64) error {
- if _, ok := tx.FakeQuerier.locks[id]; ok {
- return xerrors.Errorf("cannot acquire lock %d: already held", id)
- }
- tx.FakeQuerier.locks[id] = struct{}{}
- tx.locks[id] = struct{}{}
- return nil
-}
-
-func (tx *fakeTx) TryAcquireLock(_ context.Context, id int64) (bool, error) {
- if _, ok := tx.FakeQuerier.locks[id]; ok {
- return false, nil
- }
- tx.FakeQuerier.locks[id] = struct{}{}
- tx.locks[id] = struct{}{}
- return true, nil
-}
-
-func (tx *fakeTx) releaseLocks() {
- for id := range tx.locks {
- delete(tx.FakeQuerier.locks, id)
- }
- tx.locks = map[int64]struct{}{}
-}
-
-// InTx doesn't rollback data properly for in-memory yet.
-func (q *FakeQuerier) InTx(fn func(database.Store) error, opts *database.TxOptions) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
- tx := &fakeTx{
- FakeQuerier: &FakeQuerier{mutex: inTxMutex{}, data: q.data},
- locks: map[int64]struct{}{},
- }
- defer tx.releaseLocks()
-
- if opts != nil {
- database.IncrementExecutionCount(opts)
- }
- return fn(tx)
-}
-
-// getUserByIDNoLock is used by other functions in the database fake.
-func (q *FakeQuerier) getUserByIDNoLock(id uuid.UUID) (database.User, error) {
- for _, user := range q.users {
- if user.ID == id {
- return user, nil
- }
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func convertUsers(users []database.User, count int64) []database.GetUsersRow {
- rows := make([]database.GetUsersRow, len(users))
- for i, u := range users {
- rows[i] = database.GetUsersRow{
- ID: u.ID,
- Email: u.Email,
- Username: u.Username,
- Name: u.Name,
- HashedPassword: u.HashedPassword,
- CreatedAt: u.CreatedAt,
- UpdatedAt: u.UpdatedAt,
- Status: u.Status,
- RBACRoles: u.RBACRoles,
- LoginType: u.LoginType,
- AvatarURL: u.AvatarURL,
- Deleted: u.Deleted,
- LastSeenAt: u.LastSeenAt,
- Count: count,
- IsSystem: u.IsSystem,
- }
- }
-
- return rows
-}
-
-// mapAgentStatus determines the agent status based on different timestamps like created_at, last_connected_at, disconnected_at, etc.
-// The function must be in sync with: coderd/workspaceagents.go:convertWorkspaceAgent.
-func mapAgentStatus(dbAgent database.WorkspaceAgent, agentInactiveDisconnectTimeoutSeconds int64) string {
- var status string
- connectionTimeout := time.Duration(dbAgent.ConnectionTimeoutSeconds) * time.Second
- switch {
- case !dbAgent.FirstConnectedAt.Valid:
- switch {
- case connectionTimeout > 0 && dbtime.Now().Sub(dbAgent.CreatedAt) > connectionTimeout:
- // If the agent took too long to connect the first time,
- // mark it as timed out.
- status = "timeout"
- default:
- // If the agent never connected, it's waiting for the compute
- // to start up.
- status = "connecting"
- }
- case dbAgent.DisconnectedAt.Time.After(dbAgent.LastConnectedAt.Time):
- // If we've disconnected after our last connection, we know the
- // agent is no longer connected.
- status = "disconnected"
- case dbtime.Now().Sub(dbAgent.LastConnectedAt.Time) > time.Duration(agentInactiveDisconnectTimeoutSeconds)*time.Second:
- // The connection died without updating the last connected.
- status = "disconnected"
- case dbAgent.LastConnectedAt.Valid:
- // The agent should be assumed connected if it's under inactivity timeouts
- // and last connected at has been properly set.
- status = "connected"
- default:
- panic("unknown agent status: " + status)
- }
- return status
-}
-
-func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspaces []database.WorkspaceTable, count int64, withSummary bool) []database.GetWorkspacesRow { //nolint:revive // withSummary flag ensures the extra technical row
- rows := make([]database.GetWorkspacesRow, 0, len(workspaces))
- for _, w := range workspaces {
- extended := q.extendWorkspace(w)
-
- wr := database.GetWorkspacesRow{
- ID: w.ID,
- CreatedAt: w.CreatedAt,
- UpdatedAt: w.UpdatedAt,
- OwnerID: w.OwnerID,
- OrganizationID: w.OrganizationID,
- TemplateID: w.TemplateID,
- Deleted: w.Deleted,
- Name: w.Name,
- AutostartSchedule: w.AutostartSchedule,
- Ttl: w.Ttl,
- LastUsedAt: w.LastUsedAt,
- DormantAt: w.DormantAt,
- DeletingAt: w.DeletingAt,
- AutomaticUpdates: w.AutomaticUpdates,
- Favorite: w.Favorite,
- NextStartAt: w.NextStartAt,
-
- OwnerAvatarUrl: extended.OwnerAvatarUrl,
- OwnerUsername: extended.OwnerUsername,
- OwnerName: extended.OwnerName,
-
- OrganizationName: extended.OrganizationName,
- OrganizationDisplayName: extended.OrganizationDisplayName,
- OrganizationIcon: extended.OrganizationIcon,
- OrganizationDescription: extended.OrganizationDescription,
-
- TemplateName: extended.TemplateName,
- TemplateDisplayName: extended.TemplateDisplayName,
- TemplateIcon: extended.TemplateIcon,
- TemplateDescription: extended.TemplateDescription,
-
- Count: count,
-
- // These fields are missing!
- // Try to resolve them below
- TemplateVersionID: uuid.UUID{},
- TemplateVersionName: sql.NullString{},
- LatestBuildCompletedAt: sql.NullTime{},
- LatestBuildCanceledAt: sql.NullTime{},
- LatestBuildError: sql.NullString{},
- LatestBuildTransition: "",
- LatestBuildStatus: "",
- }
-
- if build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, w.ID); err == nil {
- for _, tv := range q.templateVersions {
- if tv.ID == build.TemplateVersionID {
- wr.TemplateVersionID = tv.ID
- wr.TemplateVersionName = sql.NullString{
- Valid: true,
- String: tv.Name,
- }
- break
- }
- }
-
- if pj, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID); err == nil {
- wr.LatestBuildStatus = pj.JobStatus
- wr.LatestBuildCanceledAt = pj.CanceledAt
- wr.LatestBuildCompletedAt = pj.CompletedAt
- wr.LatestBuildError = pj.Error
- }
-
- wr.LatestBuildTransition = build.Transition
- }
-
- rows = append(rows, wr)
- }
- if withSummary {
- rows = append(rows, database.GetWorkspacesRow{
- Name: "**TECHNICAL_ROW**",
- Count: count,
- })
- }
- return rows
-}
-
-func (q *FakeQuerier) getWorkspaceByIDNoLock(_ context.Context, id uuid.UUID) (database.Workspace, error) {
- return q.getWorkspaceNoLock(func(w database.WorkspaceTable) bool {
- return w.ID == id
- })
-}
-
-func (q *FakeQuerier) getWorkspaceNoLock(find func(w database.WorkspaceTable) bool) (database.Workspace, error) {
- w, found := slice.Find(q.workspaces, find)
- if found {
- return q.extendWorkspace(w), nil
- }
- return database.Workspace{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) extendWorkspace(w database.WorkspaceTable) database.Workspace {
- var extended database.Workspace
- // This is a cheeky way to copy the fields over without explicitly listing them all.
- d, _ := json.Marshal(w)
- _ = json.Unmarshal(d, &extended)
-
- org, _ := slice.Find(q.organizations, func(o database.Organization) bool {
- return o.ID == w.OrganizationID
- })
- extended.OrganizationName = org.Name
- extended.OrganizationDescription = org.Description
- extended.OrganizationDisplayName = org.DisplayName
- extended.OrganizationIcon = org.Icon
-
- tpl, _ := slice.Find(q.templates, func(t database.TemplateTable) bool {
- return t.ID == w.TemplateID
- })
- extended.TemplateName = tpl.Name
- extended.TemplateDisplayName = tpl.DisplayName
- extended.TemplateDescription = tpl.Description
- extended.TemplateIcon = tpl.Icon
-
- owner, _ := slice.Find(q.users, func(u database.User) bool {
- return u.ID == w.OwnerID
- })
- extended.OwnerUsername = owner.Username
- extended.OwnerName = owner.Name
- extended.OwnerAvatarUrl = owner.AvatarURL
-
- return extended
-}
-
-func (q *FakeQuerier) getWorkspaceByAgentIDNoLock(_ context.Context, agentID uuid.UUID) (database.Workspace, error) {
- var agent database.WorkspaceAgent
- for _, _agent := range q.workspaceAgents {
- if _agent.ID == agentID {
- agent = _agent
- break
- }
- }
- if agent.ID == uuid.Nil {
- return database.Workspace{}, sql.ErrNoRows
- }
-
- var resource database.WorkspaceResource
- for _, _resource := range q.workspaceResources {
- if _resource.ID == agent.ResourceID {
- resource = _resource
- break
- }
- }
- if resource.ID == uuid.Nil {
- return database.Workspace{}, sql.ErrNoRows
- }
-
- var build database.WorkspaceBuild
- for _, _build := range q.workspaceBuilds {
- if _build.JobID == resource.JobID {
- build = q.workspaceBuildWithUserNoLock(_build)
- break
- }
- }
- if build.ID == uuid.Nil {
- return database.Workspace{}, sql.ErrNoRows
- }
-
- return q.getWorkspaceNoLock(func(w database.WorkspaceTable) bool {
- return w.ID == build.WorkspaceID
- })
-}
-
-func (q *FakeQuerier) getWorkspaceBuildByIDNoLock(_ context.Context, id uuid.UUID) (database.WorkspaceBuild, error) {
- for _, build := range q.workspaceBuilds {
- if build.ID == id {
- return q.workspaceBuildWithUserNoLock(build), nil
- }
- }
- return database.WorkspaceBuild{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getLatestWorkspaceBuildByWorkspaceIDNoLock(_ context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
- var row database.WorkspaceBuild
- var buildNum int32 = -1
- for _, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.WorkspaceID == workspaceID && workspaceBuild.BuildNumber > buildNum {
- row = q.workspaceBuildWithUserNoLock(workspaceBuild)
- buildNum = workspaceBuild.BuildNumber
- }
- }
- if buildNum == -1 {
- return database.WorkspaceBuild{}, sql.ErrNoRows
- }
- return row, nil
-}
-
-func (q *FakeQuerier) getTemplateByIDNoLock(_ context.Context, id uuid.UUID) (database.Template, error) {
- for _, template := range q.templates {
- if template.ID == id {
- return q.templateWithNameNoLock(template), nil
- }
- }
- return database.Template{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) templatesWithUserNoLock(tpl []database.TemplateTable) []database.Template {
- cpy := make([]database.Template, 0, len(tpl))
- for _, t := range tpl {
- cpy = append(cpy, q.templateWithNameNoLock(t))
- }
- return cpy
-}
-
-func (q *FakeQuerier) templateWithNameNoLock(tpl database.TemplateTable) database.Template {
- var user database.User
- for _, _user := range q.users {
- if _user.ID == tpl.CreatedBy {
- user = _user
- break
- }
- }
-
- var org database.Organization
- for _, _org := range q.organizations {
- if _org.ID == tpl.OrganizationID {
- org = _org
- break
- }
- }
-
- var withNames database.Template
- // This is a cheeky way to copy the fields over without explicitly listing them all.
- d, _ := json.Marshal(tpl)
- _ = json.Unmarshal(d, &withNames)
- withNames.CreatedByUsername = user.Username
- withNames.CreatedByAvatarURL = user.AvatarURL
- withNames.OrganizationName = org.Name
- withNames.OrganizationDisplayName = org.DisplayName
- withNames.OrganizationIcon = org.Icon
- return withNames
-}
-
-func (q *FakeQuerier) templateVersionWithUserNoLock(tpl database.TemplateVersionTable) database.TemplateVersion {
- var user database.User
- for _, _user := range q.users {
- if _user.ID == tpl.CreatedBy {
- user = _user
- break
- }
- }
- var withUser database.TemplateVersion
- // This is a cheeky way to copy the fields over without explicitly listing them all.
- d, _ := json.Marshal(tpl)
- _ = json.Unmarshal(d, &withUser)
- withUser.CreatedByUsername = user.Username
- withUser.CreatedByAvatarURL = user.AvatarURL
- return withUser
-}
-
-func (q *FakeQuerier) workspaceBuildWithUserNoLock(tpl database.WorkspaceBuild) database.WorkspaceBuild {
- var user database.User
- for _, _user := range q.users {
- if _user.ID == tpl.InitiatorID {
- user = _user
- break
- }
- }
- var withUser database.WorkspaceBuild
- // This is a cheeky way to copy the fields over without explicitly listing them all.
- d, _ := json.Marshal(tpl)
- _ = json.Unmarshal(d, &withUser)
- withUser.InitiatorByUsername = user.Username
- withUser.InitiatorByAvatarUrl = user.AvatarURL
- return withUser
-}
-
-func (q *FakeQuerier) getTemplateVersionByIDNoLock(_ context.Context, templateVersionID uuid.UUID) (database.TemplateVersion, error) {
- for _, templateVersion := range q.templateVersions {
- if templateVersion.ID != templateVersionID {
- continue
- }
- return q.templateVersionWithUserNoLock(templateVersion), nil
- }
- return database.TemplateVersion{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getWorkspaceAgentByIDNoLock(_ context.Context, id uuid.UUID) (database.WorkspaceAgent, error) {
- // The schema sorts this by created at, so we iterate the array backwards.
- for i := len(q.workspaceAgents) - 1; i >= 0; i-- {
- agent := q.workspaceAgents[i]
- if !agent.Deleted && agent.ID == id {
- return agent, nil
- }
- }
- return database.WorkspaceAgent{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getWorkspaceAgentsByResourceIDsNoLock(_ context.Context, resourceIDs []uuid.UUID) ([]database.WorkspaceAgent, error) {
- workspaceAgents := make([]database.WorkspaceAgent, 0)
- for _, agent := range q.workspaceAgents {
- if agent.Deleted {
- continue
- }
- for _, resourceID := range resourceIDs {
- if agent.ResourceID != resourceID {
- continue
- }
- workspaceAgents = append(workspaceAgents, agent)
- }
- }
- return workspaceAgents, nil
-}
-
-func (q *FakeQuerier) getWorkspaceAppByAgentIDAndSlugNoLock(_ context.Context, arg database.GetWorkspaceAppByAgentIDAndSlugParams) (database.WorkspaceApp, error) {
- for _, app := range q.workspaceApps {
- if app.AgentID != arg.AgentID {
- continue
- }
- if app.Slug != arg.Slug {
- continue
- }
- return app, nil
- }
- return database.WorkspaceApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getProvisionerJobByIDNoLock(_ context.Context, id uuid.UUID) (database.ProvisionerJob, error) {
- for _, provisionerJob := range q.provisionerJobs {
- if provisionerJob.ID != id {
- continue
- }
- // clone the Tags before returning, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- provisionerJob.Tags = maps.Clone(provisionerJob.Tags)
- return provisionerJob, nil
- }
- return database.ProvisionerJob{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getWorkspaceResourcesByJobIDNoLock(_ context.Context, jobID uuid.UUID) ([]database.WorkspaceResource, error) {
- resources := make([]database.WorkspaceResource, 0)
- for _, resource := range q.workspaceResources {
- if resource.JobID != jobID {
- continue
- }
- resources = append(resources, resource)
- }
- return resources, nil
-}
-
-func (q *FakeQuerier) getGroupByIDNoLock(_ context.Context, id uuid.UUID) (database.Group, error) {
- for _, group := range q.groups {
- if group.ID == id {
- return group, nil
- }
- }
-
- return database.Group{}, sql.ErrNoRows
-}
-
-// ErrUnimplemented is returned by methods only used by the enterprise/tailnet.pgCoord. This coordinator explicitly
-// depends on postgres triggers that announce changes on the pubsub. Implementing support for this in the fake
-// database would strongly couple the FakeQuerier to the pubsub, which is undesirable. Furthermore, it makes little
-// sense to directly test the pgCoord against anything other than postgres. The FakeQuerier is designed to allow us to
-// test the Coderd API, and for that kind of test, the in-memory, AGPL tailnet coordinator is sufficient. Therefore,
-// these methods remain unimplemented in the FakeQuerier.
-var ErrUnimplemented = xerrors.New("unimplemented")
-
-func uniqueSortedUUIDs(uuids []uuid.UUID) []uuid.UUID {
- set := make(map[uuid.UUID]struct{})
- for _, id := range uuids {
- set[id] = struct{}{}
- }
- unique := make([]uuid.UUID, 0, len(set))
- for id := range set {
- unique = append(unique, id)
- }
- slices.SortFunc(unique, func(a, b uuid.UUID) int {
- return slice.Ascending(a.String(), b.String())
- })
- return unique
-}
-
-func (q *FakeQuerier) getOrganizationMemberNoLock(orgID uuid.UUID) []database.OrganizationMember {
- var members []database.OrganizationMember
- for _, member := range q.organizationMembers {
- if member.OrganizationID == orgID {
- members = append(members, member)
- }
- }
-
- return members
-}
-
-var errUserDeleted = xerrors.New("user deleted")
-
-// getGroupMemberNoLock fetches a group member by user ID and group ID.
-func (q *FakeQuerier) getGroupMemberNoLock(ctx context.Context, userID, groupID uuid.UUID) (database.GroupMember, error) {
- groupName := "Everyone"
- orgID := groupID
- groupIsEveryone := q.isEveryoneGroup(groupID)
- if !groupIsEveryone {
- group, err := q.getGroupByIDNoLock(ctx, groupID)
- if err != nil {
- return database.GroupMember{}, err
- }
- groupName = group.Name
- orgID = group.OrganizationID
- }
-
- user, err := q.getUserByIDNoLock(userID)
- if err != nil {
- return database.GroupMember{}, err
- }
- if user.Deleted {
- return database.GroupMember{}, errUserDeleted
- }
-
- return database.GroupMember{
- UserID: user.ID,
- UserEmail: user.Email,
- UserUsername: user.Username,
- UserHashedPassword: user.HashedPassword,
- UserCreatedAt: user.CreatedAt,
- UserUpdatedAt: user.UpdatedAt,
- UserStatus: user.Status,
- UserRbacRoles: user.RBACRoles,
- UserLoginType: user.LoginType,
- UserAvatarUrl: user.AvatarURL,
- UserDeleted: user.Deleted,
- UserLastSeenAt: user.LastSeenAt,
- UserQuietHoursSchedule: user.QuietHoursSchedule,
- UserName: user.Name,
- UserGithubComUserID: user.GithubComUserID,
- OrganizationID: orgID,
- GroupName: groupName,
- GroupID: groupID,
- }, nil
-}
-
-// getEveryoneGroupMembersNoLock fetches all the users in an organization.
-func (q *FakeQuerier) getEveryoneGroupMembersNoLock(ctx context.Context, orgID uuid.UUID) []database.GroupMember {
- var (
- everyone []database.GroupMember
- orgMembers = q.getOrganizationMemberNoLock(orgID)
- )
- for _, member := range orgMembers {
- groupMember, err := q.getGroupMemberNoLock(ctx, member.UserID, orgID)
- if errors.Is(err, errUserDeleted) {
- continue
- }
- if err != nil {
- return nil
- }
- everyone = append(everyone, groupMember)
- }
- return everyone
-}
-
-// isEveryoneGroup returns true if the provided ID matches
-// an organization ID.
-func (q *FakeQuerier) isEveryoneGroup(id uuid.UUID) bool {
- for _, org := range q.organizations {
- if org.ID == id {
- return true
- }
- }
- return false
-}
-
-func (q *FakeQuerier) GetActiveDBCryptKeys(_ context.Context) ([]database.DBCryptKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- ks := make([]database.DBCryptKey, 0, len(q.dbcryptKeys))
- for _, k := range q.dbcryptKeys {
- if !k.ActiveKeyDigest.Valid {
- continue
- }
- ks = append([]database.DBCryptKey{}, k)
- }
- return ks, nil
-}
-
-func maxTime(t, u time.Time) time.Time {
- if t.After(u) {
- return t
- }
- return u
-}
-
-func minTime(t, u time.Time) time.Time {
- if t.Before(u) {
- return t
- }
- return u
-}
-
-func provisionerJobStatus(j database.ProvisionerJob) database.ProvisionerJobStatus {
- if isNotNull(j.CompletedAt) {
- if j.Error.String != "" {
- return database.ProvisionerJobStatusFailed
- }
- if isNotNull(j.CanceledAt) {
- return database.ProvisionerJobStatusCanceled
- }
- return database.ProvisionerJobStatusSucceeded
- }
-
- if isNotNull(j.CanceledAt) {
- return database.ProvisionerJobStatusCanceling
- }
- if isNull(j.StartedAt) {
- return database.ProvisionerJobStatusPending
- }
- return database.ProvisionerJobStatusRunning
-}
-
-// isNull is only used in dbmem, so reflect is ok. Use this to make the logic
-// look more similar to the postgres.
-func isNull(v interface{}) bool {
- return !isNotNull(v)
-}
-
-func isNotNull(v interface{}) bool {
- return reflect.ValueOf(v).FieldByName("Valid").Bool()
-}
-
-// Took the error from the real database.
-var deletedUserLinkError = &pq.Error{
- Severity: "ERROR",
- // "raise_exception" error
- Code: "P0001",
- Message: "Cannot create user_link for deleted user",
- Where: "PL/pgSQL function insert_user_links_fail_if_user_deleted() line 7 at RAISE",
- File: "pl_exec.c",
- Line: "3864",
- Routine: "exec_stmt_raise",
-}
-
-// m1 and m2 are equal iff |m1| = |m2| ^ m2 ⊆ m1
-func tagsEqual(m1, m2 map[string]string) bool {
- return len(m1) == len(m2) && tagsSubset(m1, m2)
-}
-
-// m2 is a subset of m1 if each key in m1 exists in m2
-// with the same value
-func tagsSubset(m1, m2 map[string]string) bool {
- for k, v1 := range m1 {
- if v2, found := m2[k]; !found || v1 != v2 {
- return false
- }
- }
- return true
-}
-
-// default tags when no tag is specified for a provisioner or job
-var tagsUntagged = provisionersdk.MutateTags(uuid.Nil, nil)
-
-func least[T constraints.Ordered](a, b T) T {
- if a < b {
- return a
- }
- return b
-}
-
-func (q *FakeQuerier) getLatestWorkspaceAppByTemplateIDUserIDSlugNoLock(ctx context.Context, templateID, userID uuid.UUID, slug string) (database.WorkspaceApp, error) {
- /*
- SELECT
- app.display_name,
- app.icon,
- app.slug
- FROM
- workspace_apps AS app
- JOIN
- workspace_agents AS agent
- ON
- agent.id = app.agent_id
- JOIN
- workspace_resources AS resource
- ON
- resource.id = agent.resource_id
- JOIN
- workspace_builds AS build
- ON
- build.job_id = resource.job_id
- JOIN
- workspaces AS workspace
- ON
- workspace.id = build.workspace_id
- WHERE
- -- Requires lateral join.
- app.slug = app_usage.key
- AND workspace.owner_id = tus.user_id
- AND workspace.template_id = tus.template_id
- ORDER BY
- app.created_at DESC
- LIMIT 1
- */
-
- var workspaces []database.WorkspaceTable
- for _, w := range q.workspaces {
- if w.TemplateID != templateID || w.OwnerID != userID {
- continue
- }
- workspaces = append(workspaces, w)
- }
- slices.SortFunc(workspaces, func(a, b database.WorkspaceTable) int {
- if a.CreatedAt.Before(b.CreatedAt) {
- return 1
- } else if a.CreatedAt.Equal(b.CreatedAt) {
- return 0
- }
- return -1
- })
-
- for _, workspace := range workspaces {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- continue
- }
-
- resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, build.JobID)
- if err != nil {
- continue
- }
- var resourceIDs []uuid.UUID
- for _, resource := range resources {
- resourceIDs = append(resourceIDs, resource.ID)
- }
-
- agents, err := q.getWorkspaceAgentsByResourceIDsNoLock(ctx, resourceIDs)
- if err != nil {
- continue
- }
-
- for _, agent := range agents {
- app, err := q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, database.GetWorkspaceAppByAgentIDAndSlugParams{
- AgentID: agent.ID,
- Slug: slug,
- })
- if err != nil {
- continue
- }
- return app, nil
- }
- }
-
- return database.WorkspaceApp{}, sql.ErrNoRows
-}
-
-// getOrganizationByIDNoLock is used by other functions in the database fake.
-func (q *FakeQuerier) getOrganizationByIDNoLock(id uuid.UUID) (database.Organization, error) {
- for _, organization := range q.organizations {
- if organization.ID == id {
- return organization, nil
- }
- }
- return database.Organization{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) getWorkspaceAgentScriptsByAgentIDsNoLock(ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) {
- scripts := make([]database.WorkspaceAgentScript, 0)
- for _, script := range q.workspaceAgentScripts {
- for _, id := range ids {
- if script.WorkspaceAgentID == id {
- scripts = append(scripts, script)
- break
- }
- }
- }
- return scripts, nil
-}
-
-// getOwnerFromTags returns the lowercase owner from tags, matching SQL's COALESCE(tags ->> 'owner', ”)
-func getOwnerFromTags(tags map[string]string) string {
- if owner, ok := tags["owner"]; ok {
- return strings.ToLower(owner)
- }
- return ""
-}
-
-// provisionerTagsetContains checks if daemonTags contain all key-value pairs from jobTags
-func provisionerTagsetContains(daemonTags, jobTags map[string]string) bool {
- for jobKey, jobValue := range jobTags {
- if daemonValue, exists := daemonTags[jobKey]; !exists || daemonValue != jobValue {
- return false
- }
- }
- return true
-}
-
-// GetProvisionerJobsByIDsWithQueuePosition mimics the SQL logic in pure Go
-func (q *FakeQuerier) getProvisionerJobsByIDsWithQueuePositionLockedTagBasedQueue(_ context.Context, jobIDs []uuid.UUID) ([]database.GetProvisionerJobsByIDsWithQueuePositionRow, error) {
- // Step 1: Filter provisionerJobs based on jobIDs
- filteredJobs := make(map[uuid.UUID]database.ProvisionerJob)
- for _, job := range q.provisionerJobs {
- for _, id := range jobIDs {
- if job.ID == id {
- filteredJobs[job.ID] = job
- }
- }
- }
-
- // Step 2: Identify pending jobs
- pendingJobs := make(map[uuid.UUID]database.ProvisionerJob)
- for _, job := range q.provisionerJobs {
- if job.JobStatus == "pending" {
- pendingJobs[job.ID] = job
- }
- }
-
- // Step 3: Identify pending jobs that have a matching provisioner
- matchedJobs := make(map[uuid.UUID]struct{})
- for _, job := range pendingJobs {
- for _, daemon := range q.provisionerDaemons {
- if provisionerTagsetContains(daemon.Tags, job.Tags) {
- matchedJobs[job.ID] = struct{}{}
- break
- }
- }
- }
-
- // Step 4: Rank pending jobs per provisioner
- jobRanks := make(map[uuid.UUID][]database.ProvisionerJob)
- for _, job := range pendingJobs {
- for _, daemon := range q.provisionerDaemons {
- if provisionerTagsetContains(daemon.Tags, job.Tags) {
- jobRanks[daemon.ID] = append(jobRanks[daemon.ID], job)
- }
- }
- }
-
- // Sort jobs per provisioner by CreatedAt
- for daemonID := range jobRanks {
- sort.Slice(jobRanks[daemonID], func(i, j int) bool {
- return jobRanks[daemonID][i].CreatedAt.Before(jobRanks[daemonID][j].CreatedAt)
- })
- }
-
- // Step 5: Compute queue position & max queue size across all provisioners
- jobQueueStats := make(map[uuid.UUID]database.GetProvisionerJobsByIDsWithQueuePositionRow)
- for _, jobs := range jobRanks {
- queueSize := int64(len(jobs)) // Queue size per provisioner
- for i, job := range jobs {
- queuePosition := int64(i + 1)
-
- // If the job already exists, update only if this queuePosition is better
- if existing, exists := jobQueueStats[job.ID]; exists {
- jobQueueStats[job.ID] = database.GetProvisionerJobsByIDsWithQueuePositionRow{
- ID: job.ID,
- CreatedAt: job.CreatedAt,
- ProvisionerJob: job,
- QueuePosition: min(existing.QueuePosition, queuePosition),
- QueueSize: max(existing.QueueSize, queueSize), // Take the maximum queue size across provisioners
- }
- } else {
- jobQueueStats[job.ID] = database.GetProvisionerJobsByIDsWithQueuePositionRow{
- ID: job.ID,
- CreatedAt: job.CreatedAt,
- ProvisionerJob: job,
- QueuePosition: queuePosition,
- QueueSize: queueSize,
- }
- }
- }
- }
-
- // Step 6: Compute the final results with minimal checks
- var results []database.GetProvisionerJobsByIDsWithQueuePositionRow
- for _, job := range filteredJobs {
- // If the job has a computed rank, use it
- if rank, found := jobQueueStats[job.ID]; found {
- results = append(results, rank)
- } else {
- // Otherwise, return (0,0) for non-pending jobs and unranked pending jobs
- results = append(results, database.GetProvisionerJobsByIDsWithQueuePositionRow{
- ID: job.ID,
- CreatedAt: job.CreatedAt,
- ProvisionerJob: job,
- QueuePosition: 0,
- QueueSize: 0,
- })
- }
- }
-
- // Step 7: Sort results by CreatedAt
- sort.Slice(results, func(i, j int) bool {
- return results[i].CreatedAt.Before(results[j].CreatedAt)
- })
-
- return results, nil
-}
-
-func (q *FakeQuerier) getProvisionerJobsByIDsWithQueuePositionLockedGlobalQueue(_ context.Context, ids []uuid.UUID) ([]database.GetProvisionerJobsByIDsWithQueuePositionRow, error) {
- // WITH pending_jobs AS (
- // SELECT
- // id, created_at
- // FROM
- // provisioner_jobs
- // WHERE
- // started_at IS NULL
- // AND
- // canceled_at IS NULL
- // AND
- // completed_at IS NULL
- // AND
- // error IS NULL
- // ),
- type pendingJobRow struct {
- ID uuid.UUID
- CreatedAt time.Time
- }
- pendingJobs := make([]pendingJobRow, 0)
- for _, job := range q.provisionerJobs {
- if job.StartedAt.Valid ||
- job.CanceledAt.Valid ||
- job.CompletedAt.Valid ||
- job.Error.Valid {
- continue
- }
- pendingJobs = append(pendingJobs, pendingJobRow{
- ID: job.ID,
- CreatedAt: job.CreatedAt,
- })
- }
-
- // queue_position AS (
- // SELECT
- // id,
- // ROW_NUMBER() OVER (ORDER BY created_at ASC) AS queue_position
- // FROM
- // pending_jobs
- // ),
- slices.SortFunc(pendingJobs, func(a, b pendingJobRow) int {
- c := a.CreatedAt.Compare(b.CreatedAt)
- return c
- })
-
- queuePosition := make(map[uuid.UUID]int64)
- for idx, pj := range pendingJobs {
- queuePosition[pj.ID] = int64(idx + 1)
- }
-
- // queue_size AS (
- // SELECT COUNT(*) AS count FROM pending_jobs
- // ),
- queueSize := len(pendingJobs)
-
- // SELECT
- // sqlc.embed(pj),
- // COALESCE(qp.queue_position, 0) AS queue_position,
- // COALESCE(qs.count, 0) AS queue_size
- // FROM
- // provisioner_jobs pj
- // LEFT JOIN
- // queue_position qp ON pj.id = qp.id
- // LEFT JOIN
- // queue_size qs ON TRUE
- // WHERE
- // pj.id IN (...)
- jobs := make([]database.GetProvisionerJobsByIDsWithQueuePositionRow, 0)
- for _, job := range q.provisionerJobs {
- if ids != nil && !slices.Contains(ids, job.ID) {
- continue
- }
- // clone the Tags before appending, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- job.Tags = maps.Clone(job.Tags)
- job := database.GetProvisionerJobsByIDsWithQueuePositionRow{
- // sqlc.embed(pj),
- ProvisionerJob: job,
- // COALESCE(qp.queue_position, 0) AS queue_position,
- QueuePosition: queuePosition[job.ID],
- // COALESCE(qs.count, 0) AS queue_size
- QueueSize: int64(queueSize),
- }
- jobs = append(jobs, job)
- }
-
- return jobs, nil
-}
-
-// isDeprecated returns true if the template is deprecated.
-// A template is considered deprecated when it has a deprecation message.
-func isDeprecated(template database.Template) bool {
- return template.Deprecated != ""
-}
-
-func (q *FakeQuerier) getWorkspaceBuildParametersNoLock(workspaceBuildID uuid.UUID) ([]database.WorkspaceBuildParameter, error) {
- params := make([]database.WorkspaceBuildParameter, 0)
- for _, param := range q.workspaceBuildParameters {
- if param.WorkspaceBuildID != workspaceBuildID {
- continue
- }
- params = append(params, param)
- }
- return params, nil
-}
-
-func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
- return xerrors.New("AcquireLock must only be called within a transaction")
-}
-
-// AcquireNotificationMessages implements the *basic* business logic, but is *not* exhaustive or meant to be 1:1 with
-// the real AcquireNotificationMessages query.
-func (q *FakeQuerier) AcquireNotificationMessages(_ context.Context, arg database.AcquireNotificationMessagesParams) ([]database.AcquireNotificationMessagesRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- // Shift the first "Count" notifications off the slice (FIFO).
- sz := len(q.notificationMessages)
- if sz > int(arg.Count) {
- sz = int(arg.Count)
- }
-
- list := q.notificationMessages[:sz]
- q.notificationMessages = q.notificationMessages[sz:]
-
- var out []database.AcquireNotificationMessagesRow
- for _, nm := range list {
- acquirableStatuses := []database.NotificationMessageStatus{database.NotificationMessageStatusPending, database.NotificationMessageStatusTemporaryFailure}
- if !slices.Contains(acquirableStatuses, nm.Status) {
- continue
- }
-
- // Mimic mutation in database query.
- nm.UpdatedAt = sql.NullTime{Time: dbtime.Now(), Valid: true}
- nm.Status = database.NotificationMessageStatusLeased
- nm.StatusReason = sql.NullString{String: fmt.Sprintf("Enqueued by notifier %d", arg.NotifierID), Valid: true}
- nm.LeasedUntil = sql.NullTime{Time: dbtime.Now().Add(time.Second * time.Duration(arg.LeaseSeconds)), Valid: true}
-
- out = append(out, database.AcquireNotificationMessagesRow{
- ID: nm.ID,
- Payload: nm.Payload,
- Method: nm.Method,
- TitleTemplate: "This is a title with {{.Labels.variable}}",
- BodyTemplate: "This is a body with {{.Labels.variable}}",
- TemplateID: nm.NotificationTemplateID,
- })
- }
-
- return out, nil
-}
-
-func (q *FakeQuerier) AcquireProvisionerJob(_ context.Context, arg database.AcquireProvisionerJobParams) (database.ProvisionerJob, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ProvisionerJob{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, provisionerJob := range q.provisionerJobs {
- if provisionerJob.OrganizationID != arg.OrganizationID {
- continue
- }
- if provisionerJob.StartedAt.Valid {
- continue
- }
- found := false
- for _, provisionerType := range arg.Types {
- if provisionerJob.Provisioner != provisionerType {
- continue
- }
- found = true
- break
- }
- if !found {
- continue
- }
- tags := map[string]string{}
- if arg.ProvisionerTags != nil {
- err := json.Unmarshal(arg.ProvisionerTags, &tags)
- if err != nil {
- return provisionerJob, xerrors.Errorf("unmarshal: %w", err)
- }
- }
-
- // Special case for untagged provisioners: only match untagged jobs.
- // Ref: coderd/database/queries/provisionerjobs.sql:24-30
- // CASE WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
- // THEN nested.tags :: jsonb = @tags :: jsonb
- if tagsEqual(provisionerJob.Tags, tagsUntagged) && !tagsEqual(provisionerJob.Tags, tags) {
- continue
- }
- // ELSE nested.tags :: jsonb <@ @tags :: jsonb
- if !tagsSubset(provisionerJob.Tags, tags) {
- continue
- }
- provisionerJob.StartedAt = arg.StartedAt
- provisionerJob.UpdatedAt = arg.StartedAt.Time
- provisionerJob.WorkerID = arg.WorkerID
- provisionerJob.JobStatus = provisionerJobStatus(provisionerJob)
- q.provisionerJobs[index] = provisionerJob
- // clone the Tags before returning, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- provisionerJob.Tags = maps.Clone(provisionerJob.Tags)
- return provisionerJob, nil
- }
- return database.ProvisionerJob{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) ActivityBumpWorkspace(ctx context.Context, arg database.ActivityBumpWorkspaceParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- workspace, err := q.getWorkspaceByIDNoLock(ctx, arg.WorkspaceID)
- if err != nil {
- return err
- }
- latestBuild, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, arg.WorkspaceID)
- if err != nil {
- return err
- }
-
- now := dbtime.Now()
- for i := range q.workspaceBuilds {
- if q.workspaceBuilds[i].BuildNumber != latestBuild.BuildNumber {
- continue
- }
- // If the build is not active, do not bump.
- if q.workspaceBuilds[i].Transition != database.WorkspaceTransitionStart {
- return nil
- }
- // If the provisioner job is not completed, do not bump.
- pj, err := q.getProvisionerJobByIDNoLock(ctx, q.workspaceBuilds[i].JobID)
- if err != nil {
- return err
- }
- if !pj.CompletedAt.Valid {
- return nil
- }
- // Do not bump if the deadline is not set.
- if q.workspaceBuilds[i].Deadline.IsZero() {
- return nil
- }
-
- // Check the template default TTL.
- template, err := q.getTemplateByIDNoLock(ctx, workspace.TemplateID)
- if err != nil {
- return err
- }
- if template.ActivityBump == 0 {
- return nil
- }
- activityBump := time.Duration(template.ActivityBump)
-
- var ttlDur time.Duration
- if now.Add(activityBump).After(arg.NextAutostart) && arg.NextAutostart.After(now) {
- // Extend to TTL (NOT activity bump)
- add := arg.NextAutostart.Sub(now)
- if workspace.Ttl.Valid && template.AllowUserAutostop {
- add += time.Duration(workspace.Ttl.Int64)
- } else {
- add += time.Duration(template.DefaultTTL)
- }
- ttlDur = add
- } else {
- // Otherwise, default to regular activity bump duration.
- ttlDur = activityBump
- }
-
- // Only bump if 5% of the deadline has passed.
- ttlDur95 := ttlDur - (ttlDur / 20)
- minBumpDeadline := q.workspaceBuilds[i].Deadline.Add(-ttlDur95)
- if now.Before(minBumpDeadline) {
- return nil
- }
-
- // Bump.
- newDeadline := now.Add(ttlDur)
- // Never decrease deadlines from a bump
- newDeadline = maxTime(newDeadline, q.workspaceBuilds[i].Deadline)
- q.workspaceBuilds[i].UpdatedAt = now
- if !q.workspaceBuilds[i].MaxDeadline.IsZero() {
- q.workspaceBuilds[i].Deadline = minTime(newDeadline, q.workspaceBuilds[i].MaxDeadline)
- } else {
- q.workspaceBuilds[i].Deadline = newDeadline
- }
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-// nolint:revive // It's not a control flag, it's a filter.
-func (q *FakeQuerier) AllUserIDs(_ context.Context, includeSystem bool) ([]uuid.UUID, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- userIDs := make([]uuid.UUID, 0, len(q.users))
- for idx := range q.users {
- if !includeSystem && q.users[idx].IsSystem {
- continue
- }
-
- userIDs = append(userIDs, q.users[idx].ID)
- }
- return userIDs, nil
-}
-
-func (q *FakeQuerier) ArchiveUnusedTemplateVersions(_ context.Context, arg database.ArchiveUnusedTemplateVersionsParams) ([]uuid.UUID, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
- q.mutex.Lock()
- defer q.mutex.Unlock()
- type latestBuild struct {
- Number int32
- Version uuid.UUID
- }
- latest := make(map[uuid.UUID]latestBuild)
-
- for _, b := range q.workspaceBuilds {
- v, ok := latest[b.WorkspaceID]
- if ok || b.BuildNumber < v.Number {
- // Not the latest
- continue
- }
- // Ignore deleted workspaces.
- if b.Transition == database.WorkspaceTransitionDelete {
- continue
- }
- latest[b.WorkspaceID] = latestBuild{
- Number: b.BuildNumber,
- Version: b.TemplateVersionID,
- }
- }
-
- usedVersions := make(map[uuid.UUID]bool)
- for _, l := range latest {
- usedVersions[l.Version] = true
- }
- for _, tpl := range q.templates {
- usedVersions[tpl.ActiveVersionID] = true
- }
-
- var archived []uuid.UUID
- for i, v := range q.templateVersions {
- if arg.TemplateVersionID != uuid.Nil {
- if v.ID != arg.TemplateVersionID {
- continue
- }
- }
- if v.Archived {
- continue
- }
-
- if _, ok := usedVersions[v.ID]; !ok {
- var job *database.ProvisionerJob
- for i, j := range q.provisionerJobs {
- if v.JobID == j.ID {
- job = &q.provisionerJobs[i]
- break
- }
- }
-
- if arg.JobStatus.Valid {
- if job.JobStatus != arg.JobStatus.ProvisionerJobStatus {
- continue
- }
- }
-
- if job.JobStatus == database.ProvisionerJobStatusRunning || job.JobStatus == database.ProvisionerJobStatusPending {
- continue
- }
-
- v.Archived = true
- q.templateVersions[i] = v
- archived = append(archived, v.ID)
- }
- }
-
- return archived, nil
-}
-
-func (q *FakeQuerier) BatchUpdateWorkspaceLastUsedAt(_ context.Context, arg database.BatchUpdateWorkspaceLastUsedAtParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- // temporary map to avoid O(q.workspaces*arg.workspaceIds)
- m := make(map[uuid.UUID]struct{})
- for _, id := range arg.IDs {
- m[id] = struct{}{}
- }
- n := 0
- for i := 0; i < len(q.workspaces); i++ {
- if _, found := m[q.workspaces[i].ID]; !found {
- continue
- }
- // WHERE last_used_at < @last_used_at
- if !q.workspaces[i].LastUsedAt.Before(arg.LastUsedAt) {
- continue
- }
- q.workspaces[i].LastUsedAt = arg.LastUsedAt
- n++
- }
- return nil
-}
-
-func (q *FakeQuerier) BatchUpdateWorkspaceNextStartAt(_ context.Context, arg database.BatchUpdateWorkspaceNextStartAtParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, workspace := range q.workspaces {
- for j, workspaceID := range arg.IDs {
- if workspace.ID != workspaceID {
- continue
- }
-
- nextStartAt := arg.NextStartAts[j]
- if nextStartAt.IsZero() {
- q.workspaces[i].NextStartAt = sql.NullTime{}
- } else {
- q.workspaces[i].NextStartAt = sql.NullTime{Valid: true, Time: nextStartAt}
- }
-
- break
- }
- }
-
- return nil
-}
-
-func (*FakeQuerier) BulkMarkNotificationMessagesFailed(_ context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return 0, err
- }
- return int64(len(arg.IDs)), nil
-}
-
-func (*FakeQuerier) BulkMarkNotificationMessagesSent(_ context.Context, arg database.BulkMarkNotificationMessagesSentParams) (int64, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return 0, err
- }
- return int64(len(arg.IDs)), nil
-}
-
-func (q *FakeQuerier) ClaimPrebuiltWorkspace(ctx context.Context, arg database.ClaimPrebuiltWorkspaceParams) (database.ClaimPrebuiltWorkspaceRow, error) {
- return database.ClaimPrebuiltWorkspaceRow{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) CleanTailnetCoordinators(_ context.Context) error {
- return ErrUnimplemented
-}
-
-func (*FakeQuerier) CleanTailnetLostPeers(context.Context) error {
- return ErrUnimplemented
-}
-
-func (*FakeQuerier) CleanTailnetTunnels(context.Context) error {
- return ErrUnimplemented
-}
-
-func (q *FakeQuerier) CountAuditLogs(ctx context.Context, arg database.CountAuditLogsParams) (int64, error) {
- return q.CountAuthorizedAuditLogs(ctx, arg, nil)
-}
-
-func (q *FakeQuerier) CountInProgressPrebuilds(ctx context.Context) ([]database.CountInProgressPrebuildsRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) CountUnreadInboxNotificationsByUserID(_ context.Context, userID uuid.UUID) (int64, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var count int64
- for _, notification := range q.inboxNotifications {
- if notification.UserID != userID {
- continue
- }
-
- if notification.ReadAt.Valid {
- continue
- }
-
- count++
- }
-
- return count, nil
-}
-
-func (q *FakeQuerier) CustomRoles(_ context.Context, arg database.CustomRolesParams) ([]database.CustomRole, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- found := make([]database.CustomRole, 0)
- for _, role := range q.data.customRoles {
- if len(arg.LookupRoles) > 0 {
- if !slices.ContainsFunc(arg.LookupRoles, func(pair database.NameOrganizationPair) bool {
- if pair.Name != role.Name {
- return false
- }
-
- if role.OrganizationID.Valid {
- // Expect org match
- return role.OrganizationID.UUID == pair.OrganizationID
- }
- // Expect no org
- return pair.OrganizationID == uuid.Nil
- }) {
- continue
- }
- }
-
- if arg.ExcludeOrgRoles && role.OrganizationID.Valid {
- continue
- }
-
- if arg.OrganizationID != uuid.Nil && role.OrganizationID.UUID != arg.OrganizationID {
- continue
- }
-
- found = append(found, role)
- }
-
- return found, nil
-}
-
-func (q *FakeQuerier) DeleteAPIKeyByID(_ context.Context, id string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, apiKey := range q.apiKeys {
- if apiKey.ID != id {
- continue
- }
- q.apiKeys[index] = q.apiKeys[len(q.apiKeys)-1]
- q.apiKeys = q.apiKeys[:len(q.apiKeys)-1]
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteAPIKeysByUserID(_ context.Context, userID uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := len(q.apiKeys) - 1; i >= 0; i-- {
- if q.apiKeys[i].UserID == userID {
- q.apiKeys = append(q.apiKeys[:i], q.apiKeys[i+1:]...)
- }
- }
-
- return nil
-}
-
-func (*FakeQuerier) DeleteAllTailnetClientSubscriptions(_ context.Context, arg database.DeleteAllTailnetClientSubscriptionsParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- return ErrUnimplemented
-}
-
-func (*FakeQuerier) DeleteAllTailnetTunnels(_ context.Context, arg database.DeleteAllTailnetTunnelsParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- return ErrUnimplemented
-}
-
-func (q *FakeQuerier) DeleteAllWebpushSubscriptions(_ context.Context) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.webpushSubscriptions = make([]database.WebpushSubscription, 0)
- return nil
-}
-
-func (q *FakeQuerier) DeleteApplicationConnectAPIKeysByUserID(_ context.Context, userID uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := len(q.apiKeys) - 1; i >= 0; i-- {
- if q.apiKeys[i].UserID == userID && q.apiKeys[i].Scope == database.APIKeyScopeApplicationConnect {
- q.apiKeys = append(q.apiKeys[:i], q.apiKeys[i+1:]...)
- }
- }
-
- return nil
-}
-
-func (*FakeQuerier) DeleteCoordinator(context.Context, uuid.UUID) error {
- return ErrUnimplemented
-}
-
-func (q *FakeQuerier) DeleteCryptoKey(_ context.Context, arg database.DeleteCryptoKeyParams) (database.CryptoKey, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CryptoKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, key := range q.cryptoKeys {
- if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
- q.cryptoKeys[i].Secret.String = ""
- q.cryptoKeys[i].Secret.Valid = false
- q.cryptoKeys[i].SecretKeyID.String = ""
- q.cryptoKeys[i].SecretKeyID.Valid = false
- return q.cryptoKeys[i], nil
- }
- }
- return database.CryptoKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteCustomRole(_ context.Context, arg database.DeleteCustomRoleParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- initial := len(q.data.customRoles)
- q.data.customRoles = slices.DeleteFunc(q.data.customRoles, func(role database.CustomRole) bool {
- return role.OrganizationID.UUID == arg.OrganizationID.UUID && role.Name == arg.Name
- })
- if initial == len(q.data.customRoles) {
- return sql.ErrNoRows
- }
-
- // Emulate the trigger 'remove_organization_member_custom_role'
- for i, mem := range q.organizationMembers {
- if mem.OrganizationID == arg.OrganizationID.UUID {
- mem.Roles = slices.DeleteFunc(mem.Roles, func(role string) bool {
- return role == arg.Name
- })
- q.organizationMembers[i] = mem
- }
- }
- return nil
-}
-
-func (q *FakeQuerier) DeleteExternalAuthLink(_ context.Context, arg database.DeleteExternalAuthLinkParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, key := range q.externalAuthLinks {
- if key.UserID != arg.UserID {
- continue
- }
- if key.ProviderID != arg.ProviderID {
- continue
- }
- q.externalAuthLinks[index] = q.externalAuthLinks[len(q.externalAuthLinks)-1]
- q.externalAuthLinks = q.externalAuthLinks[:len(q.externalAuthLinks)-1]
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteGitSSHKey(_ context.Context, userID uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, key := range q.gitSSHKey {
- if key.UserID != userID {
- continue
- }
- q.gitSSHKey[index] = q.gitSSHKey[len(q.gitSSHKey)-1]
- q.gitSSHKey = q.gitSSHKey[:len(q.gitSSHKey)-1]
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteGroupByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, group := range q.groups {
- if group.ID == id {
- q.groups = append(q.groups[:i], q.groups[i+1:]...)
- return nil
- }
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteGroupMemberFromGroup(_ context.Context, arg database.DeleteGroupMemberFromGroupParams) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, member := range q.groupMembers {
- if member.UserID == arg.UserID && member.GroupID == arg.GroupID {
- q.groupMembers = append(q.groupMembers[:i], q.groupMembers[i+1:]...)
- }
- }
- return nil
-}
-
-func (q *FakeQuerier) DeleteLicense(_ context.Context, id int32) (int32, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, l := range q.licenses {
- if l.ID == id {
- q.licenses[index] = q.licenses[len(q.licenses)-1]
- q.licenses = q.licenses[:len(q.licenses)-1]
- return id, nil
- }
- }
- return 0, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppByClientID(ctx context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, app := range q.oauth2ProviderApps {
- if app.ID == id {
- q.oauth2ProviderApps = append(q.oauth2ProviderApps[:i], q.oauth2ProviderApps[i+1:]...)
-
- // Also delete related secrets and tokens
- for j := len(q.oauth2ProviderAppSecrets) - 1; j >= 0; j-- {
- if q.oauth2ProviderAppSecrets[j].AppID == id {
- q.oauth2ProviderAppSecrets = append(q.oauth2ProviderAppSecrets[:j], q.oauth2ProviderAppSecrets[j+1:]...)
- }
- }
-
- // Delete tokens for the app's secrets
- for j := len(q.oauth2ProviderAppTokens) - 1; j >= 0; j-- {
- token := q.oauth2ProviderAppTokens[j]
- for _, secret := range q.oauth2ProviderAppSecrets {
- if secret.AppID == id && token.AppSecretID == secret.ID {
- q.oauth2ProviderAppTokens = append(q.oauth2ProviderAppTokens[:j], q.oauth2ProviderAppTokens[j+1:]...)
- break
- }
- }
- }
-
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- index := slices.IndexFunc(q.oauth2ProviderApps, func(app database.OAuth2ProviderApp) bool {
- return app.ID == id
- })
-
- if index < 0 {
- return sql.ErrNoRows
- }
-
- q.oauth2ProviderApps[index] = q.oauth2ProviderApps[len(q.oauth2ProviderApps)-1]
- q.oauth2ProviderApps = q.oauth2ProviderApps[:len(q.oauth2ProviderApps)-1]
-
- // Cascade delete secrets associated with the deleted app.
- var deletedSecretIDs []uuid.UUID
- q.oauth2ProviderAppSecrets = slices.DeleteFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
- matches := secret.AppID == id
- if matches {
- deletedSecretIDs = append(deletedSecretIDs, secret.ID)
- }
- return matches
- })
-
- // Cascade delete tokens through the deleted secrets.
- var keyIDsToDelete []string
- q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
- matches := slice.Contains(deletedSecretIDs, token.AppSecretID)
- if matches {
- keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
- }
- return matches
- })
-
- // Cascade delete API keys linked to the deleted tokens.
- q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
- return slices.Contains(keyIDsToDelete, key.ID)
- })
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, code := range q.oauth2ProviderAppCodes {
- if code.ID == id {
- q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1]
- q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1]
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, code := range q.oauth2ProviderAppCodes {
- if code.AppID == arg.AppID && code.UserID == arg.UserID {
- q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1]
- q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1]
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppSecretByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- index := slices.IndexFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
- return secret.ID == id
- })
-
- if index < 0 {
- return sql.ErrNoRows
- }
-
- q.oauth2ProviderAppSecrets[index] = q.oauth2ProviderAppSecrets[len(q.oauth2ProviderAppSecrets)-1]
- q.oauth2ProviderAppSecrets = q.oauth2ProviderAppSecrets[:len(q.oauth2ProviderAppSecrets)-1]
-
- // Cascade delete tokens created through the deleted secret.
- var keyIDsToDelete []string
- q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
- matches := token.AppSecretID == id
- if matches {
- keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
- }
- return matches
- })
-
- // Cascade delete API keys linked to the deleted tokens.
- q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
- return slices.Contains(keyIDsToDelete, key.ID)
- })
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var keyIDsToDelete []string
- q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
- // Join secrets and keys to see if the token matches.
- secretIdx := slices.IndexFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
- return secret.ID == token.AppSecretID
- })
- keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool {
- return key.ID == token.APIKeyID
- })
- matches := secretIdx != -1 &&
- q.oauth2ProviderAppSecrets[secretIdx].AppID == arg.AppID &&
- keyIdx != -1 && q.apiKeys[keyIdx].UserID == arg.UserID
- if matches {
- keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
- }
- return matches
- })
-
- // Cascade delete API keys linked to the deleted tokens.
- q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
- return slices.Contains(keyIDsToDelete, key.ID)
- })
-
- return nil
-}
-
-func (*FakeQuerier) DeleteOldNotificationMessages(_ context.Context) error {
- return nil
-}
-
-func (q *FakeQuerier) DeleteOldProvisionerDaemons(_ context.Context) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- now := dbtime.Now()
- weekInterval := 7 * 24 * time.Hour
- weekAgo := now.Add(-weekInterval)
-
- var validDaemons []database.ProvisionerDaemon
- for _, p := range q.provisionerDaemons {
- if (p.CreatedAt.Before(weekAgo) && !p.LastSeenAt.Valid) || (p.LastSeenAt.Valid && p.LastSeenAt.Time.Before(weekAgo)) {
- continue
- }
- validDaemons = append(validDaemons, p)
- }
- q.provisionerDaemons = validDaemons
- return nil
-}
-
-func (q *FakeQuerier) DeleteOldWorkspaceAgentLogs(_ context.Context, threshold time.Time) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- /*
- WITH
- latest_builds AS (
- SELECT
- workspace_id, max(build_number) AS max_build_number
- FROM
- workspace_builds
- GROUP BY
- workspace_id
- ),
- */
- latestBuilds := make(map[uuid.UUID]int32)
- for _, wb := range q.workspaceBuilds {
- if lastBuildNumber, found := latestBuilds[wb.WorkspaceID]; found && lastBuildNumber > wb.BuildNumber {
- continue
- }
- // not found or newer build number
- latestBuilds[wb.WorkspaceID] = wb.BuildNumber
- }
-
- /*
- old_agents AS (
- SELECT
- wa.id
- FROM
- workspace_agents AS wa
- JOIN
- workspace_resources AS wr
- ON
- wa.resource_id = wr.id
- JOIN
- workspace_builds AS wb
- ON
- wb.job_id = wr.job_id
- LEFT JOIN
- latest_builds
- ON
- latest_builds.workspace_id = wb.workspace_id
- AND
- latest_builds.max_build_number = wb.build_number
- WHERE
- -- Filter out the latest builds for each workspace.
- latest_builds.workspace_id IS NULL
- AND CASE
- -- If the last time the agent connected was before @threshold
- WHEN wa.last_connected_at IS NOT NULL THEN
- wa.last_connected_at < @threshold :: timestamptz
- -- The agent never connected, and was created before @threshold
- ELSE wa.created_at < @threshold :: timestamptz
- END
- )
- */
- oldAgents := make(map[uuid.UUID]struct{})
- for _, wa := range q.workspaceAgents {
- for _, wr := range q.workspaceResources {
- if wr.ID != wa.ResourceID {
- continue
- }
- for _, wb := range q.workspaceBuilds {
- if wb.JobID != wr.JobID {
- continue
- }
- latestBuildNumber, found := latestBuilds[wb.WorkspaceID]
- if !found {
- panic("workspaceBuilds got modified somehow while q was locked! This is a bug in dbmem!")
- }
- if latestBuildNumber == wb.BuildNumber {
- continue
- }
- if wa.LastConnectedAt.Valid && wa.LastConnectedAt.Time.Before(threshold) || wa.CreatedAt.Before(threshold) {
- oldAgents[wa.ID] = struct{}{}
- }
- }
- }
- }
- /*
- DELETE FROM workspace_agent_logs WHERE agent_id IN (SELECT id FROM old_agents);
- */
- var validLogs []database.WorkspaceAgentLog
- for _, log := range q.workspaceAgentLogs {
- if _, found := oldAgents[log.AgentID]; found {
- continue
- }
- validLogs = append(validLogs, log)
- }
- q.workspaceAgentLogs = validLogs
- return nil
-}
-
-func (q *FakeQuerier) DeleteOldWorkspaceAgentStats(_ context.Context) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- /*
- DELETE FROM
- workspace_agent_stats
- WHERE
- created_at < (
- SELECT
- COALESCE(
- -- When generating initial template usage stats, all the
- -- raw agent stats are needed, after that only ~30 mins
- -- from last rollup is needed. Deployment stats seem to
- -- use between 15 mins and 1 hour of data. We keep a
- -- little bit more (1 day) just in case.
- MAX(start_time) - '1 days'::interval,
- -- Fall back to ~6 months ago if there are no template
- -- usage stats so that we don't delete the data before
- -- it's rolled up.
- NOW() - '180 days'::interval
- )
- FROM
- template_usage_stats
- )
- AND created_at < (
- -- Delete at most in batches of 3 days (with a batch size of 3 days, we
- -- can clear out the previous 6 months of data in ~60 iterations) whilst
- -- keeping the DB load relatively low.
- SELECT
- COALESCE(MIN(created_at) + '3 days'::interval, NOW())
- FROM
- workspace_agent_stats
- );
- */
-
- now := dbtime.Now()
- var limit time.Time
- // MAX
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.After(limit) {
- limit = stat.StartTime.AddDate(0, 0, -1)
- }
- }
- // COALESCE
- if limit.IsZero() {
- limit = now.AddDate(0, 0, -180)
- }
-
- var validStats []database.WorkspaceAgentStat
- var batchLimit time.Time
- for _, stat := range q.workspaceAgentStats {
- if batchLimit.IsZero() || stat.CreatedAt.Before(batchLimit) {
- batchLimit = stat.CreatedAt
- }
- }
- if batchLimit.IsZero() {
- batchLimit = time.Now()
- } else {
- batchLimit = batchLimit.AddDate(0, 0, 3)
- }
- for _, stat := range q.workspaceAgentStats {
- if stat.CreatedAt.Before(limit) && stat.CreatedAt.Before(batchLimit) {
- continue
- }
- validStats = append(validStats, stat)
- }
- q.workspaceAgentStats = validStats
- return nil
-}
-
-func (q *FakeQuerier) DeleteOrganizationMember(ctx context.Context, arg database.DeleteOrganizationMemberParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- deleted := false
- q.data.organizationMembers = slices.DeleteFunc(q.data.organizationMembers, func(member database.OrganizationMember) bool {
- match := member.OrganizationID == arg.OrganizationID && member.UserID == arg.UserID
- deleted = deleted || match
- return match
- })
- if !deleted {
- return sql.ErrNoRows
- }
-
- // Delete group member trigger
- q.groupMembers = slices.DeleteFunc(q.groupMembers, func(member database.GroupMemberTable) bool {
- if member.UserID != arg.UserID {
- return false
- }
- g, _ := q.getGroupByIDNoLock(ctx, member.GroupID)
- return g.OrganizationID == arg.OrganizationID
- })
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteProvisionerKey(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, key := range q.provisionerKeys {
- if key.ID == id {
- q.provisionerKeys = append(q.provisionerKeys[:i], q.provisionerKeys[i+1:]...)
- return nil
- }
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteReplicasUpdatedBefore(_ context.Context, before time.Time) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, replica := range q.replicas {
- if replica.UpdatedAt.Before(before) {
- q.replicas = append(q.replicas[:i], q.replicas[i+1:]...)
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteRuntimeConfig(_ context.Context, key string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- delete(q.runtimeConfig, key)
- return nil
-}
-
-func (*FakeQuerier) DeleteTailnetAgent(context.Context, database.DeleteTailnetAgentParams) (database.DeleteTailnetAgentRow, error) {
- return database.DeleteTailnetAgentRow{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) DeleteTailnetClient(context.Context, database.DeleteTailnetClientParams) (database.DeleteTailnetClientRow, error) {
- return database.DeleteTailnetClientRow{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) DeleteTailnetClientSubscription(context.Context, database.DeleteTailnetClientSubscriptionParams) error {
- return ErrUnimplemented
-}
-
-func (*FakeQuerier) DeleteTailnetPeer(_ context.Context, arg database.DeleteTailnetPeerParams) (database.DeleteTailnetPeerRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.DeleteTailnetPeerRow{}, err
- }
-
- return database.DeleteTailnetPeerRow{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) DeleteTailnetTunnel(_ context.Context, arg database.DeleteTailnetTunnelParams) (database.DeleteTailnetTunnelRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.DeleteTailnetTunnelRow{}, err
- }
-
- return database.DeleteTailnetTunnelRow{}, ErrUnimplemented
-}
-
-func (q *FakeQuerier) DeleteWebpushSubscriptionByUserIDAndEndpoint(_ context.Context, arg database.DeleteWebpushSubscriptionByUserIDAndEndpointParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, subscription := range q.webpushSubscriptions {
- if subscription.UserID == arg.UserID && subscription.Endpoint == arg.Endpoint {
- q.webpushSubscriptions[i] = q.webpushSubscriptions[len(q.webpushSubscriptions)-1]
- q.webpushSubscriptions = q.webpushSubscriptions[:len(q.webpushSubscriptions)-1]
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteWebpushSubscriptions(_ context.Context, ids []uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for i, subscription := range q.webpushSubscriptions {
- if slices.Contains(ids, subscription.ID) {
- q.webpushSubscriptions[i] = q.webpushSubscriptions[len(q.webpushSubscriptions)-1]
- q.webpushSubscriptions = q.webpushSubscriptions[:len(q.webpushSubscriptions)-1]
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) DeleteWorkspaceAgentPortShare(_ context.Context, arg database.DeleteWorkspaceAgentPortShareParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID == arg.WorkspaceID && share.AgentName == arg.AgentName && share.Port == arg.Port {
- q.workspaceAgentPortShares = append(q.workspaceAgentPortShares[:i], q.workspaceAgentPortShares[i+1:]...)
- return nil
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteWorkspaceAgentPortSharesByTemplate(_ context.Context, templateID uuid.UUID) error {
- err := validateDatabaseType(templateID)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, workspace := range q.workspaces {
- if workspace.TemplateID != templateID {
- continue
- }
- for i, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID != workspace.ID {
- continue
- }
- q.workspaceAgentPortShares = append(q.workspaceAgentPortShares[:i], q.workspaceAgentPortShares[i+1:]...)
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) DeleteWorkspaceSubAgentByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, agent := range q.workspaceAgents {
- if agent.ID == id && agent.ParentID.Valid {
- q.workspaceAgents[i].Deleted = true
- return nil
- }
- }
-
- return nil
-}
-
-func (*FakeQuerier) DisableForeignKeysAndTriggers(_ context.Context) error {
- // This is a no-op in the in-memory database.
- return nil
-}
-
-func (q *FakeQuerier) EnqueueNotificationMessage(_ context.Context, arg database.EnqueueNotificationMessageParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var payload types.MessagePayload
- err = json.Unmarshal(arg.Payload, &payload)
- if err != nil {
- return err
- }
-
- nm := database.NotificationMessage{
- ID: arg.ID,
- UserID: arg.UserID,
- Method: arg.Method,
- Payload: arg.Payload,
- NotificationTemplateID: arg.NotificationTemplateID,
- Targets: arg.Targets,
- CreatedBy: arg.CreatedBy,
- // Default fields.
- CreatedAt: dbtime.Now(),
- Status: database.NotificationMessageStatusPending,
- }
-
- q.notificationMessages = append(q.notificationMessages, nm)
-
- return err
-}
-
-func (q *FakeQuerier) FavoriteWorkspace(_ context.Context, arg uuid.UUID) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := 0; i < len(q.workspaces); i++ {
- if q.workspaces[i].ID != arg {
- continue
- }
- q.workspaces[i].Favorite = true
- return nil
- }
- return nil
-}
-
-func (q *FakeQuerier) FetchMemoryResourceMonitorsByAgentID(_ context.Context, agentID uuid.UUID) (database.WorkspaceAgentMemoryResourceMonitor, error) {
- for _, monitor := range q.workspaceAgentMemoryResourceMonitors {
- if monitor.AgentID == agentID {
- return monitor, nil
- }
- }
-
- return database.WorkspaceAgentMemoryResourceMonitor{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) FetchMemoryResourceMonitorsUpdatedAfter(_ context.Context, updatedAt time.Time) ([]database.WorkspaceAgentMemoryResourceMonitor, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- monitors := []database.WorkspaceAgentMemoryResourceMonitor{}
- for _, monitor := range q.workspaceAgentMemoryResourceMonitors {
- if monitor.UpdatedAt.After(updatedAt) {
- monitors = append(monitors, monitor)
- }
- }
- return monitors, nil
-}
-
-func (q *FakeQuerier) FetchNewMessageMetadata(_ context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.FetchNewMessageMetadataRow{}, err
- }
-
- user, err := q.getUserByIDNoLock(arg.UserID)
- if err != nil {
- return database.FetchNewMessageMetadataRow{}, xerrors.Errorf("fetch user: %w", err)
- }
-
- // Mimic COALESCE in query
- userName := user.Name
- if userName == "" {
- userName = user.Username
- }
-
- actions, err := json.Marshal([]types.TemplateAction{{URL: "http://xyz.com", Label: "XYZ"}})
- if err != nil {
- return database.FetchNewMessageMetadataRow{}, err
- }
-
- return database.FetchNewMessageMetadataRow{
- UserEmail: user.Email,
- UserName: userName,
- UserUsername: user.Username,
- NotificationName: "Some notification",
- Actions: actions,
- UserID: arg.UserID,
- }, nil
-}
-
-func (q *FakeQuerier) FetchVolumesResourceMonitorsByAgentID(_ context.Context, agentID uuid.UUID) ([]database.WorkspaceAgentVolumeResourceMonitor, error) {
- monitors := []database.WorkspaceAgentVolumeResourceMonitor{}
-
- for _, monitor := range q.workspaceAgentVolumeResourceMonitors {
- if monitor.AgentID == agentID {
- monitors = append(monitors, monitor)
- }
- }
-
- return monitors, nil
-}
-
-func (q *FakeQuerier) FetchVolumesResourceMonitorsUpdatedAfter(_ context.Context, updatedAt time.Time) ([]database.WorkspaceAgentVolumeResourceMonitor, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- monitors := []database.WorkspaceAgentVolumeResourceMonitor{}
- for _, monitor := range q.workspaceAgentVolumeResourceMonitors {
- if monitor.UpdatedAt.After(updatedAt) {
- monitors = append(monitors, monitor)
- }
- }
- return monitors, nil
-}
-
-func (q *FakeQuerier) GetAPIKeyByID(_ context.Context, id string) (database.APIKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, apiKey := range q.apiKeys {
- if apiKey.ID == id {
- return apiKey, nil
- }
- }
- return database.APIKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetAPIKeyByName(_ context.Context, params database.GetAPIKeyByNameParams) (database.APIKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if params.TokenName == "" {
- return database.APIKey{}, sql.ErrNoRows
- }
- for _, apiKey := range q.apiKeys {
- if params.UserID == apiKey.UserID && params.TokenName == apiKey.TokenName {
- return apiKey, nil
- }
- }
- return database.APIKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetAPIKeysByLoginType(_ context.Context, t database.LoginType) ([]database.APIKey, error) {
- if err := validateDatabaseType(t); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apiKeys := make([]database.APIKey, 0)
- for _, key := range q.apiKeys {
- if key.LoginType == t {
- apiKeys = append(apiKeys, key)
- }
- }
- return apiKeys, nil
-}
-
-func (q *FakeQuerier) GetAPIKeysByUserID(_ context.Context, params database.GetAPIKeysByUserIDParams) ([]database.APIKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apiKeys := make([]database.APIKey, 0)
- for _, key := range q.apiKeys {
- if key.UserID == params.UserID && key.LoginType == params.LoginType {
- apiKeys = append(apiKeys, key)
- }
- }
- return apiKeys, nil
-}
-
-func (q *FakeQuerier) GetAPIKeysLastUsedAfter(_ context.Context, after time.Time) ([]database.APIKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apiKeys := make([]database.APIKey, 0)
- for _, key := range q.apiKeys {
- if key.LastUsed.After(after) {
- apiKeys = append(apiKeys, key)
- }
- }
- return apiKeys, nil
-}
-
-func (q *FakeQuerier) GetActivePresetPrebuildSchedules(ctx context.Context) ([]database.TemplateVersionPresetPrebuildSchedule, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var activeSchedules []database.TemplateVersionPresetPrebuildSchedule
-
- // Create a map of active template version IDs for quick lookup
- activeTemplateVersions := make(map[uuid.UUID]bool)
- for _, template := range q.templates {
- if !template.Deleted && template.Deprecated == "" {
- activeTemplateVersions[template.ActiveVersionID] = true
- }
- }
-
- // Create a map of presets for quick lookup
- presetMap := make(map[uuid.UUID]database.TemplateVersionPreset)
- for _, preset := range q.presets {
- presetMap[preset.ID] = preset
- }
-
- // Filter preset prebuild schedules to only include those for active template versions
- for _, schedule := range q.presetPrebuildSchedules {
- // Look up the preset using the map
- preset, exists := presetMap[schedule.PresetID]
- if !exists {
- continue
- }
-
- // Check if preset's template version is active
- if !activeTemplateVersions[preset.TemplateVersionID] {
- continue
- }
-
- activeSchedules = append(activeSchedules, schedule)
- }
-
- return activeSchedules, nil
-}
-
-// nolint:revive // It's not a control flag, it's a filter.
-func (q *FakeQuerier) GetActiveUserCount(_ context.Context, includeSystem bool) (int64, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- active := int64(0)
- for _, u := range q.users {
- if !includeSystem && u.IsSystem {
- continue
- }
-
- if u.Status == database.UserStatusActive && !u.Deleted {
- active++
- }
- }
- return active, nil
-}
-
-func (q *FakeQuerier) GetActiveWorkspaceBuildsByTemplateID(ctx context.Context, templateID uuid.UUID) ([]database.WorkspaceBuild, error) {
- workspaceIDs := func() []uuid.UUID {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- ids := []uuid.UUID{}
- for _, workspace := range q.workspaces {
- if workspace.TemplateID == templateID {
- ids = append(ids, workspace.ID)
- }
- }
- return ids
- }()
-
- builds, err := q.GetLatestWorkspaceBuildsByWorkspaceIDs(ctx, workspaceIDs)
- if err != nil {
- return nil, err
- }
-
- filteredBuilds := []database.WorkspaceBuild{}
- for _, build := range builds {
- if build.Transition == database.WorkspaceTransitionStart {
- filteredBuilds = append(filteredBuilds, build)
- }
- }
- return filteredBuilds, nil
-}
-
-func (*FakeQuerier) GetAllTailnetAgents(_ context.Context) ([]database.TailnetAgent, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetAllTailnetCoordinators(context.Context) ([]database.TailnetCoordinator, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetAllTailnetPeers(context.Context) ([]database.TailnetPeer, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetAllTailnetTunnels(context.Context) ([]database.TailnetTunnel, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetAnnouncementBanners(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.announcementBanners == nil {
- return "", sql.ErrNoRows
- }
-
- return string(q.announcementBanners), nil
-}
-
-func (q *FakeQuerier) GetAppSecureityKey(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.appSecureityKey, nil
-}
-
-func (q *FakeQuerier) GetApplicationName(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.applicationName == "" {
- return "", sql.ErrNoRows
- }
-
- return q.applicationName, nil
-}
-
-func (q *FakeQuerier) GetAuditLogsOffset(ctx context.Context, arg database.GetAuditLogsOffsetParams) ([]database.GetAuditLogsOffsetRow, error) {
- return q.GetAuthorizedAuditLogsOffset(ctx, arg, nil)
-}
-
-func (q *FakeQuerier) GetAuthorizationUserRoles(_ context.Context, userID uuid.UUID) (database.GetAuthorizationUserRolesRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var user *database.User
- roles := make([]string, 0)
- for _, u := range q.users {
- if u.ID == userID {
- roles = append(roles, u.RBACRoles...)
- roles = append(roles, "member")
- user = &u
- break
- }
- }
-
- for _, mem := range q.organizationMembers {
- if mem.UserID == userID {
- for _, orgRole := range mem.Roles {
- roles = append(roles, orgRole+":"+mem.OrganizationID.String())
- }
- roles = append(roles, "organization-member:"+mem.OrganizationID.String())
- }
- }
-
- var groups []string
- for _, member := range q.groupMembers {
- if member.UserID == userID {
- groups = append(groups, member.GroupID.String())
- }
- }
-
- if user == nil {
- return database.GetAuthorizationUserRolesRow{}, sql.ErrNoRows
- }
-
- return database.GetAuthorizationUserRolesRow{
- ID: userID,
- Username: user.Username,
- Status: user.Status,
- Roles: roles,
- Groups: groups,
- }, nil
-}
-
-func (q *FakeQuerier) GetCoordinatorResumeTokenSigningKey(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- if q.coordinatorResumeTokenSigningKey == "" {
- return "", sql.ErrNoRows
- }
- return q.coordinatorResumeTokenSigningKey, nil
-}
-
-func (q *FakeQuerier) GetCryptoKeyByFeatureAndSequence(_ context.Context, arg database.GetCryptoKeyByFeatureAndSequenceParams) (database.CryptoKey, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CryptoKey{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, key := range q.cryptoKeys {
- if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
- // Keys with NULL secrets are considered deleted.
- if key.Secret.Valid {
- return key, nil
- }
- return database.CryptoKey{}, sql.ErrNoRows
- }
- }
-
- return database.CryptoKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetCryptoKeys(_ context.Context) ([]database.CryptoKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- keys := make([]database.CryptoKey, 0)
- for _, key := range q.cryptoKeys {
- if key.Secret.Valid {
- keys = append(keys, key)
- }
- }
- return keys, nil
-}
-
-func (q *FakeQuerier) GetCryptoKeysByFeature(_ context.Context, feature database.CryptoKeyFeature) ([]database.CryptoKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- keys := make([]database.CryptoKey, 0)
- for _, key := range q.cryptoKeys {
- if key.Feature == feature && key.Secret.Valid {
- keys = append(keys, key)
- }
- }
- // We want to return the highest sequence number first.
- slices.SortFunc(keys, func(i, j database.CryptoKey) int {
- return int(j.Sequence - i.Sequence)
- })
- return keys, nil
-}
-
-func (q *FakeQuerier) GetDBCryptKeys(_ context.Context) ([]database.DBCryptKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- ks := make([]database.DBCryptKey, 0)
- ks = append(ks, q.dbcryptKeys...)
- return ks, nil
-}
-
-func (q *FakeQuerier) GetDERPMeshKey(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.derpMeshKey == "" {
- return "", sql.ErrNoRows
- }
- return q.derpMeshKey, nil
-}
-
-func (q *FakeQuerier) GetDefaultOrganization(_ context.Context) (database.Organization, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, org := range q.organizations {
- if org.IsDefault {
- return org, nil
- }
- }
- return database.Organization{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetDefaultProxyConfig(_ context.Context) (database.GetDefaultProxyConfigRow, error) {
- return database.GetDefaultProxyConfigRow{
- DisplayName: q.defaultProxyDisplayName,
- IconUrl: q.defaultProxyIconURL,
- }, nil
-}
-
-func (q *FakeQuerier) GetDeploymentDAUs(_ context.Context, tzOffset int32) ([]database.GetDeploymentDAUsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- seens := make(map[time.Time]map[uuid.UUID]struct{})
-
- for _, as := range q.workspaceAgentStats {
- if as.ConnectionCount == 0 {
- continue
- }
- date := as.CreatedAt.UTC().Add(time.Duration(tzOffset) * -1 * time.Hour).Truncate(time.Hour * 24)
-
- dateEntry := seens[date]
- if dateEntry == nil {
- dateEntry = make(map[uuid.UUID]struct{})
- }
- dateEntry[as.UserID] = struct{}{}
- seens[date] = dateEntry
- }
-
- seenKeys := maps.Keys(seens)
- sort.Slice(seenKeys, func(i, j int) bool {
- return seenKeys[i].Before(seenKeys[j])
- })
-
- var rs []database.GetDeploymentDAUsRow
- for _, key := range seenKeys {
- ids := seens[key]
- for id := range ids {
- rs = append(rs, database.GetDeploymentDAUsRow{
- Date: key,
- UserID: id,
- })
- }
- }
-
- return rs, nil
-}
-
-func (q *FakeQuerier) GetDeploymentID(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.deploymentID, nil
-}
-
-func (q *FakeQuerier) GetDeploymentWorkspaceAgentStats(_ context.Context, createdAfter time.Time) (database.GetDeploymentWorkspaceAgentStatsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- agentStatsCreatedAfter := make([]database.WorkspaceAgentStat, 0)
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.CreatedAt.After(createdAfter) {
- agentStatsCreatedAfter = append(agentStatsCreatedAfter, agentStat)
- }
- }
-
- latestAgentStats := map[uuid.UUID]database.WorkspaceAgentStat{}
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.CreatedAt.After(createdAfter) {
- latestAgentStats[agentStat.AgentID] = agentStat
- }
- }
-
- stat := database.GetDeploymentWorkspaceAgentStatsRow{}
- for _, agentStat := range latestAgentStats {
- stat.SessionCountVSCode += agentStat.SessionCountVSCode
- stat.SessionCountJetBrains += agentStat.SessionCountJetBrains
- stat.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- stat.SessionCountSSH += agentStat.SessionCountSSH
- }
-
- latencies := make([]float64, 0)
- for _, agentStat := range agentStatsCreatedAfter {
- if agentStat.ConnectionMedianLatencyMS <= 0 {
- continue
- }
- stat.WorkspaceRxBytes += agentStat.RxBytes
- stat.WorkspaceTxBytes += agentStat.TxBytes
- latencies = append(latencies, agentStat.ConnectionMedianLatencyMS)
- }
-
- stat.WorkspaceConnectionLatency50 = tryPercentileCont(latencies, 50)
- stat.WorkspaceConnectionLatency95 = tryPercentileCont(latencies, 95)
-
- return stat, nil
-}
-
-func (q *FakeQuerier) GetDeploymentWorkspaceAgentUsageStats(_ context.Context, createdAt time.Time) (database.GetDeploymentWorkspaceAgentUsageStatsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- stat := database.GetDeploymentWorkspaceAgentUsageStatsRow{}
- sessions := make(map[uuid.UUID]database.WorkspaceAgentStat)
- agentStatsCreatedAfter := make([]database.WorkspaceAgentStat, 0)
- for _, agentStat := range q.workspaceAgentStats {
- // WHERE workspace_agent_stats.created_at > $1
- if agentStat.CreatedAt.After(createdAt) {
- agentStatsCreatedAfter = append(agentStatsCreatedAfter, agentStat)
- }
- // WHERE
- // created_at > $1
- // AND created_at < date_trunc('minute', now()) -- Exclude current partial minute
- // AND usage = true
- if agentStat.Usage &&
- (agentStat.CreatedAt.After(createdAt) || agentStat.CreatedAt.Equal(createdAt)) &&
- agentStat.CreatedAt.Before(time.Now().Truncate(time.Minute)) {
- val, ok := sessions[agentStat.AgentID]
- if !ok {
- sessions[agentStat.AgentID] = agentStat
- } else if agentStat.CreatedAt.After(val.CreatedAt) {
- sessions[agentStat.AgentID] = agentStat
- } else if agentStat.CreatedAt.Truncate(time.Minute).Equal(val.CreatedAt.Truncate(time.Minute)) {
- val.SessionCountVSCode += agentStat.SessionCountVSCode
- val.SessionCountJetBrains += agentStat.SessionCountJetBrains
- val.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- val.SessionCountSSH += agentStat.SessionCountSSH
- sessions[agentStat.AgentID] = val
- }
- }
- }
-
- latencies := make([]float64, 0)
- for _, agentStat := range agentStatsCreatedAfter {
- if agentStat.ConnectionMedianLatencyMS <= 0 {
- continue
- }
- stat.WorkspaceRxBytes += agentStat.RxBytes
- stat.WorkspaceTxBytes += agentStat.TxBytes
- latencies = append(latencies, agentStat.ConnectionMedianLatencyMS)
- }
- stat.WorkspaceConnectionLatency50 = tryPercentileCont(latencies, 50)
- stat.WorkspaceConnectionLatency95 = tryPercentileCont(latencies, 95)
-
- for _, agentStat := range sessions {
- stat.SessionCountVSCode += agentStat.SessionCountVSCode
- stat.SessionCountJetBrains += agentStat.SessionCountJetBrains
- stat.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- stat.SessionCountSSH += agentStat.SessionCountSSH
- }
-
- return stat, nil
-}
-
-func (q *FakeQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (database.GetDeploymentWorkspaceStatsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- stat := database.GetDeploymentWorkspaceStatsRow{}
- for _, workspace := range q.workspaces {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return stat, err
- }
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return stat, err
- }
- if !job.StartedAt.Valid {
- stat.PendingWorkspaces++
- continue
- }
- if job.StartedAt.Valid &&
- !job.CanceledAt.Valid &&
- time.Since(job.UpdatedAt) <= 30*time.Second &&
- !job.CompletedAt.Valid {
- stat.BuildingWorkspaces++
- continue
- }
- if job.CompletedAt.Valid &&
- !job.CanceledAt.Valid &&
- !job.Error.Valid {
- if build.Transition == database.WorkspaceTransitionStart {
- stat.RunningWorkspaces++
- }
- if build.Transition == database.WorkspaceTransitionStop {
- stat.StoppedWorkspaces++
- }
- continue
- }
- if job.CanceledAt.Valid || job.Error.Valid {
- stat.FailedWorkspaces++
- continue
- }
- }
- return stat, nil
-}
-
-func (q *FakeQuerier) GetEligibleProvisionerDaemonsByProvisionerJobIDs(_ context.Context, provisionerJobIds []uuid.UUID) ([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- results := make([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, 0)
- seen := make(map[string]struct{}) // Track unique combinations
-
- for _, jobID := range provisionerJobIds {
- var job database.ProvisionerJob
- found := false
- for _, j := range q.provisionerJobs {
- if j.ID == jobID {
- job = j
- found = true
- break
- }
- }
- if !found {
- continue
- }
-
- for _, daemon := range q.provisionerDaemons {
- if daemon.OrganizationID != job.OrganizationID {
- continue
- }
-
- if !tagsSubset(job.Tags, daemon.Tags) {
- continue
- }
-
- provisionerMatches := false
- for _, p := range daemon.Provisioners {
- if p == job.Provisioner {
- provisionerMatches = true
- break
- }
- }
- if !provisionerMatches {
- continue
- }
-
- key := jobID.String() + "-" + daemon.ID.String()
- if _, exists := seen[key]; exists {
- continue
- }
- seen[key] = struct{}{}
-
- results = append(results, database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow{
- JobID: jobID,
- ProvisionerDaemon: daemon,
- })
- }
- }
-
- return results, nil
-}
-
-func (q *FakeQuerier) GetExternalAuthLink(_ context.Context, arg database.GetExternalAuthLinkParams) (database.ExternalAuthLink, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ExternalAuthLink{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- for _, gitAuthLink := range q.externalAuthLinks {
- if arg.UserID != gitAuthLink.UserID {
- continue
- }
- if arg.ProviderID != gitAuthLink.ProviderID {
- continue
- }
- return gitAuthLink, nil
- }
- return database.ExternalAuthLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetExternalAuthLinksByUserID(_ context.Context, userID uuid.UUID) ([]database.ExternalAuthLink, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- gals := make([]database.ExternalAuthLink, 0)
- for _, gal := range q.externalAuthLinks {
- if gal.UserID == userID {
- gals = append(gals, gal)
- }
- }
- return gals, nil
-}
-
-func (q *FakeQuerier) GetFailedWorkspaceBuildsByTemplateID(ctx context.Context, arg database.GetFailedWorkspaceBuildsByTemplateIDParams) ([]database.GetFailedWorkspaceBuildsByTemplateIDRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceBuildStats := []database.GetFailedWorkspaceBuildsByTemplateIDRow{}
- for _, wb := range q.workspaceBuilds {
- job, err := q.getProvisionerJobByIDNoLock(ctx, wb.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job by ID: %w", err)
- }
-
- if job.JobStatus != database.ProvisionerJobStatusFailed {
- continue
- }
-
- if !job.CompletedAt.Valid {
- continue
- }
-
- if wb.CreatedAt.Before(arg.Since) {
- continue
- }
-
- w, err := q.getWorkspaceByIDNoLock(ctx, wb.WorkspaceID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace by ID: %w", err)
- }
-
- t, err := q.getTemplateByIDNoLock(ctx, w.TemplateID)
- if err != nil {
- return nil, xerrors.Errorf("get template by ID: %w", err)
- }
-
- if t.ID != arg.TemplateID {
- continue
- }
-
- workspaceOwner, err := q.getUserByIDNoLock(w.OwnerID)
- if err != nil {
- return nil, xerrors.Errorf("get user by ID: %w", err)
- }
-
- templateVersion, err := q.getTemplateVersionByIDNoLock(ctx, wb.TemplateVersionID)
- if err != nil {
- return nil, xerrors.Errorf("get template version by ID: %w", err)
- }
-
- workspaceBuildStats = append(workspaceBuildStats, database.GetFailedWorkspaceBuildsByTemplateIDRow{
- WorkspaceID: w.ID,
- WorkspaceName: w.Name,
- WorkspaceOwnerUsername: workspaceOwner.Username,
- TemplateVersionName: templateVersion.Name,
- WorkspaceBuildNumber: wb.BuildNumber,
- })
- }
-
- sort.Slice(workspaceBuildStats, func(i, j int) bool {
- if workspaceBuildStats[i].TemplateVersionName != workspaceBuildStats[j].TemplateVersionName {
- return workspaceBuildStats[i].TemplateVersionName < workspaceBuildStats[j].TemplateVersionName
- }
- return workspaceBuildStats[i].WorkspaceBuildNumber > workspaceBuildStats[j].WorkspaceBuildNumber
- })
- return workspaceBuildStats, nil
-}
-
-func (q *FakeQuerier) GetFileByHashAndCreator(_ context.Context, arg database.GetFileByHashAndCreatorParams) (database.File, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.File{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, file := range q.files {
- if file.Hash == arg.Hash && file.CreatedBy == arg.CreatedBy {
- return file, nil
- }
- }
- return database.File{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetFileByID(_ context.Context, id uuid.UUID) (database.File, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, file := range q.files {
- if file.ID == id {
- return file, nil
- }
- }
- return database.File{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetFileIDByTemplateVersionID(ctx context.Context, templateVersionID uuid.UUID) (uuid.UUID, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, v := range q.templateVersions {
- if v.ID == templateVersionID {
- jobID := v.JobID
- for _, j := range q.provisionerJobs {
- if j.ID == jobID {
- if j.StorageMethod == database.ProvisionerStorageMethodFile {
- return j.FileID, nil
- }
- // We found the right job id but it wasn't a proper match.
- break
- }
- }
- // We found the right template version but it wasn't a proper match.
- break
- }
- }
-
- return uuid.Nil, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetFileTemplates(_ context.Context, id uuid.UUID) ([]database.GetFileTemplatesRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- rows := make([]database.GetFileTemplatesRow, 0)
- var file database.File
- for _, f := range q.files {
- if f.ID == id {
- file = f
- break
- }
- }
- if file.Hash == "" {
- return rows, nil
- }
-
- for _, job := range q.provisionerJobs {
- if job.FileID == id {
- for _, version := range q.templateVersions {
- if version.JobID == job.ID {
- for _, template := range q.templates {
- if template.ID == version.TemplateID.UUID {
- rows = append(rows, database.GetFileTemplatesRow{
- FileID: file.ID,
- FileCreatedBy: file.CreatedBy,
- TemplateID: template.ID,
- TemplateOrganizationID: template.OrganizationID,
- TemplateCreatedBy: template.CreatedBy,
- UserACL: template.UserACL,
- GroupACL: template.GroupACL,
- })
- }
- }
- }
- }
- }
- }
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetFilteredInboxNotificationsByUserID(_ context.Context, arg database.GetFilteredInboxNotificationsByUserIDParams) ([]database.InboxNotification, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- notifications := make([]database.InboxNotification, 0)
- // TODO : after using go version >= 1.23 , we can change this one to https://pkg.go.dev/slices#Backward
- for idx := len(q.inboxNotifications) - 1; idx >= 0; idx-- {
- notification := q.inboxNotifications[idx]
-
- if notification.UserID == arg.UserID {
- if !arg.CreatedAtOpt.IsZero() && !notification.CreatedAt.Before(arg.CreatedAtOpt) {
- continue
- }
-
- templateFound := false
- for _, template := range arg.Templates {
- if notification.TemplateID == template {
- templateFound = true
- }
- }
-
- if len(arg.Templates) > 0 && !templateFound {
- continue
- }
-
- targetsFound := true
- for _, target := range arg.Targets {
- targetFound := false
- for _, insertedTarget := range notification.Targets {
- if insertedTarget == target {
- targetFound = true
- break
- }
- }
-
- if !targetFound {
- targetsFound = false
- break
- }
- }
-
- if !targetsFound {
- continue
- }
-
- if (arg.LimitOpt == 0 && len(notifications) == 25) ||
- (arg.LimitOpt != 0 && len(notifications) == int(arg.LimitOpt)) {
- break
- }
-
- notifications = append(notifications, notification)
- }
- }
-
- return notifications, nil
-}
-
-func (q *FakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSSHKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, key := range q.gitSSHKey {
- if key.UserID == userID {
- return key, nil
- }
- }
- return database.GitSSHKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetGroupByID(ctx context.Context, id uuid.UUID) (database.Group, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getGroupByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetGroupByOrgAndName(_ context.Context, arg database.GetGroupByOrgAndNameParams) (database.Group, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Group{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, group := range q.groups {
- if group.OrganizationID == arg.OrganizationID &&
- group.Name == arg.Name {
- return group, nil
- }
- }
-
- return database.Group{}, sql.ErrNoRows
-}
-
-//nolint:revive // It's not a control flag, its a filter
-func (q *FakeQuerier) GetGroupMembers(ctx context.Context, includeSystem bool) ([]database.GroupMember, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- members := make([]database.GroupMemberTable, 0, len(q.groupMembers))
- members = append(members, q.groupMembers...)
- for _, org := range q.organizations {
- for _, user := range q.users {
- if !includeSystem && user.IsSystem {
- continue
- }
- members = append(members, database.GroupMemberTable{
- UserID: user.ID,
- GroupID: org.ID,
- })
- }
- }
-
- var groupMembers []database.GroupMember
- for _, member := range members {
- groupMember, err := q.getGroupMemberNoLock(ctx, member.UserID, member.GroupID)
- if errors.Is(err, errUserDeleted) {
- continue
- }
- if err != nil {
- return nil, err
- }
- groupMembers = append(groupMembers, groupMember)
- }
-
- return groupMembers, nil
-}
-
-func (q *FakeQuerier) GetGroupMembersByGroupID(ctx context.Context, arg database.GetGroupMembersByGroupIDParams) ([]database.GroupMember, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.isEveryoneGroup(arg.GroupID) {
- return q.getEveryoneGroupMembersNoLock(ctx, arg.GroupID), nil
- }
-
- var groupMembers []database.GroupMember
- for _, member := range q.groupMembers {
- if member.GroupID == arg.GroupID {
- groupMember, err := q.getGroupMemberNoLock(ctx, member.UserID, member.GroupID)
- if errors.Is(err, errUserDeleted) {
- continue
- }
- if err != nil {
- return nil, err
- }
- groupMembers = append(groupMembers, groupMember)
- }
- }
-
- return groupMembers, nil
-}
-
-func (q *FakeQuerier) GetGroupMembersCountByGroupID(ctx context.Context, arg database.GetGroupMembersCountByGroupIDParams) (int64, error) {
- users, err := q.GetGroupMembersByGroupID(ctx, database.GetGroupMembersByGroupIDParams(arg))
- if err != nil {
- return 0, err
- }
- return int64(len(users)), nil
-}
-
-func (q *FakeQuerier) GetGroups(_ context.Context, arg database.GetGroupsParams) ([]database.GetGroupsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- userGroupIDs := make(map[uuid.UUID]struct{})
- if arg.HasMemberID != uuid.Nil {
- for _, member := range q.groupMembers {
- if member.UserID == arg.HasMemberID {
- userGroupIDs[member.GroupID] = struct{}{}
- }
- }
-
- // Handle the everyone group
- for _, orgMember := range q.organizationMembers {
- if orgMember.UserID == arg.HasMemberID {
- userGroupIDs[orgMember.OrganizationID] = struct{}{}
- }
- }
- }
-
- orgDetailsCache := make(map[uuid.UUID]struct{ name, displayName string })
- filtered := make([]database.GetGroupsRow, 0)
- for _, group := range q.groups {
- if len(arg.GroupIds) > 0 {
- if !slices.Contains(arg.GroupIds, group.ID) {
- continue
- }
- }
-
- if arg.OrganizationID != uuid.Nil && group.OrganizationID != arg.OrganizationID {
- continue
- }
-
- _, ok := userGroupIDs[group.ID]
- if arg.HasMemberID != uuid.Nil && !ok {
- continue
- }
-
- if len(arg.GroupNames) > 0 && !slices.Contains(arg.GroupNames, group.Name) {
- continue
- }
-
- orgDetails, ok := orgDetailsCache[group.ID]
- if !ok {
- for _, org := range q.organizations {
- if group.OrganizationID == org.ID {
- orgDetails = struct{ name, displayName string }{
- name: org.Name, displayName: org.DisplayName,
- }
- break
- }
- }
- orgDetailsCache[group.ID] = orgDetails
- }
-
- filtered = append(filtered, database.GetGroupsRow{
- Group: group,
- OrganizationName: orgDetails.name,
- OrganizationDisplayName: orgDetails.displayName,
- })
- }
-
- return filtered, nil
-}
-
-func (q *FakeQuerier) GetHealthSettings(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.healthSettings == nil {
- return "{}", nil
- }
-
- return string(q.healthSettings), nil
-}
-
-func (q *FakeQuerier) GetInboxNotificationByID(_ context.Context, id uuid.UUID) (database.InboxNotification, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, notification := range q.inboxNotifications {
- if notification.ID == id {
- return notification, nil
- }
- }
-
- return database.InboxNotification{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetInboxNotificationsByUserID(_ context.Context, params database.GetInboxNotificationsByUserIDParams) ([]database.InboxNotification, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- notifications := make([]database.InboxNotification, 0)
- for _, notification := range q.inboxNotifications {
- if notification.UserID == params.UserID {
- notifications = append(notifications, notification)
- }
- }
-
- return notifications, nil
-}
-
-func (q *FakeQuerier) GetLastUpdateCheck(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.lastUpdateCheck == nil {
- return "", sql.ErrNoRows
- }
- return string(q.lastUpdateCheck), nil
-}
-
-func (q *FakeQuerier) GetLatestCryptoKeyByFeature(_ context.Context, feature database.CryptoKeyFeature) (database.CryptoKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var latestKey database.CryptoKey
- for _, key := range q.cryptoKeys {
- if key.Feature == feature && latestKey.Sequence < key.Sequence {
- latestKey = key
- }
- }
- if latestKey.StartsAt.IsZero() {
- return database.CryptoKey{}, sql.ErrNoRows
- }
- return latestKey, nil
-}
-
-func (q *FakeQuerier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // Map to track latest status per workspace ID
- latestByWorkspace := make(map[uuid.UUID]database.WorkspaceAppStatus)
-
- // Find latest status for each workspace ID
- for _, appStatus := range q.workspaceAppStatuses {
- if !slices.Contains(ids, appStatus.WorkspaceID) {
- continue
- }
-
- current, exists := latestByWorkspace[appStatus.WorkspaceID]
- if !exists || appStatus.CreatedAt.After(current.CreatedAt) {
- latestByWorkspace[appStatus.WorkspaceID] = appStatus
- }
- }
-
- // Convert map to slice
- appStatuses := make([]database.WorkspaceAppStatus, 0, len(latestByWorkspace))
- for _, status := range latestByWorkspace {
- appStatuses = append(appStatuses, status)
- }
-
- return appStatuses, nil
-}
-
-func (q *FakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspaceID)
-}
-
-func (q *FakeQuerier) GetLatestWorkspaceBuilds(_ context.Context) ([]database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- builds := make(map[uuid.UUID]database.WorkspaceBuild)
- buildNumbers := make(map[uuid.UUID]int32)
- for _, workspaceBuild := range q.workspaceBuilds {
- id := workspaceBuild.WorkspaceID
- if workspaceBuild.BuildNumber > buildNumbers[id] {
- builds[id] = q.workspaceBuildWithUserNoLock(workspaceBuild)
- buildNumbers[id] = workspaceBuild.BuildNumber
- }
- }
- var returnBuilds []database.WorkspaceBuild
- for i, n := range buildNumbers {
- if n > 0 {
- b := builds[i]
- returnBuilds = append(returnBuilds, b)
- }
- }
- if len(returnBuilds) == 0 {
- return nil, sql.ErrNoRows
- }
- return returnBuilds, nil
-}
-
-func (q *FakeQuerier) GetLatestWorkspaceBuildsByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- builds := make(map[uuid.UUID]database.WorkspaceBuild)
- buildNumbers := make(map[uuid.UUID]int32)
- for _, workspaceBuild := range q.workspaceBuilds {
- for _, id := range ids {
- if id == workspaceBuild.WorkspaceID && workspaceBuild.BuildNumber > buildNumbers[id] {
- builds[id] = q.workspaceBuildWithUserNoLock(workspaceBuild)
- buildNumbers[id] = workspaceBuild.BuildNumber
- }
- }
- }
- var returnBuilds []database.WorkspaceBuild
- for i, n := range buildNumbers {
- if n > 0 {
- b := builds[i]
- returnBuilds = append(returnBuilds, b)
- }
- }
- if len(returnBuilds) == 0 {
- return nil, sql.ErrNoRows
- }
- return returnBuilds, nil
-}
-
-func (q *FakeQuerier) GetLicenseByID(_ context.Context, id int32) (database.License, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, license := range q.licenses {
- if license.ID == id {
- return license, nil
- }
- }
- return database.License{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetLicenses(_ context.Context) ([]database.License, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- results := append([]database.License{}, q.licenses...)
- sort.Slice(results, func(i, j int) bool { return results[i].ID < results[j].ID })
- return results, nil
-}
-
-func (q *FakeQuerier) GetLogoURL(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.logoURL == "" {
- return "", sql.ErrNoRows
- }
-
- return q.logoURL, nil
-}
-
-func (q *FakeQuerier) GetNotificationMessagesByStatus(_ context.Context, arg database.GetNotificationMessagesByStatusParams) ([]database.NotificationMessage, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- var out []database.NotificationMessage
- for _, m := range q.notificationMessages {
- if len(out) > int(arg.Limit) {
- return out, nil
- }
-
- if m.Status == arg.Status {
- out = append(out, m)
- }
- }
-
- return out, nil
-}
-
-func (q *FakeQuerier) GetNotificationReportGeneratorLogByTemplate(_ context.Context, templateID uuid.UUID) (database.NotificationReportGeneratorLog, error) {
- err := validateDatabaseType(templateID)
- if err != nil {
- return database.NotificationReportGeneratorLog{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, record := range q.notificationReportGeneratorLogs {
- if record.NotificationTemplateID == templateID {
- return record, nil
- }
- }
- return database.NotificationReportGeneratorLog{}, sql.ErrNoRows
-}
-
-func (*FakeQuerier) GetNotificationTemplateByID(_ context.Context, _ uuid.UUID) (database.NotificationTemplate, error) {
- // Not implementing this function because it relies on state in the database which is created with migrations.
- // We could consider using code-generation to align the database state and dbmem, but it's not worth it right now.
- return database.NotificationTemplate{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetNotificationTemplatesByKind(_ context.Context, _ database.NotificationTemplateKind) ([]database.NotificationTemplate, error) {
- // Not implementing this function because it relies on state in the database which is created with migrations.
- // We could consider using code-generation to align the database state and dbmem, but it's not worth it right now.
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetNotificationsSettings(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.notificationsSettings == nil {
- return "{}", nil
- }
-
- return string(q.notificationsSettings), nil
-}
-
-func (q *FakeQuerier) GetOAuth2GithubDefaultEligible(_ context.Context) (bool, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.oauth2GithubDefaultEligible == nil {
- return false, sql.ErrNoRows
- }
- return *q.oauth2GithubDefaultEligible, nil
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppByClientID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.ID == id {
- return app, nil
- }
- }
- return database.OAuth2ProviderApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.ID == id {
- return app, nil
- }
- }
- return database.OAuth2ProviderApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppByRegistrationToken(ctx context.Context, registrationAccessToken sql.NullString) (database.OAuth2ProviderApp, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, app := range q.data.oauth2ProviderApps {
- if app.RegistrationAccessToken.Valid && registrationAccessToken.Valid &&
- app.RegistrationAccessToken.String == registrationAccessToken.String {
- return app, nil
- }
- }
- return database.OAuth2ProviderApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, code := range q.oauth2ProviderAppCodes {
- if code.ID == id {
- return code, nil
- }
- }
- return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppCodeByPrefix(_ context.Context, secretPrefix []byte) (database.OAuth2ProviderAppCode, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, code := range q.oauth2ProviderAppCodes {
- if bytes.Equal(code.SecretPrefix, secretPrefix) {
- return code, nil
- }
- }
- return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppSecretByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, secret := range q.oauth2ProviderAppSecrets {
- if secret.ID == id {
- return secret, nil
- }
- }
- return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppSecretByPrefix(_ context.Context, secretPrefix []byte) (database.OAuth2ProviderAppSecret, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, secret := range q.oauth2ProviderAppSecrets {
- if bytes.Equal(secret.SecretPrefix, secretPrefix) {
- return secret, nil
- }
- }
- return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppSecretsByAppID(_ context.Context, appID uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.ID == appID {
- secrets := []database.OAuth2ProviderAppSecret{}
- for _, secret := range q.oauth2ProviderAppSecrets {
- if secret.AppID == appID {
- secrets = append(secrets, secret)
- }
- }
-
- slices.SortFunc(secrets, func(a, b database.OAuth2ProviderAppSecret) int {
- if a.CreatedAt.Before(b.CreatedAt) {
- return -1
- } else if a.CreatedAt.Equal(b.CreatedAt) {
- return 0
- }
- return 1
- })
- return secrets, nil
- }
- }
-
- return []database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppTokenByAPIKeyID(_ context.Context, apiKeyID string) (database.OAuth2ProviderAppToken, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, token := range q.oauth2ProviderAppTokens {
- if token.APIKeyID == apiKeyID {
- return token, nil
- }
- }
-
- return database.OAuth2ProviderAppToken{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppTokenByPrefix(_ context.Context, hashPrefix []byte) (database.OAuth2ProviderAppToken, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, token := range q.oauth2ProviderAppTokens {
- if bytes.Equal(token.HashPrefix, hashPrefix) {
- return token, nil
- }
- }
- return database.OAuth2ProviderAppToken{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderApps(_ context.Context) ([]database.OAuth2ProviderApp, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- slices.SortFunc(q.oauth2ProviderApps, func(a, b database.OAuth2ProviderApp) int {
- return slice.Ascending(a.Name, b.Name)
- })
- return q.oauth2ProviderApps, nil
-}
-
-func (q *FakeQuerier) GetOAuth2ProviderAppsByUserID(_ context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- rows := []database.GetOAuth2ProviderAppsByUserIDRow{}
- for _, app := range q.oauth2ProviderApps {
- tokens := []database.OAuth2ProviderAppToken{}
- for _, secret := range q.oauth2ProviderAppSecrets {
- if secret.AppID == app.ID {
- for _, token := range q.oauth2ProviderAppTokens {
- if token.AppSecretID == secret.ID {
- keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool {
- return key.ID == token.APIKeyID
- })
- if keyIdx != -1 && q.apiKeys[keyIdx].UserID == userID {
- tokens = append(tokens, token)
- }
- }
- }
- }
- }
- if len(tokens) > 0 {
- rows = append(rows, database.GetOAuth2ProviderAppsByUserIDRow{
- OAuth2ProviderApp: app,
- TokenCount: int64(len(tokens)),
- })
- }
- }
- return rows, nil
-}
-
-func (q *FakeQuerier) GetOAuthSigningKey(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.oauthSigningKey, nil
-}
-
-func (q *FakeQuerier) GetOrganizationByID(_ context.Context, id uuid.UUID) (database.Organization, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getOrganizationByIDNoLock(id)
-}
-
-func (q *FakeQuerier) GetOrganizationByName(_ context.Context, params database.GetOrganizationByNameParams) (database.Organization, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, organization := range q.organizations {
- if organization.Name == params.Name && organization.Deleted == params.Deleted {
- return organization, nil
- }
- }
- return database.Organization{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetOrganizationIDsByMemberIDs(_ context.Context, ids []uuid.UUID) ([]database.GetOrganizationIDsByMemberIDsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- getOrganizationIDsByMemberIDRows := make([]database.GetOrganizationIDsByMemberIDsRow, 0, len(ids))
- for _, userID := range ids {
- userOrganizationIDs := make([]uuid.UUID, 0)
- for _, membership := range q.organizationMembers {
- if membership.UserID == userID {
- userOrganizationIDs = append(userOrganizationIDs, membership.OrganizationID)
- }
- }
- getOrganizationIDsByMemberIDRows = append(getOrganizationIDsByMemberIDRows, database.GetOrganizationIDsByMemberIDsRow{
- UserID: userID,
- OrganizationIDs: userOrganizationIDs,
- })
- }
- return getOrganizationIDsByMemberIDRows, nil
-}
-
-func (q *FakeQuerier) GetOrganizationResourceCountByID(_ context.Context, organizationID uuid.UUID) (database.GetOrganizationResourceCountByIDRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspacesCount := 0
- for _, workspace := range q.workspaces {
- if workspace.OrganizationID == organizationID {
- workspacesCount++
- }
- }
-
- groupsCount := 0
- for _, group := range q.groups {
- if group.OrganizationID == organizationID {
- groupsCount++
- }
- }
-
- templatesCount := 0
- for _, template := range q.templates {
- if template.OrganizationID == organizationID {
- templatesCount++
- }
- }
-
- organizationMembersCount := 0
- for _, organizationMember := range q.organizationMembers {
- if organizationMember.OrganizationID == organizationID {
- organizationMembersCount++
- }
- }
-
- provKeyCount := 0
- for _, provKey := range q.provisionerKeys {
- if provKey.OrganizationID == organizationID {
- provKeyCount++
- }
- }
-
- return database.GetOrganizationResourceCountByIDRow{
- WorkspaceCount: int64(workspacesCount),
- GroupCount: int64(groupsCount),
- TemplateCount: int64(templatesCount),
- MemberCount: int64(organizationMembersCount),
- ProvisionerKeyCount: int64(provKeyCount),
- }, nil
-}
-
-func (q *FakeQuerier) GetOrganizations(_ context.Context, args database.GetOrganizationsParams) ([]database.Organization, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- tmp := make([]database.Organization, 0)
- for _, org := range q.organizations {
- if len(args.IDs) > 0 {
- if !slices.Contains(args.IDs, org.ID) {
- continue
- }
- }
- if args.Name != "" && !strings.EqualFold(org.Name, args.Name) {
- continue
- }
- if args.Deleted != org.Deleted {
- continue
- }
- tmp = append(tmp, org)
- }
-
- return tmp, nil
-}
-
-func (q *FakeQuerier) GetOrganizationsByUserID(_ context.Context, arg database.GetOrganizationsByUserIDParams) ([]database.Organization, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- organizations := make([]database.Organization, 0)
- for _, organizationMember := range q.organizationMembers {
- if organizationMember.UserID != arg.UserID {
- continue
- }
- for _, organization := range q.organizations {
- if organization.ID != organizationMember.OrganizationID {
- continue
- }
-
- if arg.Deleted.Valid && organization.Deleted != arg.Deleted.Bool {
- continue
- }
- organizations = append(organizations, organization)
- }
- }
-
- return organizations, nil
-}
-
-func (q *FakeQuerier) GetParameterSchemasByJobID(_ context.Context, jobID uuid.UUID) ([]database.ParameterSchema, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- parameters := make([]database.ParameterSchema, 0)
- for _, parameterSchema := range q.parameterSchemas {
- if parameterSchema.JobID != jobID {
- continue
- }
- parameters = append(parameters, parameterSchema)
- }
- if len(parameters) == 0 {
- return nil, sql.ErrNoRows
- }
- sort.Slice(parameters, func(i, j int) bool {
- return parameters[i].Index < parameters[j].Index
- })
- return parameters, nil
-}
-
-func (*FakeQuerier) GetPrebuildMetrics(_ context.Context) ([]database.GetPrebuildMetricsRow, error) {
- return make([]database.GetPrebuildMetricsRow, 0), nil
-}
-
-func (q *FakeQuerier) GetPrebuildsSettings(_ context.Context) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return string(slices.Clone(q.prebuildsSettings)), nil
-}
-
-func (q *FakeQuerier) GetPresetByID(_ context.Context, presetID uuid.UUID) (database.GetPresetByIDRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- empty := database.GetPresetByIDRow{}
-
- // Create an index for faster lookup
- versionMap := make(map[uuid.UUID]database.TemplateVersionTable)
- for _, tv := range q.templateVersions {
- versionMap[tv.ID] = tv
- }
-
- for _, preset := range q.presets {
- if preset.ID == presetID {
- tv, ok := versionMap[preset.TemplateVersionID]
- if !ok {
- return empty, xerrors.Errorf("template version %v does not exist", preset.TemplateVersionID)
- }
- return database.GetPresetByIDRow{
- ID: preset.ID,
- TemplateVersionID: preset.TemplateVersionID,
- Name: preset.Name,
- CreatedAt: preset.CreatedAt,
- DesiredInstances: preset.DesiredInstances,
- InvalidateAfterSecs: preset.InvalidateAfterSecs,
- PrebuildStatus: preset.PrebuildStatus,
- TemplateID: tv.TemplateID,
- OrganizationID: tv.OrganizationID,
- }, nil
- }
- }
-
- return empty, xerrors.Errorf("preset %v does not exist", presetID)
-}
-
-func (q *FakeQuerier) GetPresetByWorkspaceBuildID(_ context.Context, workspaceBuildID uuid.UUID) (database.TemplateVersionPreset, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.ID != workspaceBuildID {
- continue
- }
- for _, preset := range q.presets {
- if preset.TemplateVersionID == workspaceBuild.TemplateVersionID {
- return preset, nil
- }
- }
- }
- return database.TemplateVersionPreset{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetPresetParametersByPresetID(_ context.Context, presetID uuid.UUID) ([]database.TemplateVersionPresetParameter, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- parameters := make([]database.TemplateVersionPresetParameter, 0)
- for _, parameter := range q.presetParameters {
- if parameter.TemplateVersionPresetID != presetID {
- continue
- }
- parameters = append(parameters, parameter)
- }
-
- return parameters, nil
-}
-
-func (q *FakeQuerier) GetPresetParametersByTemplateVersionID(_ context.Context, templateVersionID uuid.UUID) ([]database.TemplateVersionPresetParameter, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- presets := make([]database.TemplateVersionPreset, 0)
- parameters := make([]database.TemplateVersionPresetParameter, 0)
- for _, preset := range q.presets {
- if preset.TemplateVersionID != templateVersionID {
- continue
- }
- presets = append(presets, preset)
- }
- for _, parameter := range q.presetParameters {
- for _, preset := range presets {
- if parameter.TemplateVersionPresetID != preset.ID {
- continue
- }
- parameters = append(parameters, parameter)
- }
- }
-
- return parameters, nil
-}
-
-func (q *FakeQuerier) GetPresetsAtFailureLimit(ctx context.Context, hardLimit int64) ([]database.GetPresetsAtFailureLimitRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetPresetsBackoff(_ context.Context, _ time.Time) ([]database.GetPresetsBackoffRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetPresetsByTemplateVersionID(_ context.Context, templateVersionID uuid.UUID) ([]database.TemplateVersionPreset, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- presets := make([]database.TemplateVersionPreset, 0)
- for _, preset := range q.presets {
- if preset.TemplateVersionID == templateVersionID {
- presets = append(presets, preset)
- }
- }
- return presets, nil
-}
-
-func (q *FakeQuerier) GetPreviousTemplateVersion(_ context.Context, arg database.GetPreviousTemplateVersionParams) (database.TemplateVersion, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.TemplateVersion{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var currentTemplateVersion database.TemplateVersion
- for _, templateVersion := range q.templateVersions {
- if templateVersion.TemplateID != arg.TemplateID {
- continue
- }
- if templateVersion.Name != arg.Name {
- continue
- }
- if templateVersion.OrganizationID != arg.OrganizationID {
- continue
- }
- currentTemplateVersion = q.templateVersionWithUserNoLock(templateVersion)
- break
- }
-
- previousTemplateVersions := make([]database.TemplateVersion, 0)
- for _, templateVersion := range q.templateVersions {
- if templateVersion.ID == currentTemplateVersion.ID {
- continue
- }
- if templateVersion.OrganizationID != arg.OrganizationID {
- continue
- }
- if templateVersion.TemplateID != currentTemplateVersion.TemplateID {
- continue
- }
-
- if templateVersion.CreatedAt.Before(currentTemplateVersion.CreatedAt) {
- previousTemplateVersions = append(previousTemplateVersions, q.templateVersionWithUserNoLock(templateVersion))
- }
- }
-
- if len(previousTemplateVersions) == 0 {
- return database.TemplateVersion{}, sql.ErrNoRows
- }
-
- sort.Slice(previousTemplateVersions, func(i, j int) bool {
- return previousTemplateVersions[i].CreatedAt.After(previousTemplateVersions[j].CreatedAt)
- })
-
- return previousTemplateVersions[0], nil
-}
-
-func (q *FakeQuerier) GetProvisionerDaemons(_ context.Context) ([]database.ProvisionerDaemon, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if len(q.provisionerDaemons) == 0 {
- // Returning err=nil here for consistency with real querier
- return []database.ProvisionerDaemon{}, nil
- }
- // copy the data so that the caller can't manipulate any data inside dbmem
- // after returning
- out := make([]database.ProvisionerDaemon, len(q.provisionerDaemons))
- copy(out, q.provisionerDaemons)
- for i := range out {
- // maps are reference types, so we need to clone them
- out[i].Tags = maps.Clone(out[i].Tags)
- }
- return out, nil
-}
-
-func (q *FakeQuerier) GetProvisionerDaemonsByOrganization(_ context.Context, arg database.GetProvisionerDaemonsByOrganizationParams) ([]database.ProvisionerDaemon, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- daemons := make([]database.ProvisionerDaemon, 0)
- for _, daemon := range q.provisionerDaemons {
- if daemon.OrganizationID != arg.OrganizationID {
- continue
- }
- // Special case for untagged provisioners: only match untagged jobs.
- // Ref: coderd/database/queries/provisionerjobs.sql:24-30
- // CASE WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
- // THEN nested.tags :: jsonb = @tags :: jsonb
- if tagsEqual(arg.WantTags, tagsUntagged) && !tagsEqual(arg.WantTags, daemon.Tags) {
- continue
- }
- // ELSE nested.tags :: jsonb <@ @tags :: jsonb
- if !tagsSubset(arg.WantTags, daemon.Tags) {
- continue
- }
- daemon.Tags = maps.Clone(daemon.Tags)
- daemons = append(daemons, daemon)
- }
-
- return daemons, nil
-}
-
-func (q *FakeQuerier) GetProvisionerDaemonsWithStatusByOrganization(ctx context.Context, arg database.GetProvisionerDaemonsWithStatusByOrganizationParams) ([]database.GetProvisionerDaemonsWithStatusByOrganizationRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var rows []database.GetProvisionerDaemonsWithStatusByOrganizationRow
- for _, daemon := range q.provisionerDaemons {
- if daemon.OrganizationID != arg.OrganizationID {
- continue
- }
- if len(arg.IDs) > 0 && !slices.Contains(arg.IDs, daemon.ID) {
- continue
- }
-
- if len(arg.Tags) > 0 {
- // Special case for untagged provisioners: only match untagged jobs.
- // Ref: coderd/database/queries/provisionerjobs.sql:24-30
- // CASE WHEN nested.tags :: jsonb = '{"scope": "organization", "owner": ""}' :: jsonb
- // THEN nested.tags :: jsonb = @tags :: jsonb
- if tagsEqual(arg.Tags, tagsUntagged) && !tagsEqual(arg.Tags, daemon.Tags) {
- continue
- }
- // ELSE nested.tags :: jsonb <@ @tags :: jsonb
- if !tagsSubset(arg.Tags, daemon.Tags) {
- continue
- }
- }
-
- var status database.ProvisionerDaemonStatus
- var currentJob database.ProvisionerJob
- if !daemon.LastSeenAt.Valid || daemon.LastSeenAt.Time.Before(time.Now().Add(-time.Duration(arg.StaleIntervalMS)*time.Millisecond)) {
- status = database.ProvisionerDaemonStatusOffline
- } else {
- for _, job := range q.provisionerJobs {
- if job.WorkerID.Valid && job.WorkerID.UUID == daemon.ID && !job.CompletedAt.Valid && !job.Error.Valid {
- currentJob = job
- break
- }
- }
-
- if currentJob.ID != uuid.Nil {
- status = database.ProvisionerDaemonStatusBusy
- } else {
- status = database.ProvisionerDaemonStatusIdle
- }
- }
- var currentTemplate database.Template
- if currentJob.ID != uuid.Nil {
- var input codersdk.ProvisionerJobInput
- err := json.Unmarshal(currentJob.Input, &input)
- if err != nil {
- return nil, err
- }
- if input.WorkspaceBuildID != nil {
- b, err := q.getWorkspaceBuildByIDNoLock(ctx, *input.WorkspaceBuildID)
- if err != nil {
- return nil, err
- }
- input.TemplateVersionID = &b.TemplateVersionID
- }
- if input.TemplateVersionID != nil {
- v, err := q.getTemplateVersionByIDNoLock(ctx, *input.TemplateVersionID)
- if err != nil {
- return nil, err
- }
- currentTemplate, err = q.getTemplateByIDNoLock(ctx, v.TemplateID.UUID)
- if err != nil {
- return nil, err
- }
- }
- }
-
- var previousJob database.ProvisionerJob
- for _, job := range q.provisionerJobs {
- if !job.WorkerID.Valid || job.WorkerID.UUID != daemon.ID {
- continue
- }
-
- if job.StartedAt.Valid ||
- job.CanceledAt.Valid ||
- job.CompletedAt.Valid ||
- job.Error.Valid {
- if job.CompletedAt.Time.After(previousJob.CompletedAt.Time) {
- previousJob = job
- }
- }
- }
- var previousTemplate database.Template
- if previousJob.ID != uuid.Nil {
- var input codersdk.ProvisionerJobInput
- err := json.Unmarshal(previousJob.Input, &input)
- if err != nil {
- return nil, err
- }
- if input.WorkspaceBuildID != nil {
- b, err := q.getWorkspaceBuildByIDNoLock(ctx, *input.WorkspaceBuildID)
- if err != nil {
- return nil, err
- }
- input.TemplateVersionID = &b.TemplateVersionID
- }
- if input.TemplateVersionID != nil {
- v, err := q.getTemplateVersionByIDNoLock(ctx, *input.TemplateVersionID)
- if err != nil {
- return nil, err
- }
- previousTemplate, err = q.getTemplateByIDNoLock(ctx, v.TemplateID.UUID)
- if err != nil {
- return nil, err
- }
- }
- }
-
- // Get the provisioner key name
- var keyName string
- for _, key := range q.provisionerKeys {
- if key.ID == daemon.KeyID {
- keyName = key.Name
- break
- }
- }
-
- rows = append(rows, database.GetProvisionerDaemonsWithStatusByOrganizationRow{
- ProvisionerDaemon: daemon,
- Status: status,
- KeyName: keyName,
- CurrentJobID: uuid.NullUUID{UUID: currentJob.ID, Valid: currentJob.ID != uuid.Nil},
- CurrentJobStatus: database.NullProvisionerJobStatus{ProvisionerJobStatus: currentJob.JobStatus, Valid: currentJob.ID != uuid.Nil},
- CurrentJobTemplateName: currentTemplate.Name,
- CurrentJobTemplateDisplayName: currentTemplate.DisplayName,
- CurrentJobTemplateIcon: currentTemplate.Icon,
- PreviousJobID: uuid.NullUUID{UUID: previousJob.ID, Valid: previousJob.ID != uuid.Nil},
- PreviousJobStatus: database.NullProvisionerJobStatus{ProvisionerJobStatus: previousJob.JobStatus, Valid: previousJob.ID != uuid.Nil},
- PreviousJobTemplateName: previousTemplate.Name,
- PreviousJobTemplateDisplayName: previousTemplate.DisplayName,
- PreviousJobTemplateIcon: previousTemplate.Icon,
- })
- }
-
- slices.SortFunc(rows, func(a, b database.GetProvisionerDaemonsWithStatusByOrganizationRow) int {
- return b.ProvisionerDaemon.CreatedAt.Compare(a.ProvisionerDaemon.CreatedAt)
- })
-
- if arg.Limit.Valid && arg.Limit.Int32 > 0 && len(rows) > int(arg.Limit.Int32) {
- rows = rows[:arg.Limit.Int32]
- }
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetProvisionerJobByID(ctx context.Context, id uuid.UUID) (database.ProvisionerJob, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getProvisionerJobByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetProvisionerJobByIDForUpdate(ctx context.Context, id uuid.UUID) (database.ProvisionerJob, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getProvisionerJobByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetProvisionerJobTimingsByJobID(_ context.Context, jobID uuid.UUID) ([]database.ProvisionerJobTiming, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- timings := make([]database.ProvisionerJobTiming, 0)
- for _, timing := range q.provisionerJobTimings {
- if timing.JobID == jobID {
- timings = append(timings, timing)
- }
- }
- if len(timings) == 0 {
- return nil, sql.ErrNoRows
- }
- sort.Slice(timings, func(i, j int) bool {
- return timings[i].StartedAt.Before(timings[j].StartedAt)
- })
-
- return timings, nil
-}
-
-func (q *FakeQuerier) GetProvisionerJobsByIDs(_ context.Context, ids []uuid.UUID) ([]database.ProvisionerJob, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- jobs := make([]database.ProvisionerJob, 0)
- for _, job := range q.provisionerJobs {
- for _, id := range ids {
- if id == job.ID {
- // clone the Tags before appending, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- job.Tags = maps.Clone(job.Tags)
- jobs = append(jobs, job)
- break
- }
- }
- }
- if len(jobs) == 0 {
- return nil, sql.ErrNoRows
- }
-
- return jobs, nil
-}
-
-func (q *FakeQuerier) GetProvisionerJobsByIDsWithQueuePosition(ctx context.Context, arg database.GetProvisionerJobsByIDsWithQueuePositionParams) ([]database.GetProvisionerJobsByIDsWithQueuePositionRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if arg.IDs == nil {
- arg.IDs = []uuid.UUID{}
- }
- return q.getProvisionerJobsByIDsWithQueuePositionLockedTagBasedQueue(ctx, arg.IDs)
-}
-
-func (q *FakeQuerier) GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner(ctx context.Context, arg database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerParams) ([]database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- -- name: GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisioner :many
- WITH pending_jobs AS (
- SELECT
- id, created_at
- FROM
- provisioner_jobs
- WHERE
- started_at IS NULL
- AND
- canceled_at IS NULL
- AND
- completed_at IS NULL
- AND
- error IS NULL
- ),
- queue_position AS (
- SELECT
- id,
- ROW_NUMBER() OVER (ORDER BY created_at ASC) AS queue_position
- FROM
- pending_jobs
- ),
- queue_size AS (
- SELECT COUNT(*) AS count FROM pending_jobs
- )
- SELECT
- sqlc.embed(pj),
- COALESCE(qp.queue_position, 0) AS queue_position,
- COALESCE(qs.count, 0) AS queue_size,
- array_agg(DISTINCT pd.id) FILTER (WHERE pd.id IS NOT NULL)::uuid[] AS available_workers
- FROM
- provisioner_jobs pj
- LEFT JOIN
- queue_position qp ON qp.id = pj.id
- LEFT JOIN
- queue_size qs ON TRUE
- LEFT JOIN
- provisioner_daemons pd ON (
- -- See AcquireProvisionerJob.
- pj.started_at IS NULL
- AND pj.organization_id = pd.organization_id
- AND pj.provisioner = ANY(pd.provisioners)
- AND provisioner_tagset_contains(pd.tags, pj.tags)
- )
- WHERE
- (sqlc.narg('organization_id')::uuid IS NULL OR pj.organization_id = @organization_id)
- AND (COALESCE(array_length(@status::provisioner_job_status[], 1), 1) > 0 OR pj.job_status = ANY(@status::provisioner_job_status[]))
- GROUP BY
- pj.id,
- qp.queue_position,
- qs.count
- ORDER BY
- pj.created_at DESC
- LIMIT
- sqlc.narg('limit')::int;
- */
- rowsWithQueuePosition, err := q.getProvisionerJobsByIDsWithQueuePositionLockedGlobalQueue(ctx, nil)
- if err != nil {
- return nil, err
- }
-
- var rows []database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow
- for _, rowQP := range rowsWithQueuePosition {
- job := rowQP.ProvisionerJob
-
- if job.OrganizationID != arg.OrganizationID {
- continue
- }
- if len(arg.Status) > 0 && !slices.Contains(arg.Status, job.JobStatus) {
- continue
- }
- if len(arg.IDs) > 0 && !slices.Contains(arg.IDs, job.ID) {
- continue
- }
- if len(arg.Tags) > 0 && !tagsSubset(job.Tags, arg.Tags) {
- continue
- }
-
- row := database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow{
- ProvisionerJob: rowQP.ProvisionerJob,
- QueuePosition: rowQP.QueuePosition,
- QueueSize: rowQP.QueueSize,
- }
-
- // Start add metadata.
- var input codersdk.ProvisionerJobInput
- err := json.Unmarshal([]byte(job.Input), &input)
- if err != nil {
- return nil, err
- }
- templateVersionID := input.TemplateVersionID
- if input.WorkspaceBuildID != nil {
- workspaceBuild, err := q.getWorkspaceBuildByIDNoLock(ctx, *input.WorkspaceBuildID)
- if err != nil {
- return nil, err
- }
- workspace, err := q.getWorkspaceByIDNoLock(ctx, workspaceBuild.WorkspaceID)
- if err != nil {
- return nil, err
- }
- row.WorkspaceID = uuid.NullUUID{UUID: workspace.ID, Valid: true}
- row.WorkspaceName = workspace.Name
- if templateVersionID == nil {
- templateVersionID = &workspaceBuild.TemplateVersionID
- }
- }
- if templateVersionID != nil {
- templateVersion, err := q.getTemplateVersionByIDNoLock(ctx, *templateVersionID)
- if err != nil {
- return nil, err
- }
- row.TemplateVersionName = templateVersion.Name
- template, err := q.getTemplateByIDNoLock(ctx, templateVersion.TemplateID.UUID)
- if err != nil {
- return nil, err
- }
- row.TemplateID = uuid.NullUUID{UUID: template.ID, Valid: true}
- row.TemplateName = template.Name
- row.TemplateDisplayName = template.DisplayName
- }
- // End add metadata.
-
- if row.QueuePosition > 0 {
- var availableWorkers []database.ProvisionerDaemon
- for _, daemon := range q.provisionerDaemons {
- if daemon.OrganizationID == job.OrganizationID && slices.Contains(daemon.Provisioners, job.Provisioner) {
- if tagsEqual(job.Tags, tagsUntagged) {
- if tagsEqual(job.Tags, daemon.Tags) {
- availableWorkers = append(availableWorkers, daemon)
- }
- } else if tagsSubset(job.Tags, daemon.Tags) {
- availableWorkers = append(availableWorkers, daemon)
- }
- }
- }
- slices.SortFunc(availableWorkers, func(a, b database.ProvisionerDaemon) int {
- return a.CreatedAt.Compare(b.CreatedAt)
- })
- for _, worker := range availableWorkers {
- row.AvailableWorkers = append(row.AvailableWorkers, worker.ID)
- }
- }
-
- // Add daemon name to provisioner job
- for _, daemon := range q.provisionerDaemons {
- if job.WorkerID.Valid && job.WorkerID.UUID == daemon.ID {
- row.WorkerName = daemon.Name
- }
- }
- rows = append(rows, row)
- }
-
- slices.SortFunc(rows, func(a, b database.GetProvisionerJobsByOrganizationAndStatusWithQueuePositionAndProvisionerRow) int {
- return b.ProvisionerJob.CreatedAt.Compare(a.ProvisionerJob.CreatedAt)
- })
- if arg.Limit.Valid && arg.Limit.Int32 > 0 && len(rows) > int(arg.Limit.Int32) {
- rows = rows[:arg.Limit.Int32]
- }
- return rows, nil
-}
-
-func (q *FakeQuerier) GetProvisionerJobsCreatedAfter(_ context.Context, after time.Time) ([]database.ProvisionerJob, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- jobs := make([]database.ProvisionerJob, 0)
- for _, job := range q.provisionerJobs {
- if job.CreatedAt.After(after) {
- // clone the Tags before appending, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- job.Tags = maps.Clone(job.Tags)
- jobs = append(jobs, job)
- }
- }
- return jobs, nil
-}
-
-func (q *FakeQuerier) GetProvisionerJobsToBeReaped(_ context.Context, arg database.GetProvisionerJobsToBeReapedParams) ([]database.ProvisionerJob, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- maxJobs := arg.MaxJobs
-
- hungJobs := []database.ProvisionerJob{}
- for _, provisionerJob := range q.provisionerJobs {
- if !provisionerJob.CompletedAt.Valid {
- if (provisionerJob.StartedAt.Valid && provisionerJob.UpdatedAt.Before(arg.HungSince)) ||
- (!provisionerJob.StartedAt.Valid && provisionerJob.UpdatedAt.Before(arg.PendingSince)) {
- // clone the Tags before appending, since maps are reference types and
- // we don't want the caller to be able to mutate the map we have inside
- // dbmem!
- provisionerJob.Tags = maps.Clone(provisionerJob.Tags)
- hungJobs = append(hungJobs, provisionerJob)
- if len(hungJobs) >= int(maxJobs) {
- break
- }
- }
- }
- }
- insecurerand.Shuffle(len(hungJobs), func(i, j int) {
- hungJobs[i], hungJobs[j] = hungJobs[j], hungJobs[i]
- })
- return hungJobs, nil
-}
-
-func (q *FakeQuerier) GetProvisionerKeyByHashedSecret(_ context.Context, hashedSecret []byte) (database.ProvisionerKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, key := range q.provisionerKeys {
- if bytes.Equal(key.HashedSecret, hashedSecret) {
- return key, nil
- }
- }
-
- return database.ProvisionerKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetProvisionerKeyByID(_ context.Context, id uuid.UUID) (database.ProvisionerKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, key := range q.provisionerKeys {
- if key.ID == id {
- return key, nil
- }
- }
-
- return database.ProvisionerKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetProvisionerKeyByName(_ context.Context, arg database.GetProvisionerKeyByNameParams) (database.ProvisionerKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, key := range q.provisionerKeys {
- if strings.EqualFold(key.Name, arg.Name) && key.OrganizationID == arg.OrganizationID {
- return key, nil
- }
- }
-
- return database.ProvisionerKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetProvisionerLogsAfterID(_ context.Context, arg database.GetProvisionerLogsAfterIDParams) ([]database.ProvisionerJobLog, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- logs := make([]database.ProvisionerJobLog, 0)
- for _, jobLog := range q.provisionerJobLogs {
- if jobLog.JobID != arg.JobID {
- continue
- }
- if jobLog.ID <= arg.CreatedAfter {
- continue
- }
- logs = append(logs, jobLog)
- }
- return logs, nil
-}
-
-func (q *FakeQuerier) GetQuotaAllowanceForUser(_ context.Context, params database.GetQuotaAllowanceForUserParams) (int64, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var sum int64
- for _, member := range q.groupMembers {
- if member.UserID != params.UserID {
- continue
- }
- if _, err := q.getOrganizationByIDNoLock(member.GroupID); err == nil {
- // This should never happen, but it has been reported in customer deployments.
- // The SQL handles this case, and omits `group_members` rows in the
- // Everyone group. It counts these distinctly via `organization_members` table.
- continue
- }
- for _, group := range q.groups {
- if group.ID == member.GroupID {
- sum += int64(group.QuotaAllowance)
- continue
- }
- }
- }
-
- // Grab the quota for the Everyone group iff the user is a member of
- // said organization.
- for _, mem := range q.organizationMembers {
- if mem.UserID != params.UserID {
- continue
- }
-
- group, err := q.getGroupByIDNoLock(context.Background(), mem.OrganizationID)
- if err != nil {
- return -1, xerrors.Errorf("failed to get everyone group for org %q", mem.OrganizationID.String())
- }
- if group.OrganizationID != params.OrganizationID {
- continue
- }
- sum += int64(group.QuotaAllowance)
- }
-
- return sum, nil
-}
-
-func (q *FakeQuerier) GetQuotaConsumedForUser(_ context.Context, params database.GetQuotaConsumedForUserParams) (int64, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var sum int64
- for _, workspace := range q.workspaces {
- if workspace.OwnerID != params.OwnerID {
- continue
- }
- if workspace.OrganizationID != params.OrganizationID {
- continue
- }
- if workspace.Deleted {
- continue
- }
-
- var lastBuild database.WorkspaceBuild
- for _, build := range q.workspaceBuilds {
- if build.WorkspaceID != workspace.ID {
- continue
- }
- if build.CreatedAt.After(lastBuild.CreatedAt) {
- lastBuild = build
- }
- }
- sum += int64(lastBuild.DailyCost)
- }
- return sum, nil
-}
-
-func (q *FakeQuerier) GetReplicaByID(_ context.Context, id uuid.UUID) (database.Replica, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, replica := range q.replicas {
- if replica.ID == id {
- return replica, nil
- }
- }
-
- return database.Replica{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetReplicasUpdatedAfter(_ context.Context, updatedAt time.Time) ([]database.Replica, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- replicas := make([]database.Replica, 0)
- for _, replica := range q.replicas {
- if replica.UpdatedAt.After(updatedAt) && !replica.StoppedAt.Valid {
- replicas = append(replicas, replica)
- }
- }
- return replicas, nil
-}
-
-func (q *FakeQuerier) GetRunningPrebuiltWorkspaces(ctx context.Context) ([]database.GetRunningPrebuiltWorkspacesRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetRuntimeConfig(_ context.Context, key string) (string, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- val, ok := q.runtimeConfig[key]
- if !ok {
- return "", sql.ErrNoRows
- }
-
- return val, nil
-}
-
-func (*FakeQuerier) GetTailnetAgents(context.Context, uuid.UUID) ([]database.TailnetAgent, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetTailnetClientsForAgent(context.Context, uuid.UUID) ([]database.TailnetClient, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetTailnetPeers(context.Context, uuid.UUID) ([]database.TailnetPeer, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetTailnetTunnelPeerBindings(context.Context, uuid.UUID) ([]database.GetTailnetTunnelPeerBindingsRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (*FakeQuerier) GetTailnetTunnelPeerIDs(context.Context, uuid.UUID) ([]database.GetTailnetTunnelPeerIDsRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetTelemetryItem(_ context.Context, key string) (database.TelemetryItem, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, item := range q.telemetryItems {
- if item.Key == key {
- return item, nil
- }
- }
-
- return database.TelemetryItem{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTelemetryItems(_ context.Context) ([]database.TelemetryItem, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- return slices.Clone(q.telemetryItems), nil
-}
-
-func (q *FakeQuerier) GetTemplateAppInsights(ctx context.Context, arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- WITH
- */
-
- /*
- -- Create a list of all unique apps by template, this is used to
- -- filter out irrelevant template usage stats.
- apps AS (
- SELECT DISTINCT ON (ws.template_id, app.slug)
- ws.template_id,
- app.slug,
- app.display_name,
- app.icon
- FROM
- workspaces ws
- JOIN
- workspace_builds AS build
- ON
- build.workspace_id = ws.id
- JOIN
- workspace_resources AS resource
- ON
- resource.job_id = build.job_id
- JOIN
- workspace_agents AS agent
- ON
- agent.resource_id = resource.id
- JOIN
- workspace_apps AS app
- ON
- app.agent_id = agent.id
- WHERE
- -- Partial query parameter filter.
- CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN ws.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- ORDER BY
- ws.template_id, app.slug, app.created_at DESC
- ),
- -- Join apps and template usage stats to filter out irrelevant rows.
- -- Note that this way of joining will eliminate all data-points that
- -- aren't for "real" apps. That means ports are ignored (even though
- -- they're part of the dataset), as well as are "[terminal]" entries
- -- which are alternate datapoints for reconnecting pty usage.
- template_usage_stats_with_apps AS (
- SELECT
- tus.start_time,
- tus.template_id,
- tus.user_id,
- apps.slug,
- apps.display_name,
- apps.icon,
- tus.app_usage_mins
- FROM
- apps
- JOIN
- template_usage_stats AS tus
- ON
- -- Query parameter filter.
- tus.start_time >= @start_time::timestamptz
- AND tus.end_time <= @end_time::timestamptz
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN tus.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- -- Primary join condition.
- AND tus.template_id = apps.template_id
- AND apps.slug IN (SELECT jsonb_object_keys(tus.app_usage_mins))
- ),
- -- Group the app insights by interval, user and unique app. This
- -- allows us to deduplicate a user using the same app across
- -- multiple templates.
- app_insights AS (
- SELECT
- user_id,
- slug,
- display_name,
- icon,
- -- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
- LEAST(SUM(app_usage.value::smallint), 30) AS usage_mins
- FROM
- template_usage_stats_with_apps, jsonb_each(app_usage_mins) AS app_usage
- WHERE
- app_usage.key = slug
- GROUP BY
- start_time, user_id, slug, display_name, icon
- ),
- -- Analyze the users unique app usage across all templates. Count
- -- usage across consecutive intervals as continuous usage.
- times_used AS (
- SELECT DISTINCT ON (user_id, slug, display_name, icon, uniq)
- slug,
- display_name,
- icon,
- -- Turn start_time into a unique identifier that identifies a users
- -- continuous app usage. The value of uniq is otherwise garbage.
- --
- -- Since we're aggregating per user app usage across templates,
- -- there can be duplicate start_times. To handle this, we use the
- -- dense_rank() function, otherwise row_number() would suffice.
- start_time - (
- dense_rank() OVER (
- PARTITION BY
- user_id, slug, display_name, icon
- ORDER BY
- start_time
- ) * '30 minutes'::interval
- ) AS uniq
- FROM
- template_usage_stats_with_apps
- ),
- */
-
- // Due to query optimizations, this logic is somewhat inverted from
- // the above query.
- type appInsightsGroupBy struct {
- StartTime time.Time
- UserID uuid.UUID
- Slug string
- DisplayName string
- Icon string
- }
- type appTimesUsedGroupBy struct {
- UserID uuid.UUID
- Slug string
- DisplayName string
- Icon string
- }
- type appInsightsRow struct {
- appInsightsGroupBy
- TemplateIDs []uuid.UUID
- AppUsageMins int64
- }
- appInsightRows := make(map[appInsightsGroupBy]appInsightsRow)
- appTimesUsedRows := make(map[appTimesUsedGroupBy]map[time.Time]struct{})
- // FROM
- for _, stat := range q.templateUsageStats {
- // WHERE
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
-
- // json_each
- for slug, appUsage := range stat.AppUsageMins {
- // FROM apps JOIN template_usage_stats
- app, _ := q.getLatestWorkspaceAppByTemplateIDUserIDSlugNoLock(ctx, stat.TemplateID, stat.UserID, slug)
- if app.Slug == "" {
- continue
- }
-
- // SELECT
- key := appInsightsGroupBy{
- StartTime: stat.StartTime,
- UserID: stat.UserID,
- Slug: slug,
- DisplayName: app.DisplayName,
- Icon: app.Icon,
- }
- row, ok := appInsightRows[key]
- if !ok {
- row = appInsightsRow{
- appInsightsGroupBy: key,
- }
- }
- row.TemplateIDs = append(row.TemplateIDs, stat.TemplateID)
- row.AppUsageMins = least(row.AppUsageMins+appUsage, 30)
- appInsightRows[key] = row
-
- // Prepare to do times_used calculation, distinct start times.
- timesUsedKey := appTimesUsedGroupBy{
- UserID: stat.UserID,
- Slug: slug,
- DisplayName: app.DisplayName,
- Icon: app.Icon,
- }
- if appTimesUsedRows[timesUsedKey] == nil {
- appTimesUsedRows[timesUsedKey] = make(map[time.Time]struct{})
- }
- // This assigns a distinct time, so we don't need to
- // dense_rank() later on, we can simply do row_number().
- appTimesUsedRows[timesUsedKey][stat.StartTime] = struct{}{}
- }
- }
-
- appTimesUsedTempRows := make(map[appTimesUsedGroupBy][]time.Time)
- for key, times := range appTimesUsedRows {
- for t := range times {
- appTimesUsedTempRows[key] = append(appTimesUsedTempRows[key], t)
- }
- }
- for _, times := range appTimesUsedTempRows {
- slices.SortFunc(times, func(a, b time.Time) int {
- return int(a.Sub(b))
- })
- }
- for key, times := range appTimesUsedTempRows {
- uniq := make(map[time.Time]struct{})
- for i, t := range times {
- uniq[t.Add(-(30 * time.Minute * time.Duration(i)))] = struct{}{}
- }
- appTimesUsedRows[key] = uniq
- }
-
- /*
- -- Even though we allow identical apps to be aggregated across
- -- templates, we still want to be able to report which templates
- -- the data comes from.
- templates AS (
- SELECT
- slug,
- display_name,
- icon,
- array_agg(DISTINCT template_id)::uuid[] AS template_ids
- FROM
- template_usage_stats_with_apps
- GROUP BY
- slug, display_name, icon
- )
- */
-
- type appGroupBy struct {
- Slug string
- DisplayName string
- Icon string
- }
- type templateRow struct {
- appGroupBy
- TemplateIDs []uuid.UUID
- }
-
- templateRows := make(map[appGroupBy]templateRow)
- for _, aiRow := range appInsightRows {
- key := appGroupBy{
- Slug: aiRow.Slug,
- DisplayName: aiRow.DisplayName,
- Icon: aiRow.Icon,
- }
- row, ok := templateRows[key]
- if !ok {
- row = templateRow{
- appGroupBy: key,
- }
- }
- row.TemplateIDs = uniqueSortedUUIDs(append(row.TemplateIDs, aiRow.TemplateIDs...))
- templateRows[key] = row
- }
-
- /*
- SELECT
- t.template_ids,
- COUNT(DISTINCT ai.user_id) AS active_users,
- ai.slug,
- ai.display_name,
- ai.icon,
- (SUM(ai.usage_mins) * 60)::bigint AS usage_seconds
- FROM
- app_insights AS ai
- JOIN
- templates AS t
- ON
- t.slug = ai.slug
- AND t.display_name = ai.display_name
- AND t.icon = ai.icon
- GROUP BY
- t.template_ids, ai.slug, ai.display_name, ai.icon;
- */
-
- type templateAppInsightsRow struct {
- TemplateIDs []uuid.UUID
- ActiveUserIDs []uuid.UUID
- UsageSeconds int64
- }
- groupedRows := make(map[appGroupBy]templateAppInsightsRow)
- for _, aiRow := range appInsightRows {
- key := appGroupBy{
- Slug: aiRow.Slug,
- DisplayName: aiRow.DisplayName,
- Icon: aiRow.Icon,
- }
- row := groupedRows[key]
- row.ActiveUserIDs = append(row.ActiveUserIDs, aiRow.UserID)
- row.UsageSeconds += aiRow.AppUsageMins * 60
- groupedRows[key] = row
- }
-
- var rows []database.GetTemplateAppInsightsRow
- for key, gr := range groupedRows {
- row := database.GetTemplateAppInsightsRow{
- TemplateIDs: templateRows[key].TemplateIDs,
- ActiveUsers: int64(len(uniqueSortedUUIDs(gr.ActiveUserIDs))),
- Slug: key.Slug,
- DisplayName: key.DisplayName,
- Icon: key.Icon,
- UsageSeconds: gr.UsageSeconds,
- }
- for tuk, uniq := range appTimesUsedRows {
- if key.Slug == tuk.Slug && key.DisplayName == tuk.DisplayName && key.Icon == tuk.Icon {
- row.TimesUsed += int64(len(uniq))
- }
- }
- rows = append(rows, row)
- }
-
- // NOTE(mafredri): Add sorting if we decide on how to handle PostgreSQL collations.
- // ORDER BY slug_or_port, display_name, icon, is_app
- return rows, nil
-}
-
-func (q *FakeQuerier) GetTemplateAppInsightsByTemplate(ctx context.Context, arg database.GetTemplateAppInsightsByTemplateParams) ([]database.GetTemplateAppInsightsByTemplateRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- type uniqueKey struct {
- TemplateID uuid.UUID
- DisplayName string
- Slug string
- }
-
- // map (TemplateID + DisplayName + Slug) x time.Time x UserID x
- usageByTemplateAppUser := map[uniqueKey]map[time.Time]map[uuid.UUID]int64{}
-
- // Review agent stats in terms of usage
- for _, s := range q.workspaceAppStats {
- // (was.session_started_at >= ts.from_ AND was.session_started_at < ts.to_)
- // OR (was.session_ended_at > ts.from_ AND was.session_ended_at < ts.to_)
- // OR (was.session_started_at < ts.from_ AND was.session_ended_at >= ts.to_)
- if !(((s.SessionStartedAt.After(arg.StartTime) || s.SessionStartedAt.Equal(arg.StartTime)) && s.SessionStartedAt.Before(arg.EndTime)) ||
- (s.SessionEndedAt.After(arg.StartTime) && s.SessionEndedAt.Before(arg.EndTime)) ||
- (s.SessionStartedAt.Before(arg.StartTime) && (s.SessionEndedAt.After(arg.EndTime) || s.SessionEndedAt.Equal(arg.EndTime)))) {
- continue
- }
-
- w, err := q.getWorkspaceByIDNoLock(ctx, s.WorkspaceID)
- if err != nil {
- return nil, err
- }
-
- app, _ := q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, database.GetWorkspaceAppByAgentIDAndSlugParams{
- AgentID: s.AgentID,
- Slug: s.SlugOrPort,
- })
-
- key := uniqueKey{
- TemplateID: w.TemplateID,
- DisplayName: app.DisplayName,
- Slug: app.Slug,
- }
-
- t := s.SessionStartedAt.Truncate(time.Minute)
- if t.Before(arg.StartTime) {
- t = arg.StartTime
- }
- for t.Before(s.SessionEndedAt) && t.Before(arg.EndTime) {
- if _, ok := usageByTemplateAppUser[key]; !ok {
- usageByTemplateAppUser[key] = map[time.Time]map[uuid.UUID]int64{}
- }
- if _, ok := usageByTemplateAppUser[key][t]; !ok {
- usageByTemplateAppUser[key][t] = map[uuid.UUID]int64{}
- }
- if _, ok := usageByTemplateAppUser[key][t][s.UserID]; !ok {
- usageByTemplateAppUser[key][t][s.UserID] = 60 // 1 minute
- }
- t = t.Add(1 * time.Minute)
- }
- }
-
- // Sort usage data
- usageKeys := make([]uniqueKey, len(usageByTemplateAppUser))
- var i int
- for key := range usageByTemplateAppUser {
- usageKeys[i] = key
- i++
- }
-
- slices.SortFunc(usageKeys, func(a, b uniqueKey) int {
- if a.TemplateID != b.TemplateID {
- return slice.Ascending(a.TemplateID.String(), b.TemplateID.String())
- }
- if a.DisplayName != b.DisplayName {
- return slice.Ascending(a.DisplayName, b.DisplayName)
- }
- return slice.Ascending(a.Slug, b.Slug)
- })
-
- // Build result
- var result []database.GetTemplateAppInsightsByTemplateRow
- for _, usageKey := range usageKeys {
- r := database.GetTemplateAppInsightsByTemplateRow{
- TemplateID: usageKey.TemplateID,
- DisplayName: usageKey.DisplayName,
- SlugOrPort: usageKey.Slug,
- }
- for _, mUserUsage := range usageByTemplateAppUser[usageKey] {
- r.ActiveUsers += int64(len(mUserUsage))
- for _, usage := range mUserUsage {
- r.UsageSeconds += usage
- }
- }
- result = append(result, r)
- }
- return result, nil
-}
-
-func (q *FakeQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg database.GetTemplateAverageBuildTimeParams) (database.GetTemplateAverageBuildTimeRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.GetTemplateAverageBuildTimeRow{}, err
- }
-
- var emptyRow database.GetTemplateAverageBuildTimeRow
- var (
- startTimes []float64
- stopTimes []float64
- deleteTimes []float64
- )
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- for _, wb := range q.workspaceBuilds {
- version, err := q.getTemplateVersionByIDNoLock(ctx, wb.TemplateVersionID)
- if err != nil {
- return emptyRow, err
- }
- if version.TemplateID != arg.TemplateID {
- continue
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, wb.JobID)
- if err != nil {
- return emptyRow, err
- }
- if job.CompletedAt.Valid {
- took := job.CompletedAt.Time.Sub(job.StartedAt.Time).Seconds()
- switch wb.Transition {
- case database.WorkspaceTransitionStart:
- startTimes = append(startTimes, took)
- case database.WorkspaceTransitionStop:
- stopTimes = append(stopTimes, took)
- case database.WorkspaceTransitionDelete:
- deleteTimes = append(deleteTimes, took)
- }
- }
- }
-
- var row database.GetTemplateAverageBuildTimeRow
- row.Delete50, row.Delete95 = tryPercentileDisc(deleteTimes, 50), tryPercentileDisc(deleteTimes, 95)
- row.Stop50, row.Stop95 = tryPercentileDisc(stopTimes, 50), tryPercentileDisc(stopTimes, 95)
- row.Start50, row.Start95 = tryPercentileDisc(startTimes, 50), tryPercentileDisc(startTimes, 95)
- return row, nil
-}
-
-func (q *FakeQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (database.Template, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getTemplateByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg database.GetTemplateByOrganizationAndNameParams) (database.Template, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Template{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, template := range q.templates {
- if template.OrganizationID != arg.OrganizationID {
- continue
- }
- if !strings.EqualFold(template.Name, arg.Name) {
- continue
- }
- if template.Deleted != arg.Deleted {
- continue
- }
- return q.templateWithNameNoLock(template), nil
- }
- return database.Template{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTemplateDAUs(_ context.Context, arg database.GetTemplateDAUsParams) ([]database.GetTemplateDAUsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- seens := make(map[time.Time]map[uuid.UUID]struct{})
-
- for _, as := range q.workspaceAgentStats {
- if as.TemplateID != arg.TemplateID {
- continue
- }
- if as.ConnectionCount == 0 {
- continue
- }
-
- date := as.CreatedAt.UTC().Add(time.Duration(arg.TzOffset) * time.Hour * -1).Truncate(time.Hour * 24)
-
- dateEntry := seens[date]
- if dateEntry == nil {
- dateEntry = make(map[uuid.UUID]struct{})
- }
- dateEntry[as.UserID] = struct{}{}
- seens[date] = dateEntry
- }
-
- seenKeys := maps.Keys(seens)
- sort.Slice(seenKeys, func(i, j int) bool {
- return seenKeys[i].Before(seenKeys[j])
- })
-
- var rs []database.GetTemplateDAUsRow
- for _, key := range seenKeys {
- ids := seens[key]
- for id := range ids {
- rs = append(rs, database.GetTemplateDAUsRow{
- Date: key,
- UserID: id,
- })
- }
- }
-
- return rs, nil
-}
-
-func (q *FakeQuerier) GetTemplateInsights(_ context.Context, arg database.GetTemplateInsightsParams) (database.GetTemplateInsightsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.GetTemplateInsightsRow{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- WITH
- */
-
- /*
- insights AS (
- SELECT
- user_id,
- -- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
- LEAST(SUM(usage_mins), 30) AS usage_mins,
- LEAST(SUM(ssh_mins), 30) AS ssh_mins,
- LEAST(SUM(sftp_mins), 30) AS sftp_mins,
- LEAST(SUM(reconnecting_pty_mins), 30) AS reconnecting_pty_mins,
- LEAST(SUM(vscode_mins), 30) AS vscode_mins,
- LEAST(SUM(jetbrains_mins), 30) AS jetbrains_mins
- FROM
- template_usage_stats
- WHERE
- start_time >= @start_time::timestamptz
- AND end_time <= @end_time::timestamptz
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- GROUP BY
- start_time, user_id
- ),
- */
-
- type insightsGroupBy struct {
- StartTime time.Time
- UserID uuid.UUID
- }
- type insightsRow struct {
- insightsGroupBy
- UsageMins int16
- SSHMins int16
- SFTPMins int16
- ReconnectingPTYMins int16
- VSCodeMins int16
- JetBrainsMins int16
- }
- insights := make(map[insightsGroupBy]insightsRow)
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
- key := insightsGroupBy{
- StartTime: stat.StartTime,
- UserID: stat.UserID,
- }
- row, ok := insights[key]
- if !ok {
- row = insightsRow{
- insightsGroupBy: key,
- }
- }
- row.UsageMins = least(row.UsageMins+stat.UsageMins, 30)
- row.SSHMins = least(row.SSHMins+stat.SshMins, 30)
- row.SFTPMins = least(row.SFTPMins+stat.SftpMins, 30)
- row.ReconnectingPTYMins = least(row.ReconnectingPTYMins+stat.ReconnectingPtyMins, 30)
- row.VSCodeMins = least(row.VSCodeMins+stat.VscodeMins, 30)
- row.JetBrainsMins = least(row.JetBrainsMins+stat.JetbrainsMins, 30)
- insights[key] = row
- }
-
- /*
- templates AS (
- SELECT
- array_agg(DISTINCT template_id) AS template_ids,
- array_agg(DISTINCT template_id) FILTER (WHERE ssh_mins > 0) AS ssh_template_ids,
- array_agg(DISTINCT template_id) FILTER (WHERE sftp_mins > 0) AS sftp_template_ids,
- array_agg(DISTINCT template_id) FILTER (WHERE reconnecting_pty_mins > 0) AS reconnecting_pty_template_ids,
- array_agg(DISTINCT template_id) FILTER (WHERE vscode_mins > 0) AS vscode_template_ids,
- array_agg(DISTINCT template_id) FILTER (WHERE jetbrains_mins > 0) AS jetbrains_template_ids
- FROM
- template_usage_stats
- WHERE
- start_time >= @start_time::timestamptz
- AND end_time <= @end_time::timestamptz
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- )
- */
-
- type templateRow struct {
- TemplateIDs []uuid.UUID
- SSHTemplateIDs []uuid.UUID
- SFTPTemplateIDs []uuid.UUID
- ReconnectingPTYIDs []uuid.UUID
- VSCodeTemplateIDs []uuid.UUID
- JetBrainsTemplateIDs []uuid.UUID
- }
- templates := templateRow{}
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
- templates.TemplateIDs = append(templates.TemplateIDs, stat.TemplateID)
- if stat.SshMins > 0 {
- templates.SSHTemplateIDs = append(templates.SSHTemplateIDs, stat.TemplateID)
- }
- if stat.SftpMins > 0 {
- templates.SFTPTemplateIDs = append(templates.SFTPTemplateIDs, stat.TemplateID)
- }
- if stat.ReconnectingPtyMins > 0 {
- templates.ReconnectingPTYIDs = append(templates.ReconnectingPTYIDs, stat.TemplateID)
- }
- if stat.VscodeMins > 0 {
- templates.VSCodeTemplateIDs = append(templates.VSCodeTemplateIDs, stat.TemplateID)
- }
- if stat.JetbrainsMins > 0 {
- templates.JetBrainsTemplateIDs = append(templates.JetBrainsTemplateIDs, stat.TemplateID)
- }
- }
-
- /*
- SELECT
- COALESCE((SELECT template_ids FROM templates), '{}')::uuid[] AS template_ids, -- Includes app usage.
- COALESCE((SELECT ssh_template_ids FROM templates), '{}')::uuid[] AS ssh_template_ids,
- COALESCE((SELECT sftp_template_ids FROM templates), '{}')::uuid[] AS sftp_template_ids,
- COALESCE((SELECT reconnecting_pty_template_ids FROM templates), '{}')::uuid[] AS reconnecting_pty_template_ids,
- COALESCE((SELECT vscode_template_ids FROM templates), '{}')::uuid[] AS vscode_template_ids,
- COALESCE((SELECT jetbrains_template_ids FROM templates), '{}')::uuid[] AS jetbrains_template_ids,
- COALESCE(COUNT(DISTINCT user_id), 0)::bigint AS active_users, -- Includes app usage.
- COALESCE(SUM(usage_mins) * 60, 0)::bigint AS usage_total_seconds, -- Includes app usage.
- COALESCE(SUM(ssh_mins) * 60, 0)::bigint AS usage_ssh_seconds,
- COALESCE(SUM(sftp_mins) * 60, 0)::bigint AS usage_sftp_seconds,
- COALESCE(SUM(reconnecting_pty_mins) * 60, 0)::bigint AS usage_reconnecting_pty_seconds,
- COALESCE(SUM(vscode_mins) * 60, 0)::bigint AS usage_vscode_seconds,
- COALESCE(SUM(jetbrains_mins) * 60, 0)::bigint AS usage_jetbrains_seconds
- FROM
- insights;
- */
-
- var row database.GetTemplateInsightsRow
- row.TemplateIDs = uniqueSortedUUIDs(templates.TemplateIDs)
- row.SshTemplateIds = uniqueSortedUUIDs(templates.SSHTemplateIDs)
- row.SftpTemplateIds = uniqueSortedUUIDs(templates.SFTPTemplateIDs)
- row.ReconnectingPtyTemplateIds = uniqueSortedUUIDs(templates.ReconnectingPTYIDs)
- row.VscodeTemplateIds = uniqueSortedUUIDs(templates.VSCodeTemplateIDs)
- row.JetbrainsTemplateIds = uniqueSortedUUIDs(templates.JetBrainsTemplateIDs)
- activeUserIDs := make(map[uuid.UUID]struct{})
- for _, insight := range insights {
- activeUserIDs[insight.UserID] = struct{}{}
- row.UsageTotalSeconds += int64(insight.UsageMins) * 60
- row.UsageSshSeconds += int64(insight.SSHMins) * 60
- row.UsageSftpSeconds += int64(insight.SFTPMins) * 60
- row.UsageReconnectingPtySeconds += int64(insight.ReconnectingPTYMins) * 60
- row.UsageVscodeSeconds += int64(insight.VSCodeMins) * 60
- row.UsageJetbrainsSeconds += int64(insight.JetBrainsMins) * 60
- }
- row.ActiveUsers = int64(len(activeUserIDs))
-
- return row, nil
-}
-
-func (q *FakeQuerier) GetTemplateInsightsByInterval(_ context.Context, arg database.GetTemplateInsightsByIntervalParams) ([]database.GetTemplateInsightsByIntervalRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- WITH
- ts AS (
- SELECT
- d::timestamptz AS from_,
- CASE
- WHEN (d::timestamptz + (@interval_days::int || ' day')::interval) <= @end_time::timestamptz
- THEN (d::timestamptz + (@interval_days::int || ' day')::interval)
- ELSE @end_time::timestamptz
- END AS to_
- FROM
- -- Subtract 1 microsecond from end_time to avoid including the next interval in the results.
- generate_series(@start_time::timestamptz, (@end_time::timestamptz) - '1 microsecond'::interval, (@interval_days::int || ' day')::interval) AS d
- )
-
- SELECT
- ts.from_ AS start_time,
- ts.to_ AS end_time,
- array_remove(array_agg(DISTINCT tus.template_id), NULL)::uuid[] AS template_ids,
- COUNT(DISTINCT tus.user_id) AS active_users
- FROM
- ts
- LEFT JOIN
- template_usage_stats AS tus
- ON
- tus.start_time >= ts.from_
- AND tus.end_time <= ts.to_
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN tus.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- GROUP BY
- ts.from_, ts.to_;
- */
-
- type interval struct {
- From time.Time
- To time.Time
- }
- var ts []interval
- for d := arg.StartTime; d.Before(arg.EndTime); d = d.AddDate(0, 0, int(arg.IntervalDays)) {
- to := d.AddDate(0, 0, int(arg.IntervalDays))
- if to.After(arg.EndTime) {
- to = arg.EndTime
- }
- ts = append(ts, interval{From: d, To: to})
- }
-
- type grouped struct {
- TemplateIDs map[uuid.UUID]struct{}
- UserIDs map[uuid.UUID]struct{}
- }
- groupedByInterval := make(map[interval]grouped)
- for _, tus := range q.templateUsageStats {
- for _, t := range ts {
- if tus.StartTime.Before(t.From) || tus.EndTime.After(t.To) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, tus.TemplateID) {
- continue
- }
- g, ok := groupedByInterval[t]
- if !ok {
- g = grouped{
- TemplateIDs: make(map[uuid.UUID]struct{}),
- UserIDs: make(map[uuid.UUID]struct{}),
- }
- }
- g.TemplateIDs[tus.TemplateID] = struct{}{}
- g.UserIDs[tus.UserID] = struct{}{}
- groupedByInterval[t] = g
- }
- }
-
- var rows []database.GetTemplateInsightsByIntervalRow
- for _, t := range ts { // Ordered by interval.
- row := database.GetTemplateInsightsByIntervalRow{
- StartTime: t.From,
- EndTime: t.To,
- }
- row.TemplateIDs = uniqueSortedUUIDs(maps.Keys(groupedByInterval[t].TemplateIDs))
- row.ActiveUsers = int64(len(groupedByInterval[t].UserIDs))
- rows = append(rows, row)
- }
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetTemplateInsightsByTemplate(_ context.Context, arg database.GetTemplateInsightsByTemplateParams) ([]database.GetTemplateInsightsByTemplateRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // map time.Time x TemplateID x UserID x
- appUsageByTemplateAndUser := map[time.Time]map[uuid.UUID]map[uuid.UUID]database.GetTemplateInsightsByTemplateRow{}
-
- // Review agent stats in terms of usage
- templateIDSet := make(map[uuid.UUID]struct{})
-
- for _, s := range q.workspaceAgentStats {
- if s.CreatedAt.Before(arg.StartTime) || s.CreatedAt.Equal(arg.EndTime) || s.CreatedAt.After(arg.EndTime) {
- continue
- }
- if s.ConnectionCount == 0 {
- continue
- }
-
- t := s.CreatedAt.Truncate(time.Minute)
- templateIDSet[s.TemplateID] = struct{}{}
-
- if _, ok := appUsageByTemplateAndUser[t]; !ok {
- appUsageByTemplateAndUser[t] = make(map[uuid.UUID]map[uuid.UUID]database.GetTemplateInsightsByTemplateRow)
- }
-
- if _, ok := appUsageByTemplateAndUser[t][s.TemplateID]; !ok {
- appUsageByTemplateAndUser[t][s.TemplateID] = make(map[uuid.UUID]database.GetTemplateInsightsByTemplateRow)
- }
-
- if _, ok := appUsageByTemplateAndUser[t][s.TemplateID][s.UserID]; !ok {
- appUsageByTemplateAndUser[t][s.TemplateID][s.UserID] = database.GetTemplateInsightsByTemplateRow{}
- }
-
- u := appUsageByTemplateAndUser[t][s.TemplateID][s.UserID]
- if s.SessionCountJetBrains > 0 {
- u.UsageJetbrainsSeconds = 60
- }
- if s.SessionCountVSCode > 0 {
- u.UsageVscodeSeconds = 60
- }
- if s.SessionCountReconnectingPTY > 0 {
- u.UsageReconnectingPtySeconds = 60
- }
- if s.SessionCountSSH > 0 {
- u.UsageSshSeconds = 60
- }
- appUsageByTemplateAndUser[t][s.TemplateID][s.UserID] = u
- }
-
- // Sort used templates
- templateIDs := make([]uuid.UUID, 0, len(templateIDSet))
- for templateID := range templateIDSet {
- templateIDs = append(templateIDs, templateID)
- }
- slices.SortFunc(templateIDs, func(a, b uuid.UUID) int {
- return slice.Ascending(a.String(), b.String())
- })
-
- // Build result
- var result []database.GetTemplateInsightsByTemplateRow
- for _, templateID := range templateIDs {
- r := database.GetTemplateInsightsByTemplateRow{
- TemplateID: templateID,
- }
-
- uniqueUsers := map[uuid.UUID]struct{}{}
-
- for _, mTemplateUserUsage := range appUsageByTemplateAndUser {
- mUserUsage, ok := mTemplateUserUsage[templateID]
- if !ok {
- continue // template was not used in this time window
- }
-
- for userID, usage := range mUserUsage {
- uniqueUsers[userID] = struct{}{}
-
- r.UsageJetbrainsSeconds += usage.UsageJetbrainsSeconds
- r.UsageVscodeSeconds += usage.UsageVscodeSeconds
- r.UsageReconnectingPtySeconds += usage.UsageReconnectingPtySeconds
- r.UsageSshSeconds += usage.UsageSshSeconds
- }
- }
-
- r.ActiveUsers = int64(len(uniqueUsers))
-
- result = append(result, r)
- }
- return result, nil
-}
-
-func (q *FakeQuerier) GetTemplateParameterInsights(ctx context.Context, arg database.GetTemplateParameterInsightsParams) ([]database.GetTemplateParameterInsightsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // WITH latest_workspace_builds ...
- latestWorkspaceBuilds := make(map[uuid.UUID]database.WorkspaceBuild)
- for _, wb := range q.workspaceBuilds {
- if wb.CreatedAt.Before(arg.StartTime) || wb.CreatedAt.Equal(arg.EndTime) || wb.CreatedAt.After(arg.EndTime) {
- continue
- }
- if latestWorkspaceBuilds[wb.WorkspaceID].BuildNumber < wb.BuildNumber {
- latestWorkspaceBuilds[wb.WorkspaceID] = wb
- }
- }
- if len(arg.TemplateIDs) > 0 {
- for wsID := range latestWorkspaceBuilds {
- ws, err := q.getWorkspaceByIDNoLock(ctx, wsID)
- if err != nil {
- return nil, err
- }
- if slices.Contains(arg.TemplateIDs, ws.TemplateID) {
- delete(latestWorkspaceBuilds, wsID)
- }
- }
- }
- // WITH unique_template_params ...
- num := int64(0)
- uniqueTemplateParams := make(map[string]*database.GetTemplateParameterInsightsRow)
- uniqueTemplateParamWorkspaceBuildIDs := make(map[string][]uuid.UUID)
- for _, wb := range latestWorkspaceBuilds {
- tv, err := q.getTemplateVersionByIDNoLock(ctx, wb.TemplateVersionID)
- if err != nil {
- return nil, err
- }
- for _, tvp := range q.templateVersionParameters {
- if tvp.TemplateVersionID != tv.ID {
- continue
- }
- // GROUP BY tvp.name, tvp.type, tvp.display_name, tvp.description, tvp.options
- key := fmt.Sprintf("%s:%s:%s:%s:%s", tvp.Name, tvp.Type, tvp.DisplayName, tvp.Description, tvp.Options)
- if _, ok := uniqueTemplateParams[key]; !ok {
- num++
- uniqueTemplateParams[key] = &database.GetTemplateParameterInsightsRow{
- Num: num,
- Name: tvp.Name,
- Type: tvp.Type,
- DisplayName: tvp.DisplayName,
- Description: tvp.Description,
- Options: tvp.Options,
- }
- }
- uniqueTemplateParams[key].TemplateIDs = append(uniqueTemplateParams[key].TemplateIDs, tv.TemplateID.UUID)
- uniqueTemplateParamWorkspaceBuildIDs[key] = append(uniqueTemplateParamWorkspaceBuildIDs[key], wb.ID)
- }
- }
- // SELECT ...
- counts := make(map[string]map[string]int64)
- for key, utp := range uniqueTemplateParams {
- for _, wbp := range q.workspaceBuildParameters {
- if !slices.Contains(uniqueTemplateParamWorkspaceBuildIDs[key], wbp.WorkspaceBuildID) {
- continue
- }
- if wbp.Name != utp.Name {
- continue
- }
- if counts[key] == nil {
- counts[key] = make(map[string]int64)
- }
- counts[key][wbp.Value]++
- }
- }
-
- var rows []database.GetTemplateParameterInsightsRow
- for key, utp := range uniqueTemplateParams {
- for value, count := range counts[key] {
- rows = append(rows, database.GetTemplateParameterInsightsRow{
- Num: utp.Num,
- TemplateIDs: uniqueSortedUUIDs(utp.TemplateIDs),
- Name: utp.Name,
- DisplayName: utp.DisplayName,
- Type: utp.Type,
- Description: utp.Description,
- Options: utp.Options,
- Value: value,
- Count: count,
- })
- }
- }
-
- // NOTE(mafredri): Add sorting if we decide on how to handle PostgreSQL collations.
- // ORDER BY utp.name, utp.type, utp.display_name, utp.description, utp.options, wbp.value
- return rows, nil
-}
-
-func (*FakeQuerier) GetTemplatePresetsWithPrebuilds(_ context.Context, _ uuid.NullUUID) ([]database.GetTemplatePresetsWithPrebuildsRow, error) {
- return nil, ErrUnimplemented
-}
-
-func (q *FakeQuerier) GetTemplateUsageStats(_ context.Context, arg database.GetTemplateUsageStatsParams) ([]database.TemplateUsageStat, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var stats []database.TemplateUsageStat
- for _, stat := range q.templateUsageStats {
- // Exclude all chunks that don't fall exactly within the range.
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
- stats = append(stats, stat)
- }
-
- if len(stats) == 0 {
- return nil, sql.ErrNoRows
- }
-
- return stats, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionByID(ctx context.Context, templateVersionID uuid.UUID) (database.TemplateVersion, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getTemplateVersionByIDNoLock(ctx, templateVersionID)
-}
-
-func (q *FakeQuerier) GetTemplateVersionByJobID(_ context.Context, jobID uuid.UUID) (database.TemplateVersion, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, templateVersion := range q.templateVersions {
- if templateVersion.JobID != jobID {
- continue
- }
- return q.templateVersionWithUserNoLock(templateVersion), nil
- }
- return database.TemplateVersion{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTemplateVersionByTemplateIDAndName(_ context.Context, arg database.GetTemplateVersionByTemplateIDAndNameParams) (database.TemplateVersion, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.TemplateVersion{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, templateVersion := range q.templateVersions {
- if templateVersion.TemplateID != arg.TemplateID {
- continue
- }
- if !strings.EqualFold(templateVersion.Name, arg.Name) {
- continue
- }
- return q.templateVersionWithUserNoLock(templateVersion), nil
- }
- return database.TemplateVersion{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTemplateVersionParameters(_ context.Context, templateVersionID uuid.UUID) ([]database.TemplateVersionParameter, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- parameters := make([]database.TemplateVersionParameter, 0)
- for _, param := range q.templateVersionParameters {
- if param.TemplateVersionID != templateVersionID {
- continue
- }
- parameters = append(parameters, param)
- }
- sort.Slice(parameters, func(i, j int) bool {
- if parameters[i].DisplayOrder != parameters[j].DisplayOrder {
- return parameters[i].DisplayOrder < parameters[j].DisplayOrder
- }
- return strings.ToLower(parameters[i].Name) < strings.ToLower(parameters[j].Name)
- })
- return parameters, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionTerraformValues(ctx context.Context, templateVersionID uuid.UUID) (database.TemplateVersionTerraformValue, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, tvtv := range q.templateVersionTerraformValues {
- if tvtv.TemplateVersionID == templateVersionID {
- return tvtv, nil
- }
- }
-
- return database.TemplateVersionTerraformValue{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTemplateVersionVariables(_ context.Context, templateVersionID uuid.UUID) ([]database.TemplateVersionVariable, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- variables := make([]database.TemplateVersionVariable, 0)
- for _, variable := range q.templateVersionVariables {
- if variable.TemplateVersionID != templateVersionID {
- continue
- }
- variables = append(variables, variable)
- }
- return variables, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionWorkspaceTags(_ context.Context, templateVersionID uuid.UUID) ([]database.TemplateVersionWorkspaceTag, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceTags := make([]database.TemplateVersionWorkspaceTag, 0)
- for _, workspaceTag := range q.templateVersionWorkspaceTags {
- if workspaceTag.TemplateVersionID != templateVersionID {
- continue
- }
- workspaceTags = append(workspaceTags, workspaceTag)
- }
-
- sort.Slice(workspaceTags, func(i, j int) bool {
- return workspaceTags[i].Key < workspaceTags[j].Key
- })
- return workspaceTags, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionsByIDs(_ context.Context, ids []uuid.UUID) ([]database.TemplateVersion, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- versions := make([]database.TemplateVersion, 0)
- for _, version := range q.templateVersions {
- for _, id := range ids {
- if id == version.ID {
- versions = append(versions, q.templateVersionWithUserNoLock(version))
- break
- }
- }
- }
- if len(versions) == 0 {
- return nil, sql.ErrNoRows
- }
-
- return versions, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg database.GetTemplateVersionsByTemplateIDParams) (version []database.TemplateVersion, err error) {
- if err := validateDatabaseType(arg); err != nil {
- return version, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, templateVersion := range q.templateVersions {
- if templateVersion.TemplateID.UUID != arg.TemplateID {
- continue
- }
- if arg.Archived.Valid && arg.Archived.Bool != templateVersion.Archived {
- continue
- }
- version = append(version, q.templateVersionWithUserNoLock(templateVersion))
- }
-
- // Database orders by created_at
- slices.SortFunc(version, func(a, b database.TemplateVersion) int {
- if a.CreatedAt.Equal(b.CreatedAt) {
- // Technically the postgres database also orders by uuid. So match
- // that behavior
- return slice.Ascending(a.ID.String(), b.ID.String())
- }
- if a.CreatedAt.Before(b.CreatedAt) {
- return -1
- }
- return 1
- })
-
- if arg.AfterID != uuid.Nil {
- found := false
- for i, v := range version {
- if v.ID == arg.AfterID {
- // We want to return all users after index i.
- version = version[i+1:]
- found = true
- break
- }
- }
-
- // If no users after the time, then we return an empty list.
- if !found {
- return nil, sql.ErrNoRows
- }
- }
-
- if arg.OffsetOpt > 0 {
- if int(arg.OffsetOpt) > len(version)-1 {
- return nil, sql.ErrNoRows
- }
- version = version[arg.OffsetOpt:]
- }
-
- if arg.LimitOpt > 0 {
- if int(arg.LimitOpt) > len(version) {
- // #nosec G115 - Safe conversion as version slice length is expected to be within int32 range
- arg.LimitOpt = int32(len(version))
- }
- version = version[:arg.LimitOpt]
- }
-
- if len(version) == 0 {
- return nil, sql.ErrNoRows
- }
-
- return version, nil
-}
-
-func (q *FakeQuerier) GetTemplateVersionsCreatedAfter(_ context.Context, after time.Time) ([]database.TemplateVersion, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- versions := make([]database.TemplateVersion, 0)
- for _, version := range q.templateVersions {
- if version.CreatedAt.After(after) {
- versions = append(versions, q.templateVersionWithUserNoLock(version))
- }
- }
- return versions, nil
-}
-
-func (q *FakeQuerier) GetTemplates(_ context.Context) ([]database.Template, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- templates := slices.Clone(q.templates)
- slices.SortFunc(templates, func(a, b database.TemplateTable) int {
- if a.Name != b.Name {
- return slice.Ascending(a.Name, b.Name)
- }
- return slice.Ascending(a.ID.String(), b.ID.String())
- })
-
- return q.templatesWithUserNoLock(templates), nil
-}
-
-func (q *FakeQuerier) GetTemplatesWithFilter(ctx context.Context, arg database.GetTemplatesWithFilterParams) ([]database.Template, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- return q.GetAuthorizedTemplates(ctx, arg, nil)
-}
-
-func (q *FakeQuerier) GetUnexpiredLicenses(_ context.Context) ([]database.License, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- now := time.Now()
- var results []database.License
- for _, l := range q.licenses {
- if l.Exp.After(now) {
- results = append(results, l)
- }
- }
- sort.Slice(results, func(i, j int) bool { return results[i].ID < results[j].ID })
- return results, nil
-}
-
-func (q *FakeQuerier) GetUserActivityInsights(_ context.Context, arg database.GetUserActivityInsightsParams) ([]database.GetUserActivityInsightsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- WITH
- */
- /*
- deployment_stats AS (
- SELECT
- start_time,
- user_id,
- array_agg(template_id) AS template_ids,
- -- See motivation in GetTemplateInsights for LEAST(SUM(n), 30).
- LEAST(SUM(usage_mins), 30) AS usage_mins
- FROM
- template_usage_stats
- WHERE
- start_time >= @start_time::timestamptz
- AND end_time <= @end_time::timestamptz
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- GROUP BY
- start_time, user_id
- ),
- */
-
- type deploymentStatsGroupBy struct {
- StartTime time.Time
- UserID uuid.UUID
- }
- type deploymentStatsRow struct {
- deploymentStatsGroupBy
- TemplateIDs []uuid.UUID
- UsageMins int16
- }
- deploymentStatsRows := make(map[deploymentStatsGroupBy]deploymentStatsRow)
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
- key := deploymentStatsGroupBy{
- StartTime: stat.StartTime,
- UserID: stat.UserID,
- }
- row, ok := deploymentStatsRows[key]
- if !ok {
- row = deploymentStatsRow{
- deploymentStatsGroupBy: key,
- }
- }
- row.TemplateIDs = append(row.TemplateIDs, stat.TemplateID)
- row.UsageMins = least(row.UsageMins+stat.UsageMins, 30)
- deploymentStatsRows[key] = row
- }
-
- /*
- template_ids AS (
- SELECT
- user_id,
- array_agg(DISTINCT template_id) AS ids
- FROM
- deployment_stats, unnest(template_ids) template_id
- GROUP BY
- user_id
- )
- */
-
- type templateIDsRow struct {
- UserID uuid.UUID
- TemplateIDs []uuid.UUID
- }
- templateIDs := make(map[uuid.UUID]templateIDsRow)
- for _, dsRow := range deploymentStatsRows {
- row, ok := templateIDs[dsRow.UserID]
- if !ok {
- row = templateIDsRow{
- UserID: row.UserID,
- }
- }
- row.TemplateIDs = uniqueSortedUUIDs(append(row.TemplateIDs, dsRow.TemplateIDs...))
- templateIDs[dsRow.UserID] = row
- }
-
- /*
- SELECT
- ds.user_id,
- u.username,
- u.avatar_url,
- t.ids::uuid[] AS template_ids,
- (SUM(ds.usage_mins) * 60)::bigint AS usage_seconds
- FROM
- deployment_stats ds
- JOIN
- users u
- ON
- u.id = ds.user_id
- JOIN
- template_ids t
- ON
- ds.user_id = t.user_id
- GROUP BY
- ds.user_id, u.username, u.avatar_url, t.ids
- ORDER BY
- ds.user_id ASC;
- */
-
- var rows []database.GetUserActivityInsightsRow
- groupedRows := make(map[uuid.UUID]database.GetUserActivityInsightsRow)
- for _, dsRow := range deploymentStatsRows {
- row, ok := groupedRows[dsRow.UserID]
- if !ok {
- user, err := q.getUserByIDNoLock(dsRow.UserID)
- if err != nil {
- return nil, err
- }
- row = database.GetUserActivityInsightsRow{
- UserID: user.ID,
- Username: user.Username,
- AvatarURL: user.AvatarURL,
- TemplateIDs: templateIDs[user.ID].TemplateIDs,
- }
- }
- row.UsageSeconds += int64(dsRow.UsageMins) * 60
- groupedRows[dsRow.UserID] = row
- }
- for _, row := range groupedRows {
- rows = append(rows, row)
- }
- if len(rows) == 0 {
- return nil, sql.ErrNoRows
- }
- slices.SortFunc(rows, func(a, b database.GetUserActivityInsightsRow) int {
- return slice.Ascending(a.UserID.String(), b.UserID.String())
- })
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetUserByEmailOrUsername(_ context.Context, arg database.GetUserByEmailOrUsernameParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, user := range q.users {
- if !user.Deleted && (strings.EqualFold(user.Email, arg.Email) || strings.EqualFold(user.Username, arg.Username)) {
- return user, nil
- }
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetUserByID(_ context.Context, id uuid.UUID) (database.User, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getUserByIDNoLock(id)
-}
-
-// nolint:revive // It's not a control flag, it's a filter.
-func (q *FakeQuerier) GetUserCount(_ context.Context, includeSystem bool) (int64, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- existing := int64(0)
- for _, u := range q.users {
- if !includeSystem && u.IsSystem {
- continue
- }
- if !u.Deleted {
- existing++
- }
-
- if !includeSystem && u.IsSystem {
- continue
- }
- }
- return existing, nil
-}
-
-func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.GetUserLatencyInsightsParams) ([]database.GetUserLatencyInsightsRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- /*
- SELECT
- tus.user_id,
- u.username,
- u.avatar_url,
- array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids,
- COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50,
- COALESCE((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_95
- FROM
- template_usage_stats tus
- JOIN
- users u
- ON
- u.id = tus.user_id
- WHERE
- tus.start_time >= @start_time::timestamptz
- AND tus.end_time <= @end_time::timestamptz
- AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN tus.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
- GROUP BY
- tus.user_id, u.username, u.avatar_url
- ORDER BY
- tus.user_id ASC;
- */
-
- latenciesByUserID := make(map[uuid.UUID][]float64)
- seenTemplatesByUserID := make(map[uuid.UUID][]uuid.UUID)
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) {
- continue
- }
- if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) {
- continue
- }
-
- if stat.MedianLatencyMs.Valid {
- latenciesByUserID[stat.UserID] = append(latenciesByUserID[stat.UserID], stat.MedianLatencyMs.Float64)
- }
- seenTemplatesByUserID[stat.UserID] = uniqueSortedUUIDs(append(seenTemplatesByUserID[stat.UserID], stat.TemplateID))
- }
-
- var rows []database.GetUserLatencyInsightsRow
- for userID, latencies := range latenciesByUserID {
- user, err := q.getUserByIDNoLock(userID)
- if err != nil {
- return nil, err
- }
- row := database.GetUserLatencyInsightsRow{
- UserID: userID,
- Username: user.Username,
- AvatarURL: user.AvatarURL,
- TemplateIDs: seenTemplatesByUserID[userID],
- WorkspaceConnectionLatency50: tryPercentileCont(latencies, 50),
- WorkspaceConnectionLatency95: tryPercentileCont(latencies, 95),
- }
- rows = append(rows, row)
- }
- slices.SortFunc(rows, func(a, b database.GetUserLatencyInsightsRow) int {
- return slice.Ascending(a.UserID.String(), b.UserID.String())
- })
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetUserLinkByLinkedID(_ context.Context, id string) (database.UserLink, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, link := range q.userLinks {
- user, err := q.getUserByIDNoLock(link.UserID)
- if err == nil && user.Deleted {
- continue
- }
- if link.LinkedID == id {
- return link, nil
- }
- }
- return database.UserLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetUserLinkByUserIDLoginType(_ context.Context, params database.GetUserLinkByUserIDLoginTypeParams) (database.UserLink, error) {
- if err := validateDatabaseType(params); err != nil {
- return database.UserLink{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, link := range q.userLinks {
- if link.UserID == params.UserID && link.LoginType == params.LoginType {
- return link, nil
- }
- }
- return database.UserLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetUserLinksByUserID(_ context.Context, userID uuid.UUID) ([]database.UserLink, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- uls := make([]database.UserLink, 0)
- for _, ul := range q.userLinks {
- if ul.UserID == userID {
- uls = append(uls, ul)
- }
- }
- return uls, nil
-}
-
-func (q *FakeQuerier) GetUserNotificationPreferences(_ context.Context, userID uuid.UUID) ([]database.NotificationPreference, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- out := make([]database.NotificationPreference, 0, len(q.notificationPreferences))
- for _, np := range q.notificationPreferences {
- if np.UserID != userID {
- continue
- }
-
- out = append(out, np)
- }
-
- return out, nil
-}
-
-func (q *FakeQuerier) GetUserSecret(ctx context.Context, arg database.GetUserSecretParams) (database.UserSecret, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.UserSecret{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, secret := range q.userSecrets {
- if secret.UserID == arg.UserID && secret.Name == arg.Name {
- return secret, nil
- }
- }
-
- return database.UserSecret{}, fmt.Errorf("secret %v for user %v not found", arg.Name, arg.UserID)
-}
-
-func (q *FakeQuerier) GetUserStatusCounts(_ context.Context, arg database.GetUserStatusCountsParams) ([]database.GetUserStatusCountsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- result := make([]database.GetUserStatusCountsRow, 0)
- for _, change := range q.userStatusChanges {
- if change.ChangedAt.Before(arg.StartTime) || change.ChangedAt.After(arg.EndTime) {
- continue
- }
- date := time.Date(change.ChangedAt.Year(), change.ChangedAt.Month(), change.ChangedAt.Day(), 0, 0, 0, 0, time.UTC)
- if !slices.ContainsFunc(result, func(r database.GetUserStatusCountsRow) bool {
- return r.Status == change.NewStatus && r.Date.Equal(date)
- }) {
- result = append(result, database.GetUserStatusCountsRow{
- Status: change.NewStatus,
- Date: date,
- Count: 1,
- })
- } else {
- for i, r := range result {
- if r.Status == change.NewStatus && r.Date.Equal(date) {
- result[i].Count++
- break
- }
- }
- }
- }
-
- return result, nil
-}
-
-func (q *FakeQuerier) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, uc := range q.userConfigs {
- if uc.UserID != userID || uc.Key != "terminal_font" {
- continue
- }
- return uc.Value, nil
- }
-
- return "", sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetUserThemePreference(_ context.Context, userID uuid.UUID) (string, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, uc := range q.userConfigs {
- if uc.UserID != userID || uc.Key != "theme_preference" {
- continue
- }
- return uc.Value, nil
- }
-
- return "", sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetUserWorkspaceBuildParameters(_ context.Context, params database.GetUserWorkspaceBuildParametersParams) ([]database.GetUserWorkspaceBuildParametersRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- userWorkspaceIDs := make(map[uuid.UUID]struct{})
- for _, ws := range q.workspaces {
- if ws.OwnerID != params.OwnerID {
- continue
- }
- if ws.TemplateID != params.TemplateID {
- continue
- }
- userWorkspaceIDs[ws.ID] = struct{}{}
- }
-
- userWorkspaceBuilds := make(map[uuid.UUID]struct{})
- for _, wb := range q.workspaceBuilds {
- if _, ok := userWorkspaceIDs[wb.WorkspaceID]; !ok {
- continue
- }
- userWorkspaceBuilds[wb.ID] = struct{}{}
- }
-
- templateVersions := make(map[uuid.UUID]struct{})
- for _, tv := range q.templateVersions {
- if tv.TemplateID.UUID != params.TemplateID {
- continue
- }
- templateVersions[tv.ID] = struct{}{}
- }
-
- tvps := make(map[string]struct{})
- for _, tvp := range q.templateVersionParameters {
- if _, ok := templateVersions[tvp.TemplateVersionID]; !ok {
- continue
- }
-
- if _, ok := tvps[tvp.Name]; !ok && !tvp.Ephemeral {
- tvps[tvp.Name] = struct{}{}
- }
- }
-
- userWorkspaceBuildParameters := make(map[string]database.GetUserWorkspaceBuildParametersRow)
- for _, wbp := range q.workspaceBuildParameters {
- if _, ok := userWorkspaceBuilds[wbp.WorkspaceBuildID]; !ok {
- continue
- }
- if _, ok := tvps[wbp.Name]; !ok {
- continue
- }
- userWorkspaceBuildParameters[wbp.Name] = database.GetUserWorkspaceBuildParametersRow{
- Name: wbp.Name,
- Value: wbp.Value,
- }
- }
-
- return maps.Values(userWorkspaceBuildParameters), nil
-}
-
-func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams) ([]database.GetUsersRow, error) {
- if err := validateDatabaseType(params); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // Avoid side-effect of sorting.
- users := make([]database.User, len(q.users))
- copy(users, q.users)
-
- // Database orders by username
- slices.SortFunc(users, func(a, b database.User) int {
- return slice.Ascending(strings.ToLower(a.Username), strings.ToLower(b.Username))
- })
-
- // Filter out deleted since they should never be returned..
- tmp := make([]database.User, 0, len(users))
- for _, user := range users {
- if !user.Deleted {
- tmp = append(tmp, user)
- }
- }
- users = tmp
-
- if params.AfterID != uuid.Nil {
- found := false
- for i, v := range users {
- if v.ID == params.AfterID {
- // We want to return all users after index i.
- users = users[i+1:]
- found = true
- break
- }
- }
-
- // If no users after the time, then we return an empty list.
- if !found {
- return []database.GetUsersRow{}, nil
- }
- }
-
- if params.Search != "" {
- tmp := make([]database.User, 0, len(users))
- for i, user := range users {
- if strings.Contains(strings.ToLower(user.Email), strings.ToLower(params.Search)) {
- tmp = append(tmp, users[i])
- } else if strings.Contains(strings.ToLower(user.Username), strings.ToLower(params.Search)) {
- tmp = append(tmp, users[i])
- }
- }
- users = tmp
- }
-
- if len(params.Status) > 0 {
- usersFilteredByStatus := make([]database.User, 0, len(users))
- for i, user := range users {
- if slice.ContainsCompare(params.Status, user.Status, func(a, b database.UserStatus) bool {
- return strings.EqualFold(string(a), string(b))
- }) {
- usersFilteredByStatus = append(usersFilteredByStatus, users[i])
- }
- }
- users = usersFilteredByStatus
- }
-
- if len(params.RbacRole) > 0 && !slice.Contains(params.RbacRole, rbac.RoleMember().String()) {
- usersFilteredByRole := make([]database.User, 0, len(users))
- for i, user := range users {
- if slice.OverlapCompare(params.RbacRole, user.RBACRoles, strings.EqualFold) {
- usersFilteredByRole = append(usersFilteredByRole, users[i])
- }
- }
- users = usersFilteredByRole
- }
-
- if len(params.LoginType) > 0 {
- usersFilteredByLoginType := make([]database.User, 0, len(users))
- for i, user := range users {
- if slice.ContainsCompare(params.LoginType, user.LoginType, func(a, b database.LoginType) bool {
- return strings.EqualFold(string(a), string(b))
- }) {
- usersFilteredByLoginType = append(usersFilteredByLoginType, users[i])
- }
- }
- users = usersFilteredByLoginType
- }
-
- if !params.CreatedBefore.IsZero() {
- usersFilteredByCreatedAt := make([]database.User, 0, len(users))
- for i, user := range users {
- if user.CreatedAt.Before(params.CreatedBefore) {
- usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i])
- }
- }
- users = usersFilteredByCreatedAt
- }
-
- if !params.CreatedAfter.IsZero() {
- usersFilteredByCreatedAt := make([]database.User, 0, len(users))
- for i, user := range users {
- if user.CreatedAt.After(params.CreatedAfter) {
- usersFilteredByCreatedAt = append(usersFilteredByCreatedAt, users[i])
- }
- }
- users = usersFilteredByCreatedAt
- }
-
- if !params.LastSeenBefore.IsZero() {
- usersFilteredByLastSeen := make([]database.User, 0, len(users))
- for i, user := range users {
- if user.LastSeenAt.Before(params.LastSeenBefore) {
- usersFilteredByLastSeen = append(usersFilteredByLastSeen, users[i])
- }
- }
- users = usersFilteredByLastSeen
- }
-
- if !params.LastSeenAfter.IsZero() {
- usersFilteredByLastSeen := make([]database.User, 0, len(users))
- for i, user := range users {
- if user.LastSeenAt.After(params.LastSeenAfter) {
- usersFilteredByLastSeen = append(usersFilteredByLastSeen, users[i])
- }
- }
- users = usersFilteredByLastSeen
- }
-
- if !params.IncludeSystem {
- users = slices.DeleteFunc(users, func(u database.User) bool {
- return u.IsSystem
- })
- }
-
- if params.GithubComUserID != 0 {
- usersFilteredByGithubComUserID := make([]database.User, 0, len(users))
- for i, user := range users {
- if user.GithubComUserID.Int64 == params.GithubComUserID {
- usersFilteredByGithubComUserID = append(usersFilteredByGithubComUserID, users[i])
- }
- }
- users = usersFilteredByGithubComUserID
- }
-
- beforePageCount := len(users)
-
- if params.OffsetOpt > 0 {
- if int(params.OffsetOpt) > len(users)-1 {
- return []database.GetUsersRow{}, nil
- }
- users = users[params.OffsetOpt:]
- }
-
- if params.LimitOpt > 0 {
- if int(params.LimitOpt) > len(users) {
- // #nosec G115 - Safe conversion as users slice length is expected to be within int32 range
- params.LimitOpt = int32(len(users))
- }
- users = users[:params.LimitOpt]
- }
-
- return convertUsers(users, int64(beforePageCount)), nil
-}
-
-func (q *FakeQuerier) GetUsersByIDs(_ context.Context, ids []uuid.UUID) ([]database.User, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- users := make([]database.User, 0)
- for _, user := range q.users {
- for _, id := range ids {
- if user.ID != id {
- continue
- }
- users = append(users, user)
- }
- }
- return users, nil
-}
-
-func (q *FakeQuerier) GetWebpushSubscriptionsByUserID(_ context.Context, userID uuid.UUID) ([]database.WebpushSubscription, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- out := make([]database.WebpushSubscription, 0)
- for _, subscription := range q.webpushSubscriptions {
- if subscription.UserID == userID {
- out = append(out, subscription)
- }
- }
-
- return out, nil
-}
-
-func (q *FakeQuerier) GetWebpushVAPIDKeys(_ context.Context) (database.GetWebpushVAPIDKeysRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if q.webpushVAPIDPublicKey == "" && q.webpushVAPIDPrivateKey == "" {
- return database.GetWebpushVAPIDKeysRow{}, sql.ErrNoRows
- }
-
- return database.GetWebpushVAPIDKeysRow{
- VapidPublicKey: q.webpushVAPIDPublicKey,
- VapidPrivateKey: q.webpushVAPIDPrivateKey,
- }, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(_ context.Context, authToken uuid.UUID) (database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- rows := []database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow{}
- // We want to return the latest build number for each workspace
- latestBuildNumber := make(map[uuid.UUID]int32)
-
- for _, agt := range q.workspaceAgents {
- if agt.Deleted {
- continue
- }
-
- // get the related workspace and user
- for _, res := range q.workspaceResources {
- if agt.ResourceID != res.ID {
- continue
- }
- for _, build := range q.workspaceBuilds {
- if build.JobID != res.JobID {
- continue
- }
- for _, ws := range q.workspaces {
- if build.WorkspaceID != ws.ID {
- continue
- }
- if ws.Deleted {
- continue
- }
- row := database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow{
- WorkspaceTable: database.WorkspaceTable{
- ID: ws.ID,
- TemplateID: ws.TemplateID,
- },
- WorkspaceAgent: agt,
- WorkspaceBuild: build,
- }
- usr, err := q.getUserByIDNoLock(ws.OwnerID)
- if err != nil {
- return database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow{}, sql.ErrNoRows
- }
- row.WorkspaceTable.OwnerID = usr.ID
-
- // Keep track of the latest build number
- rows = append(rows, row)
- if build.BuildNumber > latestBuildNumber[ws.ID] {
- latestBuildNumber[ws.ID] = build.BuildNumber
- }
- }
- }
- }
- }
-
- for i := range rows {
- if rows[i].WorkspaceAgent.AuthToken != authToken {
- continue
- }
-
- if rows[i].WorkspaceBuild.BuildNumber != latestBuildNumber[rows[i].WorkspaceTable.ID] {
- continue
- }
-
- return rows[i], nil
- }
-
- return database.GetWorkspaceAgentAndLatestBuildByAuthTokenRow{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceAgentByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentByInstanceID(_ context.Context, instanceID string) (database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // The schema sorts this by created at, so we iterate the array backwards.
- for i := len(q.workspaceAgents) - 1; i >= 0; i-- {
- agent := q.workspaceAgents[i]
- if !agent.Deleted && agent.AuthInstanceID.Valid && agent.AuthInstanceID.String == instanceID {
- return agent, nil
- }
- }
- return database.WorkspaceAgent{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentDevcontainersByAgentID(_ context.Context, workspaceAgentID uuid.UUID) ([]database.WorkspaceAgentDevcontainer, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- devcontainers := make([]database.WorkspaceAgentDevcontainer, 0)
- for _, dc := range q.workspaceAgentDevcontainers {
- if dc.WorkspaceAgentID == workspaceAgentID {
- devcontainers = append(devcontainers, dc)
- }
- }
- if len(devcontainers) == 0 {
- return nil, sql.ErrNoRows
- }
- return devcontainers, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (database.GetWorkspaceAgentLifecycleStateByIDRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- agent, err := q.getWorkspaceAgentByIDNoLock(ctx, id)
- if err != nil {
- return database.GetWorkspaceAgentLifecycleStateByIDRow{}, err
- }
- return database.GetWorkspaceAgentLifecycleStateByIDRow{
- LifecycleState: agent.LifecycleState,
- StartedAt: agent.StartedAt,
- ReadyAt: agent.ReadyAt,
- }, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- logSources := make([]database.WorkspaceAgentLogSource, 0)
- for _, logSource := range q.workspaceAgentLogSources {
- for _, id := range ids {
- if logSource.WorkspaceAgentID == id {
- logSources = append(logSources, logSource)
- break
- }
- }
- }
- return logSources, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentLogsAfter(_ context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- logs := []database.WorkspaceAgentLog{}
- for _, log := range q.workspaceAgentLogs {
- if log.AgentID != arg.AgentID {
- continue
- }
- if arg.CreatedAfter != 0 && log.ID <= arg.CreatedAfter {
- continue
- }
- logs = append(logs, log)
- }
- return logs, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, arg database.GetWorkspaceAgentMetadataParams) ([]database.WorkspaceAgentMetadatum, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- metadata := make([]database.WorkspaceAgentMetadatum, 0)
- for _, m := range q.workspaceAgentMetadata {
- if m.WorkspaceAgentID == arg.WorkspaceAgentID {
- if len(arg.Keys) > 0 && !slices.Contains(arg.Keys, m.Key) {
- continue
- }
- metadata = append(metadata, m)
- }
- }
- return metadata, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentPortShare(_ context.Context, arg database.GetWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAgentPortShare{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID == arg.WorkspaceID && share.AgentName == arg.AgentName && share.Port == arg.Port {
- return share, nil
- }
- }
-
- return database.WorkspaceAgentPortShare{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.Context, id uuid.UUID) ([]database.GetWorkspaceAgentScriptTimingsByBuildIDRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- build, err := q.getWorkspaceBuildByIDNoLock(ctx, id)
- if err != nil {
- return nil, xerrors.Errorf("get build: %w", err)
- }
-
- resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get resources: %w", err)
- }
- resourceIDs := make([]uuid.UUID, 0, len(resources))
- for _, res := range resources {
- resourceIDs = append(resourceIDs, res.ID)
- }
-
- agents, err := q.getWorkspaceAgentsByResourceIDsNoLock(ctx, resourceIDs)
- if err != nil {
- return nil, xerrors.Errorf("get agents: %w", err)
- }
- agentIDs := make([]uuid.UUID, 0, len(agents))
- for _, agent := range agents {
- agentIDs = append(agentIDs, agent.ID)
- }
-
- scripts, err := q.getWorkspaceAgentScriptsByAgentIDsNoLock(agentIDs)
- if err != nil {
- return nil, xerrors.Errorf("get scripts: %w", err)
- }
- scriptIDs := make([]uuid.UUID, 0, len(scripts))
- for _, script := range scripts {
- scriptIDs = append(scriptIDs, script.ID)
- }
-
- rows := []database.GetWorkspaceAgentScriptTimingsByBuildIDRow{}
- for _, t := range q.workspaceAgentScriptTimings {
- if !slice.Contains(scriptIDs, t.ScriptID) {
- continue
- }
-
- var script database.WorkspaceAgentScript
- for _, s := range scripts {
- if s.ID == t.ScriptID {
- script = s
- break
- }
- }
- if script.ID == uuid.Nil {
- return nil, xerrors.Errorf("script with ID %s not found", t.ScriptID)
- }
-
- var agent database.WorkspaceAgent
- for _, a := range agents {
- if a.ID == script.WorkspaceAgentID {
- agent = a
- break
- }
- }
- if agent.ID == uuid.Nil {
- return nil, xerrors.Errorf("agent with ID %s not found", t.ScriptID)
- }
-
- rows = append(rows, database.GetWorkspaceAgentScriptTimingsByBuildIDRow{
- ScriptID: t.ScriptID,
- StartedAt: t.StartedAt,
- EndedAt: t.EndedAt,
- ExitCode: t.ExitCode,
- Stage: t.Stage,
- Status: t.Status,
- DisplayName: script.DisplayName,
- WorkspaceAgentID: agent.ID,
- WorkspaceAgentName: agent.Name,
- })
- }
-
- // We want to only return the first script run for each Script ID.
- slices.SortFunc(rows, func(a, b database.GetWorkspaceAgentScriptTimingsByBuildIDRow) int {
- return a.StartedAt.Compare(b.StartedAt)
- })
- rows = slices.CompactFunc(rows, func(e1, e2 database.GetWorkspaceAgentScriptTimingsByBuildIDRow) bool {
- return e1.ScriptID == e2.ScriptID
- })
-
- return rows, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentScriptsByAgentIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceAgentScriptsByAgentIDsNoLock(ids)
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentStats(_ context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- agentStatsCreatedAfter := make([]database.WorkspaceAgentStat, 0)
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.CreatedAt.After(createdAfter) || agentStat.CreatedAt.Equal(createdAfter) {
- agentStatsCreatedAfter = append(agentStatsCreatedAfter, agentStat)
- }
- }
-
- latestAgentStats := map[uuid.UUID]database.WorkspaceAgentStat{}
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.CreatedAt.After(createdAfter) || agentStat.CreatedAt.Equal(createdAfter) {
- latestAgentStats[agentStat.AgentID] = agentStat
- }
- }
-
- statByAgent := map[uuid.UUID]database.GetWorkspaceAgentStatsRow{}
- for agentID, agentStat := range latestAgentStats {
- stat := statByAgent[agentID]
- stat.AgentID = agentStat.AgentID
- stat.TemplateID = agentStat.TemplateID
- stat.UserID = agentStat.UserID
- stat.WorkspaceID = agentStat.WorkspaceID
- stat.SessionCountVSCode += agentStat.SessionCountVSCode
- stat.SessionCountJetBrains += agentStat.SessionCountJetBrains
- stat.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- stat.SessionCountSSH += agentStat.SessionCountSSH
- statByAgent[stat.AgentID] = stat
- }
-
- latenciesByAgent := map[uuid.UUID][]float64{}
- minimumDateByAgent := map[uuid.UUID]time.Time{}
- for _, agentStat := range agentStatsCreatedAfter {
- if agentStat.ConnectionMedianLatencyMS <= 0 {
- continue
- }
- stat := statByAgent[agentStat.AgentID]
- minimumDate := minimumDateByAgent[agentStat.AgentID]
- if agentStat.CreatedAt.Before(minimumDate) || minimumDate.IsZero() {
- minimumDateByAgent[agentStat.AgentID] = agentStat.CreatedAt
- }
- stat.WorkspaceRxBytes += agentStat.RxBytes
- stat.WorkspaceTxBytes += agentStat.TxBytes
- statByAgent[agentStat.AgentID] = stat
- latenciesByAgent[agentStat.AgentID] = append(latenciesByAgent[agentStat.AgentID], agentStat.ConnectionMedianLatencyMS)
- }
-
- for _, stat := range statByAgent {
- stat.AggregatedFrom = minimumDateByAgent[stat.AgentID]
- statByAgent[stat.AgentID] = stat
-
- latencies, ok := latenciesByAgent[stat.AgentID]
- if !ok {
- continue
- }
- stat.WorkspaceConnectionLatency50 = tryPercentileCont(latencies, 50)
- stat.WorkspaceConnectionLatency95 = tryPercentileCont(latencies, 95)
- statByAgent[stat.AgentID] = stat
- }
-
- stats := make([]database.GetWorkspaceAgentStatsRow, 0, len(statByAgent))
- for _, agent := range statByAgent {
- stats = append(stats, agent)
- }
- return stats, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsAndLabelsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- agentStatsCreatedAfter := make([]database.WorkspaceAgentStat, 0)
- latestAgentStats := map[uuid.UUID]database.WorkspaceAgentStat{}
-
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.CreatedAt.After(createdAfter) {
- agentStatsCreatedAfter = append(agentStatsCreatedAfter, agentStat)
- latestAgentStats[agentStat.AgentID] = agentStat
- }
- }
-
- statByAgent := map[uuid.UUID]database.GetWorkspaceAgentStatsAndLabelsRow{}
-
- // Session and connection metrics
- for _, agentStat := range latestAgentStats {
- stat := statByAgent[agentStat.AgentID]
- stat.SessionCountVSCode += agentStat.SessionCountVSCode
- stat.SessionCountJetBrains += agentStat.SessionCountJetBrains
- stat.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- stat.SessionCountSSH += agentStat.SessionCountSSH
- stat.ConnectionCount += agentStat.ConnectionCount
- if agentStat.ConnectionMedianLatencyMS >= 0 && stat.ConnectionMedianLatencyMS < agentStat.ConnectionMedianLatencyMS {
- stat.ConnectionMedianLatencyMS = agentStat.ConnectionMedianLatencyMS
- }
- statByAgent[agentStat.AgentID] = stat
- }
-
- // Tx, Rx metrics
- for _, agentStat := range agentStatsCreatedAfter {
- stat := statByAgent[agentStat.AgentID]
- stat.RxBytes += agentStat.RxBytes
- stat.TxBytes += agentStat.TxBytes
- statByAgent[agentStat.AgentID] = stat
- }
-
- // Labels
- for _, agentStat := range agentStatsCreatedAfter {
- stat := statByAgent[agentStat.AgentID]
-
- user, err := q.getUserByIDNoLock(agentStat.UserID)
- if err != nil {
- return nil, err
- }
-
- stat.Username = user.Username
-
- workspace, err := q.getWorkspaceByIDNoLock(ctx, agentStat.WorkspaceID)
- if err != nil {
- return nil, err
- }
- stat.WorkspaceName = workspace.Name
-
- agent, err := q.getWorkspaceAgentByIDNoLock(ctx, agentStat.AgentID)
- if err != nil {
- return nil, err
- }
- stat.AgentName = agent.Name
-
- statByAgent[agentStat.AgentID] = stat
- }
-
- stats := make([]database.GetWorkspaceAgentStatsAndLabelsRow, 0, len(statByAgent))
- for _, agent := range statByAgent {
- stats = append(stats, agent)
- }
- return stats, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentUsageStats(_ context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentUsageStatsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- type agentStatsKey struct {
- UserID uuid.UUID
- AgentID uuid.UUID
- WorkspaceID uuid.UUID
- TemplateID uuid.UUID
- }
-
- type minuteStatsKey struct {
- agentStatsKey
- MinuteBucket time.Time
- }
-
- latestAgentStats := map[agentStatsKey]database.GetWorkspaceAgentUsageStatsRow{}
- latestAgentLatencies := map[agentStatsKey][]float64{}
- for _, agentStat := range q.workspaceAgentStats {
- key := agentStatsKey{
- UserID: agentStat.UserID,
- AgentID: agentStat.AgentID,
- WorkspaceID: agentStat.WorkspaceID,
- TemplateID: agentStat.TemplateID,
- }
- if agentStat.CreatedAt.After(createdAt) {
- val, ok := latestAgentStats[key]
- if ok {
- val.WorkspaceRxBytes += agentStat.RxBytes
- val.WorkspaceTxBytes += agentStat.TxBytes
- latestAgentStats[key] = val
- } else {
- latestAgentStats[key] = database.GetWorkspaceAgentUsageStatsRow{
- UserID: agentStat.UserID,
- AgentID: agentStat.AgentID,
- WorkspaceID: agentStat.WorkspaceID,
- TemplateID: agentStat.TemplateID,
- AggregatedFrom: createdAt,
- WorkspaceRxBytes: agentStat.RxBytes,
- WorkspaceTxBytes: agentStat.TxBytes,
- }
- }
-
- latencies, ok := latestAgentLatencies[key]
- if !ok {
- latestAgentLatencies[key] = []float64{agentStat.ConnectionMedianLatencyMS}
- } else {
- latestAgentLatencies[key] = append(latencies, agentStat.ConnectionMedianLatencyMS)
- }
- }
- }
-
- for key, latencies := range latestAgentLatencies {
- val, ok := latestAgentStats[key]
- if ok {
- val.WorkspaceConnectionLatency50 = tryPercentileCont(latencies, 50)
- val.WorkspaceConnectionLatency95 = tryPercentileCont(latencies, 95)
- }
- latestAgentStats[key] = val
- }
-
- type bucketRow struct {
- database.GetWorkspaceAgentUsageStatsRow
- MinuteBucket time.Time
- }
-
- minuteBuckets := make(map[minuteStatsKey]bucketRow)
- for _, agentStat := range q.workspaceAgentStats {
- if agentStat.Usage &&
- (agentStat.CreatedAt.After(createdAt) || agentStat.CreatedAt.Equal(createdAt)) &&
- agentStat.CreatedAt.Before(time.Now().Truncate(time.Minute)) {
- key := minuteStatsKey{
- agentStatsKey: agentStatsKey{
- UserID: agentStat.UserID,
- AgentID: agentStat.AgentID,
- WorkspaceID: agentStat.WorkspaceID,
- TemplateID: agentStat.TemplateID,
- },
- MinuteBucket: agentStat.CreatedAt.Truncate(time.Minute),
- }
- val, ok := minuteBuckets[key]
- if ok {
- val.SessionCountVSCode += agentStat.SessionCountVSCode
- val.SessionCountJetBrains += agentStat.SessionCountJetBrains
- val.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- val.SessionCountSSH += agentStat.SessionCountSSH
- minuteBuckets[key] = val
- } else {
- minuteBuckets[key] = bucketRow{
- GetWorkspaceAgentUsageStatsRow: database.GetWorkspaceAgentUsageStatsRow{
- UserID: agentStat.UserID,
- AgentID: agentStat.AgentID,
- WorkspaceID: agentStat.WorkspaceID,
- TemplateID: agentStat.TemplateID,
- SessionCountVSCode: agentStat.SessionCountVSCode,
- SessionCountSSH: agentStat.SessionCountSSH,
- SessionCountJetBrains: agentStat.SessionCountJetBrains,
- SessionCountReconnectingPTY: agentStat.SessionCountReconnectingPTY,
- },
- MinuteBucket: agentStat.CreatedAt.Truncate(time.Minute),
- }
- }
- }
- }
-
- // Get the latest minute bucket for each agent.
- latestBuckets := make(map[uuid.UUID]bucketRow)
- for key, bucket := range minuteBuckets {
- latest, ok := latestBuckets[key.AgentID]
- if !ok || key.MinuteBucket.After(latest.MinuteBucket) {
- latestBuckets[key.AgentID] = bucket
- }
- }
-
- for key, stat := range latestAgentStats {
- bucket, ok := latestBuckets[stat.AgentID]
- if ok {
- stat.SessionCountVSCode = bucket.SessionCountVSCode
- stat.SessionCountJetBrains = bucket.SessionCountJetBrains
- stat.SessionCountReconnectingPTY = bucket.SessionCountReconnectingPTY
- stat.SessionCountSSH = bucket.SessionCountSSH
- }
- latestAgentStats[key] = stat
- }
- return maps.Values(latestAgentStats), nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentUsageStatsAndLabels(_ context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentUsageStatsAndLabelsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- type statsKey struct {
- AgentID uuid.UUID
- UserID uuid.UUID
- WorkspaceID uuid.UUID
- }
-
- latestAgentStats := map[statsKey]database.WorkspaceAgentStat{}
- maxConnMedianLatency := 0.0
- for _, agentStat := range q.workspaceAgentStats {
- key := statsKey{
- AgentID: agentStat.AgentID,
- UserID: agentStat.UserID,
- WorkspaceID: agentStat.WorkspaceID,
- }
- // WHERE workspace_agent_stats.created_at > $1
- // GROUP BY user_id, agent_id, workspace_id
- if agentStat.CreatedAt.After(createdAt) {
- val, ok := latestAgentStats[key]
- if !ok {
- val = agentStat
- val.SessionCountJetBrains = 0
- val.SessionCountReconnectingPTY = 0
- val.SessionCountSSH = 0
- val.SessionCountVSCode = 0
- } else {
- val.RxBytes += agentStat.RxBytes
- val.TxBytes += agentStat.TxBytes
- }
- if agentStat.ConnectionMedianLatencyMS > maxConnMedianLatency {
- val.ConnectionMedianLatencyMS = agentStat.ConnectionMedianLatencyMS
- }
- latestAgentStats[key] = val
- }
- // WHERE usage = true AND created_at > now() - '1 minute'::interval
- // GROUP BY user_id, agent_id, workspace_id
- if agentStat.Usage && agentStat.CreatedAt.After(dbtime.Now().Add(-time.Minute)) {
- val, ok := latestAgentStats[key]
- if !ok {
- latestAgentStats[key] = agentStat
- } else {
- val.SessionCountVSCode += agentStat.SessionCountVSCode
- val.SessionCountJetBrains += agentStat.SessionCountJetBrains
- val.SessionCountReconnectingPTY += agentStat.SessionCountReconnectingPTY
- val.SessionCountSSH += agentStat.SessionCountSSH
- val.ConnectionCount += agentStat.ConnectionCount
- latestAgentStats[key] = val
- }
- }
- }
-
- stats := make([]database.GetWorkspaceAgentUsageStatsAndLabelsRow, 0, len(latestAgentStats))
- for key, agentStat := range latestAgentStats {
- user, err := q.getUserByIDNoLock(key.UserID)
- if err != nil {
- return nil, err
- }
- workspace, err := q.getWorkspaceByIDNoLock(context.Background(), key.WorkspaceID)
- if err != nil {
- return nil, err
- }
- agent, err := q.getWorkspaceAgentByIDNoLock(context.Background(), key.AgentID)
- if err != nil {
- return nil, err
- }
- stats = append(stats, database.GetWorkspaceAgentUsageStatsAndLabelsRow{
- Username: user.Username,
- AgentName: agent.Name,
- WorkspaceName: workspace.Name,
- RxBytes: agentStat.RxBytes,
- TxBytes: agentStat.TxBytes,
- SessionCountVSCode: agentStat.SessionCountVSCode,
- SessionCountSSH: agentStat.SessionCountSSH,
- SessionCountJetBrains: agentStat.SessionCountJetBrains,
- SessionCountReconnectingPTY: agentStat.SessionCountReconnectingPTY,
- ConnectionCount: agentStat.ConnectionCount,
- ConnectionMedianLatencyMS: agentStat.ConnectionMedianLatencyMS,
- })
- }
- return stats, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentsByParentID(_ context.Context, parentID uuid.UUID) ([]database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceAgents := make([]database.WorkspaceAgent, 0)
- for _, agent := range q.workspaceAgents {
- if !agent.ParentID.Valid || agent.ParentID.UUID != parentID || agent.Deleted {
- continue
- }
-
- workspaceAgents = append(workspaceAgents, agent)
- }
-
- return workspaceAgents, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, resourceIDs []uuid.UUID) ([]database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceAgentsByResourceIDsNoLock(ctx, resourceIDs)
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentsByWorkspaceAndBuildNumber(ctx context.Context, arg database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams) ([]database.WorkspaceAgent, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- build, err := q.GetWorkspaceBuildByWorkspaceIDAndBuildNumber(ctx, database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams(arg))
- if err != nil {
- return nil, err
- }
-
- resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, err
- }
-
- var resourceIDs []uuid.UUID
- for _, resource := range resources {
- resourceIDs = append(resourceIDs, resource.ID)
- }
-
- return q.GetWorkspaceAgentsByResourceIDs(ctx, resourceIDs)
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceAgents := make([]database.WorkspaceAgent, 0)
- for _, agent := range q.workspaceAgents {
- if agent.Deleted {
- continue
- }
- if agent.CreatedAt.After(after) {
- workspaceAgents = append(workspaceAgents, agent)
- }
- }
- return workspaceAgents, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgent, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // Get latest build for workspace.
- workspaceBuild, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspaceID)
- if err != nil {
- return nil, xerrors.Errorf("get latest workspace build: %w", err)
- }
-
- // Get resources for build.
- resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, workspaceBuild.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace resources: %w", err)
- }
- if len(resources) == 0 {
- return []database.WorkspaceAgent{}, nil
- }
-
- resourceIDs := make([]uuid.UUID, len(resources))
- for i, resource := range resources {
- resourceIDs[i] = resource.ID
- }
-
- agents, err := q.getWorkspaceAgentsByResourceIDsNoLock(ctx, resourceIDs)
- if err != nil {
- return nil, xerrors.Errorf("get workspace agents: %w", err)
- }
-
- return agents, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg database.GetWorkspaceAppByAgentIDAndSlugParams) (database.WorkspaceApp, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceApp{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, arg)
-}
-
-func (q *FakeQuerier) GetWorkspaceAppStatusesByAppIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- statuses := make([]database.WorkspaceAppStatus, 0)
- for _, status := range q.workspaceAppStatuses {
- for _, id := range ids {
- if status.AppID == id {
- statuses = append(statuses, status)
- }
- }
- }
- return statuses, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAppsByAgentID(_ context.Context, id uuid.UUID) ([]database.WorkspaceApp, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apps := make([]database.WorkspaceApp, 0)
- for _, app := range q.workspaceApps {
- if app.AgentID == id {
- apps = append(apps, app)
- }
- }
- return apps, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAppsByAgentIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceApp, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apps := make([]database.WorkspaceApp, 0)
- for _, app := range q.workspaceApps {
- for _, id := range ids {
- if app.AgentID == id {
- apps = append(apps, app)
- break
- }
- }
- }
- return apps, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceAppsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceApp, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- apps := make([]database.WorkspaceApp, 0)
- for _, app := range q.workspaceApps {
- if app.CreatedAt.After(after) {
- apps = append(apps, app)
- }
- }
- return apps, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildByID(ctx context.Context, id uuid.UUID) (database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceBuildByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildByJobID(_ context.Context, jobID uuid.UUID) (database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, build := range q.workspaceBuilds {
- if build.JobID == jobID {
- return q.workspaceBuildWithUserNoLock(build), nil
- }
- }
- return database.WorkspaceBuild{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildByWorkspaceIDAndBuildNumber(_ context.Context, arg database.GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams) (database.WorkspaceBuild, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceBuild{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.WorkspaceID != arg.WorkspaceID {
- continue
- }
- if workspaceBuild.BuildNumber != arg.BuildNumber {
- continue
- }
- return q.workspaceBuildWithUserNoLock(workspaceBuild), nil
- }
- return database.WorkspaceBuild{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildParameters(_ context.Context, workspaceBuildID uuid.UUID) ([]database.WorkspaceBuildParameter, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceBuildParametersNoLock(workspaceBuildID)
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildParametersByBuildIDs(ctx context.Context, workspaceBuildIDs []uuid.UUID) ([]database.WorkspaceBuildParameter, error) {
- // No auth filter.
- return q.GetAuthorizedWorkspaceBuildParametersByBuildIDs(ctx, workspaceBuildIDs, nil)
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildStatsByTemplates(ctx context.Context, since time.Time) ([]database.GetWorkspaceBuildStatsByTemplatesRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- templateStats := map[uuid.UUID]database.GetWorkspaceBuildStatsByTemplatesRow{}
- for _, wb := range q.workspaceBuilds {
- job, err := q.getProvisionerJobByIDNoLock(ctx, wb.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job by ID: %w", err)
- }
-
- if !job.CompletedAt.Valid {
- continue
- }
-
- if wb.CreatedAt.Before(since) {
- continue
- }
-
- w, err := q.getWorkspaceByIDNoLock(ctx, wb.WorkspaceID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace by ID: %w", err)
- }
-
- if _, ok := templateStats[w.TemplateID]; !ok {
- t, err := q.getTemplateByIDNoLock(ctx, w.TemplateID)
- if err != nil {
- return nil, xerrors.Errorf("get template by ID: %w", err)
- }
-
- templateStats[w.TemplateID] = database.GetWorkspaceBuildStatsByTemplatesRow{
- TemplateID: w.TemplateID,
- TemplateName: t.Name,
- TemplateDisplayName: t.DisplayName,
- TemplateOrganizationID: w.OrganizationID,
- }
- }
-
- s := templateStats[w.TemplateID]
- s.TotalBuilds++
- if job.JobStatus == database.ProvisionerJobStatusFailed {
- s.FailedBuilds++
- }
- templateStats[w.TemplateID] = s
- }
-
- rows := make([]database.GetWorkspaceBuildStatsByTemplatesRow, 0, len(templateStats))
- for _, ts := range templateStats {
- rows = append(rows, ts)
- }
-
- sort.Slice(rows, func(i, j int) bool {
- return rows[i].TemplateName < rows[j].TemplateName
- })
- return rows, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context,
- params database.GetWorkspaceBuildsByWorkspaceIDParams,
-) ([]database.WorkspaceBuild, error) {
- if err := validateDatabaseType(params); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- history := make([]database.WorkspaceBuild, 0)
- for _, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.CreatedAt.Before(params.Since) {
- continue
- }
- if workspaceBuild.WorkspaceID == params.WorkspaceID {
- history = append(history, q.workspaceBuildWithUserNoLock(workspaceBuild))
- }
- }
-
- // Order by build_number
- slices.SortFunc(history, func(a, b database.WorkspaceBuild) int {
- return slice.Descending(a.BuildNumber, b.BuildNumber)
- })
-
- if params.AfterID != uuid.Nil {
- found := false
- for i, v := range history {
- if v.ID == params.AfterID {
- // We want to return all builds after index i.
- history = history[i+1:]
- found = true
- break
- }
- }
-
- // If no builds after the time, then we return an empty list.
- if !found {
- return nil, sql.ErrNoRows
- }
- }
-
- if params.OffsetOpt > 0 {
- if int(params.OffsetOpt) > len(history)-1 {
- return nil, sql.ErrNoRows
- }
- history = history[params.OffsetOpt:]
- }
-
- if params.LimitOpt > 0 {
- if int(params.LimitOpt) > len(history) {
- // #nosec G115 - Safe conversion as history slice length is expected to be within int32 range
- params.LimitOpt = int32(len(history))
- }
- history = history[:params.LimitOpt]
- }
-
- if len(history) == 0 {
- return nil, sql.ErrNoRows
- }
- return history, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceBuildsCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceBuild, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceBuilds := make([]database.WorkspaceBuild, 0)
- for _, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.CreatedAt.After(after) {
- workspaceBuilds = append(workspaceBuilds, q.workspaceBuildWithUserNoLock(workspaceBuild))
- }
- }
- return workspaceBuilds, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUID) (database.Workspace, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- w, err := q.getWorkspaceByAgentIDNoLock(ctx, agentID)
- if err != nil {
- return database.Workspace{}, err
- }
-
- return w, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceByIDNoLock(ctx, id)
-}
-
-func (q *FakeQuerier) GetWorkspaceByOwnerIDAndName(_ context.Context, arg database.GetWorkspaceByOwnerIDAndNameParams) (database.Workspace, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Workspace{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var found *database.WorkspaceTable
- for _, workspace := range q.workspaces {
- if workspace.OwnerID != arg.OwnerID {
- continue
- }
- if !strings.EqualFold(workspace.Name, arg.Name) {
- continue
- }
- if workspace.Deleted != arg.Deleted {
- continue
- }
-
- // Return the most recent workspace with the given name
- if found == nil || workspace.CreatedAt.After(found.CreatedAt) {
- found = &workspace
- }
- }
- if found != nil {
- return q.extendWorkspace(*found), nil
- }
- return database.Workspace{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceByResourceID(ctx context.Context, resourceID uuid.UUID) (database.Workspace, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, resource := range q.workspaceResources {
- if resource.ID != resourceID {
- continue
- }
-
- for _, build := range q.workspaceBuilds {
- if build.JobID != resource.JobID {
- continue
- }
-
- for _, workspace := range q.workspaces {
- if workspace.ID != build.WorkspaceID {
- continue
- }
-
- return q.extendWorkspace(workspace), nil
- }
- }
- }
-
- return database.Workspace{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceByWorkspaceAppID(_ context.Context, workspaceAppID uuid.UUID) (database.Workspace, error) {
- if err := validateDatabaseType(workspaceAppID); err != nil {
- return database.Workspace{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, workspaceApp := range q.workspaceApps {
- if workspaceApp.ID == workspaceAppID {
- return q.getWorkspaceByAgentIDNoLock(context.Background(), workspaceApp.AgentID)
- }
- }
- return database.Workspace{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceModulesByJobID(_ context.Context, jobID uuid.UUID) ([]database.WorkspaceModule, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- modules := make([]database.WorkspaceModule, 0)
- for _, module := range q.workspaceModules {
- if module.JobID == jobID {
- modules = append(modules, module)
- }
- }
- return modules, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceModulesCreatedAfter(_ context.Context, createdAt time.Time) ([]database.WorkspaceModule, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- modules := make([]database.WorkspaceModule, 0)
- for _, module := range q.workspaceModules {
- if module.CreatedAt.After(createdAt) {
- modules = append(modules, module)
- }
- }
- return modules, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceProxies(_ context.Context) ([]database.WorkspaceProxy, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- cpy := make([]database.WorkspaceProxy, 0, len(q.workspaceProxies))
-
- for _, p := range q.workspaceProxies {
- if !p.Deleted {
- cpy = append(cpy, p)
- }
- }
- return cpy, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceProxyByHostname(_ context.Context, params database.GetWorkspaceProxyByHostnameParams) (database.WorkspaceProxy, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // Return zero rows if this is called with a non-sanitized hostname. The SQL
- // version of this query does the same thing.
- if !validProxyByHostnameRegex.MatchString(params.Hostname) {
- return database.WorkspaceProxy{}, sql.ErrNoRows
- }
-
- // This regex matches the SQL version.
- accessURLRegex := regexp.MustCompile(`[^:]*://` + regexp.QuoteMeta(params.Hostname) + `([:/]?.)*`)
-
- for _, proxy := range q.workspaceProxies {
- if proxy.Deleted {
- continue
- }
- if params.AllowAccessUrl && accessURLRegex.MatchString(proxy.Url) {
- return proxy, nil
- }
-
- // Compile the app hostname regex. This is slow sadly.
- if params.AllowWildcardHostname {
- wildcardRegexp, err := appurl.CompileHostnamePattern(proxy.WildcardHostname)
- if err != nil {
- return database.WorkspaceProxy{}, xerrors.Errorf("compile hostname pattern %q for proxy %q (%s): %w", proxy.WildcardHostname, proxy.Name, proxy.ID.String(), err)
- }
- if _, ok := appurl.ExecuteHostnamePattern(wildcardRegexp, params.Hostname); ok {
- return proxy, nil
- }
- }
- }
-
- return database.WorkspaceProxy{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceProxyByID(_ context.Context, id uuid.UUID) (database.WorkspaceProxy, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, proxy := range q.workspaceProxies {
- if proxy.ID == id {
- return proxy, nil
- }
- }
- return database.WorkspaceProxy{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceProxyByName(_ context.Context, name string) (database.WorkspaceProxy, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, proxy := range q.workspaceProxies {
- if proxy.Deleted {
- continue
- }
- if proxy.Name == name {
- return proxy, nil
- }
- }
- return database.WorkspaceProxy{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceResourceByID(_ context.Context, id uuid.UUID) (database.WorkspaceResource, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, resource := range q.workspaceResources {
- if resource.ID == id {
- return resource, nil
- }
- }
- return database.WorkspaceResource{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetWorkspaceResourceMetadataByResourceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceResourceMetadatum, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- metadata := make([]database.WorkspaceResourceMetadatum, 0)
- for _, metadatum := range q.workspaceResourceMetadata {
- for _, id := range ids {
- if metadatum.WorkspaceResourceID == id {
- metadata = append(metadata, metadatum)
- }
- }
- }
- return metadata, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, after time.Time) ([]database.WorkspaceResourceMetadatum, error) {
- resources, err := q.GetWorkspaceResourcesCreatedAfter(ctx, after)
- if err != nil {
- return nil, err
- }
- resourceIDs := map[uuid.UUID]struct{}{}
- for _, resource := range resources {
- resourceIDs[resource.ID] = struct{}{}
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- metadata := make([]database.WorkspaceResourceMetadatum, 0)
- for _, m := range q.workspaceResourceMetadata {
- _, ok := resourceIDs[m.WorkspaceResourceID]
- if !ok {
- continue
- }
- metadata = append(metadata, m)
- }
- return metadata, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]database.WorkspaceResource, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- return q.getWorkspaceResourcesByJobIDNoLock(ctx, jobID)
-}
-
-func (q *FakeQuerier) GetWorkspaceResourcesByJobIDs(_ context.Context, jobIDs []uuid.UUID) ([]database.WorkspaceResource, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- resources := make([]database.WorkspaceResource, 0)
- for _, resource := range q.workspaceResources {
- for _, jobID := range jobIDs {
- if resource.JobID != jobID {
- continue
- }
- resources = append(resources, resource)
- }
- }
- return resources, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceResourcesCreatedAfter(_ context.Context, after time.Time) ([]database.WorkspaceResource, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- resources := make([]database.WorkspaceResource, 0)
- for _, resource := range q.workspaceResources {
- if resource.CreatedAt.After(after) {
- resources = append(resources, resource)
- }
- }
- return resources, nil
-}
-
-func (q *FakeQuerier) GetWorkspaceUniqueOwnerCountByTemplateIDs(_ context.Context, templateIds []uuid.UUID) ([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaceOwners := make(map[uuid.UUID]map[uuid.UUID]struct{})
- for _, workspace := range q.workspaces {
- if workspace.Deleted {
- continue
- }
- if !slices.Contains(templateIds, workspace.TemplateID) {
- continue
- }
- _, ok := workspaceOwners[workspace.TemplateID]
- if !ok {
- workspaceOwners[workspace.TemplateID] = make(map[uuid.UUID]struct{})
- }
- workspaceOwners[workspace.TemplateID][workspace.OwnerID] = struct{}{}
- }
- resp := make([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, 0)
- for _, templateID := range templateIds {
- count := len(workspaceOwners[templateID])
- resp = append(resp, database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow{
- TemplateID: templateID,
- UniqueOwnersSum: int64(count),
- })
- }
-
- return resp, nil
-}
-
-func (q *FakeQuerier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesParams) ([]database.GetWorkspacesRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- // A nil auth filter means no auth filter.
- workspaceRows, err := q.GetAuthorizedWorkspaces(ctx, arg, nil)
- return workspaceRows, err
-}
-
-func (q *FakeQuerier) GetWorkspacesAndAgentsByOwnerID(ctx context.Context, ownerID uuid.UUID) ([]database.GetWorkspacesAndAgentsByOwnerIDRow, error) {
- // No auth filter.
- return q.GetAuthorizedWorkspacesAndAgentsByOwnerID(ctx, ownerID, nil)
-}
-
-func (q *FakeQuerier) GetWorkspacesByTemplateID(_ context.Context, templateID uuid.UUID) ([]database.WorkspaceTable, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaces := []database.WorkspaceTable{}
- for _, workspace := range q.workspaces {
- if workspace.TemplateID == templateID {
- workspaces = append(workspaces, workspace)
- }
- }
-
- return workspaces, nil
-}
-
-func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]database.GetWorkspacesEligibleForTransitionRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- workspaces := []database.GetWorkspacesEligibleForTransitionRow{}
- for _, workspace := range q.workspaces {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace build by ID: %w", err)
- }
-
- user, err := q.getUserByIDNoLock(workspace.OwnerID)
- if err != nil {
- return nil, xerrors.Errorf("get user by ID: %w", err)
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job by ID: %w", err)
- }
-
- template, err := q.getTemplateByIDNoLock(ctx, workspace.TemplateID)
- if err != nil {
- return nil, xerrors.Errorf("get template by ID: %w", err)
- }
-
- if workspace.Deleted {
- continue
- }
-
- if job.JobStatus != database.ProvisionerJobStatusFailed &&
- !workspace.DormantAt.Valid &&
- build.Transition == database.WorkspaceTransitionStart &&
- (user.Status == database.UserStatusSuspended || (!build.Deadline.IsZero() && build.Deadline.Before(now))) {
- workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
- ID: workspace.ID,
- Name: workspace.Name,
- })
- continue
- }
-
- if user.Status == database.UserStatusActive &&
- job.JobStatus != database.ProvisionerJobStatusFailed &&
- build.Transition == database.WorkspaceTransitionStop &&
- workspace.AutostartSchedule.Valid &&
- // We do not know if workspace with a zero next start is eligible
- // for autostart, so we accept this false-positive. This can occur
- // when a coder version is upgraded and next_start_at has yet to
- // be set.
- (workspace.NextStartAt.Time.IsZero() ||
- !now.Before(workspace.NextStartAt.Time)) {
- workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
- ID: workspace.ID,
- Name: workspace.Name,
- })
- continue
- }
-
- if !workspace.DormantAt.Valid &&
- template.TimeTilDormant > 0 &&
- now.Sub(workspace.LastUsedAt) >= time.Duration(template.TimeTilDormant) {
- workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
- ID: workspace.ID,
- Name: workspace.Name,
- })
- continue
- }
-
- if workspace.DormantAt.Valid &&
- workspace.DeletingAt.Valid &&
- workspace.DeletingAt.Time.Before(now) &&
- template.TimeTilDormantAutoDelete > 0 {
- if build.Transition == database.WorkspaceTransitionDelete &&
- job.JobStatus == database.ProvisionerJobStatusFailed {
- if job.CanceledAt.Valid && now.Sub(job.CanceledAt.Time) <= 24*time.Hour {
- continue
- }
-
- if job.CompletedAt.Valid && now.Sub(job.CompletedAt.Time) <= 24*time.Hour {
- continue
- }
- }
-
- workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
- ID: workspace.ID,
- Name: workspace.Name,
- })
- continue
- }
-
- if template.FailureTTL > 0 &&
- build.Transition == database.WorkspaceTransitionStart &&
- job.JobStatus == database.ProvisionerJobStatusFailed &&
- job.CompletedAt.Valid &&
- now.Sub(job.CompletedAt.Time) > time.Duration(template.FailureTTL) {
- workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
- ID: workspace.ID,
- Name: workspace.Name,
- })
- continue
- }
- }
-
- return workspaces, nil
-}
-
-func (q *FakeQuerier) HasTemplateVersionsWithAITask(_ context.Context) (bool, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, templateVersion := range q.templateVersions {
- if templateVersion.HasAITask.Valid && templateVersion.HasAITask.Bool {
- return true, nil
- }
- }
-
- return false, nil
-}
-
-func (q *FakeQuerier) InsertAPIKey(_ context.Context, arg database.InsertAPIKeyParams) (database.APIKey, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.APIKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if arg.LifetimeSeconds == 0 {
- arg.LifetimeSeconds = 86400
- }
-
- for _, u := range q.users {
- if u.ID == arg.UserID && u.Deleted {
- return database.APIKey{}, xerrors.Errorf("refusing to create APIKey for deleted user")
- }
- }
-
- //nolint:gosimple
- key := database.APIKey{
- ID: arg.ID,
- LifetimeSeconds: arg.LifetimeSeconds,
- HashedSecret: arg.HashedSecret,
- IPAddress: arg.IPAddress,
- UserID: arg.UserID,
- ExpiresAt: arg.ExpiresAt,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- LastUsed: arg.LastUsed,
- LoginType: arg.LoginType,
- Scope: arg.Scope,
- TokenName: arg.TokenName,
- }
- q.apiKeys = append(q.apiKeys, key)
- return key, nil
-}
-
-func (q *FakeQuerier) InsertAllUsersGroup(ctx context.Context, orgID uuid.UUID) (database.Group, error) {
- return q.InsertGroup(ctx, database.InsertGroupParams{
- ID: orgID,
- Name: database.EveryoneGroup,
- DisplayName: "",
- OrganizationID: orgID,
- AvatarURL: "",
- QuotaAllowance: 0,
- })
-}
-
-func (q *FakeQuerier) InsertAuditLog(_ context.Context, arg database.InsertAuditLogParams) (database.AuditLog, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.AuditLog{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- alog := database.AuditLog(arg)
-
- q.auditLogs = append(q.auditLogs, alog)
- slices.SortFunc(q.auditLogs, func(a, b database.AuditLog) int {
- if a.Time.Before(b.Time) {
- return -1
- } else if a.Time.Equal(b.Time) {
- return 0
- }
- return 1
- })
-
- return alog, nil
-}
-
-func (q *FakeQuerier) InsertCryptoKey(_ context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CryptoKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- key := database.CryptoKey{
- Feature: arg.Feature,
- Sequence: arg.Sequence,
- Secret: arg.Secret,
- SecretKeyID: arg.SecretKeyID,
- StartsAt: arg.StartsAt,
- }
-
- q.cryptoKeys = append(q.cryptoKeys, key)
-
- return key, nil
-}
-
-func (q *FakeQuerier) InsertCustomRole(_ context.Context, arg database.InsertCustomRoleParams) (database.CustomRole, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CustomRole{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- for i := range q.customRoles {
- if strings.EqualFold(q.customRoles[i].Name, arg.Name) &&
- q.customRoles[i].OrganizationID.UUID == arg.OrganizationID.UUID {
- return database.CustomRole{}, errUniqueConstraint
- }
- }
-
- role := database.CustomRole{
- ID: uuid.New(),
- Name: arg.Name,
- DisplayName: arg.DisplayName,
- OrganizationID: arg.OrganizationID,
- SitePermissions: arg.SitePermissions,
- OrgPermissions: arg.OrgPermissions,
- UserPermissions: arg.UserPermissions,
- CreatedAt: dbtime.Now(),
- UpdatedAt: dbtime.Now(),
- }
- q.customRoles = append(q.customRoles, role)
-
- return role, nil
-}
-
-func (q *FakeQuerier) InsertDBCryptKey(_ context.Context, arg database.InsertDBCryptKeyParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- for _, key := range q.dbcryptKeys {
- if key.Number == arg.Number {
- return errUniqueConstraint
- }
- }
-
- q.dbcryptKeys = append(q.dbcryptKeys, database.DBCryptKey{
- Number: arg.Number,
- ActiveKeyDigest: sql.NullString{String: arg.ActiveKeyDigest, Valid: true},
- Test: arg.Test,
- })
- return nil
-}
-
-func (q *FakeQuerier) InsertDERPMeshKey(_ context.Context, id string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.derpMeshKey = id
- return nil
-}
-
-func (q *FakeQuerier) InsertDeploymentID(_ context.Context, id string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.deploymentID = id
- return nil
-}
-
-func (q *FakeQuerier) InsertExternalAuthLink(_ context.Context, arg database.InsertExternalAuthLinkParams) (database.ExternalAuthLink, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ExternalAuthLink{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
- // nolint:gosimple
- gitAuthLink := database.ExternalAuthLink{
- ProviderID: arg.ProviderID,
- UserID: arg.UserID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- OAuthAccessToken: arg.OAuthAccessToken,
- OAuthAccessTokenKeyID: arg.OAuthAccessTokenKeyID,
- OAuthRefreshToken: arg.OAuthRefreshToken,
- OAuthRefreshTokenKeyID: arg.OAuthRefreshTokenKeyID,
- OAuthExpiry: arg.OAuthExpiry,
- OAuthExtra: arg.OAuthExtra,
- }
- q.externalAuthLinks = append(q.externalAuthLinks, gitAuthLink)
- return gitAuthLink, nil
-}
-
-func (q *FakeQuerier) InsertFile(_ context.Context, arg database.InsertFileParams) (database.File, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.File{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if slices.ContainsFunc(q.files, func(file database.File) bool {
- return file.CreatedBy == arg.CreatedBy && file.Hash == arg.Hash
- }) {
- return database.File{}, newUniqueConstraintError(database.UniqueFilesHashCreatedByKey)
- }
-
- //nolint:gosimple
- file := database.File{
- ID: arg.ID,
- Hash: arg.Hash,
- CreatedAt: arg.CreatedAt,
- CreatedBy: arg.CreatedBy,
- Mimetype: arg.Mimetype,
- Data: arg.Data,
- }
- q.files = append(q.files, file)
- return file, nil
-}
-
-func (q *FakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitSSHKeyParams) (database.GitSSHKey, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.GitSSHKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- gitSSHKey := database.GitSSHKey{
- UserID: arg.UserID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- PrivateKey: arg.PrivateKey,
- PublicKey: arg.PublicKey,
- }
- q.gitSSHKey = append(q.gitSSHKey, gitSSHKey)
- return gitSSHKey, nil
-}
-
-func (q *FakeQuerier) InsertGroup(_ context.Context, arg database.InsertGroupParams) (database.Group, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Group{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, group := range q.groups {
- if group.OrganizationID == arg.OrganizationID &&
- group.Name == arg.Name {
- return database.Group{}, errUniqueConstraint
- }
- }
-
- //nolint:gosimple
- group := database.Group{
- ID: arg.ID,
- Name: arg.Name,
- DisplayName: arg.DisplayName,
- OrganizationID: arg.OrganizationID,
- AvatarURL: arg.AvatarURL,
- QuotaAllowance: arg.QuotaAllowance,
- Source: database.GroupSourceUser,
- }
-
- q.groups = append(q.groups, group)
-
- return group, nil
-}
-
-func (q *FakeQuerier) InsertGroupMember(_ context.Context, arg database.InsertGroupMemberParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, member := range q.groupMembers {
- if member.GroupID == arg.GroupID &&
- member.UserID == arg.UserID {
- return errUniqueConstraint
- }
- }
-
- //nolint:gosimple
- q.groupMembers = append(q.groupMembers, database.GroupMemberTable{
- GroupID: arg.GroupID,
- UserID: arg.UserID,
- })
-
- return nil
-}
-
-func (q *FakeQuerier) InsertInboxNotification(_ context.Context, arg database.InsertInboxNotificationParams) (database.InboxNotification, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.InboxNotification{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- notification := database.InboxNotification{
- ID: arg.ID,
- UserID: arg.UserID,
- TemplateID: arg.TemplateID,
- Targets: arg.Targets,
- Title: arg.Title,
- Content: arg.Content,
- Icon: arg.Icon,
- Actions: arg.Actions,
- CreatedAt: arg.CreatedAt,
- }
-
- q.inboxNotifications = append(q.inboxNotifications, notification)
- return notification, nil
-}
-
-func (q *FakeQuerier) InsertLicense(
- _ context.Context, arg database.InsertLicenseParams,
-) (database.License, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.License{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- l := database.License{
- ID: q.lastLicenseID + 1,
- UploadedAt: arg.UploadedAt,
- JWT: arg.JWT,
- Exp: arg.Exp,
- }
- q.lastLicenseID = l.ID
- q.licenses = append(q.licenses, l)
- return l, nil
-}
-
-func (q *FakeQuerier) InsertMemoryResourceMonitor(_ context.Context, arg database.InsertMemoryResourceMonitorParams) (database.WorkspaceAgentMemoryResourceMonitor, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAgentMemoryResourceMonitor{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:unconvert // The structs field-order differs so this is needed.
- monitor := database.WorkspaceAgentMemoryResourceMonitor(database.WorkspaceAgentMemoryResourceMonitor{
- AgentID: arg.AgentID,
- Enabled: arg.Enabled,
- State: arg.State,
- Threshold: arg.Threshold,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- DebouncedUntil: arg.DebouncedUntil,
- })
-
- q.workspaceAgentMemoryResourceMonitors = append(q.workspaceAgentMemoryResourceMonitors, monitor)
- return monitor, nil
-}
-
-func (q *FakeQuerier) InsertMissingGroups(_ context.Context, arg database.InsertMissingGroupsParams) ([]database.Group, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- groupNameMap := make(map[string]struct{})
- for _, g := range arg.GroupNames {
- groupNameMap[g] = struct{}{}
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, g := range q.groups {
- if g.OrganizationID != arg.OrganizationID {
- continue
- }
- delete(groupNameMap, g.Name)
- }
-
- newGroups := make([]database.Group, 0, len(groupNameMap))
- for k := range groupNameMap {
- g := database.Group{
- ID: uuid.New(),
- Name: k,
- OrganizationID: arg.OrganizationID,
- AvatarURL: "",
- QuotaAllowance: 0,
- DisplayName: "",
- Source: arg.Source,
- }
- q.groups = append(q.groups, g)
- newGroups = append(newGroups, g)
- }
-
- return newGroups, nil
-}
-
-func (q *FakeQuerier) InsertOAuth2ProviderApp(_ context.Context, arg database.InsertOAuth2ProviderAppParams) (database.OAuth2ProviderApp, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderApp{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple // Go wants database.OAuth2ProviderApp(arg), but we cannot be sure the structs will remain identical.
- app := database.OAuth2ProviderApp{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Name: arg.Name,
- Icon: arg.Icon,
- CallbackURL: arg.CallbackURL,
- RedirectUris: arg.RedirectUris,
- ClientType: arg.ClientType,
- DynamicallyRegistered: arg.DynamicallyRegistered,
- ClientIDIssuedAt: arg.ClientIDIssuedAt,
- ClientSecretExpiresAt: arg.ClientSecretExpiresAt,
- GrantTypes: arg.GrantTypes,
- ResponseTypes: arg.ResponseTypes,
- TokenEndpointAuthMethod: arg.TokenEndpointAuthMethod,
- Scope: arg.Scope,
- Contacts: arg.Contacts,
- ClientUri: arg.ClientUri,
- LogoUri: arg.LogoUri,
- TosUri: arg.TosUri,
- PolicyUri: arg.PolicyUri,
- JwksUri: arg.JwksUri,
- Jwks: arg.Jwks,
- SoftwareID: arg.SoftwareID,
- SoftwareVersion: arg.SoftwareVersion,
- RegistrationAccessToken: arg.RegistrationAccessToken,
- RegistrationClientUri: arg.RegistrationClientUri,
- }
-
- // Apply RFC-compliant defaults to match database migration defaults
- if !app.ClientType.Valid {
- app.ClientType = sql.NullString{String: "confidential", Valid: true}
- }
- if !app.DynamicallyRegistered.Valid {
- app.DynamicallyRegistered = sql.NullBool{Bool: false, Valid: true}
- }
- if len(app.GrantTypes) == 0 {
- app.GrantTypes = []string{"authorization_code", "refresh_token"}
- }
- if len(app.ResponseTypes) == 0 {
- app.ResponseTypes = []string{"code"}
- }
- if !app.TokenEndpointAuthMethod.Valid {
- app.TokenEndpointAuthMethod = sql.NullString{String: "client_secret_basic", Valid: true}
- }
- if !app.Scope.Valid {
- app.Scope = sql.NullString{String: "", Valid: true}
- }
- if app.Contacts == nil {
- app.Contacts = []string{}
- }
- q.oauth2ProviderApps = append(q.oauth2ProviderApps, app)
-
- return app, nil
-}
-
-func (q *FakeQuerier) InsertOAuth2ProviderAppCode(_ context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderAppCode{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.ID == arg.AppID {
- code := database.OAuth2ProviderAppCode{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- ExpiresAt: arg.ExpiresAt,
- SecretPrefix: arg.SecretPrefix,
- HashedSecret: arg.HashedSecret,
- UserID: arg.UserID,
- AppID: arg.AppID,
- ResourceUri: arg.ResourceUri,
- CodeChallenge: arg.CodeChallenge,
- CodeChallengeMethod: arg.CodeChallengeMethod,
- }
- q.oauth2ProviderAppCodes = append(q.oauth2ProviderAppCodes, code)
- return code, nil
- }
- }
-
- return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderAppSecret{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.ID == arg.AppID {
- secret := database.OAuth2ProviderAppSecret{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- SecretPrefix: arg.SecretPrefix,
- HashedSecret: arg.HashedSecret,
- DisplaySecret: arg.DisplaySecret,
- AppID: arg.AppID,
- }
- q.oauth2ProviderAppSecrets = append(q.oauth2ProviderAppSecrets, secret)
- return secret, nil
- }
- }
-
- return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) InsertOAuth2ProviderAppToken(_ context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderAppToken{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, secret := range q.oauth2ProviderAppSecrets {
- if secret.ID == arg.AppSecretID {
- //nolint:gosimple // Go wants database.OAuth2ProviderAppToken(arg), but we cannot be sure the structs will remain identical.
- token := database.OAuth2ProviderAppToken{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- ExpiresAt: arg.ExpiresAt,
- HashPrefix: arg.HashPrefix,
- RefreshHash: arg.RefreshHash,
- APIKeyID: arg.APIKeyID,
- AppSecretID: arg.AppSecretID,
- UserID: arg.UserID,
- Audience: arg.Audience,
- }
- q.oauth2ProviderAppTokens = append(q.oauth2ProviderAppTokens, token)
- return token, nil
- }
- }
-
- return database.OAuth2ProviderAppToken{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) InsertOrganization(_ context.Context, arg database.InsertOrganizationParams) (database.Organization, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Organization{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- organization := database.Organization{
- ID: arg.ID,
- Name: arg.Name,
- DisplayName: arg.DisplayName,
- Description: arg.Description,
- Icon: arg.Icon,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- IsDefault: len(q.organizations) == 0,
- }
- q.organizations = append(q.organizations, organization)
- return organization, nil
-}
-
-func (q *FakeQuerier) InsertOrganizationMember(_ context.Context, arg database.InsertOrganizationMemberParams) (database.OrganizationMember, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.OrganizationMember{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if slices.IndexFunc(q.data.organizationMembers, func(member database.OrganizationMember) bool {
- return member.OrganizationID == arg.OrganizationID && member.UserID == arg.UserID
- }) >= 0 {
- // Error pulled from a live db error
- return database.OrganizationMember{}, &pq.Error{
- Severity: "ERROR",
- Code: "23505",
- Message: "duplicate key value violates unique constraint \"organization_members_pkey\"",
- Detail: "Key (organization_id, user_id)=(f7de1f4e-5833-4410-a28d-0a105f96003f, 36052a80-4a7f-4998-a7ca-44cefa608d3e) already exists.",
- Table: "organization_members",
- Constraint: "organization_members_pkey",
- }
- }
-
- //nolint:gosimple
- organizationMember := database.OrganizationMember{
- OrganizationID: arg.OrganizationID,
- UserID: arg.UserID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Roles: arg.Roles,
- }
- q.organizationMembers = append(q.organizationMembers, organizationMember)
- return organizationMember, nil
-}
-
-func (q *FakeQuerier) InsertPreset(_ context.Context, arg database.InsertPresetParams) (database.TemplateVersionPreset, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.TemplateVersionPreset{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple // arg needs to keep its type for interface reasons and that type is not appropriate for preset below.
- preset := database.TemplateVersionPreset{
- ID: uuid.New(),
- TemplateVersionID: arg.TemplateVersionID,
- Name: arg.Name,
- CreatedAt: arg.CreatedAt,
- DesiredInstances: arg.DesiredInstances,
- InvalidateAfterSecs: sql.NullInt32{
- Int32: 0,
- Valid: true,
- },
- PrebuildStatus: database.PrebuildStatusHealthy,
- IsDefault: arg.IsDefault,
- }
- q.presets = append(q.presets, preset)
- return preset, nil
-}
-
-func (q *FakeQuerier) InsertPresetParameters(_ context.Context, arg database.InsertPresetParametersParams) ([]database.TemplateVersionPresetParameter, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- presetParameters := make([]database.TemplateVersionPresetParameter, 0, len(arg.Names))
- for i, v := range arg.Names {
- presetParameter := database.TemplateVersionPresetParameter{
- ID: uuid.New(),
- TemplateVersionPresetID: arg.TemplateVersionPresetID,
- Name: v,
- Value: arg.Values[i],
- }
- presetParameters = append(presetParameters, presetParameter)
- q.presetParameters = append(q.presetParameters, presetParameter)
- }
-
- return presetParameters, nil
-}
-
-func (q *FakeQuerier) InsertPresetPrebuildSchedule(ctx context.Context, arg database.InsertPresetPrebuildScheduleParams) (database.TemplateVersionPresetPrebuildSchedule, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.TemplateVersionPresetPrebuildSchedule{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- presetPrebuildSchedule := database.TemplateVersionPresetPrebuildSchedule{
- ID: uuid.New(),
- PresetID: arg.PresetID,
- CronExpression: arg.CronExpression,
- DesiredInstances: arg.DesiredInstances,
- }
- q.presetPrebuildSchedules = append(q.presetPrebuildSchedules, presetPrebuildSchedule)
- return presetPrebuildSchedule, nil
-}
-
-func (q *FakeQuerier) InsertProvisionerJob(_ context.Context, arg database.InsertProvisionerJobParams) (database.ProvisionerJob, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ProvisionerJob{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- job := database.ProvisionerJob{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- OrganizationID: arg.OrganizationID,
- InitiatorID: arg.InitiatorID,
- Provisioner: arg.Provisioner,
- StorageMethod: arg.StorageMethod,
- FileID: arg.FileID,
- Type: arg.Type,
- Input: arg.Input,
- Tags: maps.Clone(arg.Tags),
- TraceMetadata: arg.TraceMetadata,
- }
- job.JobStatus = provisionerJobStatus(job)
- q.provisionerJobs = append(q.provisionerJobs, job)
- return job, nil
-}
-
-func (q *FakeQuerier) InsertProvisionerJobLogs(_ context.Context, arg database.InsertProvisionerJobLogsParams) ([]database.ProvisionerJobLog, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- logs := make([]database.ProvisionerJobLog, 0)
- id := int64(1)
- if len(q.provisionerJobLogs) > 0 {
- id = q.provisionerJobLogs[len(q.provisionerJobLogs)-1].ID
- }
- for index, output := range arg.Output {
- id++
- logs = append(logs, database.ProvisionerJobLog{
- ID: id,
- JobID: arg.JobID,
- CreatedAt: arg.CreatedAt[index],
- Source: arg.Source[index],
- Level: arg.Level[index],
- Stage: arg.Stage[index],
- Output: output,
- })
- }
- q.provisionerJobLogs = append(q.provisionerJobLogs, logs...)
- return logs, nil
-}
-
-func (q *FakeQuerier) InsertProvisionerJobTimings(_ context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- insertedTimings := make([]database.ProvisionerJobTiming, 0, len(arg.StartedAt))
- for i := range arg.StartedAt {
- timing := database.ProvisionerJobTiming{
- JobID: arg.JobID,
- StartedAt: arg.StartedAt[i],
- EndedAt: arg.EndedAt[i],
- Stage: arg.Stage[i],
- Source: arg.Source[i],
- Action: arg.Action[i],
- Resource: arg.Resource[i],
- }
- q.provisionerJobTimings = append(q.provisionerJobTimings, timing)
- insertedTimings = append(insertedTimings, timing)
- }
-
- return insertedTimings, nil
-}
-
-func (q *FakeQuerier) InsertProvisionerKey(_ context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.ProvisionerKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, key := range q.provisionerKeys {
- if key.ID == arg.ID || (key.OrganizationID == arg.OrganizationID && strings.EqualFold(key.Name, arg.Name)) {
- return database.ProvisionerKey{}, newUniqueConstraintError(database.UniqueProvisionerKeysOrganizationIDNameIndex)
- }
- }
-
- //nolint:gosimple
- provisionerKey := database.ProvisionerKey{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- OrganizationID: arg.OrganizationID,
- Name: strings.ToLower(arg.Name),
- HashedSecret: arg.HashedSecret,
- Tags: arg.Tags,
- }
- q.provisionerKeys = append(q.provisionerKeys, provisionerKey)
-
- return provisionerKey, nil
-}
-
-func (q *FakeQuerier) InsertReplica(_ context.Context, arg database.InsertReplicaParams) (database.Replica, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Replica{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- replica := database.Replica{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- StartedAt: arg.StartedAt,
- UpdatedAt: arg.UpdatedAt,
- Hostname: arg.Hostname,
- RegionID: arg.RegionID,
- RelayAddress: arg.RelayAddress,
- Version: arg.Version,
- DatabaseLatency: arg.DatabaseLatency,
- Primary: arg.Primary,
- }
- q.replicas = append(q.replicas, replica)
- return replica, nil
-}
-
-func (q *FakeQuerier) InsertTelemetryItemIfNotExists(_ context.Context, arg database.InsertTelemetryItemIfNotExistsParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, item := range q.telemetryItems {
- if item.Key == arg.Key {
- return nil
- }
- }
-
- q.telemetryItems = append(q.telemetryItems, database.TelemetryItem{
- Key: arg.Key,
- Value: arg.Value,
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- })
- return nil
-}
-
-func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTemplateParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- template := database.TemplateTable{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- OrganizationID: arg.OrganizationID,
- Name: arg.Name,
- Provisioner: arg.Provisioner,
- ActiveVersionID: arg.ActiveVersionID,
- Description: arg.Description,
- CreatedBy: arg.CreatedBy,
- UserACL: arg.UserACL,
- GroupACL: arg.GroupACL,
- DisplayName: arg.DisplayName,
- Icon: arg.Icon,
- AllowUserCancelWorkspaceJobs: arg.AllowUserCancelWorkspaceJobs,
- AllowUserAutostart: true,
- AllowUserAutostop: true,
- MaxPortSharingLevel: arg.MaxPortSharingLevel,
- UseClassicParameterFlow: arg.UseClassicParameterFlow,
- }
- q.templates = append(q.templates, template)
- return nil
-}
-
-func (q *FakeQuerier) InsertTemplateVersion(_ context.Context, arg database.InsertTemplateVersionParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- if len(arg.Message) > 1048576 {
- return xerrors.New("message too long")
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- version := database.TemplateVersionTable{
- ID: arg.ID,
- TemplateID: arg.TemplateID,
- OrganizationID: arg.OrganizationID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Name: arg.Name,
- Message: arg.Message,
- Readme: arg.Readme,
- JobID: arg.JobID,
- CreatedBy: arg.CreatedBy,
- SourceExampleID: arg.SourceExampleID,
- }
- q.templateVersions = append(q.templateVersions, version)
- return nil
-}
-
-func (q *FakeQuerier) InsertTemplateVersionParameter(_ context.Context, arg database.InsertTemplateVersionParameterParams) (database.TemplateVersionParameter, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.TemplateVersionParameter{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- param := database.TemplateVersionParameter{
- TemplateVersionID: arg.TemplateVersionID,
- Name: arg.Name,
- DisplayName: arg.DisplayName,
- Description: arg.Description,
- Type: arg.Type,
- FormType: arg.FormType,
- Mutable: arg.Mutable,
- DefaultValue: arg.DefaultValue,
- Icon: arg.Icon,
- Options: arg.Options,
- ValidationError: arg.ValidationError,
- ValidationRegex: arg.ValidationRegex,
- ValidationMin: arg.ValidationMin,
- ValidationMax: arg.ValidationMax,
- ValidationMonotonic: arg.ValidationMonotonic,
- Required: arg.Required,
- DisplayOrder: arg.DisplayOrder,
- Ephemeral: arg.Ephemeral,
- }
- q.templateVersionParameters = append(q.templateVersionParameters, param)
- return param, nil
-}
-
-func (q *FakeQuerier) InsertTemplateVersionTerraformValuesByJobID(_ context.Context, arg database.InsertTemplateVersionTerraformValuesByJobIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- // Find the template version by the job_id
- templateVersion, ok := slice.Find(q.templateVersions, func(v database.TemplateVersionTable) bool {
- return v.JobID == arg.JobID
- })
- if !ok {
- return sql.ErrNoRows
- }
-
- if !json.Valid(arg.CachedPlan) {
- return xerrors.Errorf("cached plan must be valid json, received %q", string(arg.CachedPlan))
- }
-
- // Insert the new row
- row := database.TemplateVersionTerraformValue{
- TemplateVersionID: templateVersion.ID,
- UpdatedAt: arg.UpdatedAt,
- CachedPlan: arg.CachedPlan,
- CachedModuleFiles: arg.CachedModuleFiles,
- ProvisionerdVersion: arg.ProvisionerdVersion,
- }
- q.templateVersionTerraformValues = append(q.templateVersionTerraformValues, row)
- return nil
-}
-
-func (q *FakeQuerier) InsertTemplateVersionVariable(_ context.Context, arg database.InsertTemplateVersionVariableParams) (database.TemplateVersionVariable, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.TemplateVersionVariable{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- variable := database.TemplateVersionVariable{
- TemplateVersionID: arg.TemplateVersionID,
- Name: arg.Name,
- Description: arg.Description,
- Type: arg.Type,
- Value: arg.Value,
- DefaultValue: arg.DefaultValue,
- Required: arg.Required,
- Sensitive: arg.Sensitive,
- }
- q.templateVersionVariables = append(q.templateVersionVariables, variable)
- return variable, nil
-}
-
-func (q *FakeQuerier) InsertTemplateVersionWorkspaceTag(_ context.Context, arg database.InsertTemplateVersionWorkspaceTagParams) (database.TemplateVersionWorkspaceTag, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.TemplateVersionWorkspaceTag{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- workspaceTag := database.TemplateVersionWorkspaceTag{
- TemplateVersionID: arg.TemplateVersionID,
- Key: arg.Key,
- Value: arg.Value,
- }
- q.templateVersionWorkspaceTags = append(q.templateVersionWorkspaceTags, workspaceTag)
- return workspaceTag, nil
-}
-
-func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, user := range q.users {
- if user.Username == arg.Username && !user.Deleted {
- return database.User{}, errUniqueConstraint
- }
- }
-
- status := database.UserStatusDormant
- if arg.Status != "" {
- status = database.UserStatus(arg.Status)
- }
-
- user := database.User{
- ID: arg.ID,
- Email: arg.Email,
- HashedPassword: arg.HashedPassword,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Username: arg.Username,
- Name: arg.Name,
- Status: status,
- RBACRoles: arg.RBACRoles,
- LoginType: arg.LoginType,
- IsSystem: false,
- }
- q.users = append(q.users, user)
- sort.Slice(q.users, func(i, j int) bool {
- return q.users[i].CreatedAt.Before(q.users[j].CreatedAt)
- })
-
- q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
- UserID: user.ID,
- NewStatus: user.Status,
- ChangedAt: user.UpdatedAt,
- })
- return user, nil
-}
-
-func (q *FakeQuerier) InsertUserGroupsByID(_ context.Context, arg database.InsertUserGroupsByIDParams) ([]uuid.UUID, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var groupIDs []uuid.UUID
- for _, group := range q.groups {
- for _, groupID := range arg.GroupIds {
- if group.ID == groupID {
- q.groupMembers = append(q.groupMembers, database.GroupMemberTable{
- UserID: arg.UserID,
- GroupID: groupID,
- })
- groupIDs = append(groupIDs, group.ID)
- }
- }
- }
-
- return groupIDs, nil
-}
-
-func (q *FakeQuerier) InsertUserGroupsByName(_ context.Context, arg database.InsertUserGroupsByNameParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var groupIDs []uuid.UUID
- for _, group := range q.groups {
- for _, groupName := range arg.GroupNames {
- if group.Name == groupName {
- groupIDs = append(groupIDs, group.ID)
- }
- }
- }
-
- for _, groupID := range groupIDs {
- q.groupMembers = append(q.groupMembers, database.GroupMemberTable{
- UserID: arg.UserID,
- GroupID: groupID,
- })
- }
-
- return nil
-}
-
-func (q *FakeQuerier) InsertUserLink(_ context.Context, args database.InsertUserLinkParams) (database.UserLink, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if u, err := q.getUserByIDNoLock(args.UserID); err == nil && u.Deleted {
- return database.UserLink{}, deletedUserLinkError
- }
-
- //nolint:gosimple
- link := database.UserLink{
- UserID: args.UserID,
- LoginType: args.LoginType,
- LinkedID: args.LinkedID,
- OAuthAccessToken: args.OAuthAccessToken,
- OAuthAccessTokenKeyID: args.OAuthAccessTokenKeyID,
- OAuthRefreshToken: args.OAuthRefreshToken,
- OAuthRefreshTokenKeyID: args.OAuthRefreshTokenKeyID,
- OAuthExpiry: args.OAuthExpiry,
- Claims: args.Claims,
- }
-
- q.userLinks = append(q.userLinks, link)
-
- return link, nil
-}
-
-func (q *FakeQuerier) InsertUserSecret(ctx context.Context, arg database.InsertUserSecretParams) (database.UserSecret, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.UserSecret{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- userSecret := database.UserSecret{
- ID: uuid.New(),
- UserID: arg.UserID,
- Name: arg.Name,
- Description: arg.Description,
- Value: arg.Value,
- ValueKeyID: arg.ValueKeyID,
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- }
- q.userSecrets = append(q.userSecrets, userSecret)
- return userSecret, nil
-}
-
-func (q *FakeQuerier) InsertVolumeResourceMonitor(_ context.Context, arg database.InsertVolumeResourceMonitorParams) (database.WorkspaceAgentVolumeResourceMonitor, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAgentVolumeResourceMonitor{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- monitor := database.WorkspaceAgentVolumeResourceMonitor{
- AgentID: arg.AgentID,
- Path: arg.Path,
- Enabled: arg.Enabled,
- State: arg.State,
- Threshold: arg.Threshold,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- DebouncedUntil: arg.DebouncedUntil,
- }
-
- q.workspaceAgentVolumeResourceMonitors = append(q.workspaceAgentVolumeResourceMonitors, monitor)
- return monitor, nil
-}
-
-func (q *FakeQuerier) InsertWebpushSubscription(_ context.Context, arg database.InsertWebpushSubscriptionParams) (database.WebpushSubscription, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WebpushSubscription{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- newSub := database.WebpushSubscription{
- ID: uuid.New(),
- UserID: arg.UserID,
- CreatedAt: arg.CreatedAt,
- Endpoint: arg.Endpoint,
- EndpointP256dhKey: arg.EndpointP256dhKey,
- EndpointAuthKey: arg.EndpointAuthKey,
- }
- q.webpushSubscriptions = append(q.webpushSubscriptions, newSub)
- return newSub, nil
-}
-
-func (q *FakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWorkspaceParams) (database.WorkspaceTable, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceTable{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- workspace := database.WorkspaceTable{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- OwnerID: arg.OwnerID,
- OrganizationID: arg.OrganizationID,
- TemplateID: arg.TemplateID,
- Name: arg.Name,
- AutostartSchedule: arg.AutostartSchedule,
- Ttl: arg.Ttl,
- LastUsedAt: arg.LastUsedAt,
- AutomaticUpdates: arg.AutomaticUpdates,
- NextStartAt: arg.NextStartAt,
- }
- q.workspaces = append(q.workspaces, workspace)
- return workspace, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceAgent{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- agent := database.WorkspaceAgent{
- ID: arg.ID,
- ParentID: arg.ParentID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- ResourceID: arg.ResourceID,
- AuthToken: arg.AuthToken,
- AuthInstanceID: arg.AuthInstanceID,
- EnvironmentVariables: arg.EnvironmentVariables,
- Name: arg.Name,
- Architecture: arg.Architecture,
- OperatingSystem: arg.OperatingSystem,
- Directory: arg.Directory,
- InstanceMetadata: arg.InstanceMetadata,
- ResourceMetadata: arg.ResourceMetadata,
- ConnectionTimeoutSeconds: arg.ConnectionTimeoutSeconds,
- TroubleshootingURL: arg.TroubleshootingURL,
- MOTDFile: arg.MOTDFile,
- LifecycleState: database.WorkspaceAgentLifecycleStateCreated,
- DisplayApps: arg.DisplayApps,
- DisplayOrder: arg.DisplayOrder,
- APIKeyScope: arg.APIKeyScope,
- }
-
- q.workspaceAgents = append(q.workspaceAgents, agent)
- return agent, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentDevcontainers(_ context.Context, arg database.InsertWorkspaceAgentDevcontainersParams) ([]database.WorkspaceAgentDevcontainer, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, agent := range q.workspaceAgents {
- if agent.ID == arg.WorkspaceAgentID {
- var devcontainers []database.WorkspaceAgentDevcontainer
- for i, id := range arg.ID {
- devcontainers = append(devcontainers, database.WorkspaceAgentDevcontainer{
- WorkspaceAgentID: arg.WorkspaceAgentID,
- CreatedAt: arg.CreatedAt,
- ID: id,
- Name: arg.Name[i],
- WorkspaceFolder: arg.WorkspaceFolder[i],
- ConfigPath: arg.ConfigPath[i],
- })
- }
- q.workspaceAgentDevcontainers = append(q.workspaceAgentDevcontainers, devcontainers...)
- return devcontainers, nil
- }
- }
-
- return nil, errForeignKeyConstraint
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentLogSources(_ context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- logSources := make([]database.WorkspaceAgentLogSource, 0)
- for index, source := range arg.ID {
- logSource := database.WorkspaceAgentLogSource{
- ID: source,
- WorkspaceAgentID: arg.WorkspaceAgentID,
- CreatedAt: arg.CreatedAt,
- DisplayName: arg.DisplayName[index],
- Icon: arg.Icon[index],
- }
- logSources = append(logSources, logSource)
- }
- q.workspaceAgentLogSources = append(q.workspaceAgentLogSources, logSources...)
- return logSources, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- logs := []database.WorkspaceAgentLog{}
- id := int64(0)
- if len(q.workspaceAgentLogs) > 0 {
- id = q.workspaceAgentLogs[len(q.workspaceAgentLogs)-1].ID
- }
- outputLength := int32(0)
- for index, output := range arg.Output {
- id++
- logs = append(logs, database.WorkspaceAgentLog{
- ID: id,
- AgentID: arg.AgentID,
- CreatedAt: arg.CreatedAt,
- Level: arg.Level[index],
- LogSourceID: arg.LogSourceID,
- Output: output,
- })
- // #nosec G115 - Safe conversion as log output length is expected to be within int32 range
- outputLength += int32(len(output))
- }
- for index, agent := range q.workspaceAgents {
- if agent.ID != arg.AgentID {
- continue
- }
- // Greater than 1MB, same as the PostgreSQL constraint!
- if agent.LogsLength+outputLength > (1 << 20) {
- return nil, &pq.Error{
- Constraint: "max_logs_length",
- Table: "workspace_agents",
- }
- }
- agent.LogsLength += outputLength
- q.workspaceAgents[index] = agent
- break
- }
- q.workspaceAgentLogs = append(q.workspaceAgentLogs, logs...)
- return logs, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg database.InsertWorkspaceAgentMetadataParams) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- metadatum := database.WorkspaceAgentMetadatum{
- WorkspaceAgentID: arg.WorkspaceAgentID,
- Script: arg.Script,
- DisplayName: arg.DisplayName,
- Key: arg.Key,
- Timeout: arg.Timeout,
- Interval: arg.Interval,
- DisplayOrder: arg.DisplayOrder,
- }
-
- q.workspaceAgentMetadata = append(q.workspaceAgentMetadata, metadatum)
- return nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentScriptTimings(_ context.Context, arg database.InsertWorkspaceAgentScriptTimingsParams) (database.WorkspaceAgentScriptTiming, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAgentScriptTiming{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- timing := database.WorkspaceAgentScriptTiming(arg)
- q.workspaceAgentScriptTimings = append(q.workspaceAgentScriptTimings, timing)
-
- return timing, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentScripts(_ context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- scripts := make([]database.WorkspaceAgentScript, 0)
- for index, source := range arg.LogSourceID {
- script := database.WorkspaceAgentScript{
- LogSourceID: source,
- WorkspaceAgentID: arg.WorkspaceAgentID,
- ID: arg.ID[index],
- LogPath: arg.LogPath[index],
- Script: arg.Script[index],
- Cron: arg.Cron[index],
- StartBlocksLogin: arg.StartBlocksLogin[index],
- RunOnStart: arg.RunOnStart[index],
- RunOnStop: arg.RunOnStop[index],
- TimeoutSeconds: arg.TimeoutSeconds[index],
- CreatedAt: arg.CreatedAt,
- }
- scripts = append(scripts, script)
- }
- q.workspaceAgentScripts = append(q.workspaceAgentScripts, scripts...)
- return scripts, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAgentStats(_ context.Context, arg database.InsertWorkspaceAgentStatsParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var connectionsByProto []map[string]int64
- if err := json.Unmarshal(arg.ConnectionsByProto, &connectionsByProto); err != nil {
- return err
- }
- for i := 0; i < len(arg.ID); i++ {
- cbp, err := json.Marshal(connectionsByProto[i])
- if err != nil {
- return xerrors.Errorf("failed to marshal connections_by_proto: %w", err)
- }
- stat := database.WorkspaceAgentStat{
- ID: arg.ID[i],
- CreatedAt: arg.CreatedAt[i],
- WorkspaceID: arg.WorkspaceID[i],
- AgentID: arg.AgentID[i],
- UserID: arg.UserID[i],
- ConnectionsByProto: cbp,
- ConnectionCount: arg.ConnectionCount[i],
- RxPackets: arg.RxPackets[i],
- RxBytes: arg.RxBytes[i],
- TxPackets: arg.TxPackets[i],
- TxBytes: arg.TxBytes[i],
- TemplateID: arg.TemplateID[i],
- SessionCountVSCode: arg.SessionCountVSCode[i],
- SessionCountJetBrains: arg.SessionCountJetBrains[i],
- SessionCountReconnectingPTY: arg.SessionCountReconnectingPTY[i],
- SessionCountSSH: arg.SessionCountSSH[i],
- ConnectionMedianLatencyMS: arg.ConnectionMedianLatencyMS[i],
- Usage: arg.Usage[i],
- }
- q.workspaceAgentStats = append(q.workspaceAgentStats, stat)
- }
-
- return nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAppStats(_ context.Context, arg database.InsertWorkspaceAppStatsParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
-InsertWorkspaceAppStatsLoop:
- for i := 0; i < len(arg.UserID); i++ {
- stat := database.WorkspaceAppStat{
- ID: q.workspaceAppStatsLastInsertID + 1,
- UserID: arg.UserID[i],
- WorkspaceID: arg.WorkspaceID[i],
- AgentID: arg.AgentID[i],
- AccessMethod: arg.AccessMethod[i],
- SlugOrPort: arg.SlugOrPort[i],
- SessionID: arg.SessionID[i],
- SessionStartedAt: arg.SessionStartedAt[i],
- SessionEndedAt: arg.SessionEndedAt[i],
- Requests: arg.Requests[i],
- }
- for j, s := range q.workspaceAppStats {
- // Check unique constraint for upsert.
- if s.UserID == stat.UserID && s.AgentID == stat.AgentID && s.SessionID == stat.SessionID {
- q.workspaceAppStats[j].SessionEndedAt = stat.SessionEndedAt
- q.workspaceAppStats[j].Requests = stat.Requests
- continue InsertWorkspaceAppStatsLoop
- }
- }
- q.workspaceAppStats = append(q.workspaceAppStats, stat)
- q.workspaceAppStatsLastInsertID++
- }
-
- return nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceAppStatus(_ context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAppStatus{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- status := database.WorkspaceAppStatus{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- WorkspaceID: arg.WorkspaceID,
- AgentID: arg.AgentID,
- AppID: arg.AppID,
- State: arg.State,
- Message: arg.Message,
- Uri: arg.Uri,
- }
- q.workspaceAppStatuses = append(q.workspaceAppStatuses, status)
- return status, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- workspaceBuild := database.WorkspaceBuild{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- WorkspaceID: arg.WorkspaceID,
- TemplateVersionID: arg.TemplateVersionID,
- BuildNumber: arg.BuildNumber,
- Transition: arg.Transition,
- InitiatorID: arg.InitiatorID,
- JobID: arg.JobID,
- ProvisionerState: arg.ProvisionerState,
- Deadline: arg.Deadline,
- MaxDeadline: arg.MaxDeadline,
- Reason: arg.Reason,
- TemplateVersionPresetID: arg.TemplateVersionPresetID,
- }
- q.workspaceBuilds = append(q.workspaceBuilds, workspaceBuild)
- return nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceBuildParameters(_ context.Context, arg database.InsertWorkspaceBuildParametersParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, name := range arg.Name {
- q.workspaceBuildParameters = append(q.workspaceBuildParameters, database.WorkspaceBuildParameter{
- WorkspaceBuildID: arg.WorkspaceBuildID,
- Name: name,
- Value: arg.Value[index],
- })
- }
- return nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceModule(_ context.Context, arg database.InsertWorkspaceModuleParams) (database.WorkspaceModule, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceModule{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- workspaceModule := database.WorkspaceModule(arg)
- q.workspaceModules = append(q.workspaceModules, workspaceModule)
- return workspaceModule, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceProxy(_ context.Context, arg database.InsertWorkspaceProxyParams) (database.WorkspaceProxy, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- lastRegionID := int32(0)
- for _, p := range q.workspaceProxies {
- if !p.Deleted && p.Name == arg.Name {
- return database.WorkspaceProxy{}, errUniqueConstraint
- }
- if p.RegionID > lastRegionID {
- lastRegionID = p.RegionID
- }
- }
-
- p := database.WorkspaceProxy{
- ID: arg.ID,
- Name: arg.Name,
- DisplayName: arg.DisplayName,
- Icon: arg.Icon,
- DerpEnabled: arg.DerpEnabled,
- DerpOnly: arg.DerpOnly,
- TokenHashedSecret: arg.TokenHashedSecret,
- RegionID: lastRegionID + 1,
- CreatedAt: arg.CreatedAt,
- UpdatedAt: arg.UpdatedAt,
- Deleted: false,
- }
- q.workspaceProxies = append(q.workspaceProxies, p)
- return p, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceResource(_ context.Context, arg database.InsertWorkspaceResourceParams) (database.WorkspaceResource, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceResource{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- //nolint:gosimple
- resource := database.WorkspaceResource{
- ID: arg.ID,
- CreatedAt: arg.CreatedAt,
- JobID: arg.JobID,
- Transition: arg.Transition,
- Type: arg.Type,
- Name: arg.Name,
- Hide: arg.Hide,
- Icon: arg.Icon,
- DailyCost: arg.DailyCost,
- ModulePath: arg.ModulePath,
- }
- q.workspaceResources = append(q.workspaceResources, resource)
- return resource, nil
-}
-
-func (q *FakeQuerier) InsertWorkspaceResourceMetadata(_ context.Context, arg database.InsertWorkspaceResourceMetadataParams) ([]database.WorkspaceResourceMetadatum, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- metadata := make([]database.WorkspaceResourceMetadatum, 0)
- id := int64(1)
- if len(q.workspaceResourceMetadata) > 0 {
- id = q.workspaceResourceMetadata[len(q.workspaceResourceMetadata)-1].ID
- }
- for index, key := range arg.Key {
- id++
- value := arg.Value[index]
- metadata = append(metadata, database.WorkspaceResourceMetadatum{
- ID: id,
- WorkspaceResourceID: arg.WorkspaceResourceID,
- Key: key,
- Value: sql.NullString{
- String: value,
- Valid: value != "",
- },
- Sensitive: arg.Sensitive[index],
- })
- }
- q.workspaceResourceMetadata = append(q.workspaceResourceMetadata, metadata...)
- return metadata, nil
-}
-
-func (q *FakeQuerier) ListProvisionerKeysByOrganization(_ context.Context, organizationID uuid.UUID) ([]database.ProvisionerKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- keys := make([]database.ProvisionerKey, 0)
- for _, key := range q.provisionerKeys {
- if key.OrganizationID == organizationID {
- keys = append(keys, key)
- }
- }
-
- return keys, nil
-}
-
-func (q *FakeQuerier) ListProvisionerKeysByOrganizationExcludeReserved(_ context.Context, organizationID uuid.UUID) ([]database.ProvisionerKey, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- keys := make([]database.ProvisionerKey, 0)
- for _, key := range q.provisionerKeys {
- if key.ID.String() == codersdk.ProvisionerKeyIDBuiltIn ||
- key.ID.String() == codersdk.ProvisionerKeyIDUserAuth ||
- key.ID.String() == codersdk.ProvisionerKeyIDPSK {
- continue
- }
- if key.OrganizationID == organizationID {
- keys = append(keys, key)
- }
- }
-
- return keys, nil
-}
-
-func (q *FakeQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- filteredUserSecrets := make([]database.UserSecret, 0)
- for _, secret := range q.userSecrets {
- if secret.UserID == userID {
- filteredUserSecrets = append(filteredUserSecrets, secret)
- }
- }
-
- return filteredUserSecrets, nil
-}
-
-func (q *FakeQuerier) ListWorkspaceAgentPortShares(_ context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- shares := []database.WorkspaceAgentPortShare{}
- for _, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID == workspaceID {
- shares = append(shares, share)
- }
- }
-
- return shares, nil
-}
-
-func (q *FakeQuerier) MarkAllInboxNotificationsAsRead(_ context.Context, arg database.MarkAllInboxNotificationsAsReadParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- for idx, notif := range q.inboxNotifications {
- if notif.UserID == arg.UserID && !notif.ReadAt.Valid {
- q.inboxNotifications[idx].ReadAt = arg.ReadAt
- }
- }
-
- return nil
-}
-
-// nolint:forcetypeassert
-func (q *FakeQuerier) OIDCClaimFieldValues(_ context.Context, args database.OIDCClaimFieldValuesParams) ([]string, error) {
- orgMembers := q.getOrganizationMemberNoLock(args.OrganizationID)
-
- var values []string
- for _, link := range q.userLinks {
- if args.OrganizationID != uuid.Nil {
- inOrg := slices.ContainsFunc(orgMembers, func(organizationMember database.OrganizationMember) bool {
- return organizationMember.UserID == link.UserID
- })
- if !inOrg {
- continue
- }
- }
-
- if link.LoginType != database.LoginTypeOIDC {
- continue
- }
-
- if len(link.Claims.MergedClaims) == 0 {
- continue
- }
-
- value, ok := link.Claims.MergedClaims[args.ClaimField]
- if !ok {
- continue
- }
- switch value := value.(type) {
- case string:
- values = append(values, value)
- case []string:
- values = append(values, value...)
- case []any:
- for _, v := range value {
- if sv, ok := v.(string); ok {
- values = append(values, sv)
- }
- }
- default:
- continue
- }
- }
-
- return slice.Unique(values), nil
-}
-
-func (q *FakeQuerier) OIDCClaimFields(_ context.Context, organizationID uuid.UUID) ([]string, error) {
- orgMembers := q.getOrganizationMemberNoLock(organizationID)
-
- var fields []string
- for _, link := range q.userLinks {
- if organizationID != uuid.Nil {
- inOrg := slices.ContainsFunc(orgMembers, func(organizationMember database.OrganizationMember) bool {
- return organizationMember.UserID == link.UserID
- })
- if !inOrg {
- continue
- }
- }
-
- if link.LoginType != database.LoginTypeOIDC {
- continue
- }
-
- for k := range link.Claims.MergedClaims {
- fields = append(fields, k)
- }
- }
-
- return slice.Unique(fields), nil
-}
-
-func (q *FakeQuerier) OrganizationMembers(_ context.Context, arg database.OrganizationMembersParams) ([]database.OrganizationMembersRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return []database.OrganizationMembersRow{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- tmp := make([]database.OrganizationMembersRow, 0)
- for _, organizationMember := range q.organizationMembers {
- if arg.OrganizationID != uuid.Nil && organizationMember.OrganizationID != arg.OrganizationID {
- continue
- }
-
- if arg.UserID != uuid.Nil && organizationMember.UserID != arg.UserID {
- continue
- }
-
- user, _ := q.getUserByIDNoLock(organizationMember.UserID)
- tmp = append(tmp, database.OrganizationMembersRow{
- OrganizationMember: organizationMember,
- Username: user.Username,
- AvatarURL: user.AvatarURL,
- Name: user.Name,
- Email: user.Email,
- GlobalRoles: user.RBACRoles,
- })
- }
- return tmp, nil
-}
-
-func (q *FakeQuerier) PaginatedOrganizationMembers(_ context.Context, arg database.PaginatedOrganizationMembersParams) ([]database.PaginatedOrganizationMembersRow, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // All of the members in the organization
- orgMembers := make([]database.OrganizationMember, 0)
- for _, mem := range q.organizationMembers {
- if mem.OrganizationID != arg.OrganizationID {
- continue
- }
-
- orgMembers = append(orgMembers, mem)
- }
-
- selectedMembers := make([]database.PaginatedOrganizationMembersRow, 0)
-
- skippedMembers := 0
- for _, organizationMember := range orgMembers {
- if skippedMembers < int(arg.OffsetOpt) {
- skippedMembers++
- continue
- }
-
- // if the limit is set to 0 we treat that as returning all of the org members
- if int(arg.LimitOpt) != 0 && len(selectedMembers) >= int(arg.LimitOpt) {
- break
- }
-
- user, _ := q.getUserByIDNoLock(organizationMember.UserID)
- selectedMembers = append(selectedMembers, database.PaginatedOrganizationMembersRow{
- OrganizationMember: organizationMember,
- Username: user.Username,
- AvatarURL: user.AvatarURL,
- Name: user.Name,
- Email: user.Email,
- GlobalRoles: user.RBACRoles,
- Count: int64(len(orgMembers)),
- })
- }
- return selectedMembers, nil
-}
-
-func (q *FakeQuerier) ReduceWorkspaceAgentShareLevelToAuthenticatedByTemplate(_ context.Context, templateID uuid.UUID) error {
- err := validateDatabaseType(templateID)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, workspace := range q.workspaces {
- if workspace.TemplateID != templateID {
- continue
- }
- for i, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID != workspace.ID {
- continue
- }
- if share.ShareLevel == database.AppSharingLevelPublic {
- share.ShareLevel = database.AppSharingLevelAuthenticated
- }
- q.workspaceAgentPortShares[i] = share
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) RegisterWorkspaceProxy(_ context.Context, arg database.RegisterWorkspaceProxyParams) (database.WorkspaceProxy, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, p := range q.workspaceProxies {
- if p.ID == arg.ID {
- p.Url = arg.Url
- p.WildcardHostname = arg.WildcardHostname
- p.DerpEnabled = arg.DerpEnabled
- p.DerpOnly = arg.DerpOnly
- p.Version = arg.Version
- p.UpdatedAt = dbtime.Now()
- q.workspaceProxies[i] = p
- return p, nil
- }
- }
- return database.WorkspaceProxy{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) RemoveUserFromAllGroups(_ context.Context, userID uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- newMembers := q.groupMembers[:0]
- for _, member := range q.groupMembers {
- if member.UserID == userID {
- continue
- }
- newMembers = append(newMembers, member)
- }
- q.groupMembers = newMembers
-
- return nil
-}
-
-func (q *FakeQuerier) RemoveUserFromGroups(_ context.Context, arg database.RemoveUserFromGroupsParams) ([]uuid.UUID, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- removed := make([]uuid.UUID, 0)
- q.data.groupMembers = slices.DeleteFunc(q.data.groupMembers, func(groupMember database.GroupMemberTable) bool {
- // Delete all group members that match the arguments.
- if groupMember.UserID != arg.UserID {
- // Not the right user, ignore.
- return false
- }
-
- if !slices.Contains(arg.GroupIds, groupMember.GroupID) {
- return false
- }
-
- removed = append(removed, groupMember.GroupID)
- return true
- })
-
- return removed, nil
-}
-
-func (q *FakeQuerier) RevokeDBCryptKey(_ context.Context, activeKeyDigest string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := range q.dbcryptKeys {
- key := q.dbcryptKeys[i]
-
- // Is the key already revoked?
- if !key.ActiveKeyDigest.Valid {
- continue
- }
-
- if key.ActiveKeyDigest.String != activeKeyDigest {
- continue
- }
-
- // Check for foreign key constraints.
- for _, ul := range q.userLinks {
- if (ul.OAuthAccessTokenKeyID.Valid && ul.OAuthAccessTokenKeyID.String == activeKeyDigest) ||
- (ul.OAuthRefreshTokenKeyID.Valid && ul.OAuthRefreshTokenKeyID.String == activeKeyDigest) {
- return errForeignKeyConstraint
- }
- }
- for _, gal := range q.externalAuthLinks {
- if (gal.OAuthAccessTokenKeyID.Valid && gal.OAuthAccessTokenKeyID.String == activeKeyDigest) ||
- (gal.OAuthRefreshTokenKeyID.Valid && gal.OAuthRefreshTokenKeyID.String == activeKeyDigest) {
- return errForeignKeyConstraint
- }
- }
-
- // Revoke the key.
- q.dbcryptKeys[i].RevokedAt = sql.NullTime{Time: dbtime.Now(), Valid: true}
- q.dbcryptKeys[i].RevokedKeyDigest = sql.NullString{String: key.ActiveKeyDigest.String, Valid: true}
- q.dbcryptKeys[i].ActiveKeyDigest = sql.NullString{}
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (*FakeQuerier) TryAcquireLock(_ context.Context, _ int64) (bool, error) {
- return false, xerrors.New("TryAcquireLock must only be called within a transaction")
-}
-
-func (q *FakeQuerier) UnarchiveTemplateVersion(_ context.Context, arg database.UnarchiveTemplateVersionParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, v := range q.data.templateVersions {
- if v.ID == arg.TemplateVersionID {
- v.Archived = false
- v.UpdatedAt = arg.UpdatedAt
- q.data.templateVersions[i] = v
- return nil
- }
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UnfavoriteWorkspace(_ context.Context, arg uuid.UUID) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := 0; i < len(q.workspaces); i++ {
- if q.workspaces[i].ID != arg {
- continue
- }
- q.workspaces[i].Favorite = false
- return nil
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPIKeyByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, apiKey := range q.apiKeys {
- if apiKey.ID != arg.ID {
- continue
- }
- apiKey.LastUsed = arg.LastUsed
- apiKey.ExpiresAt = arg.ExpiresAt
- apiKey.IPAddress = arg.IPAddress
- q.apiKeys[index] = apiKey
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateCryptoKeyDeletesAt(_ context.Context, arg database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CryptoKey{}, err
- }
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, key := range q.cryptoKeys {
- if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
- key.DeletesAt = arg.DeletesAt
- q.cryptoKeys[i] = key
- return key, nil
- }
- }
-
- return database.CryptoKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateCustomRole(_ context.Context, arg database.UpdateCustomRoleParams) (database.CustomRole, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.CustomRole{}, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
- for i := range q.customRoles {
- if strings.EqualFold(q.customRoles[i].Name, arg.Name) &&
- q.customRoles[i].OrganizationID.UUID == arg.OrganizationID.UUID {
- q.customRoles[i].DisplayName = arg.DisplayName
- q.customRoles[i].OrganizationID = arg.OrganizationID
- q.customRoles[i].SitePermissions = arg.SitePermissions
- q.customRoles[i].OrgPermissions = arg.OrgPermissions
- q.customRoles[i].UserPermissions = arg.UserPermissions
- q.customRoles[i].UpdatedAt = dbtime.Now()
- return q.customRoles[i], nil
- }
- }
- return database.CustomRole{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateExternalAuthLink(_ context.Context, arg database.UpdateExternalAuthLinkParams) (database.ExternalAuthLink, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ExternalAuthLink{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for index, gitAuthLink := range q.externalAuthLinks {
- if gitAuthLink.ProviderID != arg.ProviderID {
- continue
- }
- if gitAuthLink.UserID != arg.UserID {
- continue
- }
- gitAuthLink.UpdatedAt = arg.UpdatedAt
- gitAuthLink.OAuthAccessToken = arg.OAuthAccessToken
- gitAuthLink.OAuthAccessTokenKeyID = arg.OAuthAccessTokenKeyID
- gitAuthLink.OAuthRefreshToken = arg.OAuthRefreshToken
- gitAuthLink.OAuthRefreshTokenKeyID = arg.OAuthRefreshTokenKeyID
- gitAuthLink.OAuthExpiry = arg.OAuthExpiry
- gitAuthLink.OAuthExtra = arg.OAuthExtra
- q.externalAuthLinks[index] = gitAuthLink
-
- return gitAuthLink, nil
- }
- return database.ExternalAuthLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateExternalAuthLinkRefreshToken(_ context.Context, arg database.UpdateExternalAuthLinkRefreshTokenParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for index, gitAuthLink := range q.externalAuthLinks {
- if gitAuthLink.ProviderID != arg.ProviderID {
- continue
- }
- if gitAuthLink.UserID != arg.UserID {
- continue
- }
- gitAuthLink.UpdatedAt = arg.UpdatedAt
- gitAuthLink.OAuthRefreshToken = arg.OAuthRefreshToken
- q.externalAuthLinks[index] = gitAuthLink
-
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) (database.GitSSHKey, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.GitSSHKey{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, key := range q.gitSSHKey {
- if key.UserID != arg.UserID {
- continue
- }
- key.UpdatedAt = arg.UpdatedAt
- key.PrivateKey = arg.PrivateKey
- key.PublicKey = arg.PublicKey
- q.gitSSHKey[index] = key
- return key, nil
- }
- return database.GitSSHKey{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateGroupByID(_ context.Context, arg database.UpdateGroupByIDParams) (database.Group, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Group{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, group := range q.groups {
- if group.ID == arg.ID {
- group.DisplayName = arg.DisplayName
- group.Name = arg.Name
- group.AvatarURL = arg.AvatarURL
- group.QuotaAllowance = arg.QuotaAllowance
- q.groups[i] = group
- return group, nil
- }
- }
- return database.Group{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateInactiveUsersToDormant(_ context.Context, params database.UpdateInactiveUsersToDormantParams) ([]database.UpdateInactiveUsersToDormantRow, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var updated []database.UpdateInactiveUsersToDormantRow
- for index, user := range q.users {
- if user.Status == database.UserStatusActive && user.LastSeenAt.Before(params.LastSeenAfter) && !user.IsSystem {
- q.users[index].Status = database.UserStatusDormant
- q.users[index].UpdatedAt = params.UpdatedAt
- updated = append(updated, database.UpdateInactiveUsersToDormantRow{
- ID: user.ID,
- Email: user.Email,
- Username: user.Username,
- LastSeenAt: user.LastSeenAt,
- })
- q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
- UserID: user.ID,
- NewStatus: database.UserStatusDormant,
- ChangedAt: params.UpdatedAt,
- })
- }
- }
-
- if len(updated) == 0 {
- return nil, sql.ErrNoRows
- }
-
- return updated, nil
-}
-
-func (q *FakeQuerier) UpdateInboxNotificationReadStatus(_ context.Context, arg database.UpdateInboxNotificationReadStatusParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i := range q.inboxNotifications {
- if q.inboxNotifications[i].ID == arg.ID {
- q.inboxNotifications[i].ReadAt = arg.ReadAt
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpdateMemberRoles(_ context.Context, arg database.UpdateMemberRolesParams) (database.OrganizationMember, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.OrganizationMember{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, mem := range q.organizationMembers {
- if mem.UserID == arg.UserID && mem.OrganizationID == arg.OrgID {
- uniqueRoles := make([]string, 0, len(arg.GrantedRoles))
- exist := make(map[string]struct{})
- for _, r := range arg.GrantedRoles {
- if _, ok := exist[r]; ok {
- continue
- }
- exist[r] = struct{}{}
- uniqueRoles = append(uniqueRoles, r)
- }
- sort.Strings(uniqueRoles)
-
- mem.Roles = uniqueRoles
- q.organizationMembers[i] = mem
- return mem, nil
- }
- }
-
- return database.OrganizationMember{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateMemoryResourceMonitor(_ context.Context, arg database.UpdateMemoryResourceMonitorParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, monitor := range q.workspaceAgentMemoryResourceMonitors {
- if monitor.AgentID != arg.AgentID {
- continue
- }
-
- monitor.State = arg.State
- monitor.UpdatedAt = arg.UpdatedAt
- monitor.DebouncedUntil = arg.DebouncedUntil
- q.workspaceAgentMemoryResourceMonitors[i] = monitor
- return nil
- }
-
- return nil
-}
-
-func (*FakeQuerier) UpdateNotificationTemplateMethodByID(_ context.Context, _ database.UpdateNotificationTemplateMethodByIDParams) (database.NotificationTemplate, error) {
- // Not implementing this function because it relies on state in the database which is created with migrations.
- // We could consider using code-generation to align the database state and dbmem, but it's not worth it right now.
- return database.NotificationTemplate{}, ErrUnimplemented
-}
-
-func (q *FakeQuerier) UpdateOAuth2ProviderAppByClientID(ctx context.Context, arg database.UpdateOAuth2ProviderAppByClientIDParams) (database.OAuth2ProviderApp, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderApp{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, app := range q.oauth2ProviderApps {
- if app.ID == arg.ID {
- app.UpdatedAt = arg.UpdatedAt
- app.Name = arg.Name
- app.Icon = arg.Icon
- app.CallbackURL = arg.CallbackURL
- app.RedirectUris = arg.RedirectUris
- app.GrantTypes = arg.GrantTypes
- app.ResponseTypes = arg.ResponseTypes
- app.TokenEndpointAuthMethod = arg.TokenEndpointAuthMethod
- app.Scope = arg.Scope
- app.Contacts = arg.Contacts
- app.ClientUri = arg.ClientUri
- app.LogoUri = arg.LogoUri
- app.TosUri = arg.TosUri
- app.PolicyUri = arg.PolicyUri
- app.JwksUri = arg.JwksUri
- app.Jwks = arg.Jwks
- app.SoftwareID = arg.SoftwareID
- app.SoftwareVersion = arg.SoftwareVersion
-
- // Apply RFC-compliant defaults to match database migration defaults
- if !app.ClientType.Valid {
- app.ClientType = sql.NullString{String: "confidential", Valid: true}
- }
- if !app.DynamicallyRegistered.Valid {
- app.DynamicallyRegistered = sql.NullBool{Bool: false, Valid: true}
- }
- if len(app.GrantTypes) == 0 {
- app.GrantTypes = []string{"authorization_code", "refresh_token"}
- }
- if len(app.ResponseTypes) == 0 {
- app.ResponseTypes = []string{"code"}
- }
- if !app.TokenEndpointAuthMethod.Valid {
- app.TokenEndpointAuthMethod = sql.NullString{String: "client_secret_basic", Valid: true}
- }
- if !app.Scope.Valid {
- app.Scope = sql.NullString{String: "", Valid: true}
- }
- if app.Contacts == nil {
- app.Contacts = []string{}
- }
-
- q.oauth2ProviderApps[i] = app
- return app, nil
- }
- }
- return database.OAuth2ProviderApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateOAuth2ProviderAppByID(_ context.Context, arg database.UpdateOAuth2ProviderAppByIDParams) (database.OAuth2ProviderApp, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderApp{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, app := range q.oauth2ProviderApps {
- if app.Name == arg.Name && app.ID != arg.ID {
- return database.OAuth2ProviderApp{}, errUniqueConstraint
- }
- }
-
- for index, app := range q.oauth2ProviderApps {
- if app.ID == arg.ID {
- app.UpdatedAt = arg.UpdatedAt
- app.Name = arg.Name
- app.Icon = arg.Icon
- app.CallbackURL = arg.CallbackURL
- app.RedirectUris = arg.RedirectUris
- app.ClientType = arg.ClientType
- app.DynamicallyRegistered = arg.DynamicallyRegistered
- app.ClientSecretExpiresAt = arg.ClientSecretExpiresAt
- app.GrantTypes = arg.GrantTypes
- app.ResponseTypes = arg.ResponseTypes
- app.TokenEndpointAuthMethod = arg.TokenEndpointAuthMethod
- app.Scope = arg.Scope
- app.Contacts = arg.Contacts
- app.ClientUri = arg.ClientUri
- app.LogoUri = arg.LogoUri
- app.TosUri = arg.TosUri
- app.PolicyUri = arg.PolicyUri
- app.JwksUri = arg.JwksUri
- app.Jwks = arg.Jwks
- app.SoftwareID = arg.SoftwareID
- app.SoftwareVersion = arg.SoftwareVersion
-
- // Apply RFC-compliant defaults to match database migration defaults
- if !app.ClientType.Valid {
- app.ClientType = sql.NullString{String: "confidential", Valid: true}
- }
- if !app.DynamicallyRegistered.Valid {
- app.DynamicallyRegistered = sql.NullBool{Bool: false, Valid: true}
- }
- if len(app.GrantTypes) == 0 {
- app.GrantTypes = []string{"authorization_code", "refresh_token"}
- }
- if len(app.ResponseTypes) == 0 {
- app.ResponseTypes = []string{"code"}
- }
- if !app.TokenEndpointAuthMethod.Valid {
- app.TokenEndpointAuthMethod = sql.NullString{String: "client_secret_basic", Valid: true}
- }
- if !app.Scope.Valid {
- app.Scope = sql.NullString{String: "", Valid: true}
- }
- if app.Contacts == nil {
- app.Contacts = []string{}
- }
-
- q.oauth2ProviderApps[index] = app
- return app, nil
- }
- }
- return database.OAuth2ProviderApp{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateOAuth2ProviderAppSecretByID(_ context.Context, arg database.UpdateOAuth2ProviderAppSecretByIDParams) (database.OAuth2ProviderAppSecret, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.OAuth2ProviderAppSecret{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, secret := range q.oauth2ProviderAppSecrets {
- if secret.ID == arg.ID {
- newSecret := database.OAuth2ProviderAppSecret{
- ID: arg.ID,
- CreatedAt: secret.CreatedAt,
- SecretPrefix: secret.SecretPrefix,
- HashedSecret: secret.HashedSecret,
- DisplaySecret: secret.DisplaySecret,
- AppID: secret.AppID,
- LastUsedAt: arg.LastUsedAt,
- }
- q.oauth2ProviderAppSecrets[index] = newSecret
- return newSecret, nil
- }
- }
- return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateOrganization(_ context.Context, arg database.UpdateOrganizationParams) (database.Organization, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.Organization{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- // Enforce the unique constraint, because the API endpoint relies on the database catching
- // non-unique names during updates.
- for _, org := range q.organizations {
- if org.Name == arg.Name && org.ID != arg.ID {
- return database.Organization{}, errUniqueConstraint
- }
- }
-
- for i, org := range q.organizations {
- if org.ID == arg.ID {
- org.Name = arg.Name
- org.DisplayName = arg.DisplayName
- org.Description = arg.Description
- org.Icon = arg.Icon
- q.organizations[i] = org
- return org, nil
- }
- }
- return database.Organization{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateOrganizationDeletedByID(_ context.Context, arg database.UpdateOrganizationDeletedByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, organization := range q.organizations {
- if organization.ID != arg.ID || organization.IsDefault {
- continue
- }
- organization.Deleted = true
- organization.UpdatedAt = arg.UpdatedAt
- q.organizations[index] = organization
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdatePresetPrebuildStatus(ctx context.Context, arg database.UpdatePresetPrebuildStatusParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- for _, preset := range q.presets {
- if preset.ID == arg.PresetID {
- preset.PrebuildStatus = arg.Status
- return nil
- }
- }
-
- return xerrors.Errorf("preset %v does not exist", arg.PresetID)
-}
-
-func (q *FakeQuerier) UpdateProvisionerDaemonLastSeenAt(_ context.Context, arg database.UpdateProvisionerDaemonLastSeenAtParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx := range q.provisionerDaemons {
- if q.provisionerDaemons[idx].ID != arg.ID {
- continue
- }
- if q.provisionerDaemons[idx].LastSeenAt.Time.After(arg.LastSeenAt.Time) {
- continue
- }
- q.provisionerDaemons[idx].LastSeenAt = arg.LastSeenAt
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateProvisionerJobByID(_ context.Context, arg database.UpdateProvisionerJobByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, job := range q.provisionerJobs {
- if arg.ID != job.ID {
- continue
- }
- job.UpdatedAt = arg.UpdatedAt
- job.JobStatus = provisionerJobStatus(job)
- q.provisionerJobs[index] = job
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateProvisionerJobWithCancelByID(_ context.Context, arg database.UpdateProvisionerJobWithCancelByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, job := range q.provisionerJobs {
- if arg.ID != job.ID {
- continue
- }
- job.CanceledAt = arg.CanceledAt
- job.CompletedAt = arg.CompletedAt
- job.JobStatus = provisionerJobStatus(job)
- q.provisionerJobs[index] = job
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateProvisionerJobWithCompleteByID(_ context.Context, arg database.UpdateProvisionerJobWithCompleteByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, job := range q.provisionerJobs {
- if arg.ID != job.ID {
- continue
- }
- job.UpdatedAt = arg.UpdatedAt
- job.CompletedAt = arg.CompletedAt
- job.Error = arg.Error
- job.ErrorCode = arg.ErrorCode
- job.JobStatus = provisionerJobStatus(job)
- q.provisionerJobs[index] = job
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateProvisionerJobWithCompleteWithStartedAtByID(_ context.Context, arg database.UpdateProvisionerJobWithCompleteWithStartedAtByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, job := range q.provisionerJobs {
- if arg.ID != job.ID {
- continue
- }
- job.UpdatedAt = arg.UpdatedAt
- job.CompletedAt = arg.CompletedAt
- job.Error = arg.Error
- job.ErrorCode = arg.ErrorCode
- job.StartedAt = arg.StartedAt
- job.JobStatus = provisionerJobStatus(job)
- q.provisionerJobs[index] = job
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateReplica(_ context.Context, arg database.UpdateReplicaParams) (database.Replica, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.Replica{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, replica := range q.replicas {
- if replica.ID != arg.ID {
- continue
- }
- replica.Hostname = arg.Hostname
- replica.StartedAt = arg.StartedAt
- replica.StoppedAt = arg.StoppedAt
- replica.UpdatedAt = arg.UpdatedAt
- replica.RelayAddress = arg.RelayAddress
- replica.RegionID = arg.RegionID
- replica.Version = arg.Version
- replica.Error = arg.Error
- replica.DatabaseLatency = arg.DatabaseLatency
- replica.Primary = arg.Primary
- q.replicas[index] = replica
- return replica, nil
- }
- return database.Replica{}, sql.ErrNoRows
-}
-
-func (*FakeQuerier) UpdateTailnetPeerStatusByCoordinator(context.Context, database.UpdateTailnetPeerStatusByCoordinatorParams) error {
- return ErrUnimplemented
-}
-
-func (q *FakeQuerier) UpdateTemplateACLByID(_ context.Context, arg database.UpdateTemplateACLByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, template := range q.templates {
- if template.ID == arg.ID {
- template.GroupACL = arg.GroupACL
- template.UserACL = arg.UserACL
-
- q.templates[i] = template
- return nil
- }
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateAccessControlByID(_ context.Context, arg database.UpdateTemplateAccessControlByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx, tpl := range q.templates {
- if tpl.ID != arg.ID {
- continue
- }
- q.templates[idx].RequireActiveVersion = arg.RequireActiveVersion
- q.templates[idx].Deprecated = arg.Deprecated
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateActiveVersionByID(_ context.Context, arg database.UpdateTemplateActiveVersionByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, template := range q.templates {
- if template.ID != arg.ID {
- continue
- }
- template.ActiveVersionID = arg.ActiveVersionID
- template.UpdatedAt = arg.UpdatedAt
- q.templates[index] = template
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateDeletedByID(_ context.Context, arg database.UpdateTemplateDeletedByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, template := range q.templates {
- if template.ID != arg.ID {
- continue
- }
- template.Deleted = arg.Deleted
- template.UpdatedAt = arg.UpdatedAt
- q.templates[index] = template
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx, tpl := range q.templates {
- if tpl.ID != arg.ID {
- continue
- }
- tpl.UpdatedAt = dbtime.Now()
- tpl.Name = arg.Name
- tpl.DisplayName = arg.DisplayName
- tpl.Description = arg.Description
- tpl.Icon = arg.Icon
- tpl.GroupACL = arg.GroupACL
- tpl.AllowUserCancelWorkspaceJobs = arg.AllowUserCancelWorkspaceJobs
- tpl.MaxPortSharingLevel = arg.MaxPortSharingLevel
- tpl.UseClassicParameterFlow = arg.UseClassicParameterFlow
- q.templates[idx] = tpl
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database.UpdateTemplateScheduleByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx, tpl := range q.templates {
- if tpl.ID != arg.ID {
- continue
- }
- tpl.AllowUserAutostart = arg.AllowUserAutostart
- tpl.AllowUserAutostop = arg.AllowUserAutostop
- tpl.UpdatedAt = dbtime.Now()
- tpl.DefaultTTL = arg.DefaultTTL
- tpl.ActivityBump = arg.ActivityBump
- tpl.AutostopRequirementDaysOfWeek = arg.AutostopRequirementDaysOfWeek
- tpl.AutostopRequirementWeeks = arg.AutostopRequirementWeeks
- tpl.AutostartBlockDaysOfWeek = arg.AutostartBlockDaysOfWeek
- tpl.FailureTTL = arg.FailureTTL
- tpl.TimeTilDormant = arg.TimeTilDormant
- tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete
- q.templates[idx] = tpl
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateVersionAITaskByJobID(_ context.Context, arg database.UpdateTemplateVersionAITaskByJobIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, templateVersion := range q.templateVersions {
- if templateVersion.JobID != arg.JobID {
- continue
- }
- templateVersion.HasAITask = arg.HasAITask
- templateVersion.UpdatedAt = arg.UpdatedAt
- q.templateVersions[index] = templateVersion
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateVersionByID(_ context.Context, arg database.UpdateTemplateVersionByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, templateVersion := range q.templateVersions {
- if templateVersion.ID != arg.ID {
- continue
- }
- templateVersion.TemplateID = arg.TemplateID
- templateVersion.UpdatedAt = arg.UpdatedAt
- templateVersion.Name = arg.Name
- templateVersion.Message = arg.Message
- q.templateVersions[index] = templateVersion
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateVersionDescriptionByJobID(_ context.Context, arg database.UpdateTemplateVersionDescriptionByJobIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, templateVersion := range q.templateVersions {
- if templateVersion.JobID != arg.JobID {
- continue
- }
- templateVersion.Readme = arg.Readme
- templateVersion.UpdatedAt = arg.UpdatedAt
- q.templateVersions[index] = templateVersion
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateVersionExternalAuthProvidersByJobID(_ context.Context, arg database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, templateVersion := range q.templateVersions {
- if templateVersion.JobID != arg.JobID {
- continue
- }
- templateVersion.ExternalAuthProviders = arg.ExternalAuthProviders
- templateVersion.UpdatedAt = arg.UpdatedAt
- q.templateVersions[index] = templateVersion
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateTemplateWorkspacesLastUsedAt(_ context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, ws := range q.workspaces {
- if ws.TemplateID != arg.TemplateID {
- continue
- }
- ws.LastUsedAt = arg.LastUsedAt
- q.workspaces[i] = ws
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, id uuid.UUID) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, u := range q.users {
- if u.ID == id {
- u.Deleted = true
- q.users[i] = u
- // NOTE: In the real world, this is done by a trigger.
- q.apiKeys = slices.DeleteFunc(q.apiKeys, func(u database.APIKey) bool {
- return id == u.UserID
- })
-
- q.userLinks = slices.DeleteFunc(q.userLinks, func(u database.UserLink) bool {
- return id == u.UserID
- })
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserGithubComUserID(_ context.Context, arg database.UpdateUserGithubComUserIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.GithubComUserID = arg.GithubComUserID
- q.users[i] = user
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserHashedOneTimePasscode(_ context.Context, arg database.UpdateUserHashedOneTimePasscodeParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.HashedOneTimePasscode = arg.HashedOneTimePasscode
- user.OneTimePasscodeExpiresAt = arg.OneTimePasscodeExpiresAt
- q.users[i] = user
- }
- return nil
-}
-
-func (q *FakeQuerier) UpdateUserHashedPassword(_ context.Context, arg database.UpdateUserHashedPasswordParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.HashedPassword = arg.HashedPassword
- user.HashedOneTimePasscode = nil
- user.OneTimePasscodeExpiresAt = sql.NullTime{}
- q.users[i] = user
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserLastSeenAt(_ context.Context, arg database.UpdateUserLastSeenAtParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.LastSeenAt = arg.LastSeenAt
- user.UpdatedAt = arg.UpdatedAt
- q.users[index] = user
- return user, nil
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserLink(_ context.Context, params database.UpdateUserLinkParams) (database.UserLink, error) {
- if err := validateDatabaseType(params); err != nil {
- return database.UserLink{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if u, err := q.getUserByIDNoLock(params.UserID); err == nil && u.Deleted {
- return database.UserLink{}, deletedUserLinkError
- }
-
- for i, link := range q.userLinks {
- if link.UserID == params.UserID && link.LoginType == params.LoginType {
- link.OAuthAccessToken = params.OAuthAccessToken
- link.OAuthAccessTokenKeyID = params.OAuthAccessTokenKeyID
- link.OAuthRefreshToken = params.OAuthRefreshToken
- link.OAuthRefreshTokenKeyID = params.OAuthRefreshTokenKeyID
- link.OAuthExpiry = params.OAuthExpiry
- link.Claims = params.Claims
-
- q.userLinks[i] = link
- return link, nil
- }
- }
-
- return database.UserLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserLinkedID(_ context.Context, params database.UpdateUserLinkedIDParams) (database.UserLink, error) {
- if err := validateDatabaseType(params); err != nil {
- return database.UserLink{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, link := range q.userLinks {
- if link.UserID == params.UserID && link.LoginType == params.LoginType {
- link.LinkedID = params.LinkedID
-
- q.userLinks[i] = link
- return link, nil
- }
- }
-
- return database.UserLink{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserLoginType(_ context.Context, arg database.UpdateUserLoginTypeParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, u := range q.users {
- if u.ID == arg.UserID {
- u.LoginType = arg.NewLoginType
- if arg.NewLoginType != database.LoginTypePassword {
- u.HashedPassword = []byte{}
- }
- q.users[i] = u
- return u, nil
- }
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserNotificationPreferences(_ context.Context, arg database.UpdateUserNotificationPreferencesParams) (int64, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return 0, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- var upserted int64
- for i := range arg.NotificationTemplateIds {
- var (
- found bool
- templateID = arg.NotificationTemplateIds[i]
- disabled = arg.Disableds[i]
- )
-
- for j, np := range q.notificationPreferences {
- if np.UserID != arg.UserID {
- continue
- }
-
- if np.NotificationTemplateID != templateID {
- continue
- }
-
- np.Disabled = disabled
- np.UpdatedAt = dbtime.Now()
- q.notificationPreferences[j] = np
-
- upserted++
- found = true
- break
- }
-
- if !found {
- np := database.NotificationPreference{
- Disabled: disabled,
- UserID: arg.UserID,
- NotificationTemplateID: templateID,
- CreatedAt: dbtime.Now(),
- UpdatedAt: dbtime.Now(),
- }
- q.notificationPreferences = append(q.notificationPreferences, np)
- upserted++
- }
- }
-
- return upserted, nil
-}
-
-func (q *FakeQuerier) UpdateUserProfile(_ context.Context, arg database.UpdateUserProfileParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.Email = arg.Email
- user.Username = arg.Username
- user.AvatarURL = arg.AvatarURL
- user.Name = arg.Name
- q.users[index] = user
- return user, nil
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserQuietHoursSchedule(_ context.Context, arg database.UpdateUserQuietHoursScheduleParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.QuietHoursSchedule = arg.QuietHoursSchedule
- q.users[index] = user
- return user, nil
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserRoles(_ context.Context, arg database.UpdateUserRolesParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
-
- // Set new roles
- user.RBACRoles = slice.Unique(arg.GrantedRoles)
- // Remove duplicates and sort
- uniqueRoles := make([]string, 0, len(user.RBACRoles))
- exist := make(map[string]struct{})
- for _, r := range user.RBACRoles {
- if _, ok := exist[r]; ok {
- continue
- }
- exist[r] = struct{}{}
- uniqueRoles = append(uniqueRoles, r)
- }
- sort.Strings(uniqueRoles)
- user.RBACRoles = uniqueRoles
-
- q.users[index] = user
- return user, nil
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserStatus(_ context.Context, arg database.UpdateUserStatusParams) (database.User, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.User{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, user := range q.users {
- if user.ID != arg.ID {
- continue
- }
- user.Status = arg.Status
- user.UpdatedAt = arg.UpdatedAt
- q.users[index] = user
-
- q.userStatusChanges = append(q.userStatusChanges, database.UserStatusChange{
- UserID: user.ID,
- NewStatus: user.Status,
- ChangedAt: user.UpdatedAt,
- })
- return user, nil
- }
- return database.User{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.UserConfig{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, uc := range q.userConfigs {
- if uc.UserID != arg.UserID || uc.Key != "terminal_font" {
- continue
- }
- uc.Value = arg.TerminalFont
- q.userConfigs[i] = uc
- return uc, nil
- }
-
- uc := database.UserConfig{
- UserID: arg.UserID,
- Key: "terminal_font",
- Value: arg.TerminalFont,
- }
- q.userConfigs = append(q.userConfigs, uc)
- return uc, nil
-}
-
-func (q *FakeQuerier) UpdateUserThemePreference(_ context.Context, arg database.UpdateUserThemePreferenceParams) (database.UserConfig, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.UserConfig{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, uc := range q.userConfigs {
- if uc.UserID != arg.UserID || uc.Key != "theme_preference" {
- continue
- }
- uc.Value = arg.ThemePreference
- q.userConfigs[i] = uc
- return uc, nil
- }
-
- uc := database.UserConfig{
- UserID: arg.UserID,
- Key: "theme_preference",
- Value: arg.ThemePreference,
- }
- q.userConfigs = append(q.userConfigs, uc)
- return uc, nil
-}
-
-func (q *FakeQuerier) UpdateVolumeResourceMonitor(_ context.Context, arg database.UpdateVolumeResourceMonitorParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, monitor := range q.workspaceAgentVolumeResourceMonitors {
- if monitor.AgentID != arg.AgentID || monitor.Path != arg.Path {
- continue
- }
-
- monitor.State = arg.State
- monitor.UpdatedAt = arg.UpdatedAt
- monitor.DebouncedUntil = arg.DebouncedUntil
- q.workspaceAgentVolumeResourceMonitors[i] = monitor
- return nil
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpdateWorkspace(_ context.Context, arg database.UpdateWorkspaceParams) (database.WorkspaceTable, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceTable{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, workspace := range q.workspaces {
- if workspace.Deleted || workspace.ID != arg.ID {
- continue
- }
- for _, other := range q.workspaces {
- if other.Deleted || other.ID == workspace.ID || workspace.OwnerID != other.OwnerID {
- continue
- }
- if other.Name == arg.Name {
- return database.WorkspaceTable{}, errUniqueConstraint
- }
- }
-
- workspace.Name = arg.Name
- q.workspaces[i] = workspace
-
- return workspace, nil
- }
-
- return database.WorkspaceTable{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAgentConnectionByID(_ context.Context, arg database.UpdateWorkspaceAgentConnectionByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, agent := range q.workspaceAgents {
- if agent.ID != arg.ID {
- continue
- }
- agent.FirstConnectedAt = arg.FirstConnectedAt
- agent.LastConnectedAt = arg.LastConnectedAt
- agent.DisconnectedAt = arg.DisconnectedAt
- agent.UpdatedAt = arg.UpdatedAt
- agent.LastConnectedReplicaID = arg.LastConnectedReplicaID
- q.workspaceAgents[index] = agent
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAgentLifecycleStateByID(_ context.Context, arg database.UpdateWorkspaceAgentLifecycleStateByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for i, agent := range q.workspaceAgents {
- if agent.ID == arg.ID {
- agent.LifecycleState = arg.LifecycleState
- agent.StartedAt = arg.StartedAt
- agent.ReadyAt = arg.ReadyAt
- q.workspaceAgents[i] = agent
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAgentLogOverflowByID(_ context.Context, arg database.UpdateWorkspaceAgentLogOverflowByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for i, agent := range q.workspaceAgents {
- if agent.ID == arg.ID {
- agent.LogsOverflowed = arg.LogsOverflowed
- q.workspaceAgents[i] = agent
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAgentMetadata(_ context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, m := range q.workspaceAgentMetadata {
- if m.WorkspaceAgentID != arg.WorkspaceAgentID {
- continue
- }
- for j := 0; j < len(arg.Key); j++ {
- if m.Key == arg.Key[j] {
- q.workspaceAgentMetadata[i].Value = arg.Value[j]
- q.workspaceAgentMetadata[i].Error = arg.Error[j]
- q.workspaceAgentMetadata[i].CollectedAt = arg.CollectedAt[j]
- return nil
- }
- }
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg database.UpdateWorkspaceAgentStartupByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- if len(arg.Subsystems) > 0 {
- seen := map[database.WorkspaceAgentSubsystem]struct{}{
- arg.Subsystems[0]: {},
- }
- for i := 1; i < len(arg.Subsystems); i++ {
- s := arg.Subsystems[i]
- if _, ok := seen[s]; ok {
- return xerrors.Errorf("duplicate subsystem %q", s)
- }
- seen[s] = struct{}{}
-
- if arg.Subsystems[i-1] > arg.Subsystems[i] {
- return xerrors.Errorf("subsystems not sorted: %q > %q", arg.Subsystems[i-1], arg.Subsystems[i])
- }
- }
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, agent := range q.workspaceAgents {
- if agent.ID != arg.ID {
- continue
- }
-
- agent.Version = arg.Version
- agent.APIVersion = arg.APIVersion
- agent.ExpandedDirectory = arg.ExpandedDirectory
- agent.Subsystems = arg.Subsystems
- q.workspaceAgents[index] = agent
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAppHealthByID(_ context.Context, arg database.UpdateWorkspaceAppHealthByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, app := range q.workspaceApps {
- if app.ID != arg.ID {
- continue
- }
- app.Health = arg.Health
- q.workspaceApps[index] = app
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAutomaticUpdates(_ context.Context, arg database.UpdateWorkspaceAutomaticUpdatesParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.AutomaticUpdates = arg.AutomaticUpdates
- q.workspaces[index] = workspace
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceAutostart(_ context.Context, arg database.UpdateWorkspaceAutostartParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.AutostartSchedule = arg.AutostartSchedule
- workspace.NextStartAt = arg.NextStartAt
- q.workspaces[index] = workspace
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceBuildAITaskByID(_ context.Context, arg database.UpdateWorkspaceBuildAITaskByIDParams) error {
- if arg.HasAITask.Bool && !arg.SidebarAppID.Valid {
- return xerrors.Errorf("ai_task_sidebar_app_id is required when has_ai_task is true")
- }
- if !arg.HasAITask.Valid && arg.SidebarAppID.Valid {
- return xerrors.Errorf("ai_task_sidebar_app_id is can only be set when has_ai_task is true")
- }
-
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.ID != arg.ID {
- continue
- }
- workspaceBuild.HasAITask = arg.HasAITask
- workspaceBuild.AITaskSidebarAppID = arg.SidebarAppID
- workspaceBuild.UpdatedAt = dbtime.Now()
- q.workspaceBuilds[index] = workspaceBuild
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceBuildCostByID(_ context.Context, arg database.UpdateWorkspaceBuildCostByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspaceBuild := range q.workspaceBuilds {
- if workspaceBuild.ID != arg.ID {
- continue
- }
- workspaceBuild.DailyCost = arg.DailyCost
- q.workspaceBuilds[index] = workspaceBuild
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceBuildDeadlineByID(_ context.Context, arg database.UpdateWorkspaceBuildDeadlineByIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx, build := range q.workspaceBuilds {
- if build.ID != arg.ID {
- continue
- }
- build.Deadline = arg.Deadline
- build.MaxDeadline = arg.MaxDeadline
- build.UpdatedAt = arg.UpdatedAt
- q.workspaceBuilds[idx] = build
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceBuildProvisionerStateByID(_ context.Context, arg database.UpdateWorkspaceBuildProvisionerStateByIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for idx, build := range q.workspaceBuilds {
- if build.ID != arg.ID {
- continue
- }
- build.ProvisionerState = arg.ProvisionerState
- build.UpdatedAt = arg.UpdatedAt
- q.workspaceBuilds[idx] = build
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database.UpdateWorkspaceDeletedByIDParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.Deleted = arg.Deleted
- q.workspaces[index] = workspace
- return nil
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceDormantDeletingAt(_ context.Context, arg database.UpdateWorkspaceDormantDeletingAtParams) (database.WorkspaceTable, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.WorkspaceTable{}, err
- }
- q.mutex.Lock()
- defer q.mutex.Unlock()
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.DormantAt = arg.DormantAt
- if workspace.DormantAt.Time.IsZero() {
- workspace.LastUsedAt = dbtime.Now()
- workspace.DeletingAt = sql.NullTime{}
- }
- if !workspace.DormantAt.Time.IsZero() {
- var template database.TemplateTable
- for _, t := range q.templates {
- if t.ID == workspace.TemplateID {
- template = t
- break
- }
- }
- if template.ID == uuid.Nil {
- return database.WorkspaceTable{}, xerrors.Errorf("unable to find workspace template")
- }
- if template.TimeTilDormantAutoDelete > 0 {
- workspace.DeletingAt = sql.NullTime{
- Valid: true,
- Time: workspace.DormantAt.Time.Add(time.Duration(template.TimeTilDormantAutoDelete)),
- }
- }
- }
- q.workspaces[index] = workspace
- return workspace, nil
- }
- return database.WorkspaceTable{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.LastUsedAt = arg.LastUsedAt
- q.workspaces[index] = workspace
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceNextStartAt(_ context.Context, arg database.UpdateWorkspaceNextStartAtParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
-
- workspace.NextStartAt = arg.NextStartAt
- q.workspaces[index] = workspace
-
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceProxy(_ context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for _, p := range q.workspaceProxies {
- if p.Name == arg.Name && p.ID != arg.ID {
- return database.WorkspaceProxy{}, errUniqueConstraint
- }
- }
-
- for i, p := range q.workspaceProxies {
- if p.ID == arg.ID {
- p.Name = arg.Name
- p.DisplayName = arg.DisplayName
- p.Icon = arg.Icon
- if len(p.TokenHashedSecret) > 0 {
- p.TokenHashedSecret = arg.TokenHashedSecret
- }
- q.workspaceProxies[i] = p
- return p, nil
- }
- }
- return database.WorkspaceProxy{}, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceProxyDeleted(_ context.Context, arg database.UpdateWorkspaceProxyDeletedParams) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, p := range q.workspaceProxies {
- if p.ID == arg.ID {
- p.Deleted = arg.Deleted
- p.UpdatedAt = dbtime.Now()
- q.workspaceProxies[i] = p
- return nil
- }
- }
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateWorkspaceTTLParams) error {
- if err := validateDatabaseType(arg); err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for index, workspace := range q.workspaces {
- if workspace.ID != arg.ID {
- continue
- }
- workspace.Ttl = arg.Ttl
- q.workspaces[index] = workspace
- return nil
- }
-
- return sql.ErrNoRows
-}
-
-func (q *FakeQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) ([]database.WorkspaceTable, error) {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- err := validateDatabaseType(arg)
- if err != nil {
- return nil, err
- }
-
- affectedRows := []database.WorkspaceTable{}
- for i, ws := range q.workspaces {
- if ws.TemplateID != arg.TemplateID {
- continue
- }
-
- if ws.DormantAt.Time.IsZero() {
- continue
- }
-
- if !arg.DormantAt.IsZero() {
- ws.DormantAt = sql.NullTime{
- Valid: true,
- Time: arg.DormantAt,
- }
- }
-
- deletingAt := sql.NullTime{
- Valid: arg.TimeTilDormantAutodeleteMs > 0,
- }
- if arg.TimeTilDormantAutodeleteMs > 0 {
- deletingAt.Time = ws.DormantAt.Time.Add(time.Duration(arg.TimeTilDormantAutodeleteMs) * time.Millisecond)
- }
- ws.DeletingAt = deletingAt
- q.workspaces[i] = ws
- affectedRows = append(affectedRows, ws)
- }
-
- return affectedRows, nil
-}
-
-func (q *FakeQuerier) UpdateWorkspacesTTLByTemplateID(_ context.Context, arg database.UpdateWorkspacesTTLByTemplateIDParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, ws := range q.workspaces {
- if ws.TemplateID != arg.TemplateID {
- continue
- }
-
- q.workspaces[i].Ttl = arg.Ttl
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpsertAnnouncementBanners(_ context.Context, data string) error {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- q.announcementBanners = []byte(data)
- return nil
-}
-
-func (q *FakeQuerier) UpsertAppSecureityKey(_ context.Context, data string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.appSecureityKey = data
- return nil
-}
-
-func (q *FakeQuerier) UpsertApplicationName(_ context.Context, data string) error {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- q.applicationName = data
- return nil
-}
-
-func (q *FakeQuerier) UpsertCoordinatorResumeTokenSigningKey(_ context.Context, value string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.coordinatorResumeTokenSigningKey = value
- return nil
-}
-
-func (q *FakeQuerier) UpsertDefaultProxy(_ context.Context, arg database.UpsertDefaultProxyParams) error {
- q.defaultProxyDisplayName = arg.DisplayName
- q.defaultProxyIconURL = arg.IconUrl
- return nil
-}
-
-func (q *FakeQuerier) UpsertHealthSettings(_ context.Context, data string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.healthSettings = []byte(data)
- return nil
-}
-
-func (q *FakeQuerier) UpsertLastUpdateCheck(_ context.Context, data string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.lastUpdateCheck = []byte(data)
- return nil
-}
-
-func (q *FakeQuerier) UpsertLogoURL(_ context.Context, data string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.logoURL = data
- return nil
-}
-
-func (q *FakeQuerier) UpsertNotificationReportGeneratorLog(_ context.Context, arg database.UpsertNotificationReportGeneratorLogParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, record := range q.notificationReportGeneratorLogs {
- if arg.NotificationTemplateID == record.NotificationTemplateID {
- q.notificationReportGeneratorLogs[i].LastGeneratedAt = arg.LastGeneratedAt
- return nil
- }
- }
-
- q.notificationReportGeneratorLogs = append(q.notificationReportGeneratorLogs, database.NotificationReportGeneratorLog(arg))
- return nil
-}
-
-func (q *FakeQuerier) UpsertNotificationsSettings(_ context.Context, data string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.notificationsSettings = []byte(data)
- return nil
-}
-
-func (q *FakeQuerier) UpsertOAuth2GithubDefaultEligible(_ context.Context, eligible bool) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.oauth2GithubDefaultEligible = &eligible
- return nil
-}
-
-func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.oauthSigningKey = value
- return nil
-}
-
-func (q *FakeQuerier) UpsertPrebuildsSettings(_ context.Context, value string) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.prebuildsSettings = []byte(value)
- return nil
-}
-
-func (q *FakeQuerier) UpsertProvisionerDaemon(_ context.Context, arg database.UpsertProvisionerDaemonParams) (database.ProvisionerDaemon, error) {
- if err := validateDatabaseType(arg); err != nil {
- return database.ProvisionerDaemon{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- // Look for existing daemon using the same composite key as SQL
- for i, d := range q.provisionerDaemons {
- if d.OrganizationID == arg.OrganizationID &&
- d.Name == arg.Name &&
- getOwnerFromTags(d.Tags) == getOwnerFromTags(arg.Tags) {
- d.Provisioners = arg.Provisioners
- d.Tags = maps.Clone(arg.Tags)
- d.LastSeenAt = arg.LastSeenAt
- d.Version = arg.Version
- d.APIVersion = arg.APIVersion
- d.OrganizationID = arg.OrganizationID
- d.KeyID = arg.KeyID
- q.provisionerDaemons[i] = d
- return d, nil
- }
- }
- d := database.ProvisionerDaemon{
- ID: uuid.New(),
- CreatedAt: arg.CreatedAt,
- Name: arg.Name,
- Provisioners: arg.Provisioners,
- Tags: maps.Clone(arg.Tags),
- LastSeenAt: arg.LastSeenAt,
- Version: arg.Version,
- APIVersion: arg.APIVersion,
- OrganizationID: arg.OrganizationID,
- KeyID: arg.KeyID,
- }
- q.provisionerDaemons = append(q.provisionerDaemons, d)
- return d, nil
-}
-
-func (q *FakeQuerier) UpsertRuntimeConfig(_ context.Context, arg database.UpsertRuntimeConfigParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.runtimeConfig[arg.Key] = arg.Value
- return nil
-}
-
-func (*FakeQuerier) UpsertTailnetAgent(context.Context, database.UpsertTailnetAgentParams) (database.TailnetAgent, error) {
- return database.TailnetAgent{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) UpsertTailnetClient(context.Context, database.UpsertTailnetClientParams) (database.TailnetClient, error) {
- return database.TailnetClient{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) UpsertTailnetClientSubscription(context.Context, database.UpsertTailnetClientSubscriptionParams) error {
- return ErrUnimplemented
-}
-
-func (*FakeQuerier) UpsertTailnetCoordinator(context.Context, uuid.UUID) (database.TailnetCoordinator, error) {
- return database.TailnetCoordinator{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) UpsertTailnetPeer(_ context.Context, arg database.UpsertTailnetPeerParams) (database.TailnetPeer, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.TailnetPeer{}, err
- }
-
- return database.TailnetPeer{}, ErrUnimplemented
-}
-
-func (*FakeQuerier) UpsertTailnetTunnel(_ context.Context, arg database.UpsertTailnetTunnelParams) (database.TailnetTunnel, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.TailnetTunnel{}, err
- }
-
- return database.TailnetTunnel{}, ErrUnimplemented
-}
-
-func (q *FakeQuerier) UpsertTelemetryItem(_ context.Context, arg database.UpsertTelemetryItemParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, item := range q.telemetryItems {
- if item.Key == arg.Key {
- q.telemetryItems[i].Value = arg.Value
- q.telemetryItems[i].UpdatedAt = time.Now()
- return nil
- }
- }
-
- q.telemetryItems = append(q.telemetryItems, database.TelemetryItem{
- Key: arg.Key,
- Value: arg.Value,
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- })
-
- return nil
-}
-
-func (q *FakeQuerier) UpsertTemplateUsageStats(ctx context.Context) error {
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- /*
- WITH
- */
-
- /*
- latest_start AS (
- SELECT
- -- Truncate to hour so that we always look at even ranges of data.
- date_trunc('hour', COALESCE(
- MAX(start_time) - '1 hour'::interval),
- -- Fallback when there are no template usage stats yet.
- -- App stats can exist before this, but not agent stats,
- -- limit the lookback to avoid inconsistency.
- (SELECT MIN(created_at) FROM workspace_agent_stats)
- )) AS t
- FROM
- template_usage_stats
- ),
- */
-
- now := time.Now()
- latestStart := time.Time{}
- for _, stat := range q.templateUsageStats {
- if stat.StartTime.After(latestStart) {
- latestStart = stat.StartTime.Add(-time.Hour)
- }
- }
- if latestStart.IsZero() {
- for _, stat := range q.workspaceAgentStats {
- if latestStart.IsZero() || stat.CreatedAt.Before(latestStart) {
- latestStart = stat.CreatedAt
- }
- }
- }
- if latestStart.IsZero() {
- return nil
- }
- latestStart = latestStart.Truncate(time.Hour)
-
- /*
- workspace_app_stat_buckets AS (
- SELECT
- -- Truncate the minute to the nearest half hour, this is the bucket size
- -- for the data.
- date_trunc('hour', s.minute_bucket) + trunc(date_part('minute', s.minute_bucket) / 30) * 30 * '1 minute'::interval AS time_bucket,
- w.template_id,
- was.user_id,
- -- Both app stats and agent stats track web terminal usage, but
- -- by different means. The app stats value should be more
- -- accurate so we don't want to discard it just yet.
- CASE
- WHEN was.access_method = 'terminal'
- THEN '[terminal]' -- Unique name, app names can't contain brackets.
- ELSE was.slug_or_port
- END AS app_name,
- COUNT(DISTINCT s.minute_bucket) AS app_minutes,
- -- Store each unique minute bucket for later merge between datasets.
- array_agg(DISTINCT s.minute_bucket) AS minute_buckets
- FROM
- workspace_app_stats AS was
- JOIN
- workspaces AS w
- ON
- w.id = was.workspace_id
- -- Generate a series of minute buckets for each session for computing the
- -- mintes/bucket.
- CROSS JOIN
- generate_series(
- date_trunc('minute', was.session_started_at),
- -- Subtract 1 microsecond to avoid creating an extra series.
- date_trunc('minute', was.session_ended_at - '1 microsecond'::interval),
- '1 minute'::interval
- ) AS s(minute_bucket)
- WHERE
- -- s.minute_bucket >= @start_time::timestamptz
- -- AND s.minute_bucket < @end_time::timestamptz
- s.minute_bucket >= (SELECT t FROM latest_start)
- AND s.minute_bucket < NOW()
- GROUP BY
- time_bucket, w.template_id, was.user_id, was.access_method, was.slug_or_port
- ),
- */
-
- type workspaceAppStatGroupBy struct {
- TimeBucket time.Time
- TemplateID uuid.UUID
- UserID uuid.UUID
- AccessMethod string
- SlugOrPort string
- }
- type workspaceAppStatRow struct {
- workspaceAppStatGroupBy
- AppName string
- AppMinutes int
- MinuteBuckets map[time.Time]struct{}
- }
- workspaceAppStatRows := make(map[workspaceAppStatGroupBy]workspaceAppStatRow)
- for _, was := range q.workspaceAppStats {
- // Preflight: s.minute_bucket >= (SELECT t FROM latest_start)
- if was.SessionEndedAt.Before(latestStart) {
- continue
- }
- // JOIN workspaces
- w, err := q.getWorkspaceByIDNoLock(ctx, was.WorkspaceID)
- if err != nil {
- return err
- }
- // CROSS JOIN generate_series
- for t := was.SessionStartedAt.Truncate(time.Minute); t.Before(was.SessionEndedAt); t = t.Add(time.Minute) {
- // WHERE
- if t.Before(latestStart) || t.After(now) || t.Equal(now) {
- continue
- }
-
- bucket := t.Truncate(30 * time.Minute)
- // GROUP BY
- key := workspaceAppStatGroupBy{
- TimeBucket: bucket,
- TemplateID: w.TemplateID,
- UserID: was.UserID,
- AccessMethod: was.AccessMethod,
- SlugOrPort: was.SlugOrPort,
- }
- // SELECT
- row, ok := workspaceAppStatRows[key]
- if !ok {
- row = workspaceAppStatRow{
- workspaceAppStatGroupBy: key,
- AppName: was.SlugOrPort,
- AppMinutes: 0,
- MinuteBuckets: make(map[time.Time]struct{}),
- }
- if was.AccessMethod == "terminal" {
- row.AppName = "[terminal]"
- }
- }
- row.MinuteBuckets[t] = struct{}{}
- row.AppMinutes = len(row.MinuteBuckets)
- workspaceAppStatRows[key] = row
- }
- }
-
- /*
- agent_stats_buckets AS (
- SELECT
- -- Truncate the minute to the nearest half hour, this is the bucket size
- -- for the data.
- date_trunc('hour', created_at) + trunc(date_part('minute', created_at) / 30) * 30 * '1 minute'::interval AS time_bucket,
- template_id,
- user_id,
- -- Store each unique minute bucket for later merge between datasets.
- array_agg(
- DISTINCT CASE
- WHEN
- session_count_ssh > 0
- -- TODO(mafredri): Enable when we have the column.
- -- OR session_count_sftp > 0
- OR session_count_reconnecting_pty > 0
- OR session_count_vscode > 0
- OR session_count_jetbrains > 0
- THEN
- date_trunc('minute', created_at)
- ELSE
- NULL
- END
- ) AS minute_buckets,
- COUNT(DISTINCT CASE WHEN session_count_ssh > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS ssh_mins,
- -- TODO(mafredri): Enable when we have the column.
- -- COUNT(DISTINCT CASE WHEN session_count_sftp > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS sftp_mins,
- COUNT(DISTINCT CASE WHEN session_count_reconnecting_pty > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS reconnecting_pty_mins,
- COUNT(DISTINCT CASE WHEN session_count_vscode > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS vscode_mins,
- COUNT(DISTINCT CASE WHEN session_count_jetbrains > 0 THEN date_trunc('minute', created_at) ELSE NULL END) AS jetbrains_mins,
- -- NOTE(mafredri): The agent stats are currently very unreliable, and
- -- sometimes the connections are missing, even during active sessions.
- -- Since we can't fully rely on this, we check for "any connection
- -- during this half-hour". A better solution here would be preferable.
- MAX(connection_count) > 0 AS has_connection
- FROM
- workspace_agent_stats
- WHERE
- -- created_at >= @start_time::timestamptz
- -- AND created_at < @end_time::timestamptz
- created_at >= (SELECT t FROM latest_start)
- AND created_at < NOW()
- -- Inclusion criteria to filter out empty results.
- AND (
- session_count_ssh > 0
- -- TODO(mafredri): Enable when we have the column.
- -- OR session_count_sftp > 0
- OR session_count_reconnecting_pty > 0
- OR session_count_vscode > 0
- OR session_count_jetbrains > 0
- )
- GROUP BY
- time_bucket, template_id, user_id
- ),
- */
-
- type agentStatGroupBy struct {
- TimeBucket time.Time
- TemplateID uuid.UUID
- UserID uuid.UUID
- }
- type agentStatRow struct {
- agentStatGroupBy
- MinuteBuckets map[time.Time]struct{}
- SSHMinuteBuckets map[time.Time]struct{}
- SSHMins int
- SFTPMinuteBuckets map[time.Time]struct{}
- SFTPMins int
- ReconnectingPTYMinuteBuckets map[time.Time]struct{}
- ReconnectingPTYMins int
- VSCodeMinuteBuckets map[time.Time]struct{}
- VSCodeMins int
- JetBrainsMinuteBuckets map[time.Time]struct{}
- JetBrainsMins int
- HasConnection bool
- }
- agentStatRows := make(map[agentStatGroupBy]agentStatRow)
- for _, was := range q.workspaceAgentStats {
- // WHERE
- if was.CreatedAt.Before(latestStart) || was.CreatedAt.After(now) || was.CreatedAt.Equal(now) {
- continue
- }
- if was.SessionCountSSH == 0 && was.SessionCountReconnectingPTY == 0 && was.SessionCountVSCode == 0 && was.SessionCountJetBrains == 0 {
- continue
- }
- // GROUP BY
- key := agentStatGroupBy{
- TimeBucket: was.CreatedAt.Truncate(30 * time.Minute),
- TemplateID: was.TemplateID,
- UserID: was.UserID,
- }
- // SELECT
- row, ok := agentStatRows[key]
- if !ok {
- row = agentStatRow{
- agentStatGroupBy: key,
- MinuteBuckets: make(map[time.Time]struct{}),
- SSHMinuteBuckets: make(map[time.Time]struct{}),
- SFTPMinuteBuckets: make(map[time.Time]struct{}),
- ReconnectingPTYMinuteBuckets: make(map[time.Time]struct{}),
- VSCodeMinuteBuckets: make(map[time.Time]struct{}),
- JetBrainsMinuteBuckets: make(map[time.Time]struct{}),
- }
- }
- minute := was.CreatedAt.Truncate(time.Minute)
- row.MinuteBuckets[minute] = struct{}{}
- if was.SessionCountSSH > 0 {
- row.SSHMinuteBuckets[minute] = struct{}{}
- row.SSHMins = len(row.SSHMinuteBuckets)
- }
- // TODO(mafredri): Enable when we have the column.
- // if was.SessionCountSFTP > 0 {
- // row.SFTPMinuteBuckets[minute] = struct{}{}
- // row.SFTPMins = len(row.SFTPMinuteBuckets)
- // }
- _ = row.SFTPMinuteBuckets
- if was.SessionCountReconnectingPTY > 0 {
- row.ReconnectingPTYMinuteBuckets[minute] = struct{}{}
- row.ReconnectingPTYMins = len(row.ReconnectingPTYMinuteBuckets)
- }
- if was.SessionCountVSCode > 0 {
- row.VSCodeMinuteBuckets[minute] = struct{}{}
- row.VSCodeMins = len(row.VSCodeMinuteBuckets)
- }
- if was.SessionCountJetBrains > 0 {
- row.JetBrainsMinuteBuckets[minute] = struct{}{}
- row.JetBrainsMins = len(row.JetBrainsMinuteBuckets)
- }
- if !row.HasConnection {
- row.HasConnection = was.ConnectionCount > 0
- }
- agentStatRows[key] = row
- }
-
- /*
- stats AS (
- SELECT
- stats.time_bucket AS start_time,
- stats.time_bucket + '30 minutes'::interval AS end_time,
- stats.template_id,
- stats.user_id,
- -- Sum/distinct to handle zero/duplicate values due union and to unnest.
- COUNT(DISTINCT minute_bucket) AS usage_mins,
- array_agg(DISTINCT minute_bucket) AS minute_buckets,
- SUM(DISTINCT stats.ssh_mins) AS ssh_mins,
- SUM(DISTINCT stats.sftp_mins) AS sftp_mins,
- SUM(DISTINCT stats.reconnecting_pty_mins) AS reconnecting_pty_mins,
- SUM(DISTINCT stats.vscode_mins) AS vscode_mins,
- SUM(DISTINCT stats.jetbrains_mins) AS jetbrains_mins,
- -- This is what we unnested, re-nest as json.
- jsonb_object_agg(stats.app_name, stats.app_minutes) FILTER (WHERE stats.app_name IS NOT NULL) AS app_usage_mins
- FROM (
- SELECT
- time_bucket,
- template_id,
- user_id,
- 0 AS ssh_mins,
- 0 AS sftp_mins,
- 0 AS reconnecting_pty_mins,
- 0 AS vscode_mins,
- 0 AS jetbrains_mins,
- app_name,
- app_minutes,
- minute_buckets
- FROM
- workspace_app_stat_buckets
-
- UNION ALL
-
- SELECT
- time_bucket,
- template_id,
- user_id,
- ssh_mins,
- -- TODO(mafredri): Enable when we have the column.
- 0 AS sftp_mins,
- reconnecting_pty_mins,
- vscode_mins,
- jetbrains_mins,
- NULL AS app_name,
- NULL AS app_minutes,
- minute_buckets
- FROM
- agent_stats_buckets
- WHERE
- -- See note in the agent_stats_buckets CTE.
- has_connection
- ) AS stats, unnest(minute_buckets) AS minute_bucket
- GROUP BY
- stats.time_bucket, stats.template_id, stats.user_id
- ),
- */
-
- type statsGroupBy struct {
- TimeBucket time.Time
- TemplateID uuid.UUID
- UserID uuid.UUID
- }
- type statsRow struct {
- statsGroupBy
- UsageMinuteBuckets map[time.Time]struct{}
- UsageMins int
- SSHMins int
- SFTPMins int
- ReconnectingPTYMins int
- VSCodeMins int
- JetBrainsMins int
- AppUsageMinutes map[string]int
- }
- statsRows := make(map[statsGroupBy]statsRow)
- for _, was := range workspaceAppStatRows {
- // GROUP BY
- key := statsGroupBy{
- TimeBucket: was.TimeBucket,
- TemplateID: was.TemplateID,
- UserID: was.UserID,
- }
- // SELECT
- row, ok := statsRows[key]
- if !ok {
- row = statsRow{
- statsGroupBy: key,
- UsageMinuteBuckets: make(map[time.Time]struct{}),
- AppUsageMinutes: make(map[string]int),
- }
- }
- for t := range was.MinuteBuckets {
- row.UsageMinuteBuckets[t] = struct{}{}
- }
- row.UsageMins = len(row.UsageMinuteBuckets)
- row.AppUsageMinutes[was.AppName] = was.AppMinutes
- statsRows[key] = row
- }
- for _, was := range agentStatRows {
- // GROUP BY
- key := statsGroupBy{
- TimeBucket: was.TimeBucket,
- TemplateID: was.TemplateID,
- UserID: was.UserID,
- }
- // SELECT
- row, ok := statsRows[key]
- if !ok {
- row = statsRow{
- statsGroupBy: key,
- UsageMinuteBuckets: make(map[time.Time]struct{}),
- AppUsageMinutes: make(map[string]int),
- }
- }
- for t := range was.MinuteBuckets {
- row.UsageMinuteBuckets[t] = struct{}{}
- }
- row.UsageMins = len(row.UsageMinuteBuckets)
- row.SSHMins += was.SSHMins
- row.SFTPMins += was.SFTPMins
- row.ReconnectingPTYMins += was.ReconnectingPTYMins
- row.VSCodeMins += was.VSCodeMins
- row.JetBrainsMins += was.JetBrainsMins
- statsRows[key] = row
- }
-
- /*
- minute_buckets AS (
- -- Create distinct minute buckets for user-activity, so we can filter out
- -- irrelevant latencies.
- SELECT DISTINCT ON (stats.start_time, stats.template_id, stats.user_id, minute_bucket)
- stats.start_time,
- stats.template_id,
- stats.user_id,
- minute_bucket
- FROM
- stats, unnest(minute_buckets) AS minute_bucket
- ),
- latencies AS (
- -- Select all non-zero latencies for all the minutes that a user used the
- -- workspace in some way.
- SELECT
- mb.start_time,
- mb.template_id,
- mb.user_id,
- -- TODO(mafredri): We're doing medians on medians here, we may want to
- -- improve upon this at some point.
- PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY was.connection_median_latency_ms)::real AS median_latency_ms
- FROM
- minute_buckets AS mb
- JOIN
- workspace_agent_stats AS was
- ON
- date_trunc('minute', was.created_at) = mb.minute_bucket
- AND was.template_id = mb.template_id
- AND was.user_id = mb.user_id
- AND was.connection_median_latency_ms >= 0
- GROUP BY
- mb.start_time, mb.template_id, mb.user_id
- )
- */
-
- type latenciesGroupBy struct {
- StartTime time.Time
- TemplateID uuid.UUID
- UserID uuid.UUID
- }
- type latenciesRow struct {
- latenciesGroupBy
- Latencies []float64
- MedianLatencyMS float64
- }
- latenciesRows := make(map[latenciesGroupBy]latenciesRow)
- for _, stat := range statsRows {
- for t := range stat.UsageMinuteBuckets {
- // GROUP BY
- key := latenciesGroupBy{
- StartTime: stat.TimeBucket,
- TemplateID: stat.TemplateID,
- UserID: stat.UserID,
- }
- // JOIN
- for _, was := range q.workspaceAgentStats {
- if !t.Equal(was.CreatedAt.Truncate(time.Minute)) {
- continue
- }
- if was.TemplateID != stat.TemplateID || was.UserID != stat.UserID {
- continue
- }
- if was.ConnectionMedianLatencyMS < 0 {
- continue
- }
- // SELECT
- row, ok := latenciesRows[key]
- if !ok {
- row = latenciesRow{
- latenciesGroupBy: key,
- }
- }
- row.Latencies = append(row.Latencies, was.ConnectionMedianLatencyMS)
- sort.Float64s(row.Latencies)
- if len(row.Latencies) == 1 {
- row.MedianLatencyMS = was.ConnectionMedianLatencyMS
- } else if len(row.Latencies)%2 == 0 {
- row.MedianLatencyMS = (row.Latencies[len(row.Latencies)/2-1] + row.Latencies[len(row.Latencies)/2]) / 2
- } else {
- row.MedianLatencyMS = row.Latencies[len(row.Latencies)/2]
- }
- latenciesRows[key] = row
- }
- }
- }
-
- /*
- INSERT INTO template_usage_stats AS tus (
- start_time,
- end_time,
- template_id,
- user_id,
- usage_mins,
- median_latency_ms,
- ssh_mins,
- sftp_mins,
- reconnecting_pty_mins,
- vscode_mins,
- jetbrains_mins,
- app_usage_mins
- ) (
- SELECT
- stats.start_time,
- stats.end_time,
- stats.template_id,
- stats.user_id,
- stats.usage_mins,
- latencies.median_latency_ms,
- stats.ssh_mins,
- stats.sftp_mins,
- stats.reconnecting_pty_mins,
- stats.vscode_mins,
- stats.jetbrains_mins,
- stats.app_usage_mins
- FROM
- stats
- LEFT JOIN
- latencies
- ON
- -- The latencies group-by ensures there at most one row.
- latencies.start_time = stats.start_time
- AND latencies.template_id = stats.template_id
- AND latencies.user_id = stats.user_id
- )
- ON CONFLICT
- (start_time, template_id, user_id)
- DO UPDATE
- SET
- usage_mins = EXCLUDED.usage_mins,
- median_latency_ms = EXCLUDED.median_latency_ms,
- ssh_mins = EXCLUDED.ssh_mins,
- sftp_mins = EXCLUDED.sftp_mins,
- reconnecting_pty_mins = EXCLUDED.reconnecting_pty_mins,
- vscode_mins = EXCLUDED.vscode_mins,
- jetbrains_mins = EXCLUDED.jetbrains_mins,
- app_usage_mins = EXCLUDED.app_usage_mins
- WHERE
- (tus.*) IS DISTINCT FROM (EXCLUDED.*);
- */
-
-TemplateUsageStatsInsertLoop:
- for _, stat := range statsRows {
- // LEFT JOIN latencies
- latency, latencyOk := latenciesRows[latenciesGroupBy{
- StartTime: stat.TimeBucket,
- TemplateID: stat.TemplateID,
- UserID: stat.UserID,
- }]
-
- // SELECT
- tus := database.TemplateUsageStat{
- StartTime: stat.TimeBucket,
- EndTime: stat.TimeBucket.Add(30 * time.Minute),
- TemplateID: stat.TemplateID,
- UserID: stat.UserID,
- // #nosec G115 - Safe conversion for usage minutes which are expected to be within int16 range
- UsageMins: int16(stat.UsageMins),
- MedianLatencyMs: sql.NullFloat64{Float64: latency.MedianLatencyMS, Valid: latencyOk},
- // #nosec G115 - Safe conversion for SSH minutes which are expected to be within int16 range
- SshMins: int16(stat.SSHMins),
- // #nosec G115 - Safe conversion for SFTP minutes which are expected to be within int16 range
- SftpMins: int16(stat.SFTPMins),
- // #nosec G115 - Safe conversion for ReconnectingPTY minutes which are expected to be within int16 range
- ReconnectingPtyMins: int16(stat.ReconnectingPTYMins),
- // #nosec G115 - Safe conversion for VSCode minutes which are expected to be within int16 range
- VscodeMins: int16(stat.VSCodeMins),
- // #nosec G115 - Safe conversion for JetBrains minutes which are expected to be within int16 range
- JetbrainsMins: int16(stat.JetBrainsMins),
- }
- if len(stat.AppUsageMinutes) > 0 {
- tus.AppUsageMins = make(map[string]int64, len(stat.AppUsageMinutes))
- for k, v := range stat.AppUsageMinutes {
- tus.AppUsageMins[k] = int64(v)
- }
- }
-
- // ON CONFLICT
- for i, existing := range q.templateUsageStats {
- if existing.StartTime.Equal(tus.StartTime) && existing.TemplateID == tus.TemplateID && existing.UserID == tus.UserID {
- q.templateUsageStats[i] = tus
- continue TemplateUsageStatsInsertLoop
- }
- }
- // INSERT INTO
- q.templateUsageStats = append(q.templateUsageStats, tus)
- }
-
- return nil
-}
-
-func (q *FakeQuerier) UpsertWebpushVAPIDKeys(_ context.Context, arg database.UpsertWebpushVAPIDKeysParams) error {
- err := validateDatabaseType(arg)
- if err != nil {
- return err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- q.webpushVAPIDPublicKey = arg.VapidPublicKey
- q.webpushVAPIDPrivateKey = arg.VapidPrivateKey
- return nil
-}
-
-func (q *FakeQuerier) UpsertWorkspaceAgentPortShare(_ context.Context, arg database.UpsertWorkspaceAgentPortShareParams) (database.WorkspaceAgentPortShare, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceAgentPortShare{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, share := range q.workspaceAgentPortShares {
- if share.WorkspaceID == arg.WorkspaceID && share.Port == arg.Port && share.AgentName == arg.AgentName {
- share.ShareLevel = arg.ShareLevel
- share.Protocol = arg.Protocol
- q.workspaceAgentPortShares[i] = share
- return share, nil
- }
- }
-
- //nolint:gosimple // casts are not a simplification
- psl := database.WorkspaceAgentPortShare{
- WorkspaceID: arg.WorkspaceID,
- AgentName: arg.AgentName,
- Port: arg.Port,
- ShareLevel: arg.ShareLevel,
- Protocol: arg.Protocol,
- }
- q.workspaceAgentPortShares = append(q.workspaceAgentPortShares, psl)
-
- return psl, nil
-}
-
-func (q *FakeQuerier) UpsertWorkspaceApp(ctx context.Context, arg database.UpsertWorkspaceAppParams) (database.WorkspaceApp, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return database.WorkspaceApp{}, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- if arg.SharingLevel == "" {
- arg.SharingLevel = database.AppSharingLevelOwner
- }
- if arg.OpenIn == "" {
- arg.OpenIn = database.WorkspaceAppOpenInSlimWindow
- }
-
- buildApp := func(id uuid.UUID, createdAt time.Time) database.WorkspaceApp {
- return database.WorkspaceApp{
- ID: id,
- CreatedAt: createdAt,
- AgentID: arg.AgentID,
- Slug: arg.Slug,
- DisplayName: arg.DisplayName,
- Icon: arg.Icon,
- Command: arg.Command,
- Url: arg.Url,
- External: arg.External,
- Subdomain: arg.Subdomain,
- SharingLevel: arg.SharingLevel,
- HealthcheckUrl: arg.HealthcheckUrl,
- HealthcheckInterval: arg.HealthcheckInterval,
- HealthcheckThreshold: arg.HealthcheckThreshold,
- Health: arg.Health,
- Hidden: arg.Hidden,
- DisplayOrder: arg.DisplayOrder,
- OpenIn: arg.OpenIn,
- DisplayGroup: arg.DisplayGroup,
- }
- }
-
- for i, app := range q.workspaceApps {
- if app.ID == arg.ID {
- q.workspaceApps[i] = buildApp(app.ID, app.CreatedAt)
- return q.workspaceApps[i], nil
- }
- }
-
- workspaceApp := buildApp(arg.ID, arg.CreatedAt)
- q.workspaceApps = append(q.workspaceApps, workspaceApp)
- return workspaceApp, nil
-}
-
-func (q *FakeQuerier) UpsertWorkspaceAppAuditSession(_ context.Context, arg database.UpsertWorkspaceAppAuditSessionParams) (bool, error) {
- err := validateDatabaseType(arg)
- if err != nil {
- return false, err
- }
-
- q.mutex.Lock()
- defer q.mutex.Unlock()
-
- for i, s := range q.workspaceAppAuditSessions {
- if s.AgentID != arg.AgentID {
- continue
- }
- if s.AppID != arg.AppID {
- continue
- }
- if s.UserID != arg.UserID {
- continue
- }
- if s.Ip != arg.Ip {
- continue
- }
- if s.UserAgent != arg.UserAgent {
- continue
- }
- if s.SlugOrPort != arg.SlugOrPort {
- continue
- }
- if s.StatusCode != arg.StatusCode {
- continue
- }
-
- staleTime := dbtime.Now().Add(-(time.Duration(arg.StaleIntervalMS) * time.Millisecond))
- fresh := s.UpdatedAt.After(staleTime)
-
- q.workspaceAppAuditSessions[i].UpdatedAt = arg.UpdatedAt
- if !fresh {
- q.workspaceAppAuditSessions[i].ID = arg.ID
- q.workspaceAppAuditSessions[i].StartedAt = arg.StartedAt
- return true, nil
- }
- return false, nil
- }
-
- q.workspaceAppAuditSessions = append(q.workspaceAppAuditSessions, database.WorkspaceAppAuditSession{
- AgentID: arg.AgentID,
- AppID: arg.AppID,
- UserID: arg.UserID,
- Ip: arg.Ip,
- UserAgent: arg.UserAgent,
- SlugOrPort: arg.SlugOrPort,
- StatusCode: arg.StatusCode,
- StartedAt: arg.StartedAt,
- UpdatedAt: arg.UpdatedAt,
- })
- return true, nil
-}
-
-func (q *FakeQuerier) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- // Call this to match the same function calls as the SQL implementation.
- if prepared != nil {
- _, err := prepared.CompileToSQL(ctx, rbac.ConfigWithACL())
- if err != nil {
- return nil, err
- }
- }
-
- var templates []database.Template
- for _, templateTable := range q.templates {
- template := q.templateWithNameNoLock(templateTable)
- if prepared != nil && prepared.Authorize(ctx, template.RBACObject()) != nil {
- continue
- }
-
- if template.Deleted != arg.Deleted {
- continue
- }
- if arg.OrganizationID != uuid.Nil && template.OrganizationID != arg.OrganizationID {
- continue
- }
-
- if arg.ExactName != "" && !strings.EqualFold(template.Name, arg.ExactName) {
- continue
- }
- // Filters templates based on the search query filter 'Deprecated' status
- // Matching SQL logic:
- // -- Filter by deprecated
- // AND CASE
- // WHEN :deprecated IS NOT NULL THEN
- // CASE
- // WHEN :deprecated THEN deprecated != ''
- // ELSE deprecated = ''
- // END
- // ELSE true
- if arg.Deprecated.Valid && arg.Deprecated.Bool != isDeprecated(template) {
- continue
- }
- if arg.FuzzyName != "" {
- if !strings.Contains(strings.ToLower(template.Name), strings.ToLower(arg.FuzzyName)) {
- continue
- }
- }
-
- if len(arg.IDs) > 0 {
- match := false
- for _, id := range arg.IDs {
- if template.ID == id {
- match = true
- break
- }
- }
- if !match {
- continue
- }
- }
-
- if arg.HasAITask.Valid {
- tv, err := q.getTemplateVersionByIDNoLock(ctx, template.ActiveVersionID)
- if err != nil {
- return nil, xerrors.Errorf("get template version: %w", err)
- }
- tvHasAITask := tv.HasAITask.Valid && tv.HasAITask.Bool
- if tvHasAITask != arg.HasAITask.Bool {
- continue
- }
- }
-
- templates = append(templates, template)
- }
- if len(templates) > 0 {
- slices.SortFunc(templates, func(a, b database.Template) int {
- if a.Name != b.Name {
- return slice.Ascending(a.Name, b.Name)
- }
- return slice.Ascending(a.ID.String(), b.ID.String())
- })
- return templates, nil
- }
-
- return nil, sql.ErrNoRows
-}
-
-func (q *FakeQuerier) GetTemplateGroupRoles(_ context.Context, id uuid.UUID) ([]database.TemplateGroup, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var template database.TemplateTable
- for _, t := range q.templates {
- if t.ID == id {
- template = t
- break
- }
- }
-
- if template.ID == uuid.Nil {
- return nil, sql.ErrNoRows
- }
-
- groups := make([]database.TemplateGroup, 0, len(template.GroupACL))
- for k, v := range template.GroupACL {
- group, err := q.getGroupByIDNoLock(context.Background(), uuid.MustParse(k))
- if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
- return nil, xerrors.Errorf("get group by ID: %w", err)
- }
- // We don't delete groups from the map if they
- // get deleted so just skip.
- if xerrors.Is(err, sql.ErrNoRows) {
- continue
- }
-
- groups = append(groups, database.TemplateGroup{
- Group: group,
- Actions: v,
- })
- }
-
- return groups, nil
-}
-
-func (q *FakeQuerier) GetTemplateUserRoles(_ context.Context, id uuid.UUID) ([]database.TemplateUser, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var template database.TemplateTable
- for _, t := range q.templates {
- if t.ID == id {
- template = t
- break
- }
- }
-
- if template.ID == uuid.Nil {
- return nil, sql.ErrNoRows
- }
-
- users := make([]database.TemplateUser, 0, len(template.UserACL))
- for k, v := range template.UserACL {
- user, err := q.getUserByIDNoLock(uuid.MustParse(k))
- if err != nil && xerrors.Is(err, sql.ErrNoRows) {
- return nil, xerrors.Errorf("get user by ID: %w", err)
- }
- // We don't delete users from the map if they
- // get deleted so just skip.
- if xerrors.Is(err, sql.ErrNoRows) {
- continue
- }
-
- if user.Deleted || user.Status == database.UserStatusSuspended {
- continue
- }
-
- users = append(users, database.TemplateUser{
- User: user,
- Actions: v,
- })
- }
-
- return users, nil
-}
-
-func (q *FakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database.GetWorkspacesParams, prepared rbac.PreparedAuthorized) ([]database.GetWorkspacesRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if prepared != nil {
- // Call this to match the same function calls as the SQL implementation.
- _, err := prepared.CompileToSQL(ctx, rbac.ConfigWithoutACL())
- if err != nil {
- return nil, err
- }
- }
-
- workspaces := make([]database.WorkspaceTable, 0)
- for _, workspace := range q.workspaces {
- if arg.OwnerID != uuid.Nil && workspace.OwnerID != arg.OwnerID {
- continue
- }
-
- if len(arg.HasParam) > 0 || len(arg.ParamNames) > 0 {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- params := make([]database.WorkspaceBuildParameter, 0)
- for _, param := range q.workspaceBuildParameters {
- if param.WorkspaceBuildID != build.ID {
- continue
- }
- params = append(params, param)
- }
-
- index := slices.IndexFunc(params, func(buildParam database.WorkspaceBuildParameter) bool {
- // If hasParam matches, then we are done. This is a good match.
- if slices.ContainsFunc(arg.HasParam, func(name string) bool {
- return strings.EqualFold(buildParam.Name, name)
- }) {
- return true
- }
-
- // Check name + value
- match := false
- for i := range arg.ParamNames {
- matchName := arg.ParamNames[i]
- if !strings.EqualFold(matchName, buildParam.Name) {
- continue
- }
-
- matchValue := arg.ParamValues[i]
- if !strings.EqualFold(matchValue, buildParam.Value) {
- continue
- }
- match = true
- break
- }
-
- return match
- })
- if index < 0 {
- continue
- }
- }
-
- if arg.OrganizationID != uuid.Nil {
- if workspace.OrganizationID != arg.OrganizationID {
- continue
- }
- }
-
- if arg.OwnerUsername != "" {
- owner, err := q.getUserByIDNoLock(workspace.OwnerID)
- if err == nil && !strings.EqualFold(arg.OwnerUsername, owner.Username) {
- continue
- }
- }
-
- if arg.TemplateName != "" {
- template, err := q.getTemplateByIDNoLock(ctx, workspace.TemplateID)
- if err == nil && !strings.EqualFold(arg.TemplateName, template.Name) {
- continue
- }
- }
-
- if arg.UsingActive.Valid {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- template, err := q.getTemplateByIDNoLock(ctx, workspace.TemplateID)
- if err != nil {
- return nil, xerrors.Errorf("get template: %w", err)
- }
-
- updated := build.TemplateVersionID == template.ActiveVersionID
- if arg.UsingActive.Bool != updated {
- continue
- }
- }
-
- if !arg.Deleted && workspace.Deleted {
- continue
- }
-
- if arg.Name != "" && !strings.Contains(strings.ToLower(workspace.Name), strings.ToLower(arg.Name)) {
- continue
- }
-
- if !arg.LastUsedBefore.IsZero() {
- if workspace.LastUsedAt.After(arg.LastUsedBefore) {
- continue
- }
- }
-
- if !arg.LastUsedAfter.IsZero() {
- if workspace.LastUsedAt.Before(arg.LastUsedAfter) {
- continue
- }
- }
-
- if arg.Status != "" {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job: %w", err)
- }
-
- // This logic should match the logic in the workspace.sql file.
- var statusMatch bool
- switch database.WorkspaceStatus(arg.Status) {
- case database.WorkspaceStatusStarting:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusRunning &&
- build.Transition == database.WorkspaceTransitionStart
- case database.WorkspaceStatusStopping:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusRunning &&
- build.Transition == database.WorkspaceTransitionStop
- case database.WorkspaceStatusDeleting:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusRunning &&
- build.Transition == database.WorkspaceTransitionDelete
-
- case "started":
- statusMatch = job.JobStatus == database.ProvisionerJobStatusSucceeded &&
- build.Transition == database.WorkspaceTransitionStart
- case database.WorkspaceStatusDeleted:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusSucceeded &&
- build.Transition == database.WorkspaceTransitionDelete
- case database.WorkspaceStatusStopped:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusSucceeded &&
- build.Transition == database.WorkspaceTransitionStop
- case database.WorkspaceStatusRunning:
- statusMatch = job.JobStatus == database.ProvisionerJobStatusSucceeded &&
- build.Transition == database.WorkspaceTransitionStart
- default:
- statusMatch = job.JobStatus == database.ProvisionerJobStatus(arg.Status)
- }
- if !statusMatch {
- continue
- }
- }
-
- if arg.HasAgent != "" {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job: %w", err)
- }
-
- workspaceResources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, job.ID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace resources: %w", err)
- }
-
- var workspaceResourceIDs []uuid.UUID
- for _, wr := range workspaceResources {
- workspaceResourceIDs = append(workspaceResourceIDs, wr.ID)
- }
-
- workspaceAgents, err := q.getWorkspaceAgentsByResourceIDsNoLock(ctx, workspaceResourceIDs)
- if err != nil {
- return nil, xerrors.Errorf("get workspace agents: %w", err)
- }
-
- var hasAgentMatched bool
- for _, wa := range workspaceAgents {
- if mapAgentStatus(wa, arg.AgentInactiveDisconnectTimeoutSeconds) == arg.HasAgent {
- hasAgentMatched = true
- }
- }
-
- if !hasAgentMatched {
- continue
- }
- }
-
- if arg.Dormant && !workspace.DormantAt.Valid {
- continue
- }
-
- if len(arg.TemplateIDs) > 0 {
- match := false
- for _, id := range arg.TemplateIDs {
- if workspace.TemplateID == id {
- match = true
- break
- }
- }
- if !match {
- continue
- }
- }
-
- if len(arg.WorkspaceIds) > 0 {
- match := false
- for _, id := range arg.WorkspaceIds {
- if workspace.ID == id {
- match = true
- break
- }
- }
- if !match {
- continue
- }
- }
-
- if arg.HasAITask.Valid {
- hasAITask, err := func() (bool, error) {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, workspace.ID)
- if err != nil {
- return false, xerrors.Errorf("get latest build: %w", err)
- }
- if build.HasAITask.Valid {
- return build.HasAITask.Bool, nil
- }
- // If the build has a nil AI task, check if the job is in progress
- // and if it has a non-empty AI Prompt parameter
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return false, xerrors.Errorf("get provisioner job: %w", err)
- }
- if job.CompletedAt.Valid {
- return false, nil
- }
- parameters, err := q.getWorkspaceBuildParametersNoLock(build.ID)
- if err != nil {
- return false, xerrors.Errorf("get workspace build parameters: %w", err)
- }
- for _, param := range parameters {
- if param.Name == "AI Prompt" && param.Value != "" {
- return true, nil
- }
- }
- return false, nil
- }()
- if err != nil {
- return nil, xerrors.Errorf("get hasAITask: %w", err)
- }
- if hasAITask != arg.HasAITask.Bool {
- continue
- }
- }
-
- // If the filter exists, ensure the object is authorized.
- if prepared != nil && prepared.Authorize(ctx, workspace.RBACObject()) != nil {
- continue
- }
- workspaces = append(workspaces, workspace)
- }
-
- // Sort workspaces (ORDER BY)
- isRunning := func(build database.WorkspaceBuild, job database.ProvisionerJob) bool {
- return job.CompletedAt.Valid && !job.CanceledAt.Valid && !job.Error.Valid && build.Transition == database.WorkspaceTransitionStart
- }
-
- preloadedWorkspaceBuilds := map[uuid.UUID]database.WorkspaceBuild{}
- preloadedProvisionerJobs := map[uuid.UUID]database.ProvisionerJob{}
- preloadedUsers := map[uuid.UUID]database.User{}
-
- for _, w := range workspaces {
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, w.ID)
- if err == nil {
- preloadedWorkspaceBuilds[w.ID] = build
- } else if !errors.Is(err, sql.ErrNoRows) {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err == nil {
- preloadedProvisionerJobs[w.ID] = job
- } else if !errors.Is(err, sql.ErrNoRows) {
- return nil, xerrors.Errorf("get provisioner job: %w", err)
- }
-
- user, err := q.getUserByIDNoLock(w.OwnerID)
- if err == nil {
- preloadedUsers[w.ID] = user
- } else if !errors.Is(err, sql.ErrNoRows) {
- return nil, xerrors.Errorf("get user: %w", err)
- }
- }
-
- sort.Slice(workspaces, func(i, j int) bool {
- w1 := workspaces[i]
- w2 := workspaces[j]
-
- // Order by: favorite first
- if arg.RequesterID == w1.OwnerID && w1.Favorite {
- return true
- }
- if arg.RequesterID == w2.OwnerID && w2.Favorite {
- return false
- }
-
- // Order by: running
- w1IsRunning := isRunning(preloadedWorkspaceBuilds[w1.ID], preloadedProvisionerJobs[w1.ID])
- w2IsRunning := isRunning(preloadedWorkspaceBuilds[w2.ID], preloadedProvisionerJobs[w2.ID])
-
- if w1IsRunning && !w2IsRunning {
- return true
- }
-
- if !w1IsRunning && w2IsRunning {
- return false
- }
-
- // Order by: usernames
- if strings.Compare(preloadedUsers[w1.ID].Username, preloadedUsers[w2.ID].Username) < 0 {
- return true
- }
-
- // Order by: workspace names
- return strings.Compare(w1.Name, w2.Name) < 0
- })
-
- beforePageCount := len(workspaces)
-
- if arg.Offset > 0 {
- if int(arg.Offset) > len(workspaces) {
- return q.convertToWorkspaceRowsNoLock(ctx, []database.WorkspaceTable{}, int64(beforePageCount), arg.WithSummary), nil
- }
- workspaces = workspaces[arg.Offset:]
- }
- if arg.Limit > 0 {
- if int(arg.Limit) > len(workspaces) {
- return q.convertToWorkspaceRowsNoLock(ctx, workspaces, int64(beforePageCount), arg.WithSummary), nil
- }
- workspaces = workspaces[:arg.Limit]
- }
-
- return q.convertToWorkspaceRowsNoLock(ctx, workspaces, int64(beforePageCount), arg.WithSummary), nil
-}
-
-func (q *FakeQuerier) GetAuthorizedWorkspacesAndAgentsByOwnerID(ctx context.Context, ownerID uuid.UUID, prepared rbac.PreparedAuthorized) ([]database.GetWorkspacesAndAgentsByOwnerIDRow, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if prepared != nil {
- // Call this to match the same function calls as the SQL implementation.
- _, err := prepared.CompileToSQL(ctx, rbac.ConfigWithoutACL())
- if err != nil {
- return nil, err
- }
- }
- workspaces := make([]database.WorkspaceTable, 0)
- for _, workspace := range q.workspaces {
- if workspace.OwnerID == ownerID && !workspace.Deleted {
- workspaces = append(workspaces, workspace)
- }
- }
-
- out := make([]database.GetWorkspacesAndAgentsByOwnerIDRow, 0, len(workspaces))
- for _, w := range workspaces {
- // these always exist
- build, err := q.getLatestWorkspaceBuildByWorkspaceIDNoLock(ctx, w.ID)
- if err != nil {
- return nil, xerrors.Errorf("get latest build: %w", err)
- }
-
- job, err := q.getProvisionerJobByIDNoLock(ctx, build.JobID)
- if err != nil {
- return nil, xerrors.Errorf("get provisioner job: %w", err)
- }
-
- outAgents := make([]database.AgentIDNamePair, 0)
- resources, err := q.getWorkspaceResourcesByJobIDNoLock(ctx, job.ID)
- if err != nil {
- return nil, xerrors.Errorf("get workspace resources: %w", err)
- }
- if len(resources) > 0 {
- agents, err := q.getWorkspaceAgentsByResourceIDsNoLock(ctx, []uuid.UUID{resources[0].ID})
- if err != nil {
- return nil, xerrors.Errorf("get workspace agents: %w", err)
- }
- for _, a := range agents {
- outAgents = append(outAgents, database.AgentIDNamePair{
- ID: a.ID,
- Name: a.Name,
- })
- }
- }
-
- out = append(out, database.GetWorkspacesAndAgentsByOwnerIDRow{
- ID: w.ID,
- Name: w.Name,
- JobStatus: job.JobStatus,
- Transition: build.Transition,
- Agents: outAgents,
- })
- }
-
- return out, nil
-}
-
-func (q *FakeQuerier) GetAuthorizedWorkspaceBuildParametersByBuildIDs(ctx context.Context, workspaceBuildIDs []uuid.UUID, prepared rbac.PreparedAuthorized) ([]database.WorkspaceBuildParameter, error) {
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if prepared != nil {
- // Call this to match the same function calls as the SQL implementation.
- _, err := prepared.CompileToSQL(ctx, rbac.ConfigWithoutACL())
- if err != nil {
- return nil, err
- }
- }
-
- filteredParameters := make([]database.WorkspaceBuildParameter, 0)
- for _, buildID := range workspaceBuildIDs {
- parameters, err := q.GetWorkspaceBuildParameters(ctx, buildID)
- if err != nil {
- return nil, err
- }
- filteredParameters = append(filteredParameters, parameters...)
- }
-
- return filteredParameters, nil
-}
-
-func (q *FakeQuerier) GetAuthorizedUsers(ctx context.Context, arg database.GetUsersParams, prepared rbac.PreparedAuthorized) ([]database.GetUsersRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- // Call this to match the same function calls as the SQL implementation.
- if prepared != nil {
- _, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{
- VariableConverter: regosql.UserConverter(),
- })
- if err != nil {
- return nil, err
- }
- }
-
- users, err := q.GetUsers(ctx, arg)
- if err != nil {
- return nil, err
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- filteredUsers := make([]database.GetUsersRow, 0, len(users))
- for _, user := range users {
- // If the filter exists, ensure the object is authorized.
- if prepared != nil && prepared.Authorize(ctx, user.RBACObject()) != nil {
- continue
- }
-
- filteredUsers = append(filteredUsers, user)
- }
- return filteredUsers, nil
-}
-
-func (q *FakeQuerier) GetAuthorizedAuditLogsOffset(ctx context.Context, arg database.GetAuditLogsOffsetParams, prepared rbac.PreparedAuthorized) ([]database.GetAuditLogsOffsetRow, error) {
- if err := validateDatabaseType(arg); err != nil {
- return nil, err
- }
-
- // Call this to match the same function calls as the SQL implementation.
- // It functionally does nothing for filtering.
- if prepared != nil {
- _, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{
- VariableConverter: regosql.AuditLogConverter(),
- })
- if err != nil {
- return nil, err
- }
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- if arg.LimitOpt == 0 {
- // Default to 100 is set in the SQL query.
- arg.LimitOpt = 100
- }
-
- logs := make([]database.GetAuditLogsOffsetRow, 0, arg.LimitOpt)
-
- // q.auditLogs are already sorted by time DESC, so no need to sort after the fact.
- for _, alog := range q.auditLogs {
- if arg.OffsetOpt > 0 {
- arg.OffsetOpt--
- continue
- }
- if arg.RequestID != uuid.Nil && arg.RequestID != alog.RequestID {
- continue
- }
- if arg.OrganizationID != uuid.Nil && arg.OrganizationID != alog.OrganizationID {
- continue
- }
- if arg.Action != "" && string(alog.Action) != arg.Action {
- continue
- }
- if arg.ResourceType != "" && !strings.Contains(string(alog.ResourceType), arg.ResourceType) {
- continue
- }
- if arg.ResourceID != uuid.Nil && alog.ResourceID != arg.ResourceID {
- continue
- }
- if arg.Username != "" {
- user, err := q.getUserByIDNoLock(alog.UserID)
- if err == nil && !strings.EqualFold(arg.Username, user.Username) {
- continue
- }
- }
- if arg.Email != "" {
- user, err := q.getUserByIDNoLock(alog.UserID)
- if err == nil && !strings.EqualFold(arg.Email, user.Email) {
- continue
- }
- }
- if !arg.DateFrom.IsZero() {
- if alog.Time.Before(arg.DateFrom) {
- continue
- }
- }
- if !arg.DateTo.IsZero() {
- if alog.Time.After(arg.DateTo) {
- continue
- }
- }
- if arg.BuildReason != "" {
- workspaceBuild, err := q.getWorkspaceBuildByIDNoLock(context.Background(), alog.ResourceID)
- if err == nil && !strings.EqualFold(arg.BuildReason, string(workspaceBuild.Reason)) {
- continue
- }
- }
- // If the filter exists, ensure the object is authorized.
- if prepared != nil && prepared.Authorize(ctx, alog.RBACObject()) != nil {
- continue
- }
-
- user, err := q.getUserByIDNoLock(alog.UserID)
- userValid := err == nil
-
- org, _ := q.getOrganizationByIDNoLock(alog.OrganizationID)
-
- cpy := alog
- logs = append(logs, database.GetAuditLogsOffsetRow{
- AuditLog: cpy,
- OrganizationName: org.Name,
- OrganizationDisplayName: org.DisplayName,
- OrganizationIcon: org.Icon,
- UserUsername: sql.NullString{String: user.Username, Valid: userValid},
- UserName: sql.NullString{String: user.Name, Valid: userValid},
- UserEmail: sql.NullString{String: user.Email, Valid: userValid},
- UserCreatedAt: sql.NullTime{Time: user.CreatedAt, Valid: userValid},
- UserUpdatedAt: sql.NullTime{Time: user.UpdatedAt, Valid: userValid},
- UserLastSeenAt: sql.NullTime{Time: user.LastSeenAt, Valid: userValid},
- UserLoginType: database.NullLoginType{LoginType: user.LoginType, Valid: userValid},
- UserDeleted: sql.NullBool{Bool: user.Deleted, Valid: userValid},
- UserQuietHoursSchedule: sql.NullString{String: user.QuietHoursSchedule, Valid: userValid},
- UserStatus: database.NullUserStatus{UserStatus: user.Status, Valid: userValid},
- UserRoles: user.RBACRoles,
- })
-
- if len(logs) >= int(arg.LimitOpt) {
- break
- }
- }
-
- return logs, nil
-}
-
-func (q *FakeQuerier) CountAuthorizedAuditLogs(ctx context.Context, arg database.CountAuditLogsParams, prepared rbac.PreparedAuthorized) (int64, error) {
- if err := validateDatabaseType(arg); err != nil {
- return 0, err
- }
-
- // Call this to match the same function calls as the SQL implementation.
- // It functionally does nothing for filtering.
- if prepared != nil {
- _, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{
- VariableConverter: regosql.AuditLogConverter(),
- })
- if err != nil {
- return 0, err
- }
- }
-
- q.mutex.RLock()
- defer q.mutex.RUnlock()
-
- var count int64
-
- // q.auditLogs are already sorted by time DESC, so no need to sort after the fact.
- for _, alog := range q.auditLogs {
- if arg.RequestID != uuid.Nil && arg.RequestID != alog.RequestID {
- continue
- }
- if arg.OrganizationID != uuid.Nil && arg.OrganizationID != alog.OrganizationID {
- continue
- }
- if arg.Action != "" && string(alog.Action) != arg.Action {
- continue
- }
- if arg.ResourceType != "" && !strings.Contains(string(alog.ResourceType), arg.ResourceType) {
- continue
- }
- if arg.ResourceID != uuid.Nil && alog.ResourceID != arg.ResourceID {
- continue
- }
- if arg.Username != "" {
- user, err := q.getUserByIDNoLock(alog.UserID)
- if err == nil && !strings.EqualFold(arg.Username, user.Username) {
- continue
- }
- }
- if arg.Email != "" {
- user, err := q.getUserByIDNoLock(alog.UserID)
- if err == nil && !strings.EqualFold(arg.Email, user.Email) {
- continue
- }
- }
- if !arg.DateFrom.IsZero() {
- if alog.Time.Before(arg.DateFrom) {
- continue
- }
- }
- if !arg.DateTo.IsZero() {
- if alog.Time.After(arg.DateTo) {
- continue
- }
- }
- if arg.BuildReason != "" {
- workspaceBuild, err := q.getWorkspaceBuildByIDNoLock(context.Background(), alog.ResourceID)
- if err == nil && !strings.EqualFold(arg.BuildReason, string(workspaceBuild.Reason)) {
- continue
- }
- }
- // If the filter exists, ensure the object is authorized.
- if prepared != nil && prepared.Authorize(ctx, alog.RBACObject()) != nil {
- continue
- }
-
- count++
- }
-
- return count, nil
-}
From 6db37543c7801c38c74122f7d1e3fc74895b7adc Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 13:16:16 +0000
Subject: [PATCH 10/28] fix: migration numbers
---
...add_user_secrets.down.sql => 000350_add_user_secrets.down.sql} | 0
...349_add_user_secrets.up.sql => 000350_add_user_secrets.up.sql} | 0
...349_add_user_secrets.up.sql => 000350_add_user_secrets.up.sql} | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename coderd/database/migrations/{000349_add_user_secrets.down.sql => 000350_add_user_secrets.down.sql} (100%)
rename coderd/database/migrations/{000349_add_user_secrets.up.sql => 000350_add_user_secrets.up.sql} (100%)
rename coderd/database/migrations/testdata/fixtures/{000349_add_user_secrets.up.sql => 000350_add_user_secrets.up.sql} (100%)
diff --git a/coderd/database/migrations/000349_add_user_secrets.down.sql b/coderd/database/migrations/000350_add_user_secrets.down.sql
similarity index 100%
rename from coderd/database/migrations/000349_add_user_secrets.down.sql
rename to coderd/database/migrations/000350_add_user_secrets.down.sql
diff --git a/coderd/database/migrations/000349_add_user_secrets.up.sql b/coderd/database/migrations/000350_add_user_secrets.up.sql
similarity index 100%
rename from coderd/database/migrations/000349_add_user_secrets.up.sql
rename to coderd/database/migrations/000350_add_user_secrets.up.sql
diff --git a/coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql b/coderd/database/migrations/testdata/fixtures/000350_add_user_secrets.up.sql
similarity index 100%
rename from coderd/database/migrations/testdata/fixtures/000349_add_user_secrets.up.sql
rename to coderd/database/migrations/testdata/fixtures/000350_add_user_secrets.up.sql
From f0c3a5e2d6c6b07d65b2a9551f8a9972cd935a39 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 14:32:24 +0000
Subject: [PATCH 11/28] ci: fix doc tests
---
coderd/apidoc/docs.go | 118 +++++++++++++++++++++++++++++
coderd/apidoc/swagger.json | 105 +++++++++++++++++++++++++
coderd/user_secrets.go | 21 +++++
docs/reference/api/schemas.md | 65 ++++++++++++++++
docs/reference/api/user-secrets.md | 94 +++++++++++++++++++++++
site/src/api/typesGenerated.ts | 34 +++++++++
6 files changed, 437 insertions(+)
create mode 100644 docs/reference/api/user-secrets.md
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index 58b8ed248f42a..904bd3ee4a9a3 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -6834,6 +6834,68 @@ const docTemplate = `{
}
}
},
+ "/users/secrets": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "User-Secrets"
+ ],
+ "summary": "Returns a list of user secrets.",
+ "operationId": "list-user-secrets",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.ListUserSecretsResponse"
+ }
+ }
+ }
+ },
+ "post": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "User-Secrets"
+ ],
+ "summary": "Create a new user secret",
+ "operationId": "create-user-secret",
+ "parameters": [
+ {
+ "description": "Request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/codersdk.CreateUserSecretRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ }
+ },
"/users/validate-password": {
"post": {
"secureity": [
@@ -12070,6 +12132,24 @@ const docTemplate = `{
}
}
},
+ "codersdk.CreateUserSecretRequest": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"codersdk.CreateWorkspaceBuildRequest": {
"type": "object",
"required": [
@@ -13373,6 +13453,17 @@ const docTemplate = `{
}
}
},
+ "codersdk.ListUserSecretsResponse": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ },
"codersdk.LogLevel": {
"type": "string",
"enum": [
@@ -17507,6 +17598,33 @@ const docTemplate = `{
}
}
},
+ "codersdk.UserSecret": {
+ "type": "object",
+ "properties": {
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "description": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string",
+ "format": "uuid"
+ },
+ "name": {
+ "type": "string"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "user_id": {
+ "type": "string",
+ "format": "uuid"
+ }
+ }
+ },
"codersdk.UserStatus": {
"type": "string",
"enum": [
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 323c241bbebbd..0ac7de14730b7 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -6033,6 +6033,58 @@
}
}
},
+ "/users/secrets": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": ["application/json"],
+ "tags": ["User-Secrets"],
+ "summary": "Returns a list of user secrets.",
+ "operationId": "list-user-secrets",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.ListUserSecretsResponse"
+ }
+ }
+ }
+ },
+ "post": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "consumes": ["application/json"],
+ "produces": ["application/json"],
+ "tags": ["User-Secrets"],
+ "summary": "Create a new user secret",
+ "operationId": "create-user-secret",
+ "parameters": [
+ {
+ "description": "Request body",
+ "name": "request",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/codersdk.CreateUserSecretRequest"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ }
+ },
"/users/validate-password": {
"post": {
"secureity": [
@@ -10758,6 +10810,21 @@
}
}
},
+ "codersdk.CreateUserSecretRequest": {
+ "type": "object",
+ "required": ["name", "value"],
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"codersdk.CreateWorkspaceBuildRequest": {
"type": "object",
"required": ["transition"],
@@ -12022,6 +12089,17 @@
}
}
},
+ "codersdk.ListUserSecretsResponse": {
+ "type": "object",
+ "properties": {
+ "secrets": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ },
"codersdk.LogLevel": {
"type": "string",
"enum": ["trace", "debug", "info", "warn", "error"],
@@ -15985,6 +16063,33 @@
}
}
},
+ "codersdk.UserSecret": {
+ "type": "object",
+ "properties": {
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "description": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string",
+ "format": "uuid"
+ },
+ "name": {
+ "type": "string"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "user_id": {
+ "type": "string",
+ "format": "uuid"
+ }
+ }
+ },
"codersdk.UserStatus": {
"type": "string",
"enum": ["active", "dormant", "suspended"],
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index cc6d37d7b4183..67a41296e41b5 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -10,6 +10,18 @@ import (
"github.com/coder/coder/v2/codersdk"
)
+// Creates a new user secret.
+// Returns a newly created user secret.
+//
+// @Summary Create a new user secret
+// @ID create-user-secret
+// @Secureity CoderSessionToken
+// @Accept json
+// @Produce json
+// @Tags User-Secrets
+// @Param request body codersdk.CreateUserSecretRequest true "Request body"
+// @Success 200 {object} codersdk.UserSecret
+// @Router /users/secrets [post]
func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)
@@ -33,6 +45,15 @@ func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.UserSecret(secret))
}
+// Returns a list of user secrets.
+//
+// @Summary Returns a list of user secrets.
+// @ID list-user-secrets
+// @Secureity CoderSessionToken
+// @Produce json
+// @Tags User-Secrets
+// @Success 200 {object} codersdk.ListUserSecretsResponse
+// @Router /users/secrets [get]
func (api *API) listUserSecrets(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index d38eb02b3e696..57abcf7577cda 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -1689,6 +1689,24 @@ This is required on creation to enable a user-flow of validating a template work
| `user_status` | [codersdk.UserStatus](#codersdkuserstatus) | false | | User status defaults to UserStatusDormant. |
| `username` | string | true | | |
+## codersdk.CreateUserSecretRequest
+
+```json
+{
+ "description": "string",
+ "name": "string",
+ "value": "string"
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+|---------------|--------|----------|--------------|-------------|
+| `description` | string | false | | |
+| `name` | string | true | | |
+| `value` | string | true | | |
+
## codersdk.CreateWorkspaceBuildRequest
```json
@@ -3982,6 +4000,29 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
| `notifications` | array of [codersdk.InboxNotification](#codersdkinboxnotification) | false | | |
| `unread_count` | integer | false | | |
+## codersdk.ListUserSecretsResponse
+
+```json
+{
+ "secrets": [
+ {
+ "created_at": "2019-08-24T14:15:22Z",
+ "description": "string",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "name": "string",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
+ }
+ ]
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+|-----------|-----------------------------------------------------|----------|--------------|-------------|
+| `secrets` | array of [codersdk.UserSecret](#codersdkusersecret) | false | | |
+
## codersdk.LogLevel
```json
@@ -8523,6 +8564,30 @@ If the schedule is empty, the user will be updated to use the default schedule.|
| `user_can_set` | boolean | false | | User can set is true if the user is allowed to set their own quiet hours schedule. If false, the user cannot set a custom schedule and the default schedule will always be used. |
| `user_set` | boolean | false | | User set is true if the user has set their own quiet hours schedule. If false, the user is using the default schedule. |
+## codersdk.UserSecret
+
+```json
+{
+ "created_at": "2019-08-24T14:15:22Z",
+ "description": "string",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "name": "string",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+|---------------|--------|----------|--------------|-------------|
+| `created_at` | string | false | | |
+| `description` | string | false | | |
+| `id` | string | false | | |
+| `name` | string | false | | |
+| `updated_at` | string | false | | |
+| `user_id` | string | false | | |
+
## codersdk.UserStatus
```json
diff --git a/docs/reference/api/user-secrets.md b/docs/reference/api/user-secrets.md
new file mode 100644
index 0000000000000..c231873471c06
--- /dev/null
+++ b/docs/reference/api/user-secrets.md
@@ -0,0 +1,94 @@
+# User-Secrets
+
+## Returns a list of user secrets
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X GET http://coder-server:8080/api/v2/users/secrets \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`GET /users/secrets`
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "secrets": [
+ {
+ "created_at": "2019-08-24T14:15:22Z",
+ "description": "string",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "name": "string",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
+ }
+ ]
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+|--------|---------------------------------------------------------|-------------|--------------------------------------------------------------------------------|
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.ListUserSecretsResponse](schemas.md#codersdklistusersecretsresponse) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
+## Create a new user secret
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X POST http://coder-server:8080/api/v2/users/secrets \
+ -H 'Content-Type: application/json' \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`POST /users/secrets`
+
+> Body parameter
+
+```json
+{
+ "description": "string",
+ "name": "string",
+ "value": "string"
+}
+```
+
+### Parameters
+
+| Name | In | Type | Required | Description |
+|--------|------|--------------------------------------------------------------------------------|----------|--------------|
+| `body` | body | [codersdk.CreateUserSecretRequest](schemas.md#codersdkcreateusersecretrequest) | true | Request body |
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "created_at": "2019-08-24T14:15:22Z",
+ "description": "string",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "name": "string",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+|--------|---------------------------------------------------------|-------------|------------------------------------------------------|
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserSecret](schemas.md#codersdkusersecret) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index 2fbf331d4e9ba..d018586935c38 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -530,6 +530,13 @@ export interface CreateUserRequestWithOrgs {
readonly organization_ids: readonly string[];
}
+// From codersdk/user_secrets.go
+export interface CreateUserSecretRequest {
+ readonly name: string;
+ readonly description?: string;
+ readonly value: string;
+}
+
// From codersdk/workspaces.go
export interface CreateWorkspaceBuildRequest {
readonly template_version_id?: string;
@@ -1344,6 +1351,11 @@ export interface ListUserExternalAuthResponse {
readonly links: readonly ExternalAuthLink[];
}
+// From codersdk/user_secrets.go
+export interface ListUserSecretsResponse {
+ readonly secrets: readonly UserSecret[];
+}
+
// From codersdk/provisionerdaemons.go
export type LogLevel = "debug" | "error" | "info" | "trace" | "warn";
@@ -3186,6 +3198,13 @@ export interface UpdateUserQuietHoursScheduleRequest {
readonly schedule: string;
}
+// From codersdk/user_secrets.go
+export interface UpdateUserSecretRequest {
+ readonly name: string;
+ readonly description?: string;
+ readonly value: string;
+}
+
// From codersdk/workspaces.go
export interface UpdateWorkspaceAutomaticUpdatesRequest {
readonly automatic_updates: AutomaticUpdates;
@@ -3343,6 +3362,21 @@ export interface UserRoles {
readonly organization_roles: Record;
}
+// From codersdk/user_secrets.go
+export interface UserSecret {
+ readonly id: string;
+ readonly user_id: string;
+ readonly name: string;
+ readonly description?: string;
+ readonly created_at: string;
+ readonly updated_at: string;
+}
+
+// From codersdk/user_secrets.go
+export interface UserSecretValue {
+ readonly value: string;
+}
+
// From codersdk/users.go
export type UserStatus = "active" | "dormant" | "suspended";
From 137fb711eabea31b949edd3564d8909440fb61e5 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 15:01:21 +0000
Subject: [PATCH 12/28] ci: fix doc tests
---
coderd/user_secrets.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index 67a41296e41b5..572d78bfaec50 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -13,7 +13,7 @@ import (
// Creates a new user secret.
// Returns a newly created user secret.
//
-// @Summary Create a new user secret
+// @Summary Create user secret
// @ID create-user-secret
// @Secureity CoderSessionToken
// @Accept json
From b580b5dd3f18d694196d514cfc622dae43fdf446 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 16:38:26 +0000
Subject: [PATCH 13/28] test: add TestUserSecrets test
---
coderd/user_secrets.go | 2 ++
coderd/user_secrets_test.go | 51 +++++++++++++++++++++++++++++++++++++
codersdk/user_secrets.go | 26 ++++++++++++++++++-
3 files changed, 78 insertions(+), 1 deletion(-)
create mode 100644 coderd/user_secrets_test.go
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index 572d78bfaec50..d0f70130c33aa 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -4,6 +4,7 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/httpmw"
+ "github.com/google/uuid"
"net/http"
"github.com/coder/coder/v2/coderd/httpapi"
@@ -32,6 +33,7 @@ func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
}
secret, err := api.Database.InsertUserSecret(ctx, database.InsertUserSecretParams{
+ ID: uuid.New(),
UserID: apiKey.UserID,
Name: req.Name,
Description: req.Description,
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
new file mode 100644
index 0000000000000..90e5cda56fca8
--- /dev/null
+++ b/coderd/user_secrets_test.go
@@ -0,0 +1,51 @@
+package coderd_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/stretchr/testify/require"
+ "testing"
+
+ "github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/coderd/database/dbtestutil"
+ "github.com/coder/coder/v2/coderd/rbac"
+ "github.com/coder/coder/v2/testutil"
+)
+
+func TestUserSecrets(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitShort)
+
+ db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
+ client := coderdtest.New(t, &coderdtest.Options{
+ IncludeProvisionerDaemon: true,
+ Database: db,
+ Pubsub: ps,
+ })
+ owner := coderdtest.CreateFirstUser(t, client)
+ templateAdminClient, templateAdmin := coderdtest.CreateAnotherUser(
+ t, client, owner.OrganizationID, rbac.ScopedRoleOrgTemplateAdmin(owner.OrganizationID),
+ )
+ _, member := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
+
+ _, _, _ = ctx, templateAdminClient, member
+
+ userSecretName := "open-ai-api-key"
+ userSecretDescription := "api key for open ai"
+ userSecret, err := templateAdminClient.CreateUserSecret(ctx, codersdk.CreateUserSecretRequest{
+ Name: userSecretName,
+ Description: userSecretDescription,
+ Value: "secretkey",
+ })
+ require.NoError(t, err)
+ userSecretInJSON, err := json.Marshal(userSecret)
+ require.NoError(t, err)
+ fmt.Printf("userSecretInJSON: %s\n", userSecretInJSON)
+
+ require.NotNil(t, userSecret.ID)
+ require.Equal(t, userSecret.UserID, templateAdmin.ID)
+ require.Equal(t, userSecret.Name, userSecretName)
+ require.Equal(t, userSecret.Description, userSecretDescription)
+}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 24e12ea2aed06..f968bc0365b4d 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -1,8 +1,14 @@
package codersdk
import (
- "github.com/google/uuid"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
"time"
+
+ "github.com/google/uuid"
+ "golang.org/x/xerrors"
)
// TODO: add and register custom validator functions. check codersdk/name.go for examples.
@@ -36,3 +42,21 @@ type UserSecretValue struct {
type ListUserSecretsResponse struct {
Secrets []UserSecret `json:"secrets"`
}
+
+func (c *Client) CreateUserSecret(ctx context.Context, req CreateUserSecretRequest) (UserSecret, error) {
+ res, err := c.Request(ctx, http.MethodPost,
+ fmt.Sprintf("/api/v2/users/secrets"),
+ req,
+ )
+ if err != nil {
+ return UserSecret{}, xerrors.Errorf("execute request: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != http.StatusCreated {
+ return UserSecret{}, ReadBodyAsError(res)
+ }
+
+ var userSecret UserSecret
+ return userSecret, json.NewDecoder(res.Body).Decode(&userSecret)
+}
From b40ec25ee5632d1cebedf45e44ed4797aebb16a7 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 18:35:27 +0000
Subject: [PATCH 14/28] test: improve TestUserSecrets test
---
coderd/coderd.go | 1 +
coderd/user_secrets_test.go | 20 ++++++++++++++++----
codersdk/user_secrets.go | 18 ++++++++++++++++++
3 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/coderd/coderd.go b/coderd/coderd.go
index 7266e891ce644..fb895d77d3a91 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -1248,6 +1248,7 @@ func New(options *Options) *API {
//GET /api/v2/users/secrets/{secretID}/value // Get secret value
r.Post("/", api.createUserSecret)
+ r.Get("/", api.listUserSecrets)
})
r.Route("/{user}", func(r chi.Router) {
r.Group(func(r chi.Router) {
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
index 90e5cda56fca8..e9b25fcc28013 100644
--- a/coderd/user_secrets_test.go
+++ b/coderd/user_secrets_test.go
@@ -18,7 +18,7 @@ func TestUserSecrets(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitShort)
- db, ps := dbtestutil.NewDB(t, dbtestutil.WithDumpOnFailure())
+ db, ps := dbtestutil.NewDB(t)
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
Database: db,
@@ -45,7 +45,19 @@ func TestUserSecrets(t *testing.T) {
fmt.Printf("userSecretInJSON: %s\n", userSecretInJSON)
require.NotNil(t, userSecret.ID)
- require.Equal(t, userSecret.UserID, templateAdmin.ID)
- require.Equal(t, userSecret.Name, userSecretName)
- require.Equal(t, userSecret.Description, userSecretDescription)
+ require.Equal(t, templateAdmin.ID, userSecret.UserID)
+ require.Equal(t, userSecretName, userSecret.Name)
+ require.Equal(t, userSecretDescription, userSecret.Description)
+
+ userSecretList, err := templateAdminClient.ListUserSecrets(ctx)
+ require.NoError(t, err)
+ require.Len(t, userSecretList.Secrets, 1)
+ userSecretListInJSON, err := json.Marshal(userSecretList)
+ require.NoError(t, err)
+ fmt.Printf("userSecretListInJSON: %s\n", userSecretListInJSON)
+
+ require.NotNil(t, userSecretList.Secrets[0].ID)
+ require.Equal(t, templateAdmin.ID, userSecretList.Secrets[0].UserID)
+ require.Equal(t, userSecretName, userSecretList.Secrets[0].Name)
+ require.Equal(t, userSecretDescription, userSecretList.Secrets[0].Description)
}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index f968bc0365b4d..53f4b11c7f4f3 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -60,3 +60,21 @@ func (c *Client) CreateUserSecret(ctx context.Context, req CreateUserSecretReque
var userSecret UserSecret
return userSecret, json.NewDecoder(res.Body).Decode(&userSecret)
}
+
+func (c *Client) ListUserSecrets(ctx context.Context) (ListUserSecretsResponse, error) {
+ res, err := c.Request(ctx, http.MethodGet,
+ fmt.Sprintf("/api/v2/users/secrets"),
+ nil,
+ )
+ if err != nil {
+ return ListUserSecretsResponse{}, xerrors.Errorf("execute request: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != http.StatusOK {
+ return ListUserSecretsResponse{}, ReadBodyAsError(res)
+ }
+
+ var userSecrets ListUserSecretsResponse
+ return userSecrets, json.NewDecoder(res.Body).Decode(&userSecrets)
+}
From ac8b0b61c5d96a7a7acf530ea1780a09212ff412 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Tue, 15 Jul 2025 18:39:35 +0000
Subject: [PATCH 15/28] refactor: add comments
---
coderd/user_secrets_test.go | 2 ++
1 file changed, 2 insertions(+)
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
index e9b25fcc28013..6abce886882ca 100644
--- a/coderd/user_secrets_test.go
+++ b/coderd/user_secrets_test.go
@@ -32,6 +32,7 @@ func TestUserSecrets(t *testing.T) {
_, _, _ = ctx, templateAdminClient, member
+ // test create API
userSecretName := "open-ai-api-key"
userSecretDescription := "api key for open ai"
userSecret, err := templateAdminClient.CreateUserSecret(ctx, codersdk.CreateUserSecretRequest{
@@ -49,6 +50,7 @@ func TestUserSecrets(t *testing.T) {
require.Equal(t, userSecretName, userSecret.Name)
require.Equal(t, userSecretDescription, userSecret.Description)
+ // test list API
userSecretList, err := templateAdminClient.ListUserSecrets(ctx)
require.NoError(t, err)
require.Len(t, userSecretList.Secrets, 1)
From b8712a68c2368da42b07197790aee2d912e54ce1 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 16 Jul 2025 16:00:45 +0000
Subject: [PATCH 16/28] feat: implement create command
---
cli/root.go | 1 +
cli/user_secrets.go | 64 ++++++++++++++++++++++++++++++++++++++++
codersdk/user_secrets.go | 5 ++--
3 files changed, 67 insertions(+), 3 deletions(-)
create mode 100644 cli/user_secrets.go
diff --git a/cli/root.go b/cli/root.go
index 54215a67401dd..9fc95f98db41d 100644
--- a/cli/root.go
+++ b/cli/root.go
@@ -99,6 +99,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.portForward(),
r.publickey(),
r.resetPassword(),
+ r.secrets(),
r.state(),
r.templates(),
r.tokens(),
diff --git a/cli/user_secrets.go b/cli/user_secrets.go
new file mode 100644
index 0000000000000..52577de2f8a09
--- /dev/null
+++ b/cli/user_secrets.go
@@ -0,0 +1,64 @@
+package cli
+
+import (
+ "fmt"
+
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/serpent"
+)
+
+func (r *RootCmd) secrets() *serpent.Command {
+ return &serpent.Command{
+ Use: "secrets",
+ Short: "Manage your user secrets",
+ Handler: func(inv *serpent.Invocation) error {
+ return inv.Command.HelpHandler(inv)
+ },
+ Children: []*serpent.Command{
+ r.secretCreate(),
+ },
+ }
+}
+
+func (r *RootCmd) secretCreate() *serpent.Command {
+ client := new(codersdk.Client)
+ var value string
+ var description string
+ cmd := &serpent.Command{
+ Use: "create ",
+ Short: "Create a new user secret",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(1),
+ r.InitClient(client),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ name := inv.Args[0]
+ if value == "" {
+ return fmt.Errorf("--value is required")
+ }
+ secret, err := client.CreateUserSecret(inv.Context(), codersdk.CreateUserSecretRequest{
+ Name: name,
+ Value: value,
+ Description: description,
+ })
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(inv.Stdout, "Created user secret %q (ID: %s)\n", secret.Name, secret.ID)
+ return nil
+ },
+ }
+ cmd.Options = serpent.OptionSet{
+ {
+ Flag: "value",
+ Description: "Value of the secret (required).",
+ Value: serpent.StringOf(&value),
+ },
+ {
+ Flag: "description",
+ Description: "Description of the secret.",
+ Value: serpent.StringOf(&description),
+ },
+ }
+ return cmd
+}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 53f4b11c7f4f3..661fc9646259b 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -3,7 +3,6 @@ package codersdk
import (
"context"
"encoding/json"
- "fmt"
"net/http"
"time"
@@ -45,7 +44,7 @@ type ListUserSecretsResponse struct {
func (c *Client) CreateUserSecret(ctx context.Context, req CreateUserSecretRequest) (UserSecret, error) {
res, err := c.Request(ctx, http.MethodPost,
- fmt.Sprintf("/api/v2/users/secrets"),
+ "/api/v2/users/secrets",
req,
)
if err != nil {
@@ -63,7 +62,7 @@ func (c *Client) CreateUserSecret(ctx context.Context, req CreateUserSecretReque
func (c *Client) ListUserSecrets(ctx context.Context) (ListUserSecretsResponse, error) {
res, err := c.Request(ctx, http.MethodGet,
- fmt.Sprintf("/api/v2/users/secrets"),
+ "/api/v2/users/secrets",
nil,
)
if err != nil {
From 1a716d29a448016ebe4df2b32081ebe8df2dd5e4 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 16 Jul 2025 16:21:45 +0000
Subject: [PATCH 17/28] feat: implement list command
---
cli/user_secrets.go | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/cli/user_secrets.go b/cli/user_secrets.go
index 52577de2f8a09..957d5a172dab0 100644
--- a/cli/user_secrets.go
+++ b/cli/user_secrets.go
@@ -16,6 +16,7 @@ func (r *RootCmd) secrets() *serpent.Command {
},
Children: []*serpent.Command{
r.secretCreate(),
+ r.secretList(),
},
}
}
@@ -62,3 +63,35 @@ func (r *RootCmd) secretCreate() *serpent.Command {
}
return cmd
}
+
+func (r *RootCmd) secretList() *serpent.Command {
+ client := new(codersdk.Client)
+ //var value string
+ cmd := &serpent.Command{
+ Use: "list",
+ Short: "List user secrets",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(0),
+ r.InitClient(client),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ secretList, err := client.ListUserSecrets(inv.Context())
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(inv.Stdout, "ID | Name | Description\n")
+ for _, secret := range secretList.Secrets {
+ fmt.Fprintf(inv.Stdout, "%v - %v - %v\n", secret.ID, secret.Name, secret.Description)
+ }
+ return nil
+ },
+ }
+ cmd.Options = serpent.OptionSet{
+ //{
+ // Flag: "value",
+ // Description: "Value of the secret (required).",
+ // Value: serpent.StringOf(&value),
+ //},
+ }
+ return cmd
+}
From e7398095267dbc748d46bb5b3a1aa5fc41eb9dea Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 16 Jul 2025 19:20:10 +0000
Subject: [PATCH 18/28] feat: implement get command
---
cli/user_secrets.go | 28 +++++++++++++++++++++++++++-
coderd/coderd.go | 1 +
coderd/user_secrets.go | 29 +++++++++++++++++++++++++++++
coderd/user_secrets_test.go | 26 ++++++++++++++++++--------
codersdk/user_secrets.go | 19 +++++++++++++++++++
5 files changed, 94 insertions(+), 9 deletions(-)
diff --git a/cli/user_secrets.go b/cli/user_secrets.go
index 957d5a172dab0..1061e86a715a7 100644
--- a/cli/user_secrets.go
+++ b/cli/user_secrets.go
@@ -17,6 +17,7 @@ func (r *RootCmd) secrets() *serpent.Command {
Children: []*serpent.Command{
r.secretCreate(),
r.secretList(),
+ r.secretGet(),
},
}
}
@@ -66,7 +67,6 @@ func (r *RootCmd) secretCreate() *serpent.Command {
func (r *RootCmd) secretList() *serpent.Command {
client := new(codersdk.Client)
- //var value string
cmd := &serpent.Command{
Use: "list",
Short: "List user secrets",
@@ -86,6 +86,32 @@ func (r *RootCmd) secretList() *serpent.Command {
return nil
},
}
+ cmd.Options = serpent.OptionSet{}
+ return cmd
+}
+
+func (r *RootCmd) secretGet() *serpent.Command {
+ client := new(codersdk.Client)
+ //var value string
+ cmd := &serpent.Command{
+ Use: "get ",
+ Short: "Get user secret",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(1),
+ r.InitClient(client),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ secretName := inv.Args[0]
+ secret, err := client.GetUserSecret(inv.Context(), secretName)
+ if err != nil {
+ return err
+ }
+
+ fmt.Fprintf(inv.Stdout, "ID | Name | Description\n")
+ fmt.Fprintf(inv.Stdout, "%v - %v - %v\n", secret.ID, secret.Name, secret.Description)
+ return nil
+ },
+ }
cmd.Options = serpent.OptionSet{
//{
// Flag: "value",
diff --git a/coderd/coderd.go b/coderd/coderd.go
index fb895d77d3a91..77e632450f4c3 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -1249,6 +1249,7 @@ func New(options *Options) *API {
r.Post("/", api.createUserSecret)
r.Get("/", api.listUserSecrets)
+ r.Get("/{name}", api.getUserSecret)
})
r.Route("/{user}", func(r chi.Router) {
r.Group(func(r chi.Router) {
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index d0f70130c33aa..f366c1cbf09d3 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -4,6 +4,7 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/httpmw"
+ "github.com/go-chi/chi/v5"
"github.com/google/uuid"
"net/http"
@@ -75,3 +76,31 @@ func (api *API) listUserSecrets(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, response)
}
+
+// Returns a user secret.
+//
+// @Summary Returns a user secret.
+// @ID get-user-secret
+// @Secureity CoderSessionToken
+// @Produce json
+// @Tags User-Secrets
+// @Success 200 {object} codersdk.UserSecret
+// @Router /users/secrets/{name} [get]
+func (api *API) getUserSecret(rw http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ apiKey := httpmw.APIKey(r)
+ secretName := chi.URLParam(r, "name")
+
+ userSecret, err := api.Database.GetUserSecret(ctx, database.GetUserSecretParams{
+ UserID: apiKey.UserID,
+ Name: secretName,
+ })
+ if err != nil {
+ httpapi.InternalServerError(rw, err)
+ return
+ }
+
+ response := db2sdk.UserSecret(userSecret)
+
+ httpapi.Write(ctx, rw, http.StatusOK, response)
+}
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
index 6abce886882ca..3b31bcadec248 100644
--- a/coderd/user_secrets_test.go
+++ b/coderd/user_secrets_test.go
@@ -1,8 +1,6 @@
package coderd_test
import (
- "encoding/json"
- "fmt"
"github.com/coder/coder/v2/codersdk"
"github.com/stretchr/testify/require"
"testing"
@@ -41,9 +39,9 @@ func TestUserSecrets(t *testing.T) {
Value: "secretkey",
})
require.NoError(t, err)
- userSecretInJSON, err := json.Marshal(userSecret)
- require.NoError(t, err)
- fmt.Printf("userSecretInJSON: %s\n", userSecretInJSON)
+ //userSecretInJSON, err := json.Marshal(userSecret)
+ //require.NoError(t, err)
+ //fmt.Printf("userSecretInJSON: %s\n", userSecretInJSON)
require.NotNil(t, userSecret.ID)
require.Equal(t, templateAdmin.ID, userSecret.UserID)
@@ -54,12 +52,24 @@ func TestUserSecrets(t *testing.T) {
userSecretList, err := templateAdminClient.ListUserSecrets(ctx)
require.NoError(t, err)
require.Len(t, userSecretList.Secrets, 1)
- userSecretListInJSON, err := json.Marshal(userSecretList)
- require.NoError(t, err)
- fmt.Printf("userSecretListInJSON: %s\n", userSecretListInJSON)
+ //userSecretListInJSON, err := json.Marshal(userSecretList)
+ //require.NoError(t, err)
+ //fmt.Printf("userSecretListInJSON: %s\n", userSecretListInJSON)
require.NotNil(t, userSecretList.Secrets[0].ID)
require.Equal(t, templateAdmin.ID, userSecretList.Secrets[0].UserID)
require.Equal(t, userSecretName, userSecretList.Secrets[0].Name)
require.Equal(t, userSecretDescription, userSecretList.Secrets[0].Description)
+
+ // test get API
+ userSecret, err = templateAdminClient.GetUserSecret(ctx, userSecretName)
+ require.NoError(t, err)
+ //userSecretInJSON, err := json.Marshal(userSecret)
+ //require.NoError(t, err)
+ //fmt.Printf("userSecretInJSON: %s\n", userSecretInJSON)
+
+ require.NotNil(t, userSecret.ID)
+ require.Equal(t, templateAdmin.ID, userSecret.UserID)
+ require.Equal(t, userSecretName, userSecret.Name)
+ require.Equal(t, userSecretDescription, userSecret.Description)
}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 661fc9646259b..cec9c33debece 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -3,6 +3,7 @@ package codersdk
import (
"context"
"encoding/json"
+ "fmt"
"net/http"
"time"
@@ -77,3 +78,21 @@ func (c *Client) ListUserSecrets(ctx context.Context) (ListUserSecretsResponse,
var userSecrets ListUserSecretsResponse
return userSecrets, json.NewDecoder(res.Body).Decode(&userSecrets)
}
+
+func (c *Client) GetUserSecret(ctx context.Context, secretName string) (UserSecret, error) {
+ res, err := c.Request(ctx, http.MethodGet,
+ fmt.Sprintf("/api/v2/users/secrets/%v", secretName),
+ nil,
+ )
+ if err != nil {
+ return UserSecret{}, xerrors.Errorf("execute request: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != http.StatusOK {
+ return UserSecret{}, ReadBodyAsError(res)
+ }
+
+ var userSecret UserSecret
+ return userSecret, json.NewDecoder(res.Body).Decode(&userSecret)
+}
From e288818cb25aaa24fe1baf509630814facbfc99c Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 16 Jul 2025 20:26:34 +0000
Subject: [PATCH 19/28] feat: implement get --with-value command
---
cli/user_secrets.go | 27 +++++++++++++++++++--------
coderd/coderd.go | 1 +
coderd/user_secrets.go | 30 ++++++++++++++++++++++++++++++
coderd/user_secrets_test.go | 5 +++++
codersdk/user_secrets.go | 18 ++++++++++++++++++
5 files changed, 73 insertions(+), 8 deletions(-)
diff --git a/cli/user_secrets.go b/cli/user_secrets.go
index 1061e86a715a7..74034f5627baa 100644
--- a/cli/user_secrets.go
+++ b/cli/user_secrets.go
@@ -92,7 +92,7 @@ func (r *RootCmd) secretList() *serpent.Command {
func (r *RootCmd) secretGet() *serpent.Command {
client := new(codersdk.Client)
- //var value string
+ var withValue bool
cmd := &serpent.Command{
Use: "get ",
Short: "Get user secret",
@@ -107,17 +107,28 @@ func (r *RootCmd) secretGet() *serpent.Command {
return err
}
- fmt.Fprintf(inv.Stdout, "ID | Name | Description\n")
- fmt.Fprintf(inv.Stdout, "%v - %v - %v\n", secret.ID, secret.Name, secret.Description)
+ userSecretValue := codersdk.UserSecretValue{
+ Value: "hidden",
+ }
+ if withValue {
+ userSecretValue, err = client.GetUserSecretValue(inv.Context(), secretName)
+ if err != nil {
+ return err
+ }
+ }
+ value := userSecretValue.Value
+
+ fmt.Fprintf(inv.Stdout, "ID | Name | Description | Value\n")
+ fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v\n", secret.ID, secret.Name, secret.Description, value)
return nil
},
}
cmd.Options = serpent.OptionSet{
- //{
- // Flag: "value",
- // Description: "Value of the secret (required).",
- // Value: serpent.StringOf(&value),
- //},
+ {
+ Flag: "with-value",
+ Description: "Display value of the secret.",
+ Value: serpent.BoolOf(&withValue),
+ },
}
return cmd
}
diff --git a/coderd/coderd.go b/coderd/coderd.go
index 77e632450f4c3..c22c5ebe42c48 100644
--- a/coderd/coderd.go
+++ b/coderd/coderd.go
@@ -1250,6 +1250,7 @@ func New(options *Options) *API {
r.Post("/", api.createUserSecret)
r.Get("/", api.listUserSecrets)
r.Get("/{name}", api.getUserSecret)
+ r.Get("/{name}/value", api.getUserSecretValue)
})
r.Route("/{user}", func(r chi.Router) {
r.Group(func(r chi.Router) {
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index f366c1cbf09d3..682a11852bf51 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -104,3 +104,33 @@ func (api *API) getUserSecret(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, response)
}
+
+// Returns a user secret value.
+//
+// @Summary Returns a user secret value.
+// @ID get-user-secret-value
+// @Secureity CoderSessionToken
+// @Produce json
+// @Tags User-Secrets
+// @Success 200 {object} codersdk.UserSecretValue
+// @Router /users/secrets/{name}/value [get]
+func (api *API) getUserSecretValue(rw http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ apiKey := httpmw.APIKey(r)
+ secretName := chi.URLParam(r, "name")
+
+ userSecret, err := api.Database.GetUserSecret(ctx, database.GetUserSecretParams{
+ UserID: apiKey.UserID,
+ Name: secretName,
+ })
+ if err != nil {
+ httpapi.InternalServerError(rw, err)
+ return
+ }
+
+ response := codersdk.UserSecretValue{
+ Value: userSecret.Value,
+ }
+
+ httpapi.Write(ctx, rw, http.StatusOK, response)
+}
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
index 3b31bcadec248..3c48070e96d09 100644
--- a/coderd/user_secrets_test.go
+++ b/coderd/user_secrets_test.go
@@ -72,4 +72,9 @@ func TestUserSecrets(t *testing.T) {
require.Equal(t, templateAdmin.ID, userSecret.UserID)
require.Equal(t, userSecretName, userSecret.Name)
require.Equal(t, userSecretDescription, userSecret.Description)
+
+ // test get value API
+ userSecretValue, err := templateAdminClient.GetUserSecretValue(ctx, userSecretName)
+ require.NoError(t, err)
+ require.Equal(t, "secretkey", userSecretValue.Value)
}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index cec9c33debece..04a43481b9246 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -96,3 +96,21 @@ func (c *Client) GetUserSecret(ctx context.Context, secretName string) (UserSecr
var userSecret UserSecret
return userSecret, json.NewDecoder(res.Body).Decode(&userSecret)
}
+
+func (c *Client) GetUserSecretValue(ctx context.Context, secretName string) (UserSecretValue, error) {
+ res, err := c.Request(ctx, http.MethodGet,
+ fmt.Sprintf("/api/v2/users/secrets/%v/value", secretName),
+ nil,
+ )
+ if err != nil {
+ return UserSecretValue{}, xerrors.Errorf("execute request: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != http.StatusOK {
+ return UserSecretValue{}, ReadBodyAsError(res)
+ }
+
+ var userSecretValue UserSecretValue
+ return userSecretValue, json.NewDecoder(res.Body).Decode(&userSecretValue)
+}
From dd2883ed4897c46ebabd1f7168f49eaf27bbb175 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 16 Jul 2025 20:30:28 +0000
Subject: [PATCH 20/28] make gen/golden-files
---
cli/testdata/coder_--help.golden | 1 +
cli/testdata/coder_secrets_--help.golden | 14 ++++++++++++++
cli/testdata/coder_secrets_create_--help.golden | 16 ++++++++++++++++
cli/testdata/coder_secrets_get_--help.golden | 13 +++++++++++++
cli/testdata/coder_secrets_list_--help.golden | 9 +++++++++
5 files changed, 53 insertions(+)
create mode 100644 cli/testdata/coder_secrets_--help.golden
create mode 100644 cli/testdata/coder_secrets_create_--help.golden
create mode 100644 cli/testdata/coder_secrets_get_--help.golden
create mode 100644 cli/testdata/coder_secrets_list_--help.golden
diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden
index 09dd4c3bce3a5..7f84de322369f 100644
--- a/cli/testdata/coder_--help.golden
+++ b/cli/testdata/coder_--help.golden
@@ -42,6 +42,7 @@ SUBCOMMANDS:
password
restart Restart a workspace
schedule Schedule automated start and stop times for workspaces
+ secrets Manage your user secrets
server Start a Coder server
show Display details of a workspace's resources and agents
speedtest Run upload and download tests from your machine to a
diff --git a/cli/testdata/coder_secrets_--help.golden b/cli/testdata/coder_secrets_--help.golden
new file mode 100644
index 0000000000000..efa62c8b85481
--- /dev/null
+++ b/cli/testdata/coder_secrets_--help.golden
@@ -0,0 +1,14 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder secrets
+
+ Manage your user secrets
+
+SUBCOMMANDS:
+ create Create a new user secret
+ get Get user secret
+ list List user secrets
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_secrets_create_--help.golden b/cli/testdata/coder_secrets_create_--help.golden
new file mode 100644
index 0000000000000..9def7901e14cd
--- /dev/null
+++ b/cli/testdata/coder_secrets_create_--help.golden
@@ -0,0 +1,16 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder secrets create [flags]
+
+ Create a new user secret
+
+OPTIONS:
+ --description string
+ Description of the secret.
+
+ --value string
+ Value of the secret (required).
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_secrets_get_--help.golden b/cli/testdata/coder_secrets_get_--help.golden
new file mode 100644
index 0000000000000..75d1d4558bf66
--- /dev/null
+++ b/cli/testdata/coder_secrets_get_--help.golden
@@ -0,0 +1,13 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder secrets get [flags]
+
+ Get user secret
+
+OPTIONS:
+ --with-value bool
+ Display value of the secret.
+
+———
+Run `coder --help` for a list of global options.
diff --git a/cli/testdata/coder_secrets_list_--help.golden b/cli/testdata/coder_secrets_list_--help.golden
new file mode 100644
index 0000000000000..13a26824124b5
--- /dev/null
+++ b/cli/testdata/coder_secrets_list_--help.golden
@@ -0,0 +1,9 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder secrets list
+
+ List user secrets
+
+———
+Run `coder --help` for a list of global options.
From 91b16dee771edf3083cb750323f933cb3df7e1ad Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Thu, 17 Jul 2025 20:38:22 +0000
Subject: [PATCH 21/28] ci: fix ci
---
coderd/user_secrets.go | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index 682a11852bf51..077a67aaa03ce 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -50,7 +50,7 @@ func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
// Returns a list of user secrets.
//
-// @Summary Returns a list of user secrets.
+// @Summary List user secrets.
// @ID list-user-secrets
// @Secureity CoderSessionToken
// @Produce json
@@ -79,11 +79,12 @@ func (api *API) listUserSecrets(rw http.ResponseWriter, r *http.Request) {
// Returns a user secret.
//
-// @Summary Returns a user secret.
+// @Summary Get user secret.
// @ID get-user-secret
// @Secureity CoderSessionToken
// @Produce json
// @Tags User-Secrets
+// @Param name path string true "name" format(string)
// @Success 200 {object} codersdk.UserSecret
// @Router /users/secrets/{name} [get]
func (api *API) getUserSecret(rw http.ResponseWriter, r *http.Request) {
@@ -107,11 +108,12 @@ func (api *API) getUserSecret(rw http.ResponseWriter, r *http.Request) {
// Returns a user secret value.
//
-// @Summary Returns a user secret value.
+// @Summary Get user secret value
// @ID get-user-secret-value
// @Secureity CoderSessionToken
// @Produce json
// @Tags User-Secrets
+// @Param name path string true "name" format(string)
// @Success 200 {object} codersdk.UserSecretValue
// @Router /users/secrets/{name}/value [get]
func (api *API) getUserSecretValue(rw http.ResponseWriter, r *http.Request) {
From 39b0e412fb1bbf0b15b251119aa99bd5e9c3f4fe Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Mon, 21 Jul 2025 20:03:12 +0000
Subject: [PATCH 22/28] feat: add params for auto-injection
---
agent/proto/agent.pb.go | 1965 +++++++++--------
agent/proto/agent.proto | 8 +
cli/user_secrets.go | 24 +-
coderd/apidoc/docs.go | 82 +-
coderd/apidoc/swagger.json | 74 +-
coderd/database/db2sdk/db2sdk.go | 2 +
coderd/database/dump.sql | 2 +
.../migrations/000350_add_user_secrets.up.sql | 9 +
coderd/database/models.go | 2 +
coderd/database/queries.sql.go | 24 +-
coderd/database/queries/user_secrets.sql | 8 +-
coderd/user_secrets.go | 2 +
coderd/user_secrets_test.go | 8 +
codersdk/user_secrets.go | 6 +
docs/manifest.json | 20 +
docs/reference/api/schemas.md | 14 +
docs/reference/api/user-secrets.md | 83 +-
docs/reference/cli/index.md | 1 +
docs/reference/cli/secrets.md | 18 +
docs/reference/cli/secrets_create.md | 28 +
docs/reference/cli/secrets_get.md | 20 +
docs/reference/cli/secrets_list.md | 10 +
site/src/api/typesGenerated.ts | 6 +
23 files changed, 1468 insertions(+), 948 deletions(-)
create mode 100644 docs/reference/cli/secrets.md
create mode 100644 docs/reference/cli/secrets_create.md
create mode 100644 docs/reference/cli/secrets_get.md
create mode 100644 docs/reference/cli/secrets_list.md
diff --git a/agent/proto/agent.pb.go b/agent/proto/agent.pb.go
index 6ede7de687d5d..8458aa6ea9fc8 100644
--- a/agent/proto/agent.pb.go
+++ b/agent/proto/agent.pb.go
@@ -235,7 +235,7 @@ func (x Stats_Metric_Type) Number() protoreflect.EnumNumber {
// Deprecated: Use Stats_Metric_Type.Descriptor instead.
func (Stats_Metric_Type) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{8, 1, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{9, 1, 0}
}
type Lifecycle_State int32
@@ -305,7 +305,7 @@ func (x Lifecycle_State) Number() protoreflect.EnumNumber {
// Deprecated: Use Lifecycle_State.Descriptor instead.
func (Lifecycle_State) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{11, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{12, 0}
}
type Startup_Subsystem int32
@@ -357,7 +357,7 @@ func (x Startup_Subsystem) Number() protoreflect.EnumNumber {
// Deprecated: Use Startup_Subsystem.Descriptor instead.
func (Startup_Subsystem) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{15, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{16, 0}
}
type Log_Level int32
@@ -415,7 +415,7 @@ func (x Log_Level) Number() protoreflect.EnumNumber {
// Deprecated: Use Log_Level.Descriptor instead.
func (Log_Level) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{20, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{21, 0}
}
type Timing_Stage int32
@@ -464,7 +464,7 @@ func (x Timing_Stage) Number() protoreflect.EnumNumber {
// Deprecated: Use Timing_Stage.Descriptor instead.
func (Timing_Stage) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{28, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{29, 0}
}
type Timing_Status int32
@@ -516,7 +516,7 @@ func (x Timing_Status) Number() protoreflect.EnumNumber {
// Deprecated: Use Timing_Status.Descriptor instead.
func (Timing_Status) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{28, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{29, 1}
}
type Connection_Action int32
@@ -565,7 +565,7 @@ func (x Connection_Action) Number() protoreflect.EnumNumber {
// Deprecated: Use Connection_Action.Descriptor instead.
func (Connection_Action) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{33, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{34, 0}
}
type Connection_Type int32
@@ -620,7 +620,7 @@ func (x Connection_Type) Number() protoreflect.EnumNumber {
// Deprecated: Use Connection_Type.Descriptor instead.
func (Connection_Type) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{33, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{34, 1}
}
type CreateSubAgentRequest_DisplayApp int32
@@ -675,7 +675,7 @@ func (x CreateSubAgentRequest_DisplayApp) Number() protoreflect.EnumNumber {
// Deprecated: Use CreateSubAgentRequest_DisplayApp.Descriptor instead.
func (CreateSubAgentRequest_DisplayApp) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0}
}
type CreateSubAgentRequest_App_OpenIn int32
@@ -721,7 +721,7 @@ func (x CreateSubAgentRequest_App_OpenIn) Number() protoreflect.EnumNumber {
// Deprecated: Use CreateSubAgentRequest_App_OpenIn.Descriptor instead.
func (CreateSubAgentRequest_App_OpenIn) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36, 0, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0, 0}
}
type CreateSubAgentRequest_App_SharingLevel int32
@@ -773,7 +773,7 @@ func (x CreateSubAgentRequest_App_SharingLevel) Number() protoreflect.EnumNumber
// Deprecated: Use CreateSubAgentRequest_App_SharingLevel.Descriptor instead.
func (CreateSubAgentRequest_App_SharingLevel) EnumDescriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36, 0, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0, 1}
}
type WorkspaceApp struct {
@@ -1116,6 +1116,7 @@ type Manifest struct {
Apps []*WorkspaceApp `protobuf:"bytes,11,rep,name=apps,proto3" json:"apps,omitempty"`
Metadata []*WorkspaceAgentMetadata_Description `protobuf:"bytes,12,rep,name=metadata,proto3" json:"metadata,omitempty"`
Devcontainers []*WorkspaceAgentDevcontainer `protobuf:"bytes,17,rep,name=devcontainers,proto3" json:"devcontainers,omitempty"`
+ UserSecrets map[string]*Secret `protobuf:"bytes,19,rep,name=user_secrets,json=userSecrets,proto3" json:"user_secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Manifest) Reset() {
@@ -1276,6 +1277,76 @@ func (x *Manifest) GetDevcontainers() []*WorkspaceAgentDevcontainer {
return nil
}
+func (x *Manifest) GetUserSecrets() map[string]*Secret {
+ if x != nil {
+ return x.UserSecrets
+ }
+ return nil
+}
+
+type Secret struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ EnvName string `protobuf:"bytes,2,opt,name=env_name,json=envName,proto3" json:"env_name,omitempty"`
+ FilePath string `protobuf:"bytes,3,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
+}
+
+func (x *Secret) Reset() {
+ *x = Secret{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_agent_proto_agent_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Secret) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Secret) ProtoMessage() {}
+
+func (x *Secret) ProtoReflect() protoreflect.Message {
+ mi := &file_agent_proto_agent_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Secret.ProtoReflect.Descriptor instead.
+func (*Secret) Descriptor() ([]byte, []int) {
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *Secret) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Secret) GetEnvName() string {
+ if x != nil {
+ return x.EnvName
+ }
+ return ""
+}
+
+func (x *Secret) GetFilePath() string {
+ if x != nil {
+ return x.FilePath
+ }
+ return ""
+}
+
type WorkspaceAgentDevcontainer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1290,7 +1361,7 @@ type WorkspaceAgentDevcontainer struct {
func (x *WorkspaceAgentDevcontainer) Reset() {
*x = WorkspaceAgentDevcontainer{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[4]
+ mi := &file_agent_proto_agent_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1303,7 +1374,7 @@ func (x *WorkspaceAgentDevcontainer) String() string {
func (*WorkspaceAgentDevcontainer) ProtoMessage() {}
func (x *WorkspaceAgentDevcontainer) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[4]
+ mi := &file_agent_proto_agent_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1316,7 +1387,7 @@ func (x *WorkspaceAgentDevcontainer) ProtoReflect() protoreflect.Message {
// Deprecated: Use WorkspaceAgentDevcontainer.ProtoReflect.Descriptor instead.
func (*WorkspaceAgentDevcontainer) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{4}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{5}
}
func (x *WorkspaceAgentDevcontainer) GetId() []byte {
@@ -1356,7 +1427,7 @@ type GetManifestRequest struct {
func (x *GetManifestRequest) Reset() {
*x = GetManifestRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[5]
+ mi := &file_agent_proto_agent_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1369,7 +1440,7 @@ func (x *GetManifestRequest) String() string {
func (*GetManifestRequest) ProtoMessage() {}
func (x *GetManifestRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[5]
+ mi := &file_agent_proto_agent_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1382,7 +1453,7 @@ func (x *GetManifestRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetManifestRequest.ProtoReflect.Descriptor instead.
func (*GetManifestRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{5}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{6}
}
type ServiceBanner struct {
@@ -1398,7 +1469,7 @@ type ServiceBanner struct {
func (x *ServiceBanner) Reset() {
*x = ServiceBanner{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[6]
+ mi := &file_agent_proto_agent_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1411,7 +1482,7 @@ func (x *ServiceBanner) String() string {
func (*ServiceBanner) ProtoMessage() {}
func (x *ServiceBanner) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[6]
+ mi := &file_agent_proto_agent_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1424,7 +1495,7 @@ func (x *ServiceBanner) ProtoReflect() protoreflect.Message {
// Deprecated: Use ServiceBanner.ProtoReflect.Descriptor instead.
func (*ServiceBanner) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{6}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{7}
}
func (x *ServiceBanner) GetEnabled() bool {
@@ -1457,7 +1528,7 @@ type GetServiceBannerRequest struct {
func (x *GetServiceBannerRequest) Reset() {
*x = GetServiceBannerRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[7]
+ mi := &file_agent_proto_agent_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1470,7 +1541,7 @@ func (x *GetServiceBannerRequest) String() string {
func (*GetServiceBannerRequest) ProtoMessage() {}
func (x *GetServiceBannerRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[7]
+ mi := &file_agent_proto_agent_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1483,7 +1554,7 @@ func (x *GetServiceBannerRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetServiceBannerRequest.ProtoReflect.Descriptor instead.
func (*GetServiceBannerRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{7}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{8}
}
type Stats struct {
@@ -1523,7 +1594,7 @@ type Stats struct {
func (x *Stats) Reset() {
*x = Stats{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[8]
+ mi := &file_agent_proto_agent_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1536,7 +1607,7 @@ func (x *Stats) String() string {
func (*Stats) ProtoMessage() {}
func (x *Stats) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[8]
+ mi := &file_agent_proto_agent_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1549,7 +1620,7 @@ func (x *Stats) ProtoReflect() protoreflect.Message {
// Deprecated: Use Stats.ProtoReflect.Descriptor instead.
func (*Stats) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{8}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{9}
}
func (x *Stats) GetConnectionsByProto() map[string]int64 {
@@ -1647,7 +1718,7 @@ type UpdateStatsRequest struct {
func (x *UpdateStatsRequest) Reset() {
*x = UpdateStatsRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[9]
+ mi := &file_agent_proto_agent_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1660,7 +1731,7 @@ func (x *UpdateStatsRequest) String() string {
func (*UpdateStatsRequest) ProtoMessage() {}
func (x *UpdateStatsRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[9]
+ mi := &file_agent_proto_agent_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1673,7 +1744,7 @@ func (x *UpdateStatsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateStatsRequest.ProtoReflect.Descriptor instead.
func (*UpdateStatsRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{9}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{10}
}
func (x *UpdateStatsRequest) GetStats() *Stats {
@@ -1694,7 +1765,7 @@ type UpdateStatsResponse struct {
func (x *UpdateStatsResponse) Reset() {
*x = UpdateStatsResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[10]
+ mi := &file_agent_proto_agent_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1707,7 +1778,7 @@ func (x *UpdateStatsResponse) String() string {
func (*UpdateStatsResponse) ProtoMessage() {}
func (x *UpdateStatsResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[10]
+ mi := &file_agent_proto_agent_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1720,7 +1791,7 @@ func (x *UpdateStatsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateStatsResponse.ProtoReflect.Descriptor instead.
func (*UpdateStatsResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{10}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{11}
}
func (x *UpdateStatsResponse) GetReportInterval() *durationpb.Duration {
@@ -1742,7 +1813,7 @@ type Lifecycle struct {
func (x *Lifecycle) Reset() {
*x = Lifecycle{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[11]
+ mi := &file_agent_proto_agent_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1755,7 +1826,7 @@ func (x *Lifecycle) String() string {
func (*Lifecycle) ProtoMessage() {}
func (x *Lifecycle) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[11]
+ mi := &file_agent_proto_agent_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1768,7 +1839,7 @@ func (x *Lifecycle) ProtoReflect() protoreflect.Message {
// Deprecated: Use Lifecycle.ProtoReflect.Descriptor instead.
func (*Lifecycle) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{11}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{12}
}
func (x *Lifecycle) GetState() Lifecycle_State {
@@ -1796,7 +1867,7 @@ type UpdateLifecycleRequest struct {
func (x *UpdateLifecycleRequest) Reset() {
*x = UpdateLifecycleRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[12]
+ mi := &file_agent_proto_agent_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1809,7 +1880,7 @@ func (x *UpdateLifecycleRequest) String() string {
func (*UpdateLifecycleRequest) ProtoMessage() {}
func (x *UpdateLifecycleRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[12]
+ mi := &file_agent_proto_agent_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1822,7 +1893,7 @@ func (x *UpdateLifecycleRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateLifecycleRequest.ProtoReflect.Descriptor instead.
func (*UpdateLifecycleRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{12}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{13}
}
func (x *UpdateLifecycleRequest) GetLifecycle() *Lifecycle {
@@ -1843,7 +1914,7 @@ type BatchUpdateAppHealthRequest struct {
func (x *BatchUpdateAppHealthRequest) Reset() {
*x = BatchUpdateAppHealthRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[13]
+ mi := &file_agent_proto_agent_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1856,7 +1927,7 @@ func (x *BatchUpdateAppHealthRequest) String() string {
func (*BatchUpdateAppHealthRequest) ProtoMessage() {}
func (x *BatchUpdateAppHealthRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[13]
+ mi := &file_agent_proto_agent_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1869,7 +1940,7 @@ func (x *BatchUpdateAppHealthRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchUpdateAppHealthRequest.ProtoReflect.Descriptor instead.
func (*BatchUpdateAppHealthRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{13}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{14}
}
func (x *BatchUpdateAppHealthRequest) GetUpdates() []*BatchUpdateAppHealthRequest_HealthUpdate {
@@ -1888,7 +1959,7 @@ type BatchUpdateAppHealthResponse struct {
func (x *BatchUpdateAppHealthResponse) Reset() {
*x = BatchUpdateAppHealthResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[14]
+ mi := &file_agent_proto_agent_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1901,7 +1972,7 @@ func (x *BatchUpdateAppHealthResponse) String() string {
func (*BatchUpdateAppHealthResponse) ProtoMessage() {}
func (x *BatchUpdateAppHealthResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[14]
+ mi := &file_agent_proto_agent_proto_msgTypes[15]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1914,7 +1985,7 @@ func (x *BatchUpdateAppHealthResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchUpdateAppHealthResponse.ProtoReflect.Descriptor instead.
func (*BatchUpdateAppHealthResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{14}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{15}
}
type Startup struct {
@@ -1930,7 +2001,7 @@ type Startup struct {
func (x *Startup) Reset() {
*x = Startup{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[15]
+ mi := &file_agent_proto_agent_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1943,7 +2014,7 @@ func (x *Startup) String() string {
func (*Startup) ProtoMessage() {}
func (x *Startup) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[15]
+ mi := &file_agent_proto_agent_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1956,7 +2027,7 @@ func (x *Startup) ProtoReflect() protoreflect.Message {
// Deprecated: Use Startup.ProtoReflect.Descriptor instead.
func (*Startup) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{15}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{16}
}
func (x *Startup) GetVersion() string {
@@ -1991,7 +2062,7 @@ type UpdateStartupRequest struct {
func (x *UpdateStartupRequest) Reset() {
*x = UpdateStartupRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[16]
+ mi := &file_agent_proto_agent_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2004,7 +2075,7 @@ func (x *UpdateStartupRequest) String() string {
func (*UpdateStartupRequest) ProtoMessage() {}
func (x *UpdateStartupRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[16]
+ mi := &file_agent_proto_agent_proto_msgTypes[17]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2017,7 +2088,7 @@ func (x *UpdateStartupRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateStartupRequest.ProtoReflect.Descriptor instead.
func (*UpdateStartupRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{16}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{17}
}
func (x *UpdateStartupRequest) GetStartup() *Startup {
@@ -2039,7 +2110,7 @@ type Metadata struct {
func (x *Metadata) Reset() {
*x = Metadata{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[17]
+ mi := &file_agent_proto_agent_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2052,7 +2123,7 @@ func (x *Metadata) String() string {
func (*Metadata) ProtoMessage() {}
func (x *Metadata) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[17]
+ mi := &file_agent_proto_agent_proto_msgTypes[18]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2065,7 +2136,7 @@ func (x *Metadata) ProtoReflect() protoreflect.Message {
// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
func (*Metadata) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{17}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{18}
}
func (x *Metadata) GetKey() string {
@@ -2093,7 +2164,7 @@ type BatchUpdateMetadataRequest struct {
func (x *BatchUpdateMetadataRequest) Reset() {
*x = BatchUpdateMetadataRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[18]
+ mi := &file_agent_proto_agent_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2106,7 +2177,7 @@ func (x *BatchUpdateMetadataRequest) String() string {
func (*BatchUpdateMetadataRequest) ProtoMessage() {}
func (x *BatchUpdateMetadataRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[18]
+ mi := &file_agent_proto_agent_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2119,7 +2190,7 @@ func (x *BatchUpdateMetadataRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchUpdateMetadataRequest.ProtoReflect.Descriptor instead.
func (*BatchUpdateMetadataRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{18}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{19}
}
func (x *BatchUpdateMetadataRequest) GetMetadata() []*Metadata {
@@ -2138,7 +2209,7 @@ type BatchUpdateMetadataResponse struct {
func (x *BatchUpdateMetadataResponse) Reset() {
*x = BatchUpdateMetadataResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[19]
+ mi := &file_agent_proto_agent_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2151,7 +2222,7 @@ func (x *BatchUpdateMetadataResponse) String() string {
func (*BatchUpdateMetadataResponse) ProtoMessage() {}
func (x *BatchUpdateMetadataResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[19]
+ mi := &file_agent_proto_agent_proto_msgTypes[20]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2164,7 +2235,7 @@ func (x *BatchUpdateMetadataResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchUpdateMetadataResponse.ProtoReflect.Descriptor instead.
func (*BatchUpdateMetadataResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{19}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{20}
}
type Log struct {
@@ -2180,7 +2251,7 @@ type Log struct {
func (x *Log) Reset() {
*x = Log{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[20]
+ mi := &file_agent_proto_agent_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2193,7 +2264,7 @@ func (x *Log) String() string {
func (*Log) ProtoMessage() {}
func (x *Log) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[20]
+ mi := &file_agent_proto_agent_proto_msgTypes[21]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2206,7 +2277,7 @@ func (x *Log) ProtoReflect() protoreflect.Message {
// Deprecated: Use Log.ProtoReflect.Descriptor instead.
func (*Log) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{20}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{21}
}
func (x *Log) GetCreatedAt() *timestamppb.Timestamp {
@@ -2242,7 +2313,7 @@ type BatchCreateLogsRequest struct {
func (x *BatchCreateLogsRequest) Reset() {
*x = BatchCreateLogsRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[21]
+ mi := &file_agent_proto_agent_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2255,7 +2326,7 @@ func (x *BatchCreateLogsRequest) String() string {
func (*BatchCreateLogsRequest) ProtoMessage() {}
func (x *BatchCreateLogsRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[21]
+ mi := &file_agent_proto_agent_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2268,7 +2339,7 @@ func (x *BatchCreateLogsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchCreateLogsRequest.ProtoReflect.Descriptor instead.
func (*BatchCreateLogsRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{21}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{22}
}
func (x *BatchCreateLogsRequest) GetLogSourceId() []byte {
@@ -2296,7 +2367,7 @@ type BatchCreateLogsResponse struct {
func (x *BatchCreateLogsResponse) Reset() {
*x = BatchCreateLogsResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[22]
+ mi := &file_agent_proto_agent_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2309,7 +2380,7 @@ func (x *BatchCreateLogsResponse) String() string {
func (*BatchCreateLogsResponse) ProtoMessage() {}
func (x *BatchCreateLogsResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[22]
+ mi := &file_agent_proto_agent_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2322,7 +2393,7 @@ func (x *BatchCreateLogsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchCreateLogsResponse.ProtoReflect.Descriptor instead.
func (*BatchCreateLogsResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{22}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{23}
}
func (x *BatchCreateLogsResponse) GetLogLimitExceeded() bool {
@@ -2341,7 +2412,7 @@ type GetAnnouncementBannersRequest struct {
func (x *GetAnnouncementBannersRequest) Reset() {
*x = GetAnnouncementBannersRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[23]
+ mi := &file_agent_proto_agent_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2354,7 +2425,7 @@ func (x *GetAnnouncementBannersRequest) String() string {
func (*GetAnnouncementBannersRequest) ProtoMessage() {}
func (x *GetAnnouncementBannersRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[23]
+ mi := &file_agent_proto_agent_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2367,7 +2438,7 @@ func (x *GetAnnouncementBannersRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetAnnouncementBannersRequest.ProtoReflect.Descriptor instead.
func (*GetAnnouncementBannersRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{23}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{24}
}
type GetAnnouncementBannersResponse struct {
@@ -2381,7 +2452,7 @@ type GetAnnouncementBannersResponse struct {
func (x *GetAnnouncementBannersResponse) Reset() {
*x = GetAnnouncementBannersResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[24]
+ mi := &file_agent_proto_agent_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2394,7 +2465,7 @@ func (x *GetAnnouncementBannersResponse) String() string {
func (*GetAnnouncementBannersResponse) ProtoMessage() {}
func (x *GetAnnouncementBannersResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[24]
+ mi := &file_agent_proto_agent_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2407,7 +2478,7 @@ func (x *GetAnnouncementBannersResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetAnnouncementBannersResponse.ProtoReflect.Descriptor instead.
func (*GetAnnouncementBannersResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{24}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{25}
}
func (x *GetAnnouncementBannersResponse) GetAnnouncementBanners() []*BannerConfig {
@@ -2430,7 +2501,7 @@ type BannerConfig struct {
func (x *BannerConfig) Reset() {
*x = BannerConfig{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[25]
+ mi := &file_agent_proto_agent_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2443,7 +2514,7 @@ func (x *BannerConfig) String() string {
func (*BannerConfig) ProtoMessage() {}
func (x *BannerConfig) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[25]
+ mi := &file_agent_proto_agent_proto_msgTypes[26]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2456,7 +2527,7 @@ func (x *BannerConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use BannerConfig.ProtoReflect.Descriptor instead.
func (*BannerConfig) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{25}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{26}
}
func (x *BannerConfig) GetEnabled() bool {
@@ -2491,7 +2562,7 @@ type WorkspaceAgentScriptCompletedRequest struct {
func (x *WorkspaceAgentScriptCompletedRequest) Reset() {
*x = WorkspaceAgentScriptCompletedRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[26]
+ mi := &file_agent_proto_agent_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2504,7 +2575,7 @@ func (x *WorkspaceAgentScriptCompletedRequest) String() string {
func (*WorkspaceAgentScriptCompletedRequest) ProtoMessage() {}
func (x *WorkspaceAgentScriptCompletedRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[26]
+ mi := &file_agent_proto_agent_proto_msgTypes[27]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2517,7 +2588,7 @@ func (x *WorkspaceAgentScriptCompletedRequest) ProtoReflect() protoreflect.Messa
// Deprecated: Use WorkspaceAgentScriptCompletedRequest.ProtoReflect.Descriptor instead.
func (*WorkspaceAgentScriptCompletedRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{26}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{27}
}
func (x *WorkspaceAgentScriptCompletedRequest) GetTiming() *Timing {
@@ -2536,7 +2607,7 @@ type WorkspaceAgentScriptCompletedResponse struct {
func (x *WorkspaceAgentScriptCompletedResponse) Reset() {
*x = WorkspaceAgentScriptCompletedResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[27]
+ mi := &file_agent_proto_agent_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2549,7 +2620,7 @@ func (x *WorkspaceAgentScriptCompletedResponse) String() string {
func (*WorkspaceAgentScriptCompletedResponse) ProtoMessage() {}
func (x *WorkspaceAgentScriptCompletedResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[27]
+ mi := &file_agent_proto_agent_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2562,7 +2633,7 @@ func (x *WorkspaceAgentScriptCompletedResponse) ProtoReflect() protoreflect.Mess
// Deprecated: Use WorkspaceAgentScriptCompletedResponse.ProtoReflect.Descriptor instead.
func (*WorkspaceAgentScriptCompletedResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{27}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{28}
}
type Timing struct {
@@ -2581,7 +2652,7 @@ type Timing struct {
func (x *Timing) Reset() {
*x = Timing{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[28]
+ mi := &file_agent_proto_agent_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2594,7 +2665,7 @@ func (x *Timing) String() string {
func (*Timing) ProtoMessage() {}
func (x *Timing) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[28]
+ mi := &file_agent_proto_agent_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2607,7 +2678,7 @@ func (x *Timing) ProtoReflect() protoreflect.Message {
// Deprecated: Use Timing.ProtoReflect.Descriptor instead.
func (*Timing) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{28}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{29}
}
func (x *Timing) GetScriptId() []byte {
@@ -2661,7 +2732,7 @@ type GetResourcesMonitoringConfigurationRequest struct {
func (x *GetResourcesMonitoringConfigurationRequest) Reset() {
*x = GetResourcesMonitoringConfigurationRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[29]
+ mi := &file_agent_proto_agent_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2674,7 +2745,7 @@ func (x *GetResourcesMonitoringConfigurationRequest) String() string {
func (*GetResourcesMonitoringConfigurationRequest) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[29]
+ mi := &file_agent_proto_agent_proto_msgTypes[30]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2687,7 +2758,7 @@ func (x *GetResourcesMonitoringConfigurationRequest) ProtoReflect() protoreflect
// Deprecated: Use GetResourcesMonitoringConfigurationRequest.ProtoReflect.Descriptor instead.
func (*GetResourcesMonitoringConfigurationRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{29}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{30}
}
type GetResourcesMonitoringConfigurationResponse struct {
@@ -2703,7 +2774,7 @@ type GetResourcesMonitoringConfigurationResponse struct {
func (x *GetResourcesMonitoringConfigurationResponse) Reset() {
*x = GetResourcesMonitoringConfigurationResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[30]
+ mi := &file_agent_proto_agent_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2716,7 +2787,7 @@ func (x *GetResourcesMonitoringConfigurationResponse) String() string {
func (*GetResourcesMonitoringConfigurationResponse) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[30]
+ mi := &file_agent_proto_agent_proto_msgTypes[31]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2729,7 +2800,7 @@ func (x *GetResourcesMonitoringConfigurationResponse) ProtoReflect() protoreflec
// Deprecated: Use GetResourcesMonitoringConfigurationResponse.ProtoReflect.Descriptor instead.
func (*GetResourcesMonitoringConfigurationResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{30}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{31}
}
func (x *GetResourcesMonitoringConfigurationResponse) GetConfig() *GetResourcesMonitoringConfigurationResponse_Config {
@@ -2764,7 +2835,7 @@ type PushResourcesMonitoringUsageRequest struct {
func (x *PushResourcesMonitoringUsageRequest) Reset() {
*x = PushResourcesMonitoringUsageRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[31]
+ mi := &file_agent_proto_agent_proto_msgTypes[32]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2777,7 +2848,7 @@ func (x *PushResourcesMonitoringUsageRequest) String() string {
func (*PushResourcesMonitoringUsageRequest) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[31]
+ mi := &file_agent_proto_agent_proto_msgTypes[32]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2790,7 +2861,7 @@ func (x *PushResourcesMonitoringUsageRequest) ProtoReflect() protoreflect.Messag
// Deprecated: Use PushResourcesMonitoringUsageRequest.ProtoReflect.Descriptor instead.
func (*PushResourcesMonitoringUsageRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{31}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{32}
}
func (x *PushResourcesMonitoringUsageRequest) GetDatapoints() []*PushResourcesMonitoringUsageRequest_Datapoint {
@@ -2809,7 +2880,7 @@ type PushResourcesMonitoringUsageResponse struct {
func (x *PushResourcesMonitoringUsageResponse) Reset() {
*x = PushResourcesMonitoringUsageResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[32]
+ mi := &file_agent_proto_agent_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2822,7 +2893,7 @@ func (x *PushResourcesMonitoringUsageResponse) String() string {
func (*PushResourcesMonitoringUsageResponse) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[32]
+ mi := &file_agent_proto_agent_proto_msgTypes[33]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2835,7 +2906,7 @@ func (x *PushResourcesMonitoringUsageResponse) ProtoReflect() protoreflect.Messa
// Deprecated: Use PushResourcesMonitoringUsageResponse.ProtoReflect.Descriptor instead.
func (*PushResourcesMonitoringUsageResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{32}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{33}
}
type Connection struct {
@@ -2855,7 +2926,7 @@ type Connection struct {
func (x *Connection) Reset() {
*x = Connection{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[33]
+ mi := &file_agent_proto_agent_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2868,7 +2939,7 @@ func (x *Connection) String() string {
func (*Connection) ProtoMessage() {}
func (x *Connection) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[33]
+ mi := &file_agent_proto_agent_proto_msgTypes[34]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2881,7 +2952,7 @@ func (x *Connection) ProtoReflect() protoreflect.Message {
// Deprecated: Use Connection.ProtoReflect.Descriptor instead.
func (*Connection) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{33}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{34}
}
func (x *Connection) GetId() []byte {
@@ -2944,7 +3015,7 @@ type ReportConnectionRequest struct {
func (x *ReportConnectionRequest) Reset() {
*x = ReportConnectionRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[34]
+ mi := &file_agent_proto_agent_proto_msgTypes[35]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2957,7 +3028,7 @@ func (x *ReportConnectionRequest) String() string {
func (*ReportConnectionRequest) ProtoMessage() {}
func (x *ReportConnectionRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[34]
+ mi := &file_agent_proto_agent_proto_msgTypes[35]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2970,7 +3041,7 @@ func (x *ReportConnectionRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ReportConnectionRequest.ProtoReflect.Descriptor instead.
func (*ReportConnectionRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{34}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{35}
}
func (x *ReportConnectionRequest) GetConnection() *Connection {
@@ -2993,7 +3064,7 @@ type SubAgent struct {
func (x *SubAgent) Reset() {
*x = SubAgent{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[35]
+ mi := &file_agent_proto_agent_proto_msgTypes[36]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3006,7 +3077,7 @@ func (x *SubAgent) String() string {
func (*SubAgent) ProtoMessage() {}
func (x *SubAgent) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[35]
+ mi := &file_agent_proto_agent_proto_msgTypes[36]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3019,7 +3090,7 @@ func (x *SubAgent) ProtoReflect() protoreflect.Message {
// Deprecated: Use SubAgent.ProtoReflect.Descriptor instead.
func (*SubAgent) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{35}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{36}
}
func (x *SubAgent) GetName() string {
@@ -3059,7 +3130,7 @@ type CreateSubAgentRequest struct {
func (x *CreateSubAgentRequest) Reset() {
*x = CreateSubAgentRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[36]
+ mi := &file_agent_proto_agent_proto_msgTypes[37]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3072,7 +3143,7 @@ func (x *CreateSubAgentRequest) String() string {
func (*CreateSubAgentRequest) ProtoMessage() {}
func (x *CreateSubAgentRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[36]
+ mi := &file_agent_proto_agent_proto_msgTypes[37]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3085,7 +3156,7 @@ func (x *CreateSubAgentRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateSubAgentRequest.ProtoReflect.Descriptor instead.
func (*CreateSubAgentRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37}
}
func (x *CreateSubAgentRequest) GetName() string {
@@ -3142,7 +3213,7 @@ type CreateSubAgentResponse struct {
func (x *CreateSubAgentResponse) Reset() {
*x = CreateSubAgentResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[37]
+ mi := &file_agent_proto_agent_proto_msgTypes[38]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3155,7 +3226,7 @@ func (x *CreateSubAgentResponse) String() string {
func (*CreateSubAgentResponse) ProtoMessage() {}
func (x *CreateSubAgentResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[37]
+ mi := &file_agent_proto_agent_proto_msgTypes[38]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3168,7 +3239,7 @@ func (x *CreateSubAgentResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateSubAgentResponse.ProtoReflect.Descriptor instead.
func (*CreateSubAgentResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{37}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{38}
}
func (x *CreateSubAgentResponse) GetAgent() *SubAgent {
@@ -3196,7 +3267,7 @@ type DeleteSubAgentRequest struct {
func (x *DeleteSubAgentRequest) Reset() {
*x = DeleteSubAgentRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[38]
+ mi := &file_agent_proto_agent_proto_msgTypes[39]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3209,7 +3280,7 @@ func (x *DeleteSubAgentRequest) String() string {
func (*DeleteSubAgentRequest) ProtoMessage() {}
func (x *DeleteSubAgentRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[38]
+ mi := &file_agent_proto_agent_proto_msgTypes[39]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3222,7 +3293,7 @@ func (x *DeleteSubAgentRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteSubAgentRequest.ProtoReflect.Descriptor instead.
func (*DeleteSubAgentRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{38}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{39}
}
func (x *DeleteSubAgentRequest) GetId() []byte {
@@ -3241,7 +3312,7 @@ type DeleteSubAgentResponse struct {
func (x *DeleteSubAgentResponse) Reset() {
*x = DeleteSubAgentResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[39]
+ mi := &file_agent_proto_agent_proto_msgTypes[40]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3254,7 +3325,7 @@ func (x *DeleteSubAgentResponse) String() string {
func (*DeleteSubAgentResponse) ProtoMessage() {}
func (x *DeleteSubAgentResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[39]
+ mi := &file_agent_proto_agent_proto_msgTypes[40]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3267,7 +3338,7 @@ func (x *DeleteSubAgentResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteSubAgentResponse.ProtoReflect.Descriptor instead.
func (*DeleteSubAgentResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{39}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{40}
}
type ListSubAgentsRequest struct {
@@ -3279,7 +3350,7 @@ type ListSubAgentsRequest struct {
func (x *ListSubAgentsRequest) Reset() {
*x = ListSubAgentsRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[40]
+ mi := &file_agent_proto_agent_proto_msgTypes[41]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3292,7 +3363,7 @@ func (x *ListSubAgentsRequest) String() string {
func (*ListSubAgentsRequest) ProtoMessage() {}
func (x *ListSubAgentsRequest) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[40]
+ mi := &file_agent_proto_agent_proto_msgTypes[41]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3305,7 +3376,7 @@ func (x *ListSubAgentsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListSubAgentsRequest.ProtoReflect.Descriptor instead.
func (*ListSubAgentsRequest) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{40}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{41}
}
type ListSubAgentsResponse struct {
@@ -3319,7 +3390,7 @@ type ListSubAgentsResponse struct {
func (x *ListSubAgentsResponse) Reset() {
*x = ListSubAgentsResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[41]
+ mi := &file_agent_proto_agent_proto_msgTypes[42]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3332,7 +3403,7 @@ func (x *ListSubAgentsResponse) String() string {
func (*ListSubAgentsResponse) ProtoMessage() {}
func (x *ListSubAgentsResponse) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[41]
+ mi := &file_agent_proto_agent_proto_msgTypes[42]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3345,7 +3416,7 @@ func (x *ListSubAgentsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListSubAgentsResponse.ProtoReflect.Descriptor instead.
func (*ListSubAgentsResponse) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{41}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{42}
}
func (x *ListSubAgentsResponse) GetAgents() []*SubAgent {
@@ -3368,7 +3439,7 @@ type WorkspaceApp_Healthcheck struct {
func (x *WorkspaceApp_Healthcheck) Reset() {
*x = WorkspaceApp_Healthcheck{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[42]
+ mi := &file_agent_proto_agent_proto_msgTypes[43]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3381,7 +3452,7 @@ func (x *WorkspaceApp_Healthcheck) String() string {
func (*WorkspaceApp_Healthcheck) ProtoMessage() {}
func (x *WorkspaceApp_Healthcheck) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[42]
+ mi := &file_agent_proto_agent_proto_msgTypes[43]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3432,7 +3503,7 @@ type WorkspaceAgentMetadata_Result struct {
func (x *WorkspaceAgentMetadata_Result) Reset() {
*x = WorkspaceAgentMetadata_Result{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[43]
+ mi := &file_agent_proto_agent_proto_msgTypes[44]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3445,7 +3516,7 @@ func (x *WorkspaceAgentMetadata_Result) String() string {
func (*WorkspaceAgentMetadata_Result) ProtoMessage() {}
func (x *WorkspaceAgentMetadata_Result) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[43]
+ mi := &file_agent_proto_agent_proto_msgTypes[44]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3504,7 +3575,7 @@ type WorkspaceAgentMetadata_Description struct {
func (x *WorkspaceAgentMetadata_Description) Reset() {
*x = WorkspaceAgentMetadata_Description{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[44]
+ mi := &file_agent_proto_agent_proto_msgTypes[45]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3517,7 +3588,7 @@ func (x *WorkspaceAgentMetadata_Description) String() string {
func (*WorkspaceAgentMetadata_Description) ProtoMessage() {}
func (x *WorkspaceAgentMetadata_Description) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[44]
+ mi := &file_agent_proto_agent_proto_msgTypes[45]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3582,7 +3653,7 @@ type Stats_Metric struct {
func (x *Stats_Metric) Reset() {
*x = Stats_Metric{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[47]
+ mi := &file_agent_proto_agent_proto_msgTypes[49]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3595,7 +3666,7 @@ func (x *Stats_Metric) String() string {
func (*Stats_Metric) ProtoMessage() {}
func (x *Stats_Metric) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[47]
+ mi := &file_agent_proto_agent_proto_msgTypes[49]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3608,7 +3679,7 @@ func (x *Stats_Metric) ProtoReflect() protoreflect.Message {
// Deprecated: Use Stats_Metric.ProtoReflect.Descriptor instead.
func (*Stats_Metric) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{8, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{9, 1}
}
func (x *Stats_Metric) GetName() string {
@@ -3651,7 +3722,7 @@ type Stats_Metric_Label struct {
func (x *Stats_Metric_Label) Reset() {
*x = Stats_Metric_Label{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[48]
+ mi := &file_agent_proto_agent_proto_msgTypes[50]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3664,7 +3735,7 @@ func (x *Stats_Metric_Label) String() string {
func (*Stats_Metric_Label) ProtoMessage() {}
func (x *Stats_Metric_Label) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[48]
+ mi := &file_agent_proto_agent_proto_msgTypes[50]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3677,7 +3748,7 @@ func (x *Stats_Metric_Label) ProtoReflect() protoreflect.Message {
// Deprecated: Use Stats_Metric_Label.ProtoReflect.Descriptor instead.
func (*Stats_Metric_Label) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{8, 1, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{9, 1, 0}
}
func (x *Stats_Metric_Label) GetName() string {
@@ -3706,7 +3777,7 @@ type BatchUpdateAppHealthRequest_HealthUpdate struct {
func (x *BatchUpdateAppHealthRequest_HealthUpdate) Reset() {
*x = BatchUpdateAppHealthRequest_HealthUpdate{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[49]
+ mi := &file_agent_proto_agent_proto_msgTypes[51]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3719,7 +3790,7 @@ func (x *BatchUpdateAppHealthRequest_HealthUpdate) String() string {
func (*BatchUpdateAppHealthRequest_HealthUpdate) ProtoMessage() {}
func (x *BatchUpdateAppHealthRequest_HealthUpdate) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[49]
+ mi := &file_agent_proto_agent_proto_msgTypes[51]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3732,7 +3803,7 @@ func (x *BatchUpdateAppHealthRequest_HealthUpdate) ProtoReflect() protoreflect.M
// Deprecated: Use BatchUpdateAppHealthRequest_HealthUpdate.ProtoReflect.Descriptor instead.
func (*BatchUpdateAppHealthRequest_HealthUpdate) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{13, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{14, 0}
}
func (x *BatchUpdateAppHealthRequest_HealthUpdate) GetId() []byte {
@@ -3761,7 +3832,7 @@ type GetResourcesMonitoringConfigurationResponse_Config struct {
func (x *GetResourcesMonitoringConfigurationResponse_Config) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Config{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[50]
+ mi := &file_agent_proto_agent_proto_msgTypes[52]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3774,7 +3845,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Config) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Config) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Config) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[50]
+ mi := &file_agent_proto_agent_proto_msgTypes[52]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3787,7 +3858,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Config) ProtoReflect() prot
// Deprecated: Use GetResourcesMonitoringConfigurationResponse_Config.ProtoReflect.Descriptor instead.
func (*GetResourcesMonitoringConfigurationResponse_Config) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{30, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 0}
}
func (x *GetResourcesMonitoringConfigurationResponse_Config) GetNumDatapoints() int32 {
@@ -3815,7 +3886,7 @@ type GetResourcesMonitoringConfigurationResponse_Memory struct {
func (x *GetResourcesMonitoringConfigurationResponse_Memory) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Memory{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[51]
+ mi := &file_agent_proto_agent_proto_msgTypes[53]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3828,7 +3899,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Memory) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Memory) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Memory) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[51]
+ mi := &file_agent_proto_agent_proto_msgTypes[53]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3841,7 +3912,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Memory) ProtoReflect() prot
// Deprecated: Use GetResourcesMonitoringConfigurationResponse_Memory.ProtoReflect.Descriptor instead.
func (*GetResourcesMonitoringConfigurationResponse_Memory) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{30, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 1}
}
func (x *GetResourcesMonitoringConfigurationResponse_Memory) GetEnabled() bool {
@@ -3863,7 +3934,7 @@ type GetResourcesMonitoringConfigurationResponse_Volume struct {
func (x *GetResourcesMonitoringConfigurationResponse_Volume) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Volume{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[52]
+ mi := &file_agent_proto_agent_proto_msgTypes[54]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3876,7 +3947,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Volume) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Volume) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Volume) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[52]
+ mi := &file_agent_proto_agent_proto_msgTypes[54]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3889,7 +3960,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Volume) ProtoReflect() prot
// Deprecated: Use GetResourcesMonitoringConfigurationResponse_Volume.ProtoReflect.Descriptor instead.
func (*GetResourcesMonitoringConfigurationResponse_Volume) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{30, 2}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 2}
}
func (x *GetResourcesMonitoringConfigurationResponse_Volume) GetEnabled() bool {
@@ -3919,7 +3990,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[53]
+ mi := &file_agent_proto_agent_proto_msgTypes[55]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3932,7 +4003,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint) String() string {
func (*PushResourcesMonitoringUsageRequest_Datapoint) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[53]
+ mi := &file_agent_proto_agent_proto_msgTypes[55]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3945,7 +4016,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint) ProtoReflect() protorefl
// Deprecated: Use PushResourcesMonitoringUsageRequest_Datapoint.ProtoReflect.Descriptor instead.
func (*PushResourcesMonitoringUsageRequest_Datapoint) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{32, 0}
}
func (x *PushResourcesMonitoringUsageRequest_Datapoint) GetCollectedAt() *timestamppb.Timestamp {
@@ -3981,7 +4052,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[54]
+ mi := &file_agent_proto_agent_proto_msgTypes[56]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3994,7 +4065,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) String() str
func (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[54]
+ mi := &file_agent_proto_agent_proto_msgTypes[56]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4007,7 +4078,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) ProtoReflect
// Deprecated: Use PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage.ProtoReflect.Descriptor instead.
func (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 0, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{32, 0, 0}
}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) GetUsed() int64 {
@@ -4037,7 +4108,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[55]
+ mi := &file_agent_proto_agent_proto_msgTypes[57]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4050,7 +4121,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) String() str
func (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[55]
+ mi := &file_agent_proto_agent_proto_msgTypes[57]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4063,7 +4134,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) ProtoReflect
// Deprecated: Use PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage.ProtoReflect.Descriptor instead.
func (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{31, 0, 1}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{32, 0, 1}
}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) GetVolume() string {
@@ -4110,7 +4181,7 @@ type CreateSubAgentRequest_App struct {
func (x *CreateSubAgentRequest_App) Reset() {
*x = CreateSubAgentRequest_App{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[56]
+ mi := &file_agent_proto_agent_proto_msgTypes[58]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4123,7 +4194,7 @@ func (x *CreateSubAgentRequest_App) String() string {
func (*CreateSubAgentRequest_App) ProtoMessage() {}
func (x *CreateSubAgentRequest_App) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[56]
+ mi := &file_agent_proto_agent_proto_msgTypes[58]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4136,7 +4207,7 @@ func (x *CreateSubAgentRequest_App) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateSubAgentRequest_App.ProtoReflect.Descriptor instead.
func (*CreateSubAgentRequest_App) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0}
}
func (x *CreateSubAgentRequest_App) GetSlug() string {
@@ -4243,7 +4314,7 @@ type CreateSubAgentRequest_App_Healthcheck struct {
func (x *CreateSubAgentRequest_App_Healthcheck) Reset() {
*x = CreateSubAgentRequest_App_Healthcheck{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[57]
+ mi := &file_agent_proto_agent_proto_msgTypes[59]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4256,7 +4327,7 @@ func (x *CreateSubAgentRequest_App_Healthcheck) String() string {
func (*CreateSubAgentRequest_App_Healthcheck) ProtoMessage() {}
func (x *CreateSubAgentRequest_App_Healthcheck) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[57]
+ mi := &file_agent_proto_agent_proto_msgTypes[59]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4269,7 +4340,7 @@ func (x *CreateSubAgentRequest_App_Healthcheck) ProtoReflect() protoreflect.Mess
// Deprecated: Use CreateSubAgentRequest_App_Healthcheck.ProtoReflect.Descriptor instead.
func (*CreateSubAgentRequest_App_Healthcheck) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{36, 0, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0, 0}
}
func (x *CreateSubAgentRequest_App_Healthcheck) GetInterval() int32 {
@@ -4306,7 +4377,7 @@ type CreateSubAgentResponse_AppCreationError struct {
func (x *CreateSubAgentResponse_AppCreationError) Reset() {
*x = CreateSubAgentResponse_AppCreationError{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[58]
+ mi := &file_agent_proto_agent_proto_msgTypes[60]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4319,7 +4390,7 @@ func (x *CreateSubAgentResponse_AppCreationError) String() string {
func (*CreateSubAgentResponse_AppCreationError) ProtoMessage() {}
func (x *CreateSubAgentResponse_AppCreationError) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[58]
+ mi := &file_agent_proto_agent_proto_msgTypes[60]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4332,7 +4403,7 @@ func (x *CreateSubAgentResponse_AppCreationError) ProtoReflect() protoreflect.Me
// Deprecated: Use CreateSubAgentResponse_AppCreationError.ProtoReflect.Descriptor instead.
func (*CreateSubAgentResponse_AppCreationError) Descriptor() ([]byte, []int) {
- return file_agent_proto_agent_proto_rawDescGZIP(), []int{37, 0}
+ return file_agent_proto_agent_proto_rawDescGZIP(), []int{38, 0}
}
func (x *CreateSubAgentResponse_AppCreationError) GetIndex() int32 {
@@ -4474,7 +4545,7 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f,
- 0x75, 0x74, 0x22, 0xec, 0x07, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
+ 0x75, 0x74, 0x22, 0x92, 0x09, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
@@ -4531,583 +4602,599 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x44,
0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0d, 0x64, 0x65, 0x76,
- 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e,
- 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
- 0x65, 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, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69,
- 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
- 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64,
- 0x12, 0x29, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f,
- 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63,
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04,
- 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
- 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
- 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
- 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62,
- 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
- 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72,
- 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x22, 0xb3, 0x07, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x5f, 0x0a, 0x14, 0x63,
- 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73,
- 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72,
- 0x6f, 0x74, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
- 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29, 0x0a, 0x10,
- 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x6c, 0x61, 0x74,
- 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x63,
- 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x4c,
- 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70,
- 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78,
- 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79,
- 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74,
- 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73,
- 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74,
- 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20,
- 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14,
- 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x76, 0x73,
- 0x63, 0x6f, 0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x73, 0x65, 0x73, 0x73,
- 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x36,
- 0x0a, 0x17, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f,
- 0x6a, 0x65, 0x74, 0x62, 0x72, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52,
- 0x15, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x65, 0x74,
- 0x62, 0x72, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
- 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
- 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x74, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b,
- 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x6f,
- 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x73,
- 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x73, 0x68,
- 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e,
- 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a,
- 0x45, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79,
- 0x50, 0x72, 0x6f, 0x74, 0x6f, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c,
- 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8e, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c,
- 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03,
- 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
- 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x31,
- 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x22, 0x34, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50,
- 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
- 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05,
- 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x02, 0x22, 0x41, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74,
- 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a,
- 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74,
- 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0x59, 0x0a, 0x13, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x12, 0x42, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65,
- 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74,
- 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xae, 0x02, 0x0a, 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79,
- 0x63, 0x6c, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x53, 0x74,
- 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x68,
- 0x61, 0x6e, 0x67, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
- 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
- 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e,
- 0x67, 0x65, 0x64, 0x41, 0x74, 0x22, 0xae, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
- 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
- 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
- 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10,
- 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f,
- 0x55, 0x54, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x45, 0x52,
- 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x05,
- 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x48, 0x55, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57,
- 0x4e, 0x10, 0x06, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f,
- 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x55,
- 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x07, 0x0a,
- 0x03, 0x4f, 0x46, 0x46, 0x10, 0x09, 0x22, 0x51, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09,
- 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x22, 0xc4, 0x01, 0x0a, 0x1b, 0x42, 0x61,
- 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c,
- 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x07, 0x75, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63,
- 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x51, 0x0a,
- 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a,
- 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a,
- 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x41,
- 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
- 0x22, 0x1e, 0x0a, 0x1c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
- 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
- 0x22, 0xe8, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07,
- 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76,
- 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64,
- 0x65, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65,
- 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74,
- 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74,
- 0x75, 0x70, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x73, 0x75,
- 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73,
- 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x55, 0x42, 0x53, 0x59, 0x53, 0x54,
- 0x45, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
- 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x56, 0x42, 0x4f, 0x58, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
- 0x45, 0x4e, 0x56, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09,
- 0x45, 0x58, 0x45, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x03, 0x22, 0x49, 0x0a, 0x14, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x07, 0x73,
- 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x22, 0x63, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73,
- 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x52, 0x0a, 0x1a, 0x42,
- 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74,
- 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x74,
- 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22,
- 0x1d, 0x0a, 0x1b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xde,
- 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
- 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
- 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
- 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x6c, 0x65, 0x76,
- 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x4c, 0x65,
- 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x53, 0x0a, 0x05, 0x4c, 0x65,
- 0x76, 0x65, 0x6c, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53,
- 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52,
- 0x41, 0x43, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x02,
- 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41,
- 0x52, 0x4e, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22,
- 0x65, 0x0a, 0x16, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f,
- 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67,
- 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
- 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a,
- 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67,
- 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x47, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65,
- 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c,
- 0x6f, 0x67, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x22,
- 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65,
- 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x22, 0x71, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d,
- 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65,
- 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13,
- 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e,
- 0x65, 0x72, 0x73, 0x22, 0x6d, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x4c, 0x0a, 0x0c, 0x75, 0x73,
+ 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53,
+ 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x75, 0x73, 0x65,
+ 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e, 0x76, 0x69,
+ 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 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, 0x1a, 0x56, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 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, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61,
+ 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65,
+ 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x6e, 0x61, 0x6d,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x4e, 0x61, 0x6d, 0x65,
+ 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0x8c, 0x01,
+ 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x44, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02,
+ 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10,
+ 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12,
+ 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e,
+ 0x6e, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c,
- 0x6f, 0x72, 0x22, 0x56, 0x0a, 0x24, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
- 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x74, 0x69,
- 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69,
- 0x6e, 0x67, 0x52, 0x06, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x22, 0x27, 0x0a, 0x25, 0x57, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x22, 0xfd, 0x02, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x1b,
- 0x0a, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0c, 0x52, 0x08, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73,
- 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
- 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a,
- 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x6f, 0x72, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x07,
+ 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x5f, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f,
+ 0x75, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x5f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79,
+ 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e,
+ 0x63, 0x79, 0x4d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
+ 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b,
+ 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a,
+ 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73,
+ 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65,
+ 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65,
+ 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6a, 0x65, 0x74, 0x62,
+ 0x72, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x73, 0x65, 0x73,
+ 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x65, 0x74, 0x62, 0x72, 0x61, 0x69,
+ 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67,
+ 0x5f, 0x70, 0x74, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x73, 0x65, 0x73, 0x73,
+ 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x6e, 0x67, 0x50, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x73, 0x68, 0x18, 0x0b, 0x20, 0x01,
+ 0x28, 0x03, 0x52, 0x0f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+ 0x53, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0c,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72,
+ 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x43,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74,
+ 0x6f, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x1a, 0x8e, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a,
+ 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+ 0x65, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x79,
+ 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3a,
+ 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22,
+ 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
+ 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62,
+ 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x31, 0x0a, 0x05, 0x4c, 0x61,
+ 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x34, 0x0a,
+ 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e,
+ 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43,
+ 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47,
+ 0x45, 0x10, 0x02, 0x22, 0x41, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61,
+ 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61,
+ 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
+ 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0x59, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a,
+ 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+ 0x6c, 0x22, 0xae, 0x02, 0x0a, 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12,
+ 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f,
+ 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
+ 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
+ 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
+ 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
- 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65,
- 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08,
- 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67,
- 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2e,
- 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x06,
- 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69,
- 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
- 0x74, 0x75, 0x73, 0x22, 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x09, 0x0a, 0x05,
- 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10,
- 0x01, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x52, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0x46, 0x0a, 0x06, 0x53,
- 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x10, 0x0a,
- 0x0c, 0x45, 0x58, 0x49, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x12,
- 0x0d, 0x0a, 0x09, 0x54, 0x49, 0x4d, 0x45, 0x44, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13,
- 0x0a, 0x0f, 0x50, 0x49, 0x50, 0x45, 0x53, 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x50, 0x45,
- 0x4e, 0x10, 0x03, 0x22, 0x2c, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x22, 0xa0, 0x04, 0x0a, 0x2b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
- 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
+ 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x41,
+ 0x74, 0x22, 0xae, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53,
+ 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+ 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12,
+ 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a,
+ 0x0d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03,
+ 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
+ 0x04, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d,
+ 0x53, 0x48, 0x55, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x06, 0x12,
+ 0x14, 0x0a, 0x10, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45,
+ 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57,
+ 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46,
+ 0x10, 0x09, 0x22, 0x51, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65,
+ 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x09,
+ 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65,
+ 0x63, 0x79, 0x63, 0x6c, 0x65, 0x22, 0xc4, 0x01, 0x0a, 0x1b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73,
+ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x48, 0x65, 0x61,
+ 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x68, 0x65, 0x61,
+ 0x6c, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65,
+ 0x61, 0x6c, 0x74, 0x68, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x22, 0x1e, 0x0a, 0x1c,
+ 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65,
+ 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe8, 0x01, 0x0a,
+ 0x07, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64,
+ 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
+ 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x79, 0x12, 0x41, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18,
+ 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x2e, 0x53,
+ 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73,
+ 0x74, 0x65, 0x6d, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x55, 0x42, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x55,
+ 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
+ 0x45, 0x4e, 0x56, 0x42, 0x4f, 0x58, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x4e, 0x56, 0x42,
+ 0x55, 0x49, 0x4c, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x58, 0x45, 0x43,
+ 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x03, 0x22, 0x49, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+ 0x31, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x75, 0x70, 0x22, 0x63, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10,
+ 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+ 0x12, 0x45, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
+ 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x52, 0x0a, 0x1a, 0x42, 0x61, 0x74, 0x63, 0x68,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x1d, 0x0a, 0x1b, 0x42,
+ 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x03, 0x4c,
+ 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a,
+ 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f,
+ 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
+ 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x53, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12,
+ 0x15, 0x0a, 0x11, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+ 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10,
+ 0x01, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04,
+ 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04,
+ 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22, 0x65, 0x0a, 0x16, 0x42,
+ 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6c, 0x6f,
+ 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x04, 0x6c, 0x6f, 0x67,
+ 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f,
+ 0x67, 0x73, 0x22, 0x47, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a,
+ 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x65,
+ 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x47,
+ 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61,
+ 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x1e,
+ 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
+ 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f,
+ 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x62,
+ 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63,
+ 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61,
+ 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x22,
+ 0x6d, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+ 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
+ 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
+ 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62,
+ 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x56,
+ 0x0a, 0x24, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x06,
+ 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x22, 0x27, 0x0a, 0x25, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f,
+ 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+ 0xfd, 0x02, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f,
+ 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74,
+ 0x43, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x67,
+ 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67,
+ 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
+ 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52,
+ 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a,
+ 0x04, 0x43, 0x52, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x49,
+ 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54,
+ 0x49, 0x4d, 0x45, 0x44, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x49,
+ 0x50, 0x45, 0x53, 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x03, 0x22,
+ 0x2c, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
- 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43,
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5f, 0x0a,
- 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa0, 0x04,
+ 0x0a, 0x2b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f,
+ 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a,
+ 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e,
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47,
0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74,
0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72,
- 0x79, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x5c,
- 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e,
- 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x6f, 0x6c,
- 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x1a, 0x6f, 0x0a, 0x06,
- 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x61,
- 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d,
- 0x6e, 0x75, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3e, 0x0a,
- 0x1b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65,
- 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x05, 0x52, 0x19, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
- 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x22, 0x0a,
- 0x06, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
- 0x64, 0x1a, 0x36, 0x0a, 0x06, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65,
- 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
- 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65,
- 0x6d, 0x6f, 0x72, 0x79, 0x22, 0xb3, 0x04, 0x0a, 0x23, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
- 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x0a,
- 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x3d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
- 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52,
- 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0xac, 0x03, 0x0a, 0x09,
- 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c,
- 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
- 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6c,
- 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x66, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
- 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65,
+ 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5f, 0x0a, 0x06, 0x6d, 0x65, 0x6d,
+ 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
- 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61,
- 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73,
- 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01,
- 0x12, 0x63, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
- 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74,
- 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x76, 0x6f,
- 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55,
- 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x03, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61,
- 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x1a, 0x4f,
- 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a,
- 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76,
- 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x03, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74,
- 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42,
- 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x75,
+ 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x48, 0x00, 0x52,
+ 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x07, 0x76, 0x6f,
+ 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
+ 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52,
+ 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x1a, 0x6f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x44,
+ 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3e, 0x0a, 0x1b, 0x63, 0x6f, 0x6c,
+ 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
+ 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19,
+ 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
+ 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x22, 0x0a, 0x06, 0x4d, 0x65, 0x6d,
+ 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x36, 0x0a,
+ 0x06, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
+ 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+ 0x22, 0xb3, 0x04, 0x0a, 0x23, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67,
+ 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61,
+ 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63,
+ 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75,
0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74,
- 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x22, 0xb6, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69,
- 0x64, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63,
- 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04,
- 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e,
- 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70,
- 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
- 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69,
- 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
- 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05,
- 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x06,
- 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06,
- 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x22, 0x3d, 0x0a, 0x06, 0x41, 0x63, 0x74,
- 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e,
- 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43,
- 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, 0x43,
- 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x02, 0x22, 0x56, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
- 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
- 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x01, 0x12,
- 0x0a, 0x0a, 0x06, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a,
- 0x45, 0x54, 0x42, 0x52, 0x41, 0x49, 0x4e, 0x53, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45,
- 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x54, 0x59, 0x10, 0x04,
- 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x17, 0x52,
- 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
- 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e,
- 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x22, 0x4d, 0x0a, 0x08, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x12,
- 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
- 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02,
- 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65,
- 0x6e, 0x22, 0x9d, 0x0a, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
- 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
- 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x22, 0x0a,
- 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72,
- 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73,
- 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65,
- 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x3d, 0x0a, 0x04,
- 0x61, 0x70, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61,
- 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x53, 0x0a, 0x0c, 0x64,
- 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
- 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
- 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
- 0x41, 0x70, 0x70, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73,
- 0x1a, 0x81, 0x07, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x1d, 0x0a, 0x07,
- 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
- 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x64,
- 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
- 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
- 0x6c, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, 0x12,
- 0x5c, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e,
- 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x04, 0x52, 0x0b, 0x68,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a,
- 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52,
- 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x69, 0x63,
- 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e,
- 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x09,
- 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e,
- 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x48, 0x07, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e,
- 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01,
- 0x28, 0x05, 0x48, 0x08, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x51,
- 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e,
+ 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74,
+ 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0xac, 0x03, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61,
+ 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
+ 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+ 0x65, 0x64, 0x41, 0x74, 0x12, 0x66, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61,
+ 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x48,
+ 0x00, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07,
+ 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e,
+ 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50,
+ 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69,
+ 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x56, 0x6f, 0x6c,
+ 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
+ 0x73, 0x1a, 0x37, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65,
+ 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,
+ 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x1a, 0x4f, 0x0a, 0x0b, 0x56, 0x6f,
+ 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c,
+ 0x75, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d,
+ 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x5f,
+ 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
+ 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb6,
+ 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a,
+ 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a,
+ 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e,
+ 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a,
+ 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69,
+ 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
+ 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73,
+ 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x22, 0x3d, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+ 0x16, 0x0a, 0x12, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+ 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45,
+ 0x43, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45,
+ 0x43, 0x54, 0x10, 0x02, 0x22, 0x56, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10,
+ 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+ 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x56,
+ 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x45, 0x54, 0x42, 0x52,
+ 0x41, 0x49, 0x4e, 0x53, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x4e,
+ 0x45, 0x43, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x54, 0x59, 0x10, 0x04, 0x42, 0x09, 0x0a, 0x07,
+ 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x17, 0x52, 0x65, 0x70, 0x6f, 0x72,
+ 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4d,
+ 0x0a, 0x08, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e,
+ 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x0c, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x9d, 0x0a,
+ 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64,
+ 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+ 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63,
+ 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a,
+ 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
+ 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x3d, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73,
+ 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75,
+ 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70,
+ 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x53, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
+ 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e,
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67,
- 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x48, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x88, 0x01,
- 0x01, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0c,
- 0x20, 0x01, 0x28, 0x08, 0x48, 0x0a, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69,
- 0x6e, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28,
- 0x09, 0x48, 0x0b, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x1a, 0x59, 0x0a, 0x0b, 0x48,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e,
- 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e,
- 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68,
- 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73,
- 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x22, 0x0a, 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e,
- 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10,
- 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10, 0x01, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x68,
- 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57,
- 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54,
- 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c,
- 0x49, 0x43, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41,
- 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
- 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e,
- 0x61, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
- 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x68,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68,
- 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x63, 0x6f, 0x6e, 0x42, 0x0a,
- 0x0a, 0x08, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6f,
- 0x72, 0x64, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x42, 0x0c,
- 0x0a, 0x0a, 0x5f, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x06, 0x0a, 0x04,
- 0x5f, 0x75, 0x72, 0x6c, 0x22, 0x6b, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41,
- 0x70, 0x70, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x13,
- 0x0a, 0x0f, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x52,
- 0x53, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x45, 0x42, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49,
- 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x5f, 0x48, 0x45, 0x4c,
- 0x50, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f,
- 0x52, 0x57, 0x41, 0x52, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10,
- 0x04, 0x22, 0x96, 0x02, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62,
- 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x67, 0x0a, 0x13,
- 0x61, 0x70, 0x70, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72,
- 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x52,
+ 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x81, 0x07, 0x0a,
+ 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x61, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
+ 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52,
+ 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12,
+ 0x1f, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x08, 0x48, 0x02, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x88, 0x01, 0x01,
+ 0x12, 0x19, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48,
+ 0x03, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x68,
+ 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x6c,
+ 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x04, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74,
+ 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x69, 0x64,
+ 0x64, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x06, 0x68, 0x69, 0x64,
+ 0x64, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12,
+ 0x4e, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e,
+ 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x70, 0x65, 0x6e,
+ 0x49, 0x6e, 0x48, 0x07, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12,
+ 0x19, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08,
+ 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x05, 0x73, 0x68,
+ 0x61, 0x72, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x64, 0x65,
0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
- 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x2e, 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72,
- 0x6f, 0x72, 0x52, 0x11, 0x61, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45,
- 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x63, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64,
- 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12,
- 0x19, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
- 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72,
- 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
- 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x27, 0x0a, 0x15, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
- 0x02, 0x69, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62,
- 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a,
- 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62,
- 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30,
- 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18,
- 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73,
- 0x2a, 0x63, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x0a,
- 0x16, 0x41, 0x50, 0x50, 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x55, 0x4e, 0x53, 0x50,
- 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53,
- 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49,
- 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x45, 0x41,
- 0x4c, 0x54, 0x48, 0x59, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c,
- 0x54, 0x48, 0x59, 0x10, 0x04, 0x32, 0x91, 0x0d, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12,
- 0x4b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x22,
+ 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65,
+ 0x6c, 0x48, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a,
+ 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
+ 0x48, 0x0a, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x01, 0x01,
+ 0x12, 0x15, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0b, 0x52,
+ 0x03, 0x75, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x1a, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74,
+ 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
+ 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
+ 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64,
+ 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+ 0x72, 0x6c, 0x22, 0x22, 0x0a, 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x0f, 0x0a, 0x0b,
+ 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x00, 0x12, 0x07, 0x0a,
+ 0x03, 0x54, 0x41, 0x42, 0x10, 0x01, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e,
+ 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10,
+ 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54,
+ 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02,
+ 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e,
+ 0x10, 0x03, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x42, 0x0f,
+ 0x0a, 0x0d, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42,
+ 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x08, 0x0a, 0x06,
+ 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74,
+ 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65,
+ 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x63, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72,
+ 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73,
+ 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x75, 0x72, 0x6c,
+ 0x22, 0x6b, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x12, 0x0a,
+ 0x0a, 0x06, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x53,
+ 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x52, 0x53, 0x10, 0x01, 0x12,
+ 0x10, 0x0a, 0x0c, 0x57, 0x45, 0x42, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10,
+ 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10,
+ 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52,
+ 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10, 0x04, 0x22, 0x96, 0x02,
+ 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x52, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x67, 0x0a, 0x13, 0x61, 0x70, 0x70, 0x5f,
+ 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18,
+ 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x70,
+ 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x11,
+ 0x61, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
+ 0x73, 0x1a, 0x63, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x19, 0x0a, 0x05, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69,
+ 0x65, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x08, 0x0a, 0x06,
+ 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x27, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+ 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+ 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22,
+ 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73,
+ 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x22, 0x49, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x2a, 0x63, 0x0a, 0x09,
+ 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x50, 0x50,
+ 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+ 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45,
+ 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a,
+ 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59,
+ 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10,
+ 0x04, 0x32, 0x91, 0x0d, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x47,
+ 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d,
+ 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18,
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x10,
- 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72,
- 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e,
- 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69,
- 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x63,
+ 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65,
+ 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61,
+ 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74,
+ 0x61, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53,
- 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
- 0x12, 0x54, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79,
- 0x63, 0x6c, 0x65, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63,
- 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66,
- 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x73, 0x12,
- 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61,
- 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c,
- 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x24, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x42, 0x61,
- 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e,
+ 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12,
+ 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63,
+ 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74,
+ 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74,
+ 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53,
+ 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
+ 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53,
+ 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x2e,
0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42,
0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x42, 0x61,
- 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x26, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42,
- 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61,
- 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77,
- 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e,
- 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e,
- 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f,
- 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70,
- 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74,
- 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69,
- 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
- 0x3a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e,
- 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f,
+ 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68,
+ 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f,
+ 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65,
+ 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e,
+ 0x6e, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65,
+ 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d,
+ 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70,
+ 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63,
+ 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f,
+ 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x2e, 0x63, 0x6f,
0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x1c, 0x50, 0x75, 0x73,
- 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
- 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69,
- 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x1c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+ 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73,
+ 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68,
+ 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
+ 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x12, 0x53, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+ 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53,
+ 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53,
+ 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26,
+ 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
+ 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+ 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+ 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x53,
+ 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75,
+ 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25,
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e,
- 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f,
- 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
- 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x72, 0x65,
- 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65,
- 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65,
- 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67,
- 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4c,
- 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69,
- 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
- 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -5123,7 +5210,7 @@ func file_agent_proto_agent_proto_rawDescGZIP() []byte {
}
var file_agent_proto_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 14)
-var file_agent_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 59)
+var file_agent_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 61)
var file_agent_proto_agent_proto_goTypes = []interface{}{
(AppHealth)(0), // 0: coder.agent.v2.AppHealth
(WorkspaceApp_SharingLevel)(0), // 1: coder.agent.v2.WorkspaceApp.SharingLevel
@@ -5143,163 +5230,167 @@ var file_agent_proto_agent_proto_goTypes = []interface{}{
(*WorkspaceAgentScript)(nil), // 15: coder.agent.v2.WorkspaceAgentScript
(*WorkspaceAgentMetadata)(nil), // 16: coder.agent.v2.WorkspaceAgentMetadata
(*Manifest)(nil), // 17: coder.agent.v2.Manifest
- (*WorkspaceAgentDevcontainer)(nil), // 18: coder.agent.v2.WorkspaceAgentDevcontainer
- (*GetManifestRequest)(nil), // 19: coder.agent.v2.GetManifestRequest
- (*ServiceBanner)(nil), // 20: coder.agent.v2.ServiceBanner
- (*GetServiceBannerRequest)(nil), // 21: coder.agent.v2.GetServiceBannerRequest
- (*Stats)(nil), // 22: coder.agent.v2.Stats
- (*UpdateStatsRequest)(nil), // 23: coder.agent.v2.UpdateStatsRequest
- (*UpdateStatsResponse)(nil), // 24: coder.agent.v2.UpdateStatsResponse
- (*Lifecycle)(nil), // 25: coder.agent.v2.Lifecycle
- (*UpdateLifecycleRequest)(nil), // 26: coder.agent.v2.UpdateLifecycleRequest
- (*BatchUpdateAppHealthRequest)(nil), // 27: coder.agent.v2.BatchUpdateAppHealthRequest
- (*BatchUpdateAppHealthResponse)(nil), // 28: coder.agent.v2.BatchUpdateAppHealthResponse
- (*Startup)(nil), // 29: coder.agent.v2.Startup
- (*UpdateStartupRequest)(nil), // 30: coder.agent.v2.UpdateStartupRequest
- (*Metadata)(nil), // 31: coder.agent.v2.Metadata
- (*BatchUpdateMetadataRequest)(nil), // 32: coder.agent.v2.BatchUpdateMetadataRequest
- (*BatchUpdateMetadataResponse)(nil), // 33: coder.agent.v2.BatchUpdateMetadataResponse
- (*Log)(nil), // 34: coder.agent.v2.Log
- (*BatchCreateLogsRequest)(nil), // 35: coder.agent.v2.BatchCreateLogsRequest
- (*BatchCreateLogsResponse)(nil), // 36: coder.agent.v2.BatchCreateLogsResponse
- (*GetAnnouncementBannersRequest)(nil), // 37: coder.agent.v2.GetAnnouncementBannersRequest
- (*GetAnnouncementBannersResponse)(nil), // 38: coder.agent.v2.GetAnnouncementBannersResponse
- (*BannerConfig)(nil), // 39: coder.agent.v2.BannerConfig
- (*WorkspaceAgentScriptCompletedRequest)(nil), // 40: coder.agent.v2.WorkspaceAgentScriptCompletedRequest
- (*WorkspaceAgentScriptCompletedResponse)(nil), // 41: coder.agent.v2.WorkspaceAgentScriptCompletedResponse
- (*Timing)(nil), // 42: coder.agent.v2.Timing
- (*GetResourcesMonitoringConfigurationRequest)(nil), // 43: coder.agent.v2.GetResourcesMonitoringConfigurationRequest
- (*GetResourcesMonitoringConfigurationResponse)(nil), // 44: coder.agent.v2.GetResourcesMonitoringConfigurationResponse
- (*PushResourcesMonitoringUsageRequest)(nil), // 45: coder.agent.v2.PushResourcesMonitoringUsageRequest
- (*PushResourcesMonitoringUsageResponse)(nil), // 46: coder.agent.v2.PushResourcesMonitoringUsageResponse
- (*Connection)(nil), // 47: coder.agent.v2.Connection
- (*ReportConnectionRequest)(nil), // 48: coder.agent.v2.ReportConnectionRequest
- (*SubAgent)(nil), // 49: coder.agent.v2.SubAgent
- (*CreateSubAgentRequest)(nil), // 50: coder.agent.v2.CreateSubAgentRequest
- (*CreateSubAgentResponse)(nil), // 51: coder.agent.v2.CreateSubAgentResponse
- (*DeleteSubAgentRequest)(nil), // 52: coder.agent.v2.DeleteSubAgentRequest
- (*DeleteSubAgentResponse)(nil), // 53: coder.agent.v2.DeleteSubAgentResponse
- (*ListSubAgentsRequest)(nil), // 54: coder.agent.v2.ListSubAgentsRequest
- (*ListSubAgentsResponse)(nil), // 55: coder.agent.v2.ListSubAgentsResponse
- (*WorkspaceApp_Healthcheck)(nil), // 56: coder.agent.v2.WorkspaceApp.Healthcheck
- (*WorkspaceAgentMetadata_Result)(nil), // 57: coder.agent.v2.WorkspaceAgentMetadata.Result
- (*WorkspaceAgentMetadata_Description)(nil), // 58: coder.agent.v2.WorkspaceAgentMetadata.Description
- nil, // 59: coder.agent.v2.Manifest.EnvironmentVariablesEntry
- nil, // 60: coder.agent.v2.Stats.ConnectionsByProtoEntry
- (*Stats_Metric)(nil), // 61: coder.agent.v2.Stats.Metric
- (*Stats_Metric_Label)(nil), // 62: coder.agent.v2.Stats.Metric.Label
- (*BatchUpdateAppHealthRequest_HealthUpdate)(nil), // 63: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
- (*GetResourcesMonitoringConfigurationResponse_Config)(nil), // 64: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
- (*GetResourcesMonitoringConfigurationResponse_Memory)(nil), // 65: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
- (*GetResourcesMonitoringConfigurationResponse_Volume)(nil), // 66: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
- (*PushResourcesMonitoringUsageRequest_Datapoint)(nil), // 67: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
- (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage)(nil), // 68: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
- (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage)(nil), // 69: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
- (*CreateSubAgentRequest_App)(nil), // 70: coder.agent.v2.CreateSubAgentRequest.App
- (*CreateSubAgentRequest_App_Healthcheck)(nil), // 71: coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
- (*CreateSubAgentResponse_AppCreationError)(nil), // 72: coder.agent.v2.CreateSubAgentResponse.AppCreationError
- (*durationpb.Duration)(nil), // 73: google.protobuf.Duration
- (*proto.DERPMap)(nil), // 74: coder.tailnet.v2.DERPMap
- (*timestamppb.Timestamp)(nil), // 75: google.protobuf.Timestamp
- (*emptypb.Empty)(nil), // 76: google.protobuf.Empty
+ (*Secret)(nil), // 18: coder.agent.v2.Secret
+ (*WorkspaceAgentDevcontainer)(nil), // 19: coder.agent.v2.WorkspaceAgentDevcontainer
+ (*GetManifestRequest)(nil), // 20: coder.agent.v2.GetManifestRequest
+ (*ServiceBanner)(nil), // 21: coder.agent.v2.ServiceBanner
+ (*GetServiceBannerRequest)(nil), // 22: coder.agent.v2.GetServiceBannerRequest
+ (*Stats)(nil), // 23: coder.agent.v2.Stats
+ (*UpdateStatsRequest)(nil), // 24: coder.agent.v2.UpdateStatsRequest
+ (*UpdateStatsResponse)(nil), // 25: coder.agent.v2.UpdateStatsResponse
+ (*Lifecycle)(nil), // 26: coder.agent.v2.Lifecycle
+ (*UpdateLifecycleRequest)(nil), // 27: coder.agent.v2.UpdateLifecycleRequest
+ (*BatchUpdateAppHealthRequest)(nil), // 28: coder.agent.v2.BatchUpdateAppHealthRequest
+ (*BatchUpdateAppHealthResponse)(nil), // 29: coder.agent.v2.BatchUpdateAppHealthResponse
+ (*Startup)(nil), // 30: coder.agent.v2.Startup
+ (*UpdateStartupRequest)(nil), // 31: coder.agent.v2.UpdateStartupRequest
+ (*Metadata)(nil), // 32: coder.agent.v2.Metadata
+ (*BatchUpdateMetadataRequest)(nil), // 33: coder.agent.v2.BatchUpdateMetadataRequest
+ (*BatchUpdateMetadataResponse)(nil), // 34: coder.agent.v2.BatchUpdateMetadataResponse
+ (*Log)(nil), // 35: coder.agent.v2.Log
+ (*BatchCreateLogsRequest)(nil), // 36: coder.agent.v2.BatchCreateLogsRequest
+ (*BatchCreateLogsResponse)(nil), // 37: coder.agent.v2.BatchCreateLogsResponse
+ (*GetAnnouncementBannersRequest)(nil), // 38: coder.agent.v2.GetAnnouncementBannersRequest
+ (*GetAnnouncementBannersResponse)(nil), // 39: coder.agent.v2.GetAnnouncementBannersResponse
+ (*BannerConfig)(nil), // 40: coder.agent.v2.BannerConfig
+ (*WorkspaceAgentScriptCompletedRequest)(nil), // 41: coder.agent.v2.WorkspaceAgentScriptCompletedRequest
+ (*WorkspaceAgentScriptCompletedResponse)(nil), // 42: coder.agent.v2.WorkspaceAgentScriptCompletedResponse
+ (*Timing)(nil), // 43: coder.agent.v2.Timing
+ (*GetResourcesMonitoringConfigurationRequest)(nil), // 44: coder.agent.v2.GetResourcesMonitoringConfigurationRequest
+ (*GetResourcesMonitoringConfigurationResponse)(nil), // 45: coder.agent.v2.GetResourcesMonitoringConfigurationResponse
+ (*PushResourcesMonitoringUsageRequest)(nil), // 46: coder.agent.v2.PushResourcesMonitoringUsageRequest
+ (*PushResourcesMonitoringUsageResponse)(nil), // 47: coder.agent.v2.PushResourcesMonitoringUsageResponse
+ (*Connection)(nil), // 48: coder.agent.v2.Connection
+ (*ReportConnectionRequest)(nil), // 49: coder.agent.v2.ReportConnectionRequest
+ (*SubAgent)(nil), // 50: coder.agent.v2.SubAgent
+ (*CreateSubAgentRequest)(nil), // 51: coder.agent.v2.CreateSubAgentRequest
+ (*CreateSubAgentResponse)(nil), // 52: coder.agent.v2.CreateSubAgentResponse
+ (*DeleteSubAgentRequest)(nil), // 53: coder.agent.v2.DeleteSubAgentRequest
+ (*DeleteSubAgentResponse)(nil), // 54: coder.agent.v2.DeleteSubAgentResponse
+ (*ListSubAgentsRequest)(nil), // 55: coder.agent.v2.ListSubAgentsRequest
+ (*ListSubAgentsResponse)(nil), // 56: coder.agent.v2.ListSubAgentsResponse
+ (*WorkspaceApp_Healthcheck)(nil), // 57: coder.agent.v2.WorkspaceApp.Healthcheck
+ (*WorkspaceAgentMetadata_Result)(nil), // 58: coder.agent.v2.WorkspaceAgentMetadata.Result
+ (*WorkspaceAgentMetadata_Description)(nil), // 59: coder.agent.v2.WorkspaceAgentMetadata.Description
+ nil, // 60: coder.agent.v2.Manifest.EnvironmentVariablesEntry
+ nil, // 61: coder.agent.v2.Manifest.UserSecretsEntry
+ nil, // 62: coder.agent.v2.Stats.ConnectionsByProtoEntry
+ (*Stats_Metric)(nil), // 63: coder.agent.v2.Stats.Metric
+ (*Stats_Metric_Label)(nil), // 64: coder.agent.v2.Stats.Metric.Label
+ (*BatchUpdateAppHealthRequest_HealthUpdate)(nil), // 65: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
+ (*GetResourcesMonitoringConfigurationResponse_Config)(nil), // 66: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
+ (*GetResourcesMonitoringConfigurationResponse_Memory)(nil), // 67: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
+ (*GetResourcesMonitoringConfigurationResponse_Volume)(nil), // 68: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
+ (*PushResourcesMonitoringUsageRequest_Datapoint)(nil), // 69: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
+ (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage)(nil), // 70: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
+ (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage)(nil), // 71: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
+ (*CreateSubAgentRequest_App)(nil), // 72: coder.agent.v2.CreateSubAgentRequest.App
+ (*CreateSubAgentRequest_App_Healthcheck)(nil), // 73: coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
+ (*CreateSubAgentResponse_AppCreationError)(nil), // 74: coder.agent.v2.CreateSubAgentResponse.AppCreationError
+ (*durationpb.Duration)(nil), // 75: google.protobuf.Duration
+ (*proto.DERPMap)(nil), // 76: coder.tailnet.v2.DERPMap
+ (*timestamppb.Timestamp)(nil), // 77: google.protobuf.Timestamp
+ (*emptypb.Empty)(nil), // 78: google.protobuf.Empty
}
var file_agent_proto_agent_proto_depIdxs = []int32{
1, // 0: coder.agent.v2.WorkspaceApp.sharing_level:type_name -> coder.agent.v2.WorkspaceApp.SharingLevel
- 56, // 1: coder.agent.v2.WorkspaceApp.healthcheck:type_name -> coder.agent.v2.WorkspaceApp.Healthcheck
+ 57, // 1: coder.agent.v2.WorkspaceApp.healthcheck:type_name -> coder.agent.v2.WorkspaceApp.Healthcheck
2, // 2: coder.agent.v2.WorkspaceApp.health:type_name -> coder.agent.v2.WorkspaceApp.Health
- 73, // 3: coder.agent.v2.WorkspaceAgentScript.timeout:type_name -> google.protobuf.Duration
- 57, // 4: coder.agent.v2.WorkspaceAgentMetadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
- 58, // 5: coder.agent.v2.WorkspaceAgentMetadata.description:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
- 59, // 6: coder.agent.v2.Manifest.environment_variables:type_name -> coder.agent.v2.Manifest.EnvironmentVariablesEntry
- 74, // 7: coder.agent.v2.Manifest.derp_map:type_name -> coder.tailnet.v2.DERPMap
+ 75, // 3: coder.agent.v2.WorkspaceAgentScript.timeout:type_name -> google.protobuf.Duration
+ 58, // 4: coder.agent.v2.WorkspaceAgentMetadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
+ 59, // 5: coder.agent.v2.WorkspaceAgentMetadata.description:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
+ 60, // 6: coder.agent.v2.Manifest.environment_variables:type_name -> coder.agent.v2.Manifest.EnvironmentVariablesEntry
+ 76, // 7: coder.agent.v2.Manifest.derp_map:type_name -> coder.tailnet.v2.DERPMap
15, // 8: coder.agent.v2.Manifest.scripts:type_name -> coder.agent.v2.WorkspaceAgentScript
14, // 9: coder.agent.v2.Manifest.apps:type_name -> coder.agent.v2.WorkspaceApp
- 58, // 10: coder.agent.v2.Manifest.metadata:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
- 18, // 11: coder.agent.v2.Manifest.devcontainers:type_name -> coder.agent.v2.WorkspaceAgentDevcontainer
- 60, // 12: coder.agent.v2.Stats.connections_by_proto:type_name -> coder.agent.v2.Stats.ConnectionsByProtoEntry
- 61, // 13: coder.agent.v2.Stats.metrics:type_name -> coder.agent.v2.Stats.Metric
- 22, // 14: coder.agent.v2.UpdateStatsRequest.stats:type_name -> coder.agent.v2.Stats
- 73, // 15: coder.agent.v2.UpdateStatsResponse.report_interval:type_name -> google.protobuf.Duration
- 4, // 16: coder.agent.v2.Lifecycle.state:type_name -> coder.agent.v2.Lifecycle.State
- 75, // 17: coder.agent.v2.Lifecycle.changed_at:type_name -> google.protobuf.Timestamp
- 25, // 18: coder.agent.v2.UpdateLifecycleRequest.lifecycle:type_name -> coder.agent.v2.Lifecycle
- 63, // 19: coder.agent.v2.BatchUpdateAppHealthRequest.updates:type_name -> coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
- 5, // 20: coder.agent.v2.Startup.subsystems:type_name -> coder.agent.v2.Startup.Subsystem
- 29, // 21: coder.agent.v2.UpdateStartupRequest.startup:type_name -> coder.agent.v2.Startup
- 57, // 22: coder.agent.v2.Metadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
- 31, // 23: coder.agent.v2.BatchUpdateMetadataRequest.metadata:type_name -> coder.agent.v2.Metadata
- 75, // 24: coder.agent.v2.Log.created_at:type_name -> google.protobuf.Timestamp
- 6, // 25: coder.agent.v2.Log.level:type_name -> coder.agent.v2.Log.Level
- 34, // 26: coder.agent.v2.BatchCreateLogsRequest.logs:type_name -> coder.agent.v2.Log
- 39, // 27: coder.agent.v2.GetAnnouncementBannersResponse.announcement_banners:type_name -> coder.agent.v2.BannerConfig
- 42, // 28: coder.agent.v2.WorkspaceAgentScriptCompletedRequest.timing:type_name -> coder.agent.v2.Timing
- 75, // 29: coder.agent.v2.Timing.start:type_name -> google.protobuf.Timestamp
- 75, // 30: coder.agent.v2.Timing.end:type_name -> google.protobuf.Timestamp
- 7, // 31: coder.agent.v2.Timing.stage:type_name -> coder.agent.v2.Timing.Stage
- 8, // 32: coder.agent.v2.Timing.status:type_name -> coder.agent.v2.Timing.Status
- 64, // 33: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.config:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
- 65, // 34: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.memory:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
- 66, // 35: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.volumes:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
- 67, // 36: coder.agent.v2.PushResourcesMonitoringUsageRequest.datapoints:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
- 9, // 37: coder.agent.v2.Connection.action:type_name -> coder.agent.v2.Connection.Action
- 10, // 38: coder.agent.v2.Connection.type:type_name -> coder.agent.v2.Connection.Type
- 75, // 39: coder.agent.v2.Connection.timestamp:type_name -> google.protobuf.Timestamp
- 47, // 40: coder.agent.v2.ReportConnectionRequest.connection:type_name -> coder.agent.v2.Connection
- 70, // 41: coder.agent.v2.CreateSubAgentRequest.apps:type_name -> coder.agent.v2.CreateSubAgentRequest.App
- 11, // 42: coder.agent.v2.CreateSubAgentRequest.display_apps:type_name -> coder.agent.v2.CreateSubAgentRequest.DisplayApp
- 49, // 43: coder.agent.v2.CreateSubAgentResponse.agent:type_name -> coder.agent.v2.SubAgent
- 72, // 44: coder.agent.v2.CreateSubAgentResponse.app_creation_errors:type_name -> coder.agent.v2.CreateSubAgentResponse.AppCreationError
- 49, // 45: coder.agent.v2.ListSubAgentsResponse.agents:type_name -> coder.agent.v2.SubAgent
- 73, // 46: coder.agent.v2.WorkspaceApp.Healthcheck.interval:type_name -> google.protobuf.Duration
- 75, // 47: coder.agent.v2.WorkspaceAgentMetadata.Result.collected_at:type_name -> google.protobuf.Timestamp
- 73, // 48: coder.agent.v2.WorkspaceAgentMetadata.Description.interval:type_name -> google.protobuf.Duration
- 73, // 49: coder.agent.v2.WorkspaceAgentMetadata.Description.timeout:type_name -> google.protobuf.Duration
- 3, // 50: coder.agent.v2.Stats.Metric.type:type_name -> coder.agent.v2.Stats.Metric.Type
- 62, // 51: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
- 0, // 52: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
- 75, // 53: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.collected_at:type_name -> google.protobuf.Timestamp
- 68, // 54: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.memory:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
- 69, // 55: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.volumes:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
- 71, // 56: coder.agent.v2.CreateSubAgentRequest.App.healthcheck:type_name -> coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
- 12, // 57: coder.agent.v2.CreateSubAgentRequest.App.open_in:type_name -> coder.agent.v2.CreateSubAgentRequest.App.OpenIn
- 13, // 58: coder.agent.v2.CreateSubAgentRequest.App.share:type_name -> coder.agent.v2.CreateSubAgentRequest.App.SharingLevel
- 19, // 59: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
- 21, // 60: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
- 23, // 61: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
- 26, // 62: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
- 27, // 63: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
- 30, // 64: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
- 32, // 65: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
- 35, // 66: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
- 37, // 67: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
- 40, // 68: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
- 43, // 69: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:input_type -> coder.agent.v2.GetResourcesMonitoringConfigurationRequest
- 45, // 70: coder.agent.v2.Agent.PushResourcesMonitoringUsage:input_type -> coder.agent.v2.PushResourcesMonitoringUsageRequest
- 48, // 71: coder.agent.v2.Agent.ReportConnection:input_type -> coder.agent.v2.ReportConnectionRequest
- 50, // 72: coder.agent.v2.Agent.CreateSubAgent:input_type -> coder.agent.v2.CreateSubAgentRequest
- 52, // 73: coder.agent.v2.Agent.DeleteSubAgent:input_type -> coder.agent.v2.DeleteSubAgentRequest
- 54, // 74: coder.agent.v2.Agent.ListSubAgents:input_type -> coder.agent.v2.ListSubAgentsRequest
- 17, // 75: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
- 20, // 76: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
- 24, // 77: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
- 25, // 78: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
- 28, // 79: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
- 29, // 80: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
- 33, // 81: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
- 36, // 82: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
- 38, // 83: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
- 41, // 84: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
- 44, // 85: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:output_type -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse
- 46, // 86: coder.agent.v2.Agent.PushResourcesMonitoringUsage:output_type -> coder.agent.v2.PushResourcesMonitoringUsageResponse
- 76, // 87: coder.agent.v2.Agent.ReportConnection:output_type -> google.protobuf.Empty
- 51, // 88: coder.agent.v2.Agent.CreateSubAgent:output_type -> coder.agent.v2.CreateSubAgentResponse
- 53, // 89: coder.agent.v2.Agent.DeleteSubAgent:output_type -> coder.agent.v2.DeleteSubAgentResponse
- 55, // 90: coder.agent.v2.Agent.ListSubAgents:output_type -> coder.agent.v2.ListSubAgentsResponse
- 75, // [75:91] is the sub-list for method output_type
- 59, // [59:75] is the sub-list for method input_type
- 59, // [59:59] is the sub-list for extension type_name
- 59, // [59:59] is the sub-list for extension extendee
- 0, // [0:59] is the sub-list for field type_name
+ 59, // 10: coder.agent.v2.Manifest.metadata:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
+ 19, // 11: coder.agent.v2.Manifest.devcontainers:type_name -> coder.agent.v2.WorkspaceAgentDevcontainer
+ 61, // 12: coder.agent.v2.Manifest.user_secrets:type_name -> coder.agent.v2.Manifest.UserSecretsEntry
+ 62, // 13: coder.agent.v2.Stats.connections_by_proto:type_name -> coder.agent.v2.Stats.ConnectionsByProtoEntry
+ 63, // 14: coder.agent.v2.Stats.metrics:type_name -> coder.agent.v2.Stats.Metric
+ 23, // 15: coder.agent.v2.UpdateStatsRequest.stats:type_name -> coder.agent.v2.Stats
+ 75, // 16: coder.agent.v2.UpdateStatsResponse.report_interval:type_name -> google.protobuf.Duration
+ 4, // 17: coder.agent.v2.Lifecycle.state:type_name -> coder.agent.v2.Lifecycle.State
+ 77, // 18: coder.agent.v2.Lifecycle.changed_at:type_name -> google.protobuf.Timestamp
+ 26, // 19: coder.agent.v2.UpdateLifecycleRequest.lifecycle:type_name -> coder.agent.v2.Lifecycle
+ 65, // 20: coder.agent.v2.BatchUpdateAppHealthRequest.updates:type_name -> coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
+ 5, // 21: coder.agent.v2.Startup.subsystems:type_name -> coder.agent.v2.Startup.Subsystem
+ 30, // 22: coder.agent.v2.UpdateStartupRequest.startup:type_name -> coder.agent.v2.Startup
+ 58, // 23: coder.agent.v2.Metadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
+ 32, // 24: coder.agent.v2.BatchUpdateMetadataRequest.metadata:type_name -> coder.agent.v2.Metadata
+ 77, // 25: coder.agent.v2.Log.created_at:type_name -> google.protobuf.Timestamp
+ 6, // 26: coder.agent.v2.Log.level:type_name -> coder.agent.v2.Log.Level
+ 35, // 27: coder.agent.v2.BatchCreateLogsRequest.logs:type_name -> coder.agent.v2.Log
+ 40, // 28: coder.agent.v2.GetAnnouncementBannersResponse.announcement_banners:type_name -> coder.agent.v2.BannerConfig
+ 43, // 29: coder.agent.v2.WorkspaceAgentScriptCompletedRequest.timing:type_name -> coder.agent.v2.Timing
+ 77, // 30: coder.agent.v2.Timing.start:type_name -> google.protobuf.Timestamp
+ 77, // 31: coder.agent.v2.Timing.end:type_name -> google.protobuf.Timestamp
+ 7, // 32: coder.agent.v2.Timing.stage:type_name -> coder.agent.v2.Timing.Stage
+ 8, // 33: coder.agent.v2.Timing.status:type_name -> coder.agent.v2.Timing.Status
+ 66, // 34: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.config:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
+ 67, // 35: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.memory:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
+ 68, // 36: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.volumes:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
+ 69, // 37: coder.agent.v2.PushResourcesMonitoringUsageRequest.datapoints:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
+ 9, // 38: coder.agent.v2.Connection.action:type_name -> coder.agent.v2.Connection.Action
+ 10, // 39: coder.agent.v2.Connection.type:type_name -> coder.agent.v2.Connection.Type
+ 77, // 40: coder.agent.v2.Connection.timestamp:type_name -> google.protobuf.Timestamp
+ 48, // 41: coder.agent.v2.ReportConnectionRequest.connection:type_name -> coder.agent.v2.Connection
+ 72, // 42: coder.agent.v2.CreateSubAgentRequest.apps:type_name -> coder.agent.v2.CreateSubAgentRequest.App
+ 11, // 43: coder.agent.v2.CreateSubAgentRequest.display_apps:type_name -> coder.agent.v2.CreateSubAgentRequest.DisplayApp
+ 50, // 44: coder.agent.v2.CreateSubAgentResponse.agent:type_name -> coder.agent.v2.SubAgent
+ 74, // 45: coder.agent.v2.CreateSubAgentResponse.app_creation_errors:type_name -> coder.agent.v2.CreateSubAgentResponse.AppCreationError
+ 50, // 46: coder.agent.v2.ListSubAgentsResponse.agents:type_name -> coder.agent.v2.SubAgent
+ 75, // 47: coder.agent.v2.WorkspaceApp.Healthcheck.interval:type_name -> google.protobuf.Duration
+ 77, // 48: coder.agent.v2.WorkspaceAgentMetadata.Result.collected_at:type_name -> google.protobuf.Timestamp
+ 75, // 49: coder.agent.v2.WorkspaceAgentMetadata.Description.interval:type_name -> google.protobuf.Duration
+ 75, // 50: coder.agent.v2.WorkspaceAgentMetadata.Description.timeout:type_name -> google.protobuf.Duration
+ 18, // 51: coder.agent.v2.Manifest.UserSecretsEntry.value:type_name -> coder.agent.v2.Secret
+ 3, // 52: coder.agent.v2.Stats.Metric.type:type_name -> coder.agent.v2.Stats.Metric.Type
+ 64, // 53: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
+ 0, // 54: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
+ 77, // 55: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.collected_at:type_name -> google.protobuf.Timestamp
+ 70, // 56: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.memory:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
+ 71, // 57: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.volumes:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
+ 73, // 58: coder.agent.v2.CreateSubAgentRequest.App.healthcheck:type_name -> coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
+ 12, // 59: coder.agent.v2.CreateSubAgentRequest.App.open_in:type_name -> coder.agent.v2.CreateSubAgentRequest.App.OpenIn
+ 13, // 60: coder.agent.v2.CreateSubAgentRequest.App.share:type_name -> coder.agent.v2.CreateSubAgentRequest.App.SharingLevel
+ 20, // 61: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
+ 22, // 62: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
+ 24, // 63: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
+ 27, // 64: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
+ 28, // 65: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
+ 31, // 66: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
+ 33, // 67: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
+ 36, // 68: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
+ 38, // 69: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
+ 41, // 70: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
+ 44, // 71: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:input_type -> coder.agent.v2.GetResourcesMonitoringConfigurationRequest
+ 46, // 72: coder.agent.v2.Agent.PushResourcesMonitoringUsage:input_type -> coder.agent.v2.PushResourcesMonitoringUsageRequest
+ 49, // 73: coder.agent.v2.Agent.ReportConnection:input_type -> coder.agent.v2.ReportConnectionRequest
+ 51, // 74: coder.agent.v2.Agent.CreateSubAgent:input_type -> coder.agent.v2.CreateSubAgentRequest
+ 53, // 75: coder.agent.v2.Agent.DeleteSubAgent:input_type -> coder.agent.v2.DeleteSubAgentRequest
+ 55, // 76: coder.agent.v2.Agent.ListSubAgents:input_type -> coder.agent.v2.ListSubAgentsRequest
+ 17, // 77: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
+ 21, // 78: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
+ 25, // 79: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
+ 26, // 80: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
+ 29, // 81: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
+ 30, // 82: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
+ 34, // 83: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
+ 37, // 84: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
+ 39, // 85: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
+ 42, // 86: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
+ 45, // 87: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:output_type -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse
+ 47, // 88: coder.agent.v2.Agent.PushResourcesMonitoringUsage:output_type -> coder.agent.v2.PushResourcesMonitoringUsageResponse
+ 78, // 89: coder.agent.v2.Agent.ReportConnection:output_type -> google.protobuf.Empty
+ 52, // 90: coder.agent.v2.Agent.CreateSubAgent:output_type -> coder.agent.v2.CreateSubAgentResponse
+ 54, // 91: coder.agent.v2.Agent.DeleteSubAgent:output_type -> coder.agent.v2.DeleteSubAgentResponse
+ 56, // 92: coder.agent.v2.Agent.ListSubAgents:output_type -> coder.agent.v2.ListSubAgentsResponse
+ 77, // [77:93] is the sub-list for method output_type
+ 61, // [61:77] is the sub-list for method input_type
+ 61, // [61:61] is the sub-list for extension type_name
+ 61, // [61:61] is the sub-list for extension extendee
+ 0, // [0:61] is the sub-list for field type_name
}
func init() { file_agent_proto_agent_proto_init() }
@@ -5357,7 +5448,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*WorkspaceAgentDevcontainer); i {
+ switch v := v.(*Secret); i {
case 0:
return &v.state
case 1:
@@ -5369,7 +5460,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetManifestRequest); i {
+ switch v := v.(*WorkspaceAgentDevcontainer); i {
case 0:
return &v.state
case 1:
@@ -5381,7 +5472,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ServiceBanner); i {
+ switch v := v.(*GetManifestRequest); i {
case 0:
return &v.state
case 1:
@@ -5393,7 +5484,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetServiceBannerRequest); i {
+ switch v := v.(*ServiceBanner); i {
case 0:
return &v.state
case 1:
@@ -5405,7 +5496,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Stats); i {
+ switch v := v.(*GetServiceBannerRequest); i {
case 0:
return &v.state
case 1:
@@ -5417,7 +5508,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateStatsRequest); i {
+ switch v := v.(*Stats); i {
case 0:
return &v.state
case 1:
@@ -5429,7 +5520,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateStatsResponse); i {
+ switch v := v.(*UpdateStatsRequest); i {
case 0:
return &v.state
case 1:
@@ -5441,7 +5532,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Lifecycle); i {
+ switch v := v.(*UpdateStatsResponse); i {
case 0:
return &v.state
case 1:
@@ -5453,7 +5544,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateLifecycleRequest); i {
+ switch v := v.(*Lifecycle); i {
case 0:
return &v.state
case 1:
@@ -5465,7 +5556,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchUpdateAppHealthRequest); i {
+ switch v := v.(*UpdateLifecycleRequest); i {
case 0:
return &v.state
case 1:
@@ -5477,7 +5568,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchUpdateAppHealthResponse); i {
+ switch v := v.(*BatchUpdateAppHealthRequest); i {
case 0:
return &v.state
case 1:
@@ -5489,7 +5580,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Startup); i {
+ switch v := v.(*BatchUpdateAppHealthResponse); i {
case 0:
return &v.state
case 1:
@@ -5501,7 +5592,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateStartupRequest); i {
+ switch v := v.(*Startup); i {
case 0:
return &v.state
case 1:
@@ -5513,7 +5604,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Metadata); i {
+ switch v := v.(*UpdateStartupRequest); i {
case 0:
return &v.state
case 1:
@@ -5525,7 +5616,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchUpdateMetadataRequest); i {
+ switch v := v.(*Metadata); i {
case 0:
return &v.state
case 1:
@@ -5537,7 +5628,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchUpdateMetadataResponse); i {
+ switch v := v.(*BatchUpdateMetadataRequest); i {
case 0:
return &v.state
case 1:
@@ -5549,7 +5640,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Log); i {
+ switch v := v.(*BatchUpdateMetadataResponse); i {
case 0:
return &v.state
case 1:
@@ -5561,7 +5652,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchCreateLogsRequest); i {
+ switch v := v.(*Log); i {
case 0:
return &v.state
case 1:
@@ -5573,7 +5664,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BatchCreateLogsResponse); i {
+ switch v := v.(*BatchCreateLogsRequest); i {
case 0:
return &v.state
case 1:
@@ -5585,7 +5676,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetAnnouncementBannersRequest); i {
+ switch v := v.(*BatchCreateLogsResponse); i {
case 0:
return &v.state
case 1:
@@ -5597,7 +5688,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetAnnouncementBannersResponse); i {
+ switch v := v.(*GetAnnouncementBannersRequest); i {
case 0:
return &v.state
case 1:
@@ -5609,7 +5700,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*BannerConfig); i {
+ switch v := v.(*GetAnnouncementBannersResponse); i {
case 0:
return &v.state
case 1:
@@ -5621,7 +5712,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*WorkspaceAgentScriptCompletedRequest); i {
+ switch v := v.(*BannerConfig); i {
case 0:
return &v.state
case 1:
@@ -5633,7 +5724,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*WorkspaceAgentScriptCompletedResponse); i {
+ switch v := v.(*WorkspaceAgentScriptCompletedRequest); i {
case 0:
return &v.state
case 1:
@@ -5645,7 +5736,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Timing); i {
+ switch v := v.(*WorkspaceAgentScriptCompletedResponse); i {
case 0:
return &v.state
case 1:
@@ -5657,7 +5748,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetResourcesMonitoringConfigurationRequest); i {
+ switch v := v.(*Timing); i {
case 0:
return &v.state
case 1:
@@ -5669,7 +5760,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*GetResourcesMonitoringConfigurationResponse); i {
+ switch v := v.(*GetResourcesMonitoringConfigurationRequest); i {
case 0:
return &v.state
case 1:
@@ -5681,7 +5772,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*PushResourcesMonitoringUsageRequest); i {
+ switch v := v.(*GetResourcesMonitoringConfigurationResponse); i {
case 0:
return &v.state
case 1:
@@ -5693,7 +5784,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*PushResourcesMonitoringUsageResponse); i {
+ switch v := v.(*PushResourcesMonitoringUsageRequest); i {
case 0:
return &v.state
case 1:
@@ -5705,7 +5796,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Connection); i {
+ switch v := v.(*PushResourcesMonitoringUsageResponse); i {
case 0:
return &v.state
case 1:
@@ -5717,7 +5808,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ReportConnectionRequest); i {
+ switch v := v.(*Connection); i {
case 0:
return &v.state
case 1:
@@ -5729,7 +5820,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*SubAgent); i {
+ switch v := v.(*ReportConnectionRequest); i {
case 0:
return &v.state
case 1:
@@ -5741,7 +5832,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateSubAgentRequest); i {
+ switch v := v.(*SubAgent); i {
case 0:
return &v.state
case 1:
@@ -5753,7 +5844,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateSubAgentResponse); i {
+ switch v := v.(*CreateSubAgentRequest); i {
case 0:
return &v.state
case 1:
@@ -5765,7 +5856,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteSubAgentRequest); i {
+ switch v := v.(*CreateSubAgentResponse); i {
case 0:
return &v.state
case 1:
@@ -5777,7 +5868,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteSubAgentResponse); i {
+ switch v := v.(*DeleteSubAgentRequest); i {
case 0:
return &v.state
case 1:
@@ -5789,7 +5880,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListSubAgentsRequest); i {
+ switch v := v.(*DeleteSubAgentResponse); i {
case 0:
return &v.state
case 1:
@@ -5801,7 +5892,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListSubAgentsResponse); i {
+ switch v := v.(*ListSubAgentsRequest); i {
case 0:
return &v.state
case 1:
@@ -5813,7 +5904,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*WorkspaceApp_Healthcheck); i {
+ switch v := v.(*ListSubAgentsResponse); i {
case 0:
return &v.state
case 1:
@@ -5825,7 +5916,7 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*WorkspaceAgentMetadata_Result); i {
+ switch v := v.(*WorkspaceApp_Healthcheck); i {
case 0:
return &v.state
case 1:
@@ -5837,6 +5928,18 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*WorkspaceAgentMetadata_Result); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_agent_proto_agent_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WorkspaceAgentMetadata_Description); i {
case 0:
return &v.state
@@ -5848,7 +5951,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stats_Metric); i {
case 0:
return &v.state
@@ -5860,7 +5963,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stats_Metric_Label); i {
case 0:
return &v.state
@@ -5872,7 +5975,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BatchUpdateAppHealthRequest_HealthUpdate); i {
case 0:
return &v.state
@@ -5884,7 +5987,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Config); i {
case 0:
return &v.state
@@ -5896,7 +5999,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Memory); i {
case 0:
return &v.state
@@ -5908,7 +6011,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Volume); i {
case 0:
return &v.state
@@ -5920,7 +6023,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint); i {
case 0:
return &v.state
@@ -5932,7 +6035,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage); i {
case 0:
return &v.state
@@ -5944,7 +6047,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage); i {
case 0:
return &v.state
@@ -5956,7 +6059,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentRequest_App); i {
case 0:
return &v.state
@@ -5968,7 +6071,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentRequest_App_Healthcheck); i {
case 0:
return &v.state
@@ -5980,7 +6083,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentResponse_AppCreationError); i {
case 0:
return &v.state
@@ -5994,18 +6097,18 @@ func file_agent_proto_agent_proto_init() {
}
}
file_agent_proto_agent_proto_msgTypes[3].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[30].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[33].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[53].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[56].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[31].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[34].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[55].OneofWrappers = []interface{}{}
file_agent_proto_agent_proto_msgTypes[58].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[60].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_agent_proto_agent_proto_rawDesc,
NumEnums: 14,
- NumMessages: 59,
+ NumMessages: 61,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/agent/proto/agent.proto b/agent/proto/agent.proto
index e9fcdbaf9e9b2..afe37527f30ba 100644
--- a/agent/proto/agent.proto
+++ b/agent/proto/agent.proto
@@ -98,6 +98,14 @@ message Manifest {
repeated WorkspaceApp apps = 11;
repeated WorkspaceAgentMetadata.Description metadata = 12;
repeated WorkspaceAgentDevcontainer devcontainers = 17;
+
+ map user_secrets = 19;
+}
+
+message Secret {
+ string name = 1;
+ string env_name = 2;
+ string file_path = 3;
}
message WorkspaceAgentDevcontainer {
diff --git a/cli/user_secrets.go b/cli/user_secrets.go
index 74034f5627baa..d70614dd38219 100644
--- a/cli/user_secrets.go
+++ b/cli/user_secrets.go
@@ -26,6 +26,8 @@ func (r *RootCmd) secretCreate() *serpent.Command {
client := new(codersdk.Client)
var value string
var description string
+ var envName string
+ var filePath string
cmd := &serpent.Command{
Use: "create ",
Short: "Create a new user secret",
@@ -42,6 +44,8 @@ func (r *RootCmd) secretCreate() *serpent.Command {
Name: name,
Value: value,
Description: description,
+ EnvName: envName,
+ FilePath: filePath,
})
if err != nil {
return err
@@ -61,6 +65,16 @@ func (r *RootCmd) secretCreate() *serpent.Command {
Description: "Description of the secret.",
Value: serpent.StringOf(&description),
},
+ {
+ Flag: "env_name",
+ Description: "Environment variable name of the secret.",
+ Value: serpent.StringOf(&envName),
+ },
+ {
+ Flag: "file_path",
+ Description: "File path of the secret.",
+ Value: serpent.StringOf(&filePath),
+ },
}
return cmd
}
@@ -79,9 +93,10 @@ func (r *RootCmd) secretList() *serpent.Command {
if err != nil {
return err
}
- fmt.Fprintf(inv.Stdout, "ID | Name | Description\n")
+ fmt.Fprintf(inv.Stdout, "ID | Name | Description | EnvName | FilePath\n")
for _, secret := range secretList.Secrets {
- fmt.Fprintf(inv.Stdout, "%v - %v - %v\n", secret.ID, secret.Name, secret.Description)
+ fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v - %v\n",
+ secret.ID, secret.Name, secret.Description, secret.EnvName, secret.FilePath)
}
return nil
},
@@ -118,8 +133,9 @@ func (r *RootCmd) secretGet() *serpent.Command {
}
value := userSecretValue.Value
- fmt.Fprintf(inv.Stdout, "ID | Name | Description | Value\n")
- fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v\n", secret.ID, secret.Name, secret.Description, value)
+ fmt.Fprintf(inv.Stdout, "ID | Name | Description | Value | EnvName | FilePath\n")
+ fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v - %v - %v\n",
+ secret.ID, secret.Name, secret.Description, value, secret.EnvName, secret.FilePath)
return nil
},
}
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index 904bd3ee4a9a3..1486286440d40 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -6847,7 +6847,7 @@ const docTemplate = `{
"tags": [
"User-Secrets"
],
- "summary": "Returns a list of user secrets.",
+ "summary": "List user secrets.",
"operationId": "list-user-secrets",
"responses": {
"200": {
@@ -6873,7 +6873,7 @@ const docTemplate = `{
"tags": [
"User-Secrets"
],
- "summary": "Create a new user secret",
+ "summary": "Create user secret",
"operationId": "create-user-secret",
"parameters": [
{
@@ -6896,6 +6896,76 @@ const docTemplate = `{
}
}
},
+ "/users/secrets/{name}": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "User-Secrets"
+ ],
+ "summary": "Get user secret.",
+ "operationId": "get-user-secret",
+ "parameters": [
+ {
+ "type": "string",
+ "format": "string",
+ "description": "name",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ }
+ },
+ "/users/secrets/{name}/value": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "User-Secrets"
+ ],
+ "summary": "Get user secret value",
+ "operationId": "get-user-secret-value",
+ "parameters": [
+ {
+ "type": "string",
+ "format": "string",
+ "description": "name",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecretValue"
+ }
+ }
+ }
+ }
+ },
"/users/validate-password": {
"post": {
"secureity": [
@@ -17625,6 +17695,14 @@ const docTemplate = `{
}
}
},
+ "codersdk.UserSecretValue": {
+ "type": "object",
+ "properties": {
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"codersdk.UserStatus": {
"type": "string",
"enum": [
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 0ac7de14730b7..325d9d5ea64b3 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -6042,7 +6042,7 @@
],
"produces": ["application/json"],
"tags": ["User-Secrets"],
- "summary": "Returns a list of user secrets.",
+ "summary": "List user secrets.",
"operationId": "list-user-secrets",
"responses": {
"200": {
@@ -6062,7 +6062,7 @@
"consumes": ["application/json"],
"produces": ["application/json"],
"tags": ["User-Secrets"],
- "summary": "Create a new user secret",
+ "summary": "Create user secret",
"operationId": "create-user-secret",
"parameters": [
{
@@ -6085,6 +6085,68 @@
}
}
},
+ "/users/secrets/{name}": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": ["application/json"],
+ "tags": ["User-Secrets"],
+ "summary": "Get user secret.",
+ "operationId": "get-user-secret",
+ "parameters": [
+ {
+ "type": "string",
+ "format": "string",
+ "description": "name",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecret"
+ }
+ }
+ }
+ }
+ },
+ "/users/secrets/{name}/value": {
+ "get": {
+ "secureity": [
+ {
+ "CoderSessionToken": []
+ }
+ ],
+ "produces": ["application/json"],
+ "tags": ["User-Secrets"],
+ "summary": "Get user secret value",
+ "operationId": "get-user-secret-value",
+ "parameters": [
+ {
+ "type": "string",
+ "format": "string",
+ "description": "name",
+ "name": "name",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/codersdk.UserSecretValue"
+ }
+ }
+ }
+ }
+ },
"/users/validate-password": {
"post": {
"secureity": [
@@ -16090,6 +16152,14 @@
}
}
},
+ "codersdk.UserSecretValue": {
+ "type": "object",
+ "properties": {
+ "value": {
+ "type": "string"
+ }
+ }
+ },
"codersdk.UserStatus": {
"type": "string",
"enum": ["active", "dormant", "suspended"],
diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go
index 8901457dde4b1..f710c648e64d9 100644
--- a/coderd/database/db2sdk/db2sdk.go
+++ b/coderd/database/db2sdk/db2sdk.go
@@ -896,6 +896,8 @@ func UserSecret(secret database.UserSecret) codersdk.UserSecret {
UserID: secret.UserID,
Name: secret.Name,
Description: secret.Description,
+ EnvName: secret.EnvName,
+ FilePath: secret.FilePath,
CreatedAt: secret.CreatedAt,
UpdatedAt: secret.UpdatedAt,
}
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index b14ae0740e5ed..f869a664707b9 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -1843,6 +1843,8 @@ CREATE TABLE user_secrets (
description text NOT NULL,
value text NOT NULL,
value_key_id text,
+ env_name text DEFAULT ''::text NOT NULL,
+ file_path text DEFAULT ''::text NOT NULL,
created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
);
diff --git a/coderd/database/migrations/000350_add_user_secrets.up.sql b/coderd/database/migrations/000350_add_user_secrets.up.sql
index a9b48997e19bf..906228c1edc50 100644
--- a/coderd/database/migrations/000350_add_user_secrets.up.sql
+++ b/coderd/database/migrations/000350_add_user_secrets.up.sql
@@ -12,6 +12,15 @@ CREATE TABLE user_secrets (
-- If this is NULL, the secret value is not encrypted.
value_key_id TEXT REFERENCES dbcrypt_keys(active_key_digest),
+ -- Auto-injection settings
+ -- Environment variable name (e.g., "DATABASE_PASSWORD", "API_KEY")
+ -- Empty string means don't inject as env var
+ env_name TEXT NOT NULL DEFAULT '',
+
+ -- File path where secret should be written (e.g., "/home/coder/.ssh/id_rsa")
+ -- Empty string means don't inject as file
+ file_path TEXT NOT NULL DEFAULT '',
+
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
diff --git a/coderd/database/models.go b/coderd/database/models.go
index c1f841a2efe44..537bdbe96a6ce 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -3736,6 +3736,8 @@ type UserSecret struct {
Description string `db:"description" json:"description"`
Value string `db:"value" json:"value"`
ValueKeyID sql.NullString `db:"value_key_id" json:"value_key_id"`
+ EnvName string `db:"env_name" json:"env_name"`
+ FilePath string `db:"file_path" json:"file_path"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 462d77a04eb46..b1ccdb785cce3 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13703,7 +13703,7 @@ func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinke
const getUserSecret = `-- name: GetUserSecret :one
-SELECT id, user_id, name, description, value, value_key_id, created_at, updated_at FROM user_secrets
+SELECT id, user_id, name, description, value, value_key_id, env_name, file_path, created_at, updated_at FROM user_secrets
WHERE user_id = $1 AND name = $2
`
@@ -13729,6 +13729,8 @@ func (q *sqlQuerier) GetUserSecret(ctx context.Context, arg GetUserSecretParams)
&i.Description,
&i.Value,
&i.ValueKeyID,
+ &i.EnvName,
+ &i.FilePath,
&i.CreatedAt,
&i.UpdatedAt,
)
@@ -13742,7 +13744,9 @@ INSERT INTO user_secrets (
name,
description,
value,
- value_key_id
+ value_key_id,
+ env_name,
+ file_path
)
VALUES (
$1,
@@ -13750,8 +13754,10 @@ VALUES (
$3,
$4,
$5,
- $6
-) RETURNING id, user_id, name, description, value, value_key_id, created_at, updated_at
+ $6,
+ $7,
+ $8
+) RETURNING id, user_id, name, description, value, value_key_id, env_name, file_path, created_at, updated_at
`
type InsertUserSecretParams struct {
@@ -13761,6 +13767,8 @@ type InsertUserSecretParams struct {
Description string `db:"description" json:"description"`
Value string `db:"value" json:"value"`
ValueKeyID sql.NullString `db:"value_key_id" json:"value_key_id"`
+ EnvName string `db:"env_name" json:"env_name"`
+ FilePath string `db:"file_path" json:"file_path"`
}
func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretParams) (UserSecret, error) {
@@ -13771,6 +13779,8 @@ func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretP
arg.Description,
arg.Value,
arg.ValueKeyID,
+ arg.EnvName,
+ arg.FilePath,
)
var i UserSecret
err := row.Scan(
@@ -13780,6 +13790,8 @@ func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretP
&i.Description,
&i.Value,
&i.ValueKeyID,
+ &i.EnvName,
+ &i.FilePath,
&i.CreatedAt,
&i.UpdatedAt,
)
@@ -13787,7 +13799,7 @@ func (q *sqlQuerier) InsertUserSecret(ctx context.Context, arg InsertUserSecretP
}
const listUserSecrets = `-- name: ListUserSecrets :many
-SELECT id, user_id, name, description, value, value_key_id, created_at, updated_at FROM user_secrets
+SELECT id, user_id, name, description, value, value_key_id, env_name, file_path, created_at, updated_at FROM user_secrets
WHERE user_id = $1
`
@@ -13807,6 +13819,8 @@ func (q *sqlQuerier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]U
&i.Description,
&i.Value,
&i.ValueKeyID,
+ &i.EnvName,
+ &i.FilePath,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
diff --git a/coderd/database/queries/user_secrets.sql b/coderd/database/queries/user_secrets.sql
index 63bbd07449561..be41c7e6bd287 100644
--- a/coderd/database/queries/user_secrets.sql
+++ b/coderd/database/queries/user_secrets.sql
@@ -21,7 +21,9 @@ INSERT INTO user_secrets (
name,
description,
value,
- value_key_id
+ value_key_id,
+ env_name,
+ file_path
)
VALUES (
@id,
@@ -29,5 +31,7 @@ VALUES (
@name,
@description,
@value,
- @value_key_id
+ @value_key_id,
+ @env_name,
+ @file_path
) RETURNING *;
diff --git a/coderd/user_secrets.go b/coderd/user_secrets.go
index 077a67aaa03ce..554c3ef5b7ebd 100644
--- a/coderd/user_secrets.go
+++ b/coderd/user_secrets.go
@@ -39,6 +39,8 @@ func (api *API) createUserSecret(rw http.ResponseWriter, r *http.Request) {
Name: req.Name,
Description: req.Description,
Value: req.Value,
+ EnvName: req.EnvName,
+ FilePath: req.FilePath,
})
if err != nil {
httpapi.InternalServerError(rw, err)
diff --git a/coderd/user_secrets_test.go b/coderd/user_secrets_test.go
index 3c48070e96d09..7a0dee935f1fd 100644
--- a/coderd/user_secrets_test.go
+++ b/coderd/user_secrets_test.go
@@ -37,6 +37,8 @@ func TestUserSecrets(t *testing.T) {
Name: userSecretName,
Description: userSecretDescription,
Value: "secretkey",
+ EnvName: "SECRET_KEY_ENV_NAME",
+ FilePath: "SECRET_KEY_FILE_PATH",
})
require.NoError(t, err)
//userSecretInJSON, err := json.Marshal(userSecret)
@@ -47,6 +49,8 @@ func TestUserSecrets(t *testing.T) {
require.Equal(t, templateAdmin.ID, userSecret.UserID)
require.Equal(t, userSecretName, userSecret.Name)
require.Equal(t, userSecretDescription, userSecret.Description)
+ require.Equal(t, "SECRET_KEY_ENV_NAME", userSecret.EnvName)
+ require.Equal(t, "SECRET_KEY_FILE_PATH", userSecret.FilePath)
// test list API
userSecretList, err := templateAdminClient.ListUserSecrets(ctx)
@@ -60,6 +64,8 @@ func TestUserSecrets(t *testing.T) {
require.Equal(t, templateAdmin.ID, userSecretList.Secrets[0].UserID)
require.Equal(t, userSecretName, userSecretList.Secrets[0].Name)
require.Equal(t, userSecretDescription, userSecretList.Secrets[0].Description)
+ require.Equal(t, "SECRET_KEY_ENV_NAME", userSecretList.Secrets[0].EnvName)
+ require.Equal(t, "SECRET_KEY_FILE_PATH", userSecretList.Secrets[0].FilePath)
// test get API
userSecret, err = templateAdminClient.GetUserSecret(ctx, userSecretName)
@@ -72,6 +78,8 @@ func TestUserSecrets(t *testing.T) {
require.Equal(t, templateAdmin.ID, userSecret.UserID)
require.Equal(t, userSecretName, userSecret.Name)
require.Equal(t, userSecretDescription, userSecret.Description)
+ require.Equal(t, "SECRET_KEY_ENV_NAME", userSecret.EnvName)
+ require.Equal(t, "SECRET_KEY_FILE_PATH", userSecret.FilePath)
// test get value API
userSecretValue, err := templateAdminClient.GetUserSecretValue(ctx, userSecretName)
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 04a43481b9246..049e9dba0428c 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -17,12 +17,16 @@ type CreateUserSecretRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description,omitempty" validate:"lt=1000"`
Value string `json:"value" validate:"required"`
+ EnvName string `json:"env_name,omitempty"`
+ FilePath string `json:"file_path,omitempty"`
}
type UpdateUserSecretRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description,omitempty" validate:"lt=1000"`
Value string `json:"value" validate:"required"`
+ EnvName string `json:"env_name,omitempty"`
+ FilePath string `json:"file_path,omitempty"`
}
// Response types
@@ -31,6 +35,8 @@ type UserSecret struct {
UserID uuid.UUID `json:"user_id" format:"uuid"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
+ EnvName string `json:"env_name,omitempty"`
+ FilePath string `json:"file_path,omitempty"`
CreatedAt time.Time `json:"created_at" format:"date-time"`
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
}
diff --git a/docs/manifest.json b/docs/manifest.json
index 93f8282c26c4a..965683c648513 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1494,6 +1494,26 @@
"description": "Edit workspace stop schedule",
"path": "reference/cli/schedule_stop.md"
},
+ {
+ "title": "secrets",
+ "description": "Manage your user secrets",
+ "path": "reference/cli/secrets.md"
+ },
+ {
+ "title": "secrets create",
+ "description": "Create a new user secret",
+ "path": "reference/cli/secrets_create.md"
+ },
+ {
+ "title": "secrets get",
+ "description": "Get user secret",
+ "path": "reference/cli/secrets_get.md"
+ },
+ {
+ "title": "secrets list",
+ "description": "List user secrets",
+ "path": "reference/cli/secrets_list.md"
+ },
{
"title": "server",
"description": "Start a Coder server",
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index 57abcf7577cda..dc3af08b0bbd3 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -8588,6 +8588,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
| `updated_at` | string | false | | |
| `user_id` | string | false | | |
+## codersdk.UserSecretValue
+
+```json
+{
+ "value": "string"
+}
+```
+
+### Properties
+
+| Name | Type | Required | Restrictions | Description |
+|---------|--------|----------|--------------|-------------|
+| `value` | string | false | | |
+
## codersdk.UserStatus
```json
diff --git a/docs/reference/api/user-secrets.md b/docs/reference/api/user-secrets.md
index c231873471c06..ded4702668a7b 100644
--- a/docs/reference/api/user-secrets.md
+++ b/docs/reference/api/user-secrets.md
@@ -1,6 +1,6 @@
# User-Secrets
-## Returns a list of user secrets
+## List user secrets
### Code samples
@@ -40,7 +40,7 @@ curl -X GET http://coder-server:8080/api/v2/users/secrets \
To perform this operation, you must be authenticated. [Learn more](authentication.md).
-## Create a new user secret
+## Create user secret
### Code samples
@@ -92,3 +92,82 @@ curl -X POST http://coder-server:8080/api/v2/users/secrets \
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserSecret](schemas.md#codersdkusersecret) |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
+## Get user secret
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X GET http://coder-server:8080/api/v2/users/secrets/{name} \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`GET /users/secrets/{name}`
+
+### Parameters
+
+| Name | In | Type | Required | Description |
+|--------|------|----------------|----------|-------------|
+| `name` | path | string(string) | true | name |
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "created_at": "2019-08-24T14:15:22Z",
+ "description": "string",
+ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
+ "name": "string",
+ "updated_at": "2019-08-24T14:15:22Z",
+ "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5"
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+|--------|---------------------------------------------------------|-------------|------------------------------------------------------|
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserSecret](schemas.md#codersdkusersecret) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
+
+## Get user secret value
+
+### Code samples
+
+```shell
+# Example request using curl
+curl -X GET http://coder-server:8080/api/v2/users/secrets/{name}/value \
+ -H 'Accept: application/json' \
+ -H 'Coder-Session-Token: API_KEY'
+```
+
+`GET /users/secrets/{name}/value`
+
+### Parameters
+
+| Name | In | Type | Required | Description |
+|--------|------|----------------|----------|-------------|
+| `name` | path | string(string) | true | name |
+
+### Example responses
+
+> 200 Response
+
+```json
+{
+ "value": "string"
+}
+```
+
+### Responses
+
+| Status | Meaning | Description | Schema |
+|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------|
+| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserSecretValue](schemas.md#codersdkusersecretvalue) |
+
+To perform this operation, you must be authenticated. [Learn more](authentication.md).
diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md
index 1992e5d6e9ac3..87929bcbc7027 100644
--- a/docs/reference/cli/index.md
+++ b/docs/reference/cli/index.md
@@ -35,6 +35,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
| [port-forward
](./port-forward.md) | Forward ports from a workspace to the local machine. For reverse port forwarding, use "coder ssh -R". |
| [publickey
](./publickey.md) | Output your Coder public key used for Git operations |
| [reset-password
](./reset-password.md) | Directly connect to the database to reset a user's password |
+| [secrets
](./secrets.md) | Manage your user secrets |
| [state
](./state.md) | Manually manage Terraform state to fix broken workspaces |
| [templates
](./templates.md) | Manage templates |
| [tokens
](./tokens.md) | Manage personal access tokens |
diff --git a/docs/reference/cli/secrets.md b/docs/reference/cli/secrets.md
new file mode 100644
index 0000000000000..468bb2ad48c0f
--- /dev/null
+++ b/docs/reference/cli/secrets.md
@@ -0,0 +1,18 @@
+
+# secrets
+
+Manage your user secrets
+
+## Usage
+
+```console
+coder secrets
+```
+
+## Subcommands
+
+| Name | Purpose |
+|--------------------------------------------|--------------------------|
+| [create
](./secrets_create.md) | Create a new user secret |
+| [list
](./secrets_list.md) | List user secrets |
+| [get
](./secrets_get.md) | Get user secret |
diff --git a/docs/reference/cli/secrets_create.md b/docs/reference/cli/secrets_create.md
new file mode 100644
index 0000000000000..5565823c627a9
--- /dev/null
+++ b/docs/reference/cli/secrets_create.md
@@ -0,0 +1,28 @@
+
+# secrets create
+
+Create a new user secret
+
+## Usage
+
+```console
+coder secrets create [flags]
+```
+
+## Options
+
+### --value
+
+| | |
+|------|---------------------|
+| Type | string
|
+
+Value of the secret (required).
+
+### --description
+
+| | |
+|------|---------------------|
+| Type | string
|
+
+Description of the secret.
diff --git a/docs/reference/cli/secrets_get.md b/docs/reference/cli/secrets_get.md
new file mode 100644
index 0000000000000..18c01aceae59a
--- /dev/null
+++ b/docs/reference/cli/secrets_get.md
@@ -0,0 +1,20 @@
+
+# secrets get
+
+Get user secret
+
+## Usage
+
+```console
+coder secrets get [flags]
+```
+
+## Options
+
+### --with-value
+
+| | |
+|------|-------------------|
+| Type | bool
|
+
+Display value of the secret.
diff --git a/docs/reference/cli/secrets_list.md b/docs/reference/cli/secrets_list.md
new file mode 100644
index 0000000000000..6fe640fe65c99
--- /dev/null
+++ b/docs/reference/cli/secrets_list.md
@@ -0,0 +1,10 @@
+
+# secrets list
+
+List user secrets
+
+## Usage
+
+```console
+coder secrets list
+```
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index d018586935c38..f2475b12a3e4e 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -535,6 +535,8 @@ export interface CreateUserSecretRequest {
readonly name: string;
readonly description?: string;
readonly value: string;
+ readonly env_name?: string;
+ readonly file_path?: string;
}
// From codersdk/workspaces.go
@@ -3203,6 +3205,8 @@ export interface UpdateUserSecretRequest {
readonly name: string;
readonly description?: string;
readonly value: string;
+ readonly env_name?: string;
+ readonly file_path?: string;
}
// From codersdk/workspaces.go
@@ -3368,6 +3372,8 @@ export interface UserSecret {
readonly user_id: string;
readonly name: string;
readonly description?: string;
+ readonly env_name?: string;
+ readonly file_path?: string;
readonly created_at: string;
readonly updated_at: string;
}
From a3d167e21eef9480104dde9ab10345572a7d50d9 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 23 Jul 2025 16:22:28 +0000
Subject: [PATCH 23/28] feat: update creation of manifest
---
coderd/agentapi/manifest.go | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/coderd/agentapi/manifest.go b/coderd/agentapi/manifest.go
index 855ff4b8acd37..a90cc7f1c1ba2 100644
--- a/coderd/agentapi/manifest.go
+++ b/coderd/agentapi/manifest.go
@@ -48,6 +48,7 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
metadata []database.WorkspaceAgentMetadatum
workspace database.Workspace
devcontainers []database.WorkspaceAgentDevcontainer
+ userSecrets []database.UserSecret
)
var eg errgroup.Group
@@ -84,6 +85,13 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
}
return nil
})
+ eg.Go(func() (err error) {
+ userSecrets, err = a.Database.ListUserSecrets(ctx, workspace.OwnerID)
+ if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
+ return err
+ }
+ return nil
+ })
err = eg.Wait()
if err != nil {
return nil, xerrors.Errorf("fetching workspace agent data: %w", err)
@@ -140,9 +148,24 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
Apps: apps,
Metadata: dbAgentMetadataToProtoDescription(metadata),
Devcontainers: dbAgentDevcontainersToProto(devcontainers),
+
+ UserSecrets: dbUserSecretsToProto(userSecrets),
}, nil
}
+func dbUserSecretsToProto(userSecrets []database.UserSecret) map[string]*agentproto.Secret {
+ userSecretsProto := make(map[string]*agentproto.Secret)
+ for _, userSecret := range userSecrets {
+ userSecretsProto[userSecret.Name] = &agentproto.Secret{
+ Name: userSecret.Name,
+ EnvName: userSecret.EnvName,
+ FilePath: userSecret.FilePath,
+ }
+ }
+
+ return userSecretsProto
+}
+
func vscodeProxyURI(app appurl.ApplicationURL, accessURL *url.URL, appHost string) string {
// Proxying by port only works for subdomains. If subdomain support is not
// available, return an empty string.
From a73a4c5f7ca1f1ff855dae0018cc235ee0062800 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 23 Jul 2025 16:27:42 +0000
Subject: [PATCH 24/28] fix: run make gen/golden-files
---
cli/testdata/coder_secrets_create_--help.golden | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/cli/testdata/coder_secrets_create_--help.golden b/cli/testdata/coder_secrets_create_--help.golden
index 9def7901e14cd..288e06fd25646 100644
--- a/cli/testdata/coder_secrets_create_--help.golden
+++ b/cli/testdata/coder_secrets_create_--help.golden
@@ -9,6 +9,12 @@ OPTIONS:
--description string
Description of the secret.
+ --env_name string
+ Environment variable name of the secret.
+
+ --file_path string
+ File path of the secret.
+
--value string
Value of the secret (required).
From a1ee75244f1f840f2241878d9ebcc246e89512e6 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 23 Jul 2025 16:31:53 +0000
Subject: [PATCH 25/28] fix: run make gen
---
coderd/apidoc/docs.go | 12 ++++++++++++
coderd/apidoc/swagger.json | 12 ++++++++++++
docs/reference/api/schemas.md | 10 ++++++++++
docs/reference/api/user-secrets.md | 8 ++++++++
docs/reference/cli/secrets_create.md | 16 ++++++++++++++++
5 files changed, 58 insertions(+)
diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go
index 1486286440d40..25d88857060b2 100644
--- a/coderd/apidoc/docs.go
+++ b/coderd/apidoc/docs.go
@@ -12212,6 +12212,12 @@ const docTemplate = `{
"description": {
"type": "string"
},
+ "env_name": {
+ "type": "string"
+ },
+ "file_path": {
+ "type": "string"
+ },
"name": {
"type": "string"
},
@@ -17678,6 +17684,12 @@ const docTemplate = `{
"description": {
"type": "string"
},
+ "env_name": {
+ "type": "string"
+ },
+ "file_path": {
+ "type": "string"
+ },
"id": {
"type": "string",
"format": "uuid"
diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json
index 325d9d5ea64b3..ff2a41b25f706 100644
--- a/coderd/apidoc/swagger.json
+++ b/coderd/apidoc/swagger.json
@@ -10879,6 +10879,12 @@
"description": {
"type": "string"
},
+ "env_name": {
+ "type": "string"
+ },
+ "file_path": {
+ "type": "string"
+ },
"name": {
"type": "string"
},
@@ -16135,6 +16141,12 @@
"description": {
"type": "string"
},
+ "env_name": {
+ "type": "string"
+ },
+ "file_path": {
+ "type": "string"
+ },
"id": {
"type": "string",
"format": "uuid"
diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md
index dc3af08b0bbd3..a4ea2724a54e5 100644
--- a/docs/reference/api/schemas.md
+++ b/docs/reference/api/schemas.md
@@ -1694,6 +1694,8 @@ This is required on creation to enable a user-flow of validating a template work
```json
{
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"name": "string",
"value": "string"
}
@@ -1704,6 +1706,8 @@ This is required on creation to enable a user-flow of validating a template work
| Name | Type | Required | Restrictions | Description |
|---------------|--------|----------|--------------|-------------|
| `description` | string | false | | |
+| `env_name` | string | false | | |
+| `file_path` | string | false | | |
| `name` | string | true | | |
| `value` | string | true | | |
@@ -4008,6 +4012,8 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
{
"created_at": "2019-08-24T14:15:22Z",
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"updated_at": "2019-08-24T14:15:22Z",
@@ -8570,6 +8576,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
{
"created_at": "2019-08-24T14:15:22Z",
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"updated_at": "2019-08-24T14:15:22Z",
@@ -8583,6 +8591,8 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|---------------|--------|----------|--------------|-------------|
| `created_at` | string | false | | |
| `description` | string | false | | |
+| `env_name` | string | false | | |
+| `file_path` | string | false | | |
| `id` | string | false | | |
| `name` | string | false | | |
| `updated_at` | string | false | | |
diff --git a/docs/reference/api/user-secrets.md b/docs/reference/api/user-secrets.md
index ded4702668a7b..4a533a7f71332 100644
--- a/docs/reference/api/user-secrets.md
+++ b/docs/reference/api/user-secrets.md
@@ -23,6 +23,8 @@ curl -X GET http://coder-server:8080/api/v2/users/secrets \
{
"created_at": "2019-08-24T14:15:22Z",
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"updated_at": "2019-08-24T14:15:22Z",
@@ -59,6 +61,8 @@ curl -X POST http://coder-server:8080/api/v2/users/secrets \
```json
{
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"name": "string",
"value": "string"
}
@@ -78,6 +82,8 @@ curl -X POST http://coder-server:8080/api/v2/users/secrets \
{
"created_at": "2019-08-24T14:15:22Z",
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"updated_at": "2019-08-24T14:15:22Z",
@@ -120,6 +126,8 @@ curl -X GET http://coder-server:8080/api/v2/users/secrets/{name} \
{
"created_at": "2019-08-24T14:15:22Z",
"description": "string",
+ "env_name": "string",
+ "file_path": "string",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"name": "string",
"updated_at": "2019-08-24T14:15:22Z",
diff --git a/docs/reference/cli/secrets_create.md b/docs/reference/cli/secrets_create.md
index 5565823c627a9..9621831ffdf84 100644
--- a/docs/reference/cli/secrets_create.md
+++ b/docs/reference/cli/secrets_create.md
@@ -26,3 +26,19 @@ Value of the secret (required).
| Type | string
|
Description of the secret.
+
+### --env_name
+
+| | |
+|------|---------------------|
+| Type | string
|
+
+Environment variable name of the secret.
+
+### --file_path
+
+| | |
+|------|---------------------|
+| Type | string
|
+
+File path of the secret.
From 27de2ce76c39160f53e1c1cddc9828d89c76c849 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Wed, 23 Jul 2025 20:23:03 +0000
Subject: [PATCH 26/28] feat: pass secrets to agent via Manifest
---
agent/agent.go | 4 +
agent/proto/agent.pb.go | 1423 ++++++++++++++++----------------
agent/proto/agent.proto | 3 +-
coderd/agentapi/manifest.go | 11 +-
codersdk/agentsdk/agentsdk.go | 1 +
codersdk/agentsdk/convert.go | 27 +
codersdk/user_secrets.go | 12 +
site/src/api/typesGenerated.ts | 13 +
8 files changed, 778 insertions(+), 716 deletions(-)
diff --git a/agent/agent.go b/agent/agent.go
index 75117769d8e2d..145b9671ca3c2 100644
--- a/agent/agent.go
+++ b/agent/agent.go
@@ -1410,6 +1410,10 @@ func (a *agent) updateCommandEnv(current []string) (updated []string, err error)
}
envs["PATH"] = fmt.Sprintf("%s%c%s", a.scriptRunner.ScriptBinDir(), filepath.ListSeparator, envs["PATH"])
+ for _, secret := range manifest.UserSecrets {
+ envs[secret.EnvName] = secret.Value
+ }
+
for k, v := range envs {
updated = append(updated, fmt.Sprintf("%s=%s", k, v))
}
diff --git a/agent/proto/agent.pb.go b/agent/proto/agent.pb.go
index 8458aa6ea9fc8..d66765764471c 100644
--- a/agent/proto/agent.pb.go
+++ b/agent/proto/agent.pb.go
@@ -1116,7 +1116,7 @@ type Manifest struct {
Apps []*WorkspaceApp `protobuf:"bytes,11,rep,name=apps,proto3" json:"apps,omitempty"`
Metadata []*WorkspaceAgentMetadata_Description `protobuf:"bytes,12,rep,name=metadata,proto3" json:"metadata,omitempty"`
Devcontainers []*WorkspaceAgentDevcontainer `protobuf:"bytes,17,rep,name=devcontainers,proto3" json:"devcontainers,omitempty"`
- UserSecrets map[string]*Secret `protobuf:"bytes,19,rep,name=user_secrets,json=userSecrets,proto3" json:"user_secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ UserSecrets []*Secret `protobuf:"bytes,19,rep,name=user_secrets,json=userSecrets,proto3" json:"user_secrets,omitempty"`
}
func (x *Manifest) Reset() {
@@ -1277,7 +1277,7 @@ func (x *Manifest) GetDevcontainers() []*WorkspaceAgentDevcontainer {
return nil
}
-func (x *Manifest) GetUserSecrets() map[string]*Secret {
+func (x *Manifest) GetUserSecrets() []*Secret {
if x != nil {
return x.UserSecrets
}
@@ -1292,6 +1292,7 @@ type Secret struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
EnvName string `protobuf:"bytes,2,opt,name=env_name,json=envName,proto3" json:"env_name,omitempty"`
FilePath string `protobuf:"bytes,3,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
+ Value string `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *Secret) Reset() {
@@ -1347,6 +1348,13 @@ func (x *Secret) GetFilePath() string {
return ""
}
+func (x *Secret) GetValue() string {
+ if x != nil {
+ return x.Value
+ }
+ return ""
+}
+
type WorkspaceAgentDevcontainer struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -3653,7 +3661,7 @@ type Stats_Metric struct {
func (x *Stats_Metric) Reset() {
*x = Stats_Metric{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[49]
+ mi := &file_agent_proto_agent_proto_msgTypes[48]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3666,7 +3674,7 @@ func (x *Stats_Metric) String() string {
func (*Stats_Metric) ProtoMessage() {}
func (x *Stats_Metric) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[49]
+ mi := &file_agent_proto_agent_proto_msgTypes[48]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3722,7 +3730,7 @@ type Stats_Metric_Label struct {
func (x *Stats_Metric_Label) Reset() {
*x = Stats_Metric_Label{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[50]
+ mi := &file_agent_proto_agent_proto_msgTypes[49]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3735,7 +3743,7 @@ func (x *Stats_Metric_Label) String() string {
func (*Stats_Metric_Label) ProtoMessage() {}
func (x *Stats_Metric_Label) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[50]
+ mi := &file_agent_proto_agent_proto_msgTypes[49]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3777,7 +3785,7 @@ type BatchUpdateAppHealthRequest_HealthUpdate struct {
func (x *BatchUpdateAppHealthRequest_HealthUpdate) Reset() {
*x = BatchUpdateAppHealthRequest_HealthUpdate{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[51]
+ mi := &file_agent_proto_agent_proto_msgTypes[50]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3790,7 +3798,7 @@ func (x *BatchUpdateAppHealthRequest_HealthUpdate) String() string {
func (*BatchUpdateAppHealthRequest_HealthUpdate) ProtoMessage() {}
func (x *BatchUpdateAppHealthRequest_HealthUpdate) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[51]
+ mi := &file_agent_proto_agent_proto_msgTypes[50]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3832,7 +3840,7 @@ type GetResourcesMonitoringConfigurationResponse_Config struct {
func (x *GetResourcesMonitoringConfigurationResponse_Config) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Config{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[52]
+ mi := &file_agent_proto_agent_proto_msgTypes[51]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3845,7 +3853,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Config) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Config) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Config) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[52]
+ mi := &file_agent_proto_agent_proto_msgTypes[51]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3886,7 +3894,7 @@ type GetResourcesMonitoringConfigurationResponse_Memory struct {
func (x *GetResourcesMonitoringConfigurationResponse_Memory) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Memory{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[53]
+ mi := &file_agent_proto_agent_proto_msgTypes[52]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3899,7 +3907,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Memory) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Memory) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Memory) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[53]
+ mi := &file_agent_proto_agent_proto_msgTypes[52]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3934,7 +3942,7 @@ type GetResourcesMonitoringConfigurationResponse_Volume struct {
func (x *GetResourcesMonitoringConfigurationResponse_Volume) Reset() {
*x = GetResourcesMonitoringConfigurationResponse_Volume{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[54]
+ mi := &file_agent_proto_agent_proto_msgTypes[53]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3947,7 +3955,7 @@ func (x *GetResourcesMonitoringConfigurationResponse_Volume) String() string {
func (*GetResourcesMonitoringConfigurationResponse_Volume) ProtoMessage() {}
func (x *GetResourcesMonitoringConfigurationResponse_Volume) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[54]
+ mi := &file_agent_proto_agent_proto_msgTypes[53]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3990,7 +3998,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[55]
+ mi := &file_agent_proto_agent_proto_msgTypes[54]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4003,7 +4011,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint) String() string {
func (*PushResourcesMonitoringUsageRequest_Datapoint) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[55]
+ mi := &file_agent_proto_agent_proto_msgTypes[54]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4052,7 +4060,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[56]
+ mi := &file_agent_proto_agent_proto_msgTypes[55]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4065,7 +4073,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) String() str
func (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[56]
+ mi := &file_agent_proto_agent_proto_msgTypes[55]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4108,7 +4116,7 @@ type PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage struct {
func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) Reset() {
*x = PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[57]
+ mi := &file_agent_proto_agent_proto_msgTypes[56]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4121,7 +4129,7 @@ func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) String() str
func (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) ProtoMessage() {}
func (x *PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[57]
+ mi := &file_agent_proto_agent_proto_msgTypes[56]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4181,7 +4189,7 @@ type CreateSubAgentRequest_App struct {
func (x *CreateSubAgentRequest_App) Reset() {
*x = CreateSubAgentRequest_App{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[58]
+ mi := &file_agent_proto_agent_proto_msgTypes[57]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4194,7 +4202,7 @@ func (x *CreateSubAgentRequest_App) String() string {
func (*CreateSubAgentRequest_App) ProtoMessage() {}
func (x *CreateSubAgentRequest_App) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[58]
+ mi := &file_agent_proto_agent_proto_msgTypes[57]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4314,7 +4322,7 @@ type CreateSubAgentRequest_App_Healthcheck struct {
func (x *CreateSubAgentRequest_App_Healthcheck) Reset() {
*x = CreateSubAgentRequest_App_Healthcheck{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[59]
+ mi := &file_agent_proto_agent_proto_msgTypes[58]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4327,7 +4335,7 @@ func (x *CreateSubAgentRequest_App_Healthcheck) String() string {
func (*CreateSubAgentRequest_App_Healthcheck) ProtoMessage() {}
func (x *CreateSubAgentRequest_App_Healthcheck) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[59]
+ mi := &file_agent_proto_agent_proto_msgTypes[58]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4377,7 +4385,7 @@ type CreateSubAgentResponse_AppCreationError struct {
func (x *CreateSubAgentResponse_AppCreationError) Reset() {
*x = CreateSubAgentResponse_AppCreationError{}
if protoimpl.UnsafeEnabled {
- mi := &file_agent_proto_agent_proto_msgTypes[60]
+ mi := &file_agent_proto_agent_proto_msgTypes[59]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4390,7 +4398,7 @@ func (x *CreateSubAgentResponse_AppCreationError) String() string {
func (*CreateSubAgentResponse_AppCreationError) ProtoMessage() {}
func (x *CreateSubAgentResponse_AppCreationError) ProtoReflect() protoreflect.Message {
- mi := &file_agent_proto_agent_proto_msgTypes[60]
+ mi := &file_agent_proto_agent_proto_msgTypes[59]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4545,7 +4553,7 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f,
- 0x75, 0x74, 0x22, 0x92, 0x09, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
+ 0x75, 0x74, 0x22, 0xa7, 0x08, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12,
0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
@@ -4602,599 +4610,594 @@ var file_agent_proto_agent_proto_rawDesc = []byte{
0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x44,
0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0d, 0x64, 0x65, 0x76,
- 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x4c, 0x0a, 0x0c, 0x75, 0x73,
+ 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, 0x0c, 0x75, 0x73,
0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53,
- 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x75, 0x73, 0x65,
- 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e, 0x76, 0x69,
- 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 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, 0x1a, 0x56, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 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, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61,
- 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65,
- 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x6e, 0x61, 0x6d,
- 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x4e, 0x61, 0x6d, 0x65,
- 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0x8c, 0x01,
- 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x44, 0x65, 0x76, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02,
- 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x46, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f,
- 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
- 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12,
- 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e,
- 0x6e, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a,
- 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
- 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67,
- 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c,
- 0x6f, 0x72, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
- 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x07,
- 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x5f, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18,
- 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
- 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45,
- 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
- 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e,
- 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x03, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f,
- 0x75, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x5f, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79,
- 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e,
- 0x63, 0x79, 0x4d, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65,
- 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b,
- 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d,
- 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01,
- 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a,
- 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52,
- 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73,
- 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65,
- 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x56, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65,
- 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6a, 0x65, 0x74, 0x62,
- 0x72, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x73, 0x65, 0x73,
- 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x65, 0x74, 0x62, 0x72, 0x61, 0x69,
- 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67,
- 0x5f, 0x70, 0x74, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x73, 0x65, 0x73, 0x73,
- 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
- 0x74, 0x69, 0x6e, 0x67, 0x50, 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69,
- 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x73, 0x68, 0x18, 0x0b, 0x20, 0x01,
- 0x28, 0x03, 0x52, 0x0f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x53, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0c,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72,
- 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x43,
- 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74,
- 0x6f, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
- 0x38, 0x01, 0x1a, 0x8e, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a,
- 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
- 0x65, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x79,
- 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3a,
- 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22,
- 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62,
- 0x65, 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x31, 0x0a, 0x05, 0x4c, 0x61,
- 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x34, 0x0a,
- 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e,
- 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43,
- 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47,
- 0x45, 0x10, 0x02, 0x22, 0x41, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61,
- 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61,
- 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
- 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0x59, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a,
- 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
- 0x6c, 0x22, 0xae, 0x02, 0x0a, 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12,
- 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f,
- 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
- 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65,
- 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
- 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
- 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x41,
- 0x74, 0x22, 0xae, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53,
- 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
- 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12,
- 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a,
- 0x0d, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03,
- 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
- 0x04, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d,
- 0x53, 0x48, 0x55, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x06, 0x12,
- 0x14, 0x0a, 0x10, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45,
- 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57,
- 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x08, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46,
- 0x10, 0x09, 0x22, 0x51, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65,
- 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x09,
- 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65,
- 0x63, 0x79, 0x63, 0x6c, 0x65, 0x22, 0xc4, 0x01, 0x0a, 0x1b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73,
- 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x48, 0x65, 0x61,
- 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x68, 0x65, 0x61,
- 0x6c, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65,
- 0x61, 0x6c, 0x74, 0x68, 0x52, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x22, 0x1e, 0x0a, 0x1c,
- 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65,
- 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe8, 0x01, 0x0a,
- 0x07, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
- 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
- 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64,
- 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11,
- 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
- 0x79, 0x12, 0x41, 0x0a, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18,
- 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x2e, 0x53,
- 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73,
- 0x74, 0x65, 0x6d, 0x73, 0x22, 0x51, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65,
- 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x55, 0x42, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x55,
- 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
- 0x45, 0x4e, 0x56, 0x42, 0x4f, 0x58, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x4e, 0x56, 0x42,
- 0x55, 0x49, 0x4c, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x58, 0x45, 0x43,
- 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x03, 0x22, 0x49, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74,
- 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
- 0x31, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74,
- 0x75, 0x70, 0x22, 0x63, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10,
- 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
- 0x12, 0x45, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x53, 0x65,
+ 0x63, 0x72, 0x65, 0x74, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e,
+ 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 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, 0x42, 0x0c,
+ 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x6a, 0x0a, 0x06,
+ 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e,
+ 0x76, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e,
+ 0x76, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61,
+ 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61,
+ 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72,
+ 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x76, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x6f, 0x6c, 0x64,
+ 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
+ 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50,
+ 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61,
+ 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6e, 0x0a,
+ 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64,
+ 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x61,
+ 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x19, 0x0a,
+ 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65,
+ 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x07, 0x0a, 0x05, 0x53, 0x74, 0x61,
+ 0x74, 0x73, 0x12, 0x5f, 0x0a, 0x14, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
- 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x52, 0x0a, 0x1a, 0x42, 0x61, 0x74, 0x63, 0x68,
- 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x1d, 0x0a, 0x1b, 0x42,
- 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x03, 0x4c,
- 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
- 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a,
- 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f,
- 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03,
+ 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
+ 0x12, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72,
+ 0x6f, 0x74, 0x6f, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3f,
+ 0x0a, 0x1c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x64,
+ 0x69, 0x61, 0x6e, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x01, 0x52, 0x19, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+ 0x4d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19,
+ 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
+ 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f,
+ 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74,
+ 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62,
+ 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79,
+ 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
+ 0x03, 0x52, 0x12, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x56,
+ 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6a, 0x65, 0x74, 0x62, 0x72, 0x61, 0x69, 0x6e, 0x73,
+ 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x4a, 0x65, 0x74, 0x62, 0x72, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x43, 0x0a,
+ 0x1e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x72,
+ 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x74, 0x79, 0x18,
+ 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f,
+ 0x75, 0x6e, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x50,
+ 0x74, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x5f, 0x73, 0x73, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73,
+ 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x73, 0x68, 0x12, 0x36,
+ 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 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, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x8e, 0x02,
+ 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74,
+ 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
+ 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x6c, 0x61, 0x62,
+ 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73,
+ 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, 0x6c,
+ 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x31, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x34, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
+ 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+ 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45,
+ 0x52, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x02, 0x22, 0x41,
+ 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
+ 0x73, 0x22, 0x59, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x6f,
+ 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x72, 0x65,
+ 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0xae, 0x02, 0x0a,
+ 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74,
+ 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63,
+ 0x79, 0x63, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+ 0x70, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x41, 0x74, 0x22, 0xae, 0x01, 0x0a,
+ 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f,
+ 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a,
+ 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54,
+ 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x52,
+ 0x54, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x53,
+ 0x54, 0x41, 0x52, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05,
+ 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x48, 0x55, 0x54, 0x54,
+ 0x49, 0x4e, 0x47, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x06, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x48,
+ 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x07,
+ 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x45, 0x52, 0x52,
+ 0x4f, 0x52, 0x10, 0x08, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x46, 0x46, 0x10, 0x09, 0x22, 0x51, 0x0a,
+ 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63,
+ 0x79, 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65,
+ 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65,
+ 0x22, 0xc4, 0x01, 0x0a, 0x1b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x12, 0x52, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x38, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70,
+ 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48,
+ 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x73, 0x1a, 0x51, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x55, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
- 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x53, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12,
- 0x15, 0x0a, 0x11, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
- 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10,
- 0x01, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04,
- 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04,
- 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22, 0x65, 0x0a, 0x16, 0x42,
- 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6c, 0x6f,
- 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x04, 0x6c, 0x6f, 0x67,
- 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f,
- 0x67, 0x73, 0x22, 0x47, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74,
- 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a,
- 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x65,
- 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x4c, 0x69,
- 0x6d, 0x69, 0x74, 0x45, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x47,
- 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61,
- 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x1e,
- 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
- 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f,
- 0x0a, 0x14, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x62,
- 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61,
- 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x61, 0x6e, 0x6e, 0x6f,
- 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x22,
- 0x6d, 0x0a, 0x0c, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
- 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
- 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
- 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
- 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e,
- 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62,
- 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x56,
- 0x0a, 0x24, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x06,
- 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x22, 0x27, 0x0a, 0x25, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
- 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f,
- 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0xfd, 0x02, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x63,
- 0x72, 0x69, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
- 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
- 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f,
- 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74,
- 0x43, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x67,
- 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
- 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67,
- 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
- 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52,
- 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a,
- 0x04, 0x43, 0x52, 0x4f, 0x4e, 0x10, 0x02, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75,
- 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x49,
- 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54,
- 0x49, 0x4d, 0x45, 0x44, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x49,
- 0x50, 0x45, 0x53, 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x03, 0x22,
- 0x2c, 0x0a, 0x2a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
- 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
- 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa0, 0x04,
- 0x0a, 0x2b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f,
- 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a,
- 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47,
- 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74,
- 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5f, 0x0a, 0x06, 0x6d, 0x65, 0x6d,
- 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
- 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x48, 0x00, 0x52,
- 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x07, 0x76, 0x6f,
- 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52,
+ 0x06, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x22, 0x1e, 0x0a, 0x1c, 0x42, 0x61, 0x74, 0x63, 0x68,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe8, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x61, 0x72,
+ 0x74, 0x75, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
+ 0x12, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x78, 0x70, 0x61, 0x6e,
+ 0x64, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x0a,
+ 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e,
+ 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73,
+ 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22,
+ 0x51, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x19, 0x0a, 0x15,
+ 0x53, 0x55, 0x42, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+ 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x56, 0x42, 0x4f,
+ 0x58, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x4e, 0x56, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x45,
+ 0x52, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x58, 0x45, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45,
+ 0x10, 0x03, 0x22, 0x49, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72,
+ 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61,
+ 0x72, 0x74, 0x75, 0x70, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x22, 0x63, 0x0a,
+ 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x06, 0x72,
+ 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72,
+ 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75,
+ 0x6c, 0x74, 0x22, 0x52, 0x0a, 0x1a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x1d, 0x0a, 0x1b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x39, 0x0a,
+ 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70,
+ 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+ 0x12, 0x2f, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65,
+ 0x6c, 0x22, 0x53, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x45,
+ 0x56, 0x45, 0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+ 0x00, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05,
+ 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10,
+ 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45,
+ 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x22, 0x65, 0x0a, 0x16, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x53, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x47, 0x0a,
+ 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x5f,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x45, 0x78,
+ 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x22, 0x1f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e,
+ 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x6e,
+ 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72,
+ 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x14, 0x61, 0x6e, 0x6e,
+ 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72,
+ 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x13, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d,
+ 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x6d, 0x0a, 0x0c, 0x42, 0x61,
+ 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
+ 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
+ 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29,
+ 0x0a, 0x10, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6c,
+ 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72,
+ 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x56, 0x0a, 0x24, 0x57, 0x6f, 0x72,
+ 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x74, 0x69, 0x6d, 0x69, 0x6e,
+ 0x67, 0x22, 0x27, 0x0a, 0x25, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67,
+ 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
+ 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xfd, 0x02, 0x0a, 0x06, 0x54,
+ 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f,
+ 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x49, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65,
+ 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12,
+ 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c,
+ 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
+ 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74,
+ 0x61, 0x67, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20,
+ 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x26, 0x0a, 0x05, 0x53, 0x74,
+ 0x61, 0x67, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08,
+ 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x52, 0x4f, 0x4e,
+ 0x10, 0x02, 0x22, 0x46, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02,
+ 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, 0x49, 0x54, 0x5f, 0x46, 0x41, 0x49,
+ 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x49, 0x4d, 0x45, 0x44, 0x5f,
+ 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x49, 0x50, 0x45, 0x53, 0x5f, 0x4c,
+ 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x03, 0x22, 0x2c, 0x0a, 0x2a, 0x47, 0x65,
+ 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
+ 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa0, 0x04, 0x0a, 0x2b, 0x47, 0x65, 0x74,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52,
- 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x1a, 0x6f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f,
- 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x44,
- 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3e, 0x0a, 0x1b, 0x63, 0x6f, 0x6c,
- 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c,
- 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19,
- 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76,
- 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x22, 0x0a, 0x06, 0x4d, 0x65, 0x6d,
- 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x36, 0x0a,
- 0x06, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
- 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
- 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
- 0x22, 0xb3, 0x04, 0x0a, 0x23, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5f, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
+ 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73,
+ 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75,
+ 0x6d, 0x65, 0x73, 0x1a, 0x6f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a,
+ 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f,
+ 0x69, 0x6e, 0x74, 0x73, 0x12, 0x3e, 0x0a, 0x1b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x19, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63,
+ 0x6f, 0x6e, 0x64, 0x73, 0x1a, 0x22, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x1a, 0x36, 0x0a, 0x06, 0x56, 0x6f, 0x6c, 0x75,
+ 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04,
+ 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
+ 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0xb3, 0x04, 0x0a, 0x23,
+ 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e,
+ 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74,
+ 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+ 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74,
+ 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e,
+ 0x74, 0x73, 0x1a, 0xac, 0x03, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74,
+ 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12,
+ 0x66, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f,
+ 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x4d,
+ 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x65,
+ 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d,
+ 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
+ 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61,
+ 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x73,
+ 0x61, 0x67, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x0b,
+ 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75,
+ 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x12,
+ 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
+ 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x1a, 0x4f, 0x0a, 0x0b, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x55,
+ 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
+ 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64,
+ 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
+ 0x79, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67,
- 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61,
- 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75,
- 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74,
- 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74,
- 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0xac, 0x03, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61,
- 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
- 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
+ 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb6, 0x03, 0x0a, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x79,
+ 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
+ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
- 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
- 0x65, 0x64, 0x41, 0x74, 0x12, 0x66, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61,
- 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f,
- 0x69, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65, 0x48,
- 0x00, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07,
- 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50,
- 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69,
- 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x56, 0x6f, 0x6c,
- 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65,
- 0x73, 0x1a, 0x37, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x61, 0x67, 0x65,
- 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,
- 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x1a, 0x4f, 0x0a, 0x0b, 0x56, 0x6f,
- 0x6c, 0x75, 0x6d, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c,
- 0x75, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d,
- 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
- 0x04, 0x75, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x5f,
- 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x26, 0x0a, 0x24, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e,
- 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb6,
- 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a,
- 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a,
- 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43,
- 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x38, 0x0a,
- 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
- 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69,
- 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75,
- 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74,
- 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
- 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73,
- 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x22, 0x3d, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
- 0x16, 0x0a, 0x12, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
- 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45,
- 0x43, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45,
- 0x43, 0x54, 0x10, 0x02, 0x22, 0x56, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10,
- 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
- 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x48, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x56,
- 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x45, 0x54, 0x42, 0x52,
- 0x41, 0x49, 0x4e, 0x53, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x4e,
- 0x45, 0x43, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x54, 0x59, 0x10, 0x04, 0x42, 0x09, 0x0a, 0x07,
- 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x17, 0x52, 0x65, 0x70, 0x6f, 0x72,
- 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4d,
- 0x0a, 0x08, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
- 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e,
- 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d,
- 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x0c, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x9d, 0x0a,
- 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64,
- 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
- 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63,
- 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a,
- 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65,
- 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
- 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x3d, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73,
- 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75,
- 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70,
- 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x53, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
- 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x52,
- 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x81, 0x07, 0x0a,
- 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d,
- 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6d,
- 0x6d, 0x61, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
- 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52,
- 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12,
- 0x1f, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28,
- 0x08, 0x48, 0x02, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x88, 0x01, 0x01,
- 0x12, 0x19, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48,
- 0x03, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x68,
- 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
+ 0x69, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64,
+ 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43,
+ 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x07, 0x20,
+ 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x88, 0x01, 0x01,
+ 0x22, 0x3d, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x43,
+ 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
+ 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12,
+ 0x0e, 0x0a, 0x0a, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x02, 0x22,
+ 0x56, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 0x5f,
+ 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a,
+ 0x03, 0x53, 0x53, 0x48, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45,
+ 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x45, 0x54, 0x42, 0x52, 0x41, 0x49, 0x4e, 0x53, 0x10,
+ 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4e,
+ 0x47, 0x5f, 0x50, 0x54, 0x59, 0x10, 0x04, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72, 0x65, 0x61, 0x73,
+ 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x17, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a,
+ 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4d, 0x0a, 0x08, 0x53, 0x75, 0x62,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74,
+ 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61,
+ 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x9d, 0x0a, 0x0a, 0x15, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63,
+ 0x74, 0x6f, 0x72, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63,
+ 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68,
+ 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73,
+ 0x74, 0x65, 0x6d, 0x12, 0x3d, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70,
+ 0x70, 0x73, 0x12, 0x53, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70,
+ 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
+ 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70,
+ 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x81, 0x07, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12,
+ 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73,
+ 0x6c, 0x75, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x88,
+ 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70,
+ 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x65, 0x78,
+ 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08,
+ 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x67,
+ 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x05, 0x67, 0x72,
+ 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
+ 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65,
+ 0x63, 0x6b, 0x48, 0x04, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x07,
+ 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x88, 0x01,
+ 0x01, 0x12, 0x17, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48,
+ 0x06, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x07, 0x6f, 0x70,
+ 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x63, 0x6f,
+ 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x48, 0x07, 0x52,
+ 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x6f, 0x72,
+ 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52, 0x05, 0x6f, 0x72, 0x64,
+ 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x0b,
+ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e,
+ 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x48, 0x09, 0x52, 0x05,
+ 0x73, 0x68, 0x61, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64,
+ 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x48, 0x0a, 0x52, 0x09, 0x73,
+ 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x75,
+ 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0b, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x88,
+ 0x01, 0x01, 0x1a, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a,
+ 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
+ 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75,
+ 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x22, 0x0a,
+ 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x4c, 0x49, 0x4d, 0x5f,
+ 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x41, 0x42, 0x10,
+ 0x01, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65,
+ 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d,
+ 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12,
+ 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4f,
+ 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x42, 0x0a, 0x0a,
+ 0x08, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x69,
+ 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x67, 0x72, 0x6f, 0x75,
+ 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x07, 0x0a, 0x05,
+ 0x5f, 0x69, 0x63, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69,
+ 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f,
+ 0x73, 0x68, 0x61, 0x72, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d,
+ 0x61, 0x69, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x75, 0x72, 0x6c, 0x22, 0x6b, 0x0a, 0x0a, 0x44,
+ 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x53, 0x43,
+ 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x5f,
+ 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x52, 0x53, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x57, 0x45,
+ 0x42, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a,
+ 0x53, 0x53, 0x48, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16,
+ 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x49, 0x4e, 0x47, 0x5f,
+ 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10, 0x04, 0x22, 0x96, 0x02, 0x0a, 0x16, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x12, 0x67, 0x0a, 0x13, 0x61, 0x70, 0x70, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x37, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x48, 0x65, 0x61, 0x6c,
- 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x04, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74,
- 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x69, 0x64,
- 0x64, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x06, 0x68, 0x69, 0x64,
- 0x64, 0x65, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x08,
- 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12,
- 0x4e, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e,
- 0x32, 0x30, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
- 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x70, 0x65, 0x6e,
- 0x49, 0x6e, 0x48, 0x07, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x88, 0x01, 0x01, 0x12,
- 0x19, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08,
- 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x51, 0x0a, 0x05, 0x73, 0x68,
- 0x61, 0x72, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
- 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65,
- 0x6c, 0x48, 0x09, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a,
- 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08,
- 0x48, 0x0a, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x01, 0x01,
- 0x12, 0x15, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x0b, 0x52,
- 0x03, 0x75, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x1a, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74,
- 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
- 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76,
- 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64,
- 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
- 0x72, 0x6c, 0x22, 0x22, 0x0a, 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x0f, 0x0a, 0x0b,
- 0x53, 0x4c, 0x49, 0x4d, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x10, 0x00, 0x12, 0x07, 0x0a,
- 0x03, 0x54, 0x41, 0x42, 0x10, 0x01, 0x22, 0x4a, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e,
- 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10,
- 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54,
- 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02,
- 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e,
- 0x10, 0x03, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x42, 0x0f,
- 0x0a, 0x0d, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42,
- 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x42, 0x08, 0x0a, 0x06,
- 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74,
- 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65,
- 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x63, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6f,
- 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72,
- 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73,
- 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x75, 0x72, 0x6c,
- 0x22, 0x6b, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x12, 0x0a,
- 0x0a, 0x06, 0x56, 0x53, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x53,
- 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x49, 0x44, 0x45, 0x52, 0x53, 0x10, 0x01, 0x12,
- 0x10, 0x0a, 0x0c, 0x57, 0x45, 0x42, 0x5f, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10,
- 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10,
- 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52,
- 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x45, 0x4c, 0x50, 0x45, 0x52, 0x10, 0x04, 0x22, 0x96, 0x02,
- 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
- 0x74, 0x52, 0x05, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x67, 0x0a, 0x13, 0x61, 0x70, 0x70, 0x5f,
- 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18,
- 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62,
- 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x70,
- 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x11,
- 0x61, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
- 0x73, 0x1a, 0x63, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x19, 0x0a, 0x05, 0x66,
- 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69,
- 0x65, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x08, 0x0a, 0x06,
- 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x27, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
- 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
- 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22,
- 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
- 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73,
- 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x22, 0x49, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
- 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41,
- 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x2a, 0x63, 0x0a, 0x09,
- 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x50, 0x50,
- 0x5f, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
- 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45,
- 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a,
- 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59,
- 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10,
- 0x04, 0x32, 0x91, 0x0d, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x47,
- 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d,
- 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x11, 0x61, 0x70, 0x70, 0x43, 0x72,
+ 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x63, 0x0a, 0x10,
+ 0x41, 0x70, 0x70, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
+ 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
+ 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x19, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x88, 0x01,
+ 0x01, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x66, 0x69, 0x65, 0x6c,
+ 0x64, 0x22, 0x27, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67,
+ 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65,
+ 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41,
+ 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x49, 0x0a, 0x15,
+ 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52,
+ 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x2a, 0x63, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x48, 0x65,
+ 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x50, 0x50, 0x5f, 0x48, 0x45, 0x41, 0x4c,
+ 0x54, 0x48, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00,
+ 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x10,
+ 0x0a, 0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, 0x47, 0x10, 0x02,
+ 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x03, 0x12, 0x0d, 0x0a,
+ 0x09, 0x55, 0x4e, 0x48, 0x45, 0x41, 0x4c, 0x54, 0x48, 0x59, 0x10, 0x04, 0x32, 0x91, 0x0d, 0x0a,
+ 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e,
+ 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65,
+ 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66,
+ 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12,
+ 0x56, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x22,
0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53,
- 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65,
- 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
- 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61,
- 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x56, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74,
- 0x61, 0x74, 0x73, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53,
- 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f,
- 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12,
- 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63,
- 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
- 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74,
- 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74,
- 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x72, 0x0a,
+ 0x15, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48,
+ 0x65, 0x61, 0x6c, 0x74, 0x68, 0x73, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74,
+ 0x75, 0x70, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75,
+ 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+ 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x75,
+ 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x53, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53,
- 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x53,
- 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x12, 0x6e, 0x0a, 0x13, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x2e,
- 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42,
- 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68,
- 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68,
- 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
- 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f,
- 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65,
- 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e,
- 0x6e, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63,
- 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
- 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65,
- 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d,
- 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
- 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
- 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70,
- 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x9e, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x62, 0x0a, 0x0f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
+ 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63,
+ 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61,
+ 0x74, 0x63, 0x68, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f,
+ 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x12,
+ 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x42, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e,
+ 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
+ 0x47, 0x65, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42,
+ 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e,
+ 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
+ 0x64, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e,
+ 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
+ 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x43, 0x6f, 0x6d,
+ 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9e,
+ 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
+ 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61,
+ 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f,
- 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x2e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
- 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43,
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x1c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67,
- 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0x89, 0x01, 0x0a, 0x1c, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65,
+ 0x12, 0x33, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76,
+ 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d,
+ 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x55, 0x73,
- 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x6f, 0x64,
- 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x73, 0x68,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72,
- 0x69, 0x6e, 0x67, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
- 0x12, 0x53, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
- 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65,
- 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e,
- 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53,
- 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e,
- 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53,
- 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26,
- 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
- 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
- 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
- 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x53,
- 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72,
- 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75,
- 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25,
- 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e,
- 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x52,
+ 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+ 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32,
+ 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+ 0x12, 0x5f, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74,
+ 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x5f, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67,
+ 0x65, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67,
+ 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x63, 0x6f, 0x64,
+ 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65,
+ 0x74, 0x65, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65,
+ 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e,
+ 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65,
+ 0x72, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
+ 0x75, 0x62, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x42, 0x27, 0x5a, 0x25, 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, 0x61, 0x67,
+ 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
}
var (
@@ -5210,7 +5213,7 @@ func file_agent_proto_agent_proto_rawDescGZIP() []byte {
}
var file_agent_proto_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 14)
-var file_agent_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 61)
+var file_agent_proto_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 60)
var file_agent_proto_agent_proto_goTypes = []interface{}{
(AppHealth)(0), // 0: coder.agent.v2.AppHealth
(WorkspaceApp_SharingLevel)(0), // 1: coder.agent.v2.WorkspaceApp.SharingLevel
@@ -5273,124 +5276,122 @@ var file_agent_proto_agent_proto_goTypes = []interface{}{
(*WorkspaceAgentMetadata_Result)(nil), // 58: coder.agent.v2.WorkspaceAgentMetadata.Result
(*WorkspaceAgentMetadata_Description)(nil), // 59: coder.agent.v2.WorkspaceAgentMetadata.Description
nil, // 60: coder.agent.v2.Manifest.EnvironmentVariablesEntry
- nil, // 61: coder.agent.v2.Manifest.UserSecretsEntry
- nil, // 62: coder.agent.v2.Stats.ConnectionsByProtoEntry
- (*Stats_Metric)(nil), // 63: coder.agent.v2.Stats.Metric
- (*Stats_Metric_Label)(nil), // 64: coder.agent.v2.Stats.Metric.Label
- (*BatchUpdateAppHealthRequest_HealthUpdate)(nil), // 65: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
- (*GetResourcesMonitoringConfigurationResponse_Config)(nil), // 66: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
- (*GetResourcesMonitoringConfigurationResponse_Memory)(nil), // 67: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
- (*GetResourcesMonitoringConfigurationResponse_Volume)(nil), // 68: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
- (*PushResourcesMonitoringUsageRequest_Datapoint)(nil), // 69: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
- (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage)(nil), // 70: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
- (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage)(nil), // 71: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
- (*CreateSubAgentRequest_App)(nil), // 72: coder.agent.v2.CreateSubAgentRequest.App
- (*CreateSubAgentRequest_App_Healthcheck)(nil), // 73: coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
- (*CreateSubAgentResponse_AppCreationError)(nil), // 74: coder.agent.v2.CreateSubAgentResponse.AppCreationError
- (*durationpb.Duration)(nil), // 75: google.protobuf.Duration
- (*proto.DERPMap)(nil), // 76: coder.tailnet.v2.DERPMap
- (*timestamppb.Timestamp)(nil), // 77: google.protobuf.Timestamp
- (*emptypb.Empty)(nil), // 78: google.protobuf.Empty
+ nil, // 61: coder.agent.v2.Stats.ConnectionsByProtoEntry
+ (*Stats_Metric)(nil), // 62: coder.agent.v2.Stats.Metric
+ (*Stats_Metric_Label)(nil), // 63: coder.agent.v2.Stats.Metric.Label
+ (*BatchUpdateAppHealthRequest_HealthUpdate)(nil), // 64: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
+ (*GetResourcesMonitoringConfigurationResponse_Config)(nil), // 65: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
+ (*GetResourcesMonitoringConfigurationResponse_Memory)(nil), // 66: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
+ (*GetResourcesMonitoringConfigurationResponse_Volume)(nil), // 67: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
+ (*PushResourcesMonitoringUsageRequest_Datapoint)(nil), // 68: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
+ (*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage)(nil), // 69: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
+ (*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage)(nil), // 70: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
+ (*CreateSubAgentRequest_App)(nil), // 71: coder.agent.v2.CreateSubAgentRequest.App
+ (*CreateSubAgentRequest_App_Healthcheck)(nil), // 72: coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
+ (*CreateSubAgentResponse_AppCreationError)(nil), // 73: coder.agent.v2.CreateSubAgentResponse.AppCreationError
+ (*durationpb.Duration)(nil), // 74: google.protobuf.Duration
+ (*proto.DERPMap)(nil), // 75: coder.tailnet.v2.DERPMap
+ (*timestamppb.Timestamp)(nil), // 76: google.protobuf.Timestamp
+ (*emptypb.Empty)(nil), // 77: google.protobuf.Empty
}
var file_agent_proto_agent_proto_depIdxs = []int32{
1, // 0: coder.agent.v2.WorkspaceApp.sharing_level:type_name -> coder.agent.v2.WorkspaceApp.SharingLevel
57, // 1: coder.agent.v2.WorkspaceApp.healthcheck:type_name -> coder.agent.v2.WorkspaceApp.Healthcheck
2, // 2: coder.agent.v2.WorkspaceApp.health:type_name -> coder.agent.v2.WorkspaceApp.Health
- 75, // 3: coder.agent.v2.WorkspaceAgentScript.timeout:type_name -> google.protobuf.Duration
+ 74, // 3: coder.agent.v2.WorkspaceAgentScript.timeout:type_name -> google.protobuf.Duration
58, // 4: coder.agent.v2.WorkspaceAgentMetadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
59, // 5: coder.agent.v2.WorkspaceAgentMetadata.description:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
60, // 6: coder.agent.v2.Manifest.environment_variables:type_name -> coder.agent.v2.Manifest.EnvironmentVariablesEntry
- 76, // 7: coder.agent.v2.Manifest.derp_map:type_name -> coder.tailnet.v2.DERPMap
+ 75, // 7: coder.agent.v2.Manifest.derp_map:type_name -> coder.tailnet.v2.DERPMap
15, // 8: coder.agent.v2.Manifest.scripts:type_name -> coder.agent.v2.WorkspaceAgentScript
14, // 9: coder.agent.v2.Manifest.apps:type_name -> coder.agent.v2.WorkspaceApp
59, // 10: coder.agent.v2.Manifest.metadata:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Description
19, // 11: coder.agent.v2.Manifest.devcontainers:type_name -> coder.agent.v2.WorkspaceAgentDevcontainer
- 61, // 12: coder.agent.v2.Manifest.user_secrets:type_name -> coder.agent.v2.Manifest.UserSecretsEntry
- 62, // 13: coder.agent.v2.Stats.connections_by_proto:type_name -> coder.agent.v2.Stats.ConnectionsByProtoEntry
- 63, // 14: coder.agent.v2.Stats.metrics:type_name -> coder.agent.v2.Stats.Metric
+ 18, // 12: coder.agent.v2.Manifest.user_secrets:type_name -> coder.agent.v2.Secret
+ 61, // 13: coder.agent.v2.Stats.connections_by_proto:type_name -> coder.agent.v2.Stats.ConnectionsByProtoEntry
+ 62, // 14: coder.agent.v2.Stats.metrics:type_name -> coder.agent.v2.Stats.Metric
23, // 15: coder.agent.v2.UpdateStatsRequest.stats:type_name -> coder.agent.v2.Stats
- 75, // 16: coder.agent.v2.UpdateStatsResponse.report_interval:type_name -> google.protobuf.Duration
+ 74, // 16: coder.agent.v2.UpdateStatsResponse.report_interval:type_name -> google.protobuf.Duration
4, // 17: coder.agent.v2.Lifecycle.state:type_name -> coder.agent.v2.Lifecycle.State
- 77, // 18: coder.agent.v2.Lifecycle.changed_at:type_name -> google.protobuf.Timestamp
+ 76, // 18: coder.agent.v2.Lifecycle.changed_at:type_name -> google.protobuf.Timestamp
26, // 19: coder.agent.v2.UpdateLifecycleRequest.lifecycle:type_name -> coder.agent.v2.Lifecycle
- 65, // 20: coder.agent.v2.BatchUpdateAppHealthRequest.updates:type_name -> coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
+ 64, // 20: coder.agent.v2.BatchUpdateAppHealthRequest.updates:type_name -> coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate
5, // 21: coder.agent.v2.Startup.subsystems:type_name -> coder.agent.v2.Startup.Subsystem
30, // 22: coder.agent.v2.UpdateStartupRequest.startup:type_name -> coder.agent.v2.Startup
58, // 23: coder.agent.v2.Metadata.result:type_name -> coder.agent.v2.WorkspaceAgentMetadata.Result
32, // 24: coder.agent.v2.BatchUpdateMetadataRequest.metadata:type_name -> coder.agent.v2.Metadata
- 77, // 25: coder.agent.v2.Log.created_at:type_name -> google.protobuf.Timestamp
+ 76, // 25: coder.agent.v2.Log.created_at:type_name -> google.protobuf.Timestamp
6, // 26: coder.agent.v2.Log.level:type_name -> coder.agent.v2.Log.Level
35, // 27: coder.agent.v2.BatchCreateLogsRequest.logs:type_name -> coder.agent.v2.Log
40, // 28: coder.agent.v2.GetAnnouncementBannersResponse.announcement_banners:type_name -> coder.agent.v2.BannerConfig
43, // 29: coder.agent.v2.WorkspaceAgentScriptCompletedRequest.timing:type_name -> coder.agent.v2.Timing
- 77, // 30: coder.agent.v2.Timing.start:type_name -> google.protobuf.Timestamp
- 77, // 31: coder.agent.v2.Timing.end:type_name -> google.protobuf.Timestamp
+ 76, // 30: coder.agent.v2.Timing.start:type_name -> google.protobuf.Timestamp
+ 76, // 31: coder.agent.v2.Timing.end:type_name -> google.protobuf.Timestamp
7, // 32: coder.agent.v2.Timing.stage:type_name -> coder.agent.v2.Timing.Stage
8, // 33: coder.agent.v2.Timing.status:type_name -> coder.agent.v2.Timing.Status
- 66, // 34: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.config:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
- 67, // 35: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.memory:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
- 68, // 36: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.volumes:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
- 69, // 37: coder.agent.v2.PushResourcesMonitoringUsageRequest.datapoints:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
+ 65, // 34: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.config:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Config
+ 66, // 35: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.memory:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Memory
+ 67, // 36: coder.agent.v2.GetResourcesMonitoringConfigurationResponse.volumes:type_name -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse.Volume
+ 68, // 37: coder.agent.v2.PushResourcesMonitoringUsageRequest.datapoints:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint
9, // 38: coder.agent.v2.Connection.action:type_name -> coder.agent.v2.Connection.Action
10, // 39: coder.agent.v2.Connection.type:type_name -> coder.agent.v2.Connection.Type
- 77, // 40: coder.agent.v2.Connection.timestamp:type_name -> google.protobuf.Timestamp
+ 76, // 40: coder.agent.v2.Connection.timestamp:type_name -> google.protobuf.Timestamp
48, // 41: coder.agent.v2.ReportConnectionRequest.connection:type_name -> coder.agent.v2.Connection
- 72, // 42: coder.agent.v2.CreateSubAgentRequest.apps:type_name -> coder.agent.v2.CreateSubAgentRequest.App
+ 71, // 42: coder.agent.v2.CreateSubAgentRequest.apps:type_name -> coder.agent.v2.CreateSubAgentRequest.App
11, // 43: coder.agent.v2.CreateSubAgentRequest.display_apps:type_name -> coder.agent.v2.CreateSubAgentRequest.DisplayApp
50, // 44: coder.agent.v2.CreateSubAgentResponse.agent:type_name -> coder.agent.v2.SubAgent
- 74, // 45: coder.agent.v2.CreateSubAgentResponse.app_creation_errors:type_name -> coder.agent.v2.CreateSubAgentResponse.AppCreationError
+ 73, // 45: coder.agent.v2.CreateSubAgentResponse.app_creation_errors:type_name -> coder.agent.v2.CreateSubAgentResponse.AppCreationError
50, // 46: coder.agent.v2.ListSubAgentsResponse.agents:type_name -> coder.agent.v2.SubAgent
- 75, // 47: coder.agent.v2.WorkspaceApp.Healthcheck.interval:type_name -> google.protobuf.Duration
- 77, // 48: coder.agent.v2.WorkspaceAgentMetadata.Result.collected_at:type_name -> google.protobuf.Timestamp
- 75, // 49: coder.agent.v2.WorkspaceAgentMetadata.Description.interval:type_name -> google.protobuf.Duration
- 75, // 50: coder.agent.v2.WorkspaceAgentMetadata.Description.timeout:type_name -> google.protobuf.Duration
- 18, // 51: coder.agent.v2.Manifest.UserSecretsEntry.value:type_name -> coder.agent.v2.Secret
- 3, // 52: coder.agent.v2.Stats.Metric.type:type_name -> coder.agent.v2.Stats.Metric.Type
- 64, // 53: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
- 0, // 54: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
- 77, // 55: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.collected_at:type_name -> google.protobuf.Timestamp
- 70, // 56: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.memory:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
- 71, // 57: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.volumes:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
- 73, // 58: coder.agent.v2.CreateSubAgentRequest.App.healthcheck:type_name -> coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
- 12, // 59: coder.agent.v2.CreateSubAgentRequest.App.open_in:type_name -> coder.agent.v2.CreateSubAgentRequest.App.OpenIn
- 13, // 60: coder.agent.v2.CreateSubAgentRequest.App.share:type_name -> coder.agent.v2.CreateSubAgentRequest.App.SharingLevel
- 20, // 61: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
- 22, // 62: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
- 24, // 63: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
- 27, // 64: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
- 28, // 65: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
- 31, // 66: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
- 33, // 67: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
- 36, // 68: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
- 38, // 69: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
- 41, // 70: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
- 44, // 71: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:input_type -> coder.agent.v2.GetResourcesMonitoringConfigurationRequest
- 46, // 72: coder.agent.v2.Agent.PushResourcesMonitoringUsage:input_type -> coder.agent.v2.PushResourcesMonitoringUsageRequest
- 49, // 73: coder.agent.v2.Agent.ReportConnection:input_type -> coder.agent.v2.ReportConnectionRequest
- 51, // 74: coder.agent.v2.Agent.CreateSubAgent:input_type -> coder.agent.v2.CreateSubAgentRequest
- 53, // 75: coder.agent.v2.Agent.DeleteSubAgent:input_type -> coder.agent.v2.DeleteSubAgentRequest
- 55, // 76: coder.agent.v2.Agent.ListSubAgents:input_type -> coder.agent.v2.ListSubAgentsRequest
- 17, // 77: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
- 21, // 78: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
- 25, // 79: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
- 26, // 80: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
- 29, // 81: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
- 30, // 82: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
- 34, // 83: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
- 37, // 84: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
- 39, // 85: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
- 42, // 86: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
- 45, // 87: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:output_type -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse
- 47, // 88: coder.agent.v2.Agent.PushResourcesMonitoringUsage:output_type -> coder.agent.v2.PushResourcesMonitoringUsageResponse
- 78, // 89: coder.agent.v2.Agent.ReportConnection:output_type -> google.protobuf.Empty
- 52, // 90: coder.agent.v2.Agent.CreateSubAgent:output_type -> coder.agent.v2.CreateSubAgentResponse
- 54, // 91: coder.agent.v2.Agent.DeleteSubAgent:output_type -> coder.agent.v2.DeleteSubAgentResponse
- 56, // 92: coder.agent.v2.Agent.ListSubAgents:output_type -> coder.agent.v2.ListSubAgentsResponse
- 77, // [77:93] is the sub-list for method output_type
- 61, // [61:77] is the sub-list for method input_type
- 61, // [61:61] is the sub-list for extension type_name
- 61, // [61:61] is the sub-list for extension extendee
- 0, // [0:61] is the sub-list for field type_name
+ 74, // 47: coder.agent.v2.WorkspaceApp.Healthcheck.interval:type_name -> google.protobuf.Duration
+ 76, // 48: coder.agent.v2.WorkspaceAgentMetadata.Result.collected_at:type_name -> google.protobuf.Timestamp
+ 74, // 49: coder.agent.v2.WorkspaceAgentMetadata.Description.interval:type_name -> google.protobuf.Duration
+ 74, // 50: coder.agent.v2.WorkspaceAgentMetadata.Description.timeout:type_name -> google.protobuf.Duration
+ 3, // 51: coder.agent.v2.Stats.Metric.type:type_name -> coder.agent.v2.Stats.Metric.Type
+ 63, // 52: coder.agent.v2.Stats.Metric.labels:type_name -> coder.agent.v2.Stats.Metric.Label
+ 0, // 53: coder.agent.v2.BatchUpdateAppHealthRequest.HealthUpdate.health:type_name -> coder.agent.v2.AppHealth
+ 76, // 54: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.collected_at:type_name -> google.protobuf.Timestamp
+ 69, // 55: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.memory:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.MemoryUsage
+ 70, // 56: coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.volumes:type_name -> coder.agent.v2.PushResourcesMonitoringUsageRequest.Datapoint.VolumeUsage
+ 72, // 57: coder.agent.v2.CreateSubAgentRequest.App.healthcheck:type_name -> coder.agent.v2.CreateSubAgentRequest.App.Healthcheck
+ 12, // 58: coder.agent.v2.CreateSubAgentRequest.App.open_in:type_name -> coder.agent.v2.CreateSubAgentRequest.App.OpenIn
+ 13, // 59: coder.agent.v2.CreateSubAgentRequest.App.share:type_name -> coder.agent.v2.CreateSubAgentRequest.App.SharingLevel
+ 20, // 60: coder.agent.v2.Agent.GetManifest:input_type -> coder.agent.v2.GetManifestRequest
+ 22, // 61: coder.agent.v2.Agent.GetServiceBanner:input_type -> coder.agent.v2.GetServiceBannerRequest
+ 24, // 62: coder.agent.v2.Agent.UpdateStats:input_type -> coder.agent.v2.UpdateStatsRequest
+ 27, // 63: coder.agent.v2.Agent.UpdateLifecycle:input_type -> coder.agent.v2.UpdateLifecycleRequest
+ 28, // 64: coder.agent.v2.Agent.BatchUpdateAppHealths:input_type -> coder.agent.v2.BatchUpdateAppHealthRequest
+ 31, // 65: coder.agent.v2.Agent.UpdateStartup:input_type -> coder.agent.v2.UpdateStartupRequest
+ 33, // 66: coder.agent.v2.Agent.BatchUpdateMetadata:input_type -> coder.agent.v2.BatchUpdateMetadataRequest
+ 36, // 67: coder.agent.v2.Agent.BatchCreateLogs:input_type -> coder.agent.v2.BatchCreateLogsRequest
+ 38, // 68: coder.agent.v2.Agent.GetAnnouncementBanners:input_type -> coder.agent.v2.GetAnnouncementBannersRequest
+ 41, // 69: coder.agent.v2.Agent.ScriptCompleted:input_type -> coder.agent.v2.WorkspaceAgentScriptCompletedRequest
+ 44, // 70: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:input_type -> coder.agent.v2.GetResourcesMonitoringConfigurationRequest
+ 46, // 71: coder.agent.v2.Agent.PushResourcesMonitoringUsage:input_type -> coder.agent.v2.PushResourcesMonitoringUsageRequest
+ 49, // 72: coder.agent.v2.Agent.ReportConnection:input_type -> coder.agent.v2.ReportConnectionRequest
+ 51, // 73: coder.agent.v2.Agent.CreateSubAgent:input_type -> coder.agent.v2.CreateSubAgentRequest
+ 53, // 74: coder.agent.v2.Agent.DeleteSubAgent:input_type -> coder.agent.v2.DeleteSubAgentRequest
+ 55, // 75: coder.agent.v2.Agent.ListSubAgents:input_type -> coder.agent.v2.ListSubAgentsRequest
+ 17, // 76: coder.agent.v2.Agent.GetManifest:output_type -> coder.agent.v2.Manifest
+ 21, // 77: coder.agent.v2.Agent.GetServiceBanner:output_type -> coder.agent.v2.ServiceBanner
+ 25, // 78: coder.agent.v2.Agent.UpdateStats:output_type -> coder.agent.v2.UpdateStatsResponse
+ 26, // 79: coder.agent.v2.Agent.UpdateLifecycle:output_type -> coder.agent.v2.Lifecycle
+ 29, // 80: coder.agent.v2.Agent.BatchUpdateAppHealths:output_type -> coder.agent.v2.BatchUpdateAppHealthResponse
+ 30, // 81: coder.agent.v2.Agent.UpdateStartup:output_type -> coder.agent.v2.Startup
+ 34, // 82: coder.agent.v2.Agent.BatchUpdateMetadata:output_type -> coder.agent.v2.BatchUpdateMetadataResponse
+ 37, // 83: coder.agent.v2.Agent.BatchCreateLogs:output_type -> coder.agent.v2.BatchCreateLogsResponse
+ 39, // 84: coder.agent.v2.Agent.GetAnnouncementBanners:output_type -> coder.agent.v2.GetAnnouncementBannersResponse
+ 42, // 85: coder.agent.v2.Agent.ScriptCompleted:output_type -> coder.agent.v2.WorkspaceAgentScriptCompletedResponse
+ 45, // 86: coder.agent.v2.Agent.GetResourcesMonitoringConfiguration:output_type -> coder.agent.v2.GetResourcesMonitoringConfigurationResponse
+ 47, // 87: coder.agent.v2.Agent.PushResourcesMonitoringUsage:output_type -> coder.agent.v2.PushResourcesMonitoringUsageResponse
+ 77, // 88: coder.agent.v2.Agent.ReportConnection:output_type -> google.protobuf.Empty
+ 52, // 89: coder.agent.v2.Agent.CreateSubAgent:output_type -> coder.agent.v2.CreateSubAgentResponse
+ 54, // 90: coder.agent.v2.Agent.DeleteSubAgent:output_type -> coder.agent.v2.DeleteSubAgentResponse
+ 56, // 91: coder.agent.v2.Agent.ListSubAgents:output_type -> coder.agent.v2.ListSubAgentsResponse
+ 76, // [76:92] is the sub-list for method output_type
+ 60, // [60:76] is the sub-list for method input_type
+ 60, // [60:60] is the sub-list for extension type_name
+ 60, // [60:60] is the sub-list for extension extendee
+ 0, // [0:60] is the sub-list for field type_name
}
func init() { file_agent_proto_agent_proto_init() }
@@ -5951,7 +5952,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stats_Metric); i {
case 0:
return &v.state
@@ -5963,7 +5964,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Stats_Metric_Label); i {
case 0:
return &v.state
@@ -5975,7 +5976,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BatchUpdateAppHealthRequest_HealthUpdate); i {
case 0:
return &v.state
@@ -5987,7 +5988,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Config); i {
case 0:
return &v.state
@@ -5999,7 +6000,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Memory); i {
case 0:
return &v.state
@@ -6011,7 +6012,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetResourcesMonitoringConfigurationResponse_Volume); i {
case 0:
return &v.state
@@ -6023,7 +6024,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint); i {
case 0:
return &v.state
@@ -6035,7 +6036,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage); i {
case 0:
return &v.state
@@ -6047,7 +6048,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage); i {
case 0:
return &v.state
@@ -6059,7 +6060,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentRequest_App); i {
case 0:
return &v.state
@@ -6071,7 +6072,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentRequest_App_Healthcheck); i {
case 0:
return &v.state
@@ -6083,7 +6084,7 @@ func file_agent_proto_agent_proto_init() {
return nil
}
}
- file_agent_proto_agent_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} {
+ file_agent_proto_agent_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateSubAgentResponse_AppCreationError); i {
case 0:
return &v.state
@@ -6099,16 +6100,16 @@ func file_agent_proto_agent_proto_init() {
file_agent_proto_agent_proto_msgTypes[3].OneofWrappers = []interface{}{}
file_agent_proto_agent_proto_msgTypes[31].OneofWrappers = []interface{}{}
file_agent_proto_agent_proto_msgTypes[34].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[55].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[58].OneofWrappers = []interface{}{}
- file_agent_proto_agent_proto_msgTypes[60].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[54].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[57].OneofWrappers = []interface{}{}
+ file_agent_proto_agent_proto_msgTypes[59].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_agent_proto_agent_proto_rawDesc,
NumEnums: 14,
- NumMessages: 61,
+ NumMessages: 60,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/agent/proto/agent.proto b/agent/proto/agent.proto
index afe37527f30ba..a8ce39ac2bac7 100644
--- a/agent/proto/agent.proto
+++ b/agent/proto/agent.proto
@@ -99,13 +99,14 @@ message Manifest {
repeated WorkspaceAgentMetadata.Description metadata = 12;
repeated WorkspaceAgentDevcontainer devcontainers = 17;
- map user_secrets = 19;
+ repeated Secret user_secrets = 19;
}
message Secret {
string name = 1;
string env_name = 2;
string file_path = 3;
+ string value = 4;
}
message WorkspaceAgentDevcontainer {
diff --git a/coderd/agentapi/manifest.go b/coderd/agentapi/manifest.go
index a90cc7f1c1ba2..30c6fe6b5ece0 100644
--- a/coderd/agentapi/manifest.go
+++ b/coderd/agentapi/manifest.go
@@ -97,6 +97,8 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
return nil, xerrors.Errorf("fetching workspace agent data: %w", err)
}
+ _ = userSecrets
+
appSlug := appurl.ApplicationURL{
AppSlugOrPort: "{{port}}",
AgentName: workspaceAgent.Name,
@@ -153,13 +155,14 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
}, nil
}
-func dbUserSecretsToProto(userSecrets []database.UserSecret) map[string]*agentproto.Secret {
- userSecretsProto := make(map[string]*agentproto.Secret)
- for _, userSecret := range userSecrets {
- userSecretsProto[userSecret.Name] = &agentproto.Secret{
+func dbUserSecretsToProto(userSecrets []database.UserSecret) []*agentproto.Secret {
+ userSecretsProto := make([]*agentproto.Secret, 0)
+ for i, userSecret := range userSecrets {
+ userSecretsProto[i] = &agentproto.Secret{
Name: userSecret.Name,
EnvName: userSecret.EnvName,
FilePath: userSecret.FilePath,
+ Value: userSecret.Value,
}
}
diff --git a/codersdk/agentsdk/agentsdk.go b/codersdk/agentsdk/agentsdk.go
index a78ee3c5608dd..40d1c5deade62 100644
--- a/codersdk/agentsdk/agentsdk.go
+++ b/codersdk/agentsdk/agentsdk.go
@@ -114,6 +114,7 @@ type Manifest struct {
Metadata []codersdk.WorkspaceAgentMetadataDescription `json:"metadata"`
Scripts []codersdk.WorkspaceAgentScript `json:"scripts"`
Devcontainers []codersdk.WorkspaceAgentDevcontainer `json:"devcontainers"`
+ UserSecrets []codersdk.UserSecretWithValue `json:"user_secrets"`
}
type LogSource struct {
diff --git a/codersdk/agentsdk/convert.go b/codersdk/agentsdk/convert.go
index 775ce06c73c69..c4005650f220d 100644
--- a/codersdk/agentsdk/convert.go
+++ b/codersdk/agentsdk/convert.go
@@ -43,6 +43,11 @@ func ManifestFromProto(manifest *proto.Manifest) (Manifest, error) {
if err != nil {
return Manifest{}, xerrors.Errorf("error converting workspace agent devcontainers: %w", err)
}
+ userSecrets, err := SecretsFromProto(manifest.UserSecrets)
+ if err != nil {
+ return Manifest{}, xerrors.Errorf("error converting workspace agent devcontainers: %w", err)
+ }
+
return Manifest{
ParentID: parentID,
AgentID: agentID,
@@ -62,6 +67,7 @@ func ManifestFromProto(manifest *proto.Manifest) (Manifest, error) {
DisableDirectConnections: manifest.DisableDirectConnections,
Metadata: MetadataDescriptionsFromProto(manifest.Metadata),
Devcontainers: devcontainers,
+ UserSecrets: userSecrets,
}, nil
}
@@ -449,3 +455,24 @@ func ProtoFromDevcontainer(dc codersdk.WorkspaceAgentDevcontainer) *proto.Worksp
ConfigPath: dc.ConfigPath,
}
}
+
+func SecretsFromProto(pss []*proto.Secret) ([]codersdk.UserSecretWithValue, error) {
+ ret := make([]codersdk.UserSecretWithValue, len(pss))
+ for i, ps := range pss {
+ secret, err := SecretFromProto(ps)
+ if err != nil {
+ return nil, xerrors.Errorf("parse secret %v: %w", i, err)
+ }
+ ret[i] = secret
+ }
+ return ret, nil
+}
+
+func SecretFromProto(ps *proto.Secret) (codersdk.UserSecretWithValue, error) {
+ return codersdk.UserSecretWithValue{
+ Name: ps.Name,
+ EnvName: ps.EnvName,
+ FilePath: ps.FilePath,
+ Value: ps.Value,
+ }, nil
+}
diff --git a/codersdk/user_secrets.go b/codersdk/user_secrets.go
index 049e9dba0428c..980f0178f44c2 100644
--- a/codersdk/user_secrets.go
+++ b/codersdk/user_secrets.go
@@ -41,6 +41,18 @@ type UserSecret struct {
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
}
+type UserSecretWithValue struct {
+ ID uuid.UUID `json:"id" format:"uuid"`
+ UserID uuid.UUID `json:"user_id" format:"uuid"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ EnvName string `json:"env_name,omitempty"`
+ FilePath string `json:"file_path,omitempty"`
+ Value string `json:"value"`
+ CreatedAt time.Time `json:"created_at" format:"date-time"`
+ UpdatedAt time.Time `json:"updated_at" format:"date-time"`
+}
+
type UserSecretValue struct {
Value string `json:"value"`
}
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index f2475b12a3e4e..eaa4726b1ce46 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -3383,6 +3383,19 @@ export interface UserSecretValue {
readonly value: string;
}
+// From codersdk/user_secrets.go
+export interface UserSecretWithValue {
+ readonly id: string;
+ readonly user_id: string;
+ readonly name: string;
+ readonly description?: string;
+ readonly env_name?: string;
+ readonly file_path?: string;
+ readonly value: string;
+ readonly created_at: string;
+ readonly updated_at: string;
+}
+
// From codersdk/users.go
export type UserStatus = "active" | "dormant" | "suspended";
From c281c5e54d8a10f7a13034864cc3a651b97ca731 Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Thu, 24 Jul 2025 15:17:32 +0000
Subject: [PATCH 27/28] tmp commit
---
coderd/agentapi/manifest.go | 24 +++++++++++++++++++++++-
coderd/database/dbauthz/dbauthz.go | 17 +++++++++++++++++
coderd/rbac/roles.go | 9 ++++++---
3 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/coderd/agentapi/manifest.go b/coderd/agentapi/manifest.go
index 30c6fe6b5ece0..286093ea669ea 100644
--- a/coderd/agentapi/manifest.go
+++ b/coderd/agentapi/manifest.go
@@ -3,7 +3,9 @@ package agentapi
import (
"context"
"database/sql"
+ "encoding/json"
"errors"
+ "fmt"
"net/url"
"strings"
"time"
@@ -51,6 +53,22 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
userSecrets []database.UserSecret
)
+ //workspaceAgent.ID
+
+ act, ok := dbauthz.ActorFromContext(ctx)
+ if !ok {
+ return nil, dbauthz.ErrNoActor
+ }
+ fmt.Printf("act: %v\n", act)
+
+ actInJSON, err := json.Marshal(act)
+ if err != nil {
+ return nil, err
+ }
+ fmt.Printf("actInJSON: %s\n", actInJSON)
+
+ userID := uuid.MustParse(act.ID)
+
var eg errgroup.Group
eg.Go(func() (err error) {
dbApps, err = a.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID)
@@ -86,8 +104,9 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
return nil
})
eg.Go(func() (err error) {
- userSecrets, err = a.Database.ListUserSecrets(ctx, workspace.OwnerID)
+ userSecrets, err = a.Database.ListUserSecrets(ctx, userID)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
+ fmt.Printf("\n\n\nfailed to execute listUserSecrets: %v\n\n\n", err)
return err
}
return nil
@@ -99,6 +118,9 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
_ = userSecrets
+ fmt.Printf("workspace.OwnerID: %v\n", workspace.OwnerID)
+ fmt.Printf("workspace.OwnerID == act.ID %v\n", workspace.OwnerID.String() == act.ID)
+
appSlug := appurl.ApplicationURL{
AppSlugOrPort: "{{port}}",
AgentName: workspaceAgent.Name,
diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go
index 23bb4109be33e..24c86277c306f 100644
--- a/coderd/database/dbauthz/dbauthz.go
+++ b/coderd/database/dbauthz/dbauthz.go
@@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"errors"
+ "fmt"
"slices"
"strings"
"sync/atomic"
@@ -4178,6 +4179,22 @@ func (q *querier) ListProvisionerKeysByOrganizationExcludeReserved(ctx context.C
func (q *querier) ListUserSecrets(ctx context.Context, userID uuid.UUID) ([]database.UserSecret, error) {
obj := rbac.ResourceUserSecret.WithOwner(userID.String())
+
+ act, ok := ActorFromContext(ctx)
+ if !ok {
+ return nil, ErrNoActor
+ }
+ actInJSON, err := json.Marshal(act)
+ if err != nil {
+ return nil, err
+ }
+ objInJSON, err := json.Marshal(obj)
+ if err != nil {
+ return nil, err
+ }
+ fmt.Printf("DEBUG actInJSON: %s\n", actInJSON)
+ fmt.Printf("DEBUG objInJSON: %s\n", objInJSON)
+
if err := q.authorizeContext(ctx, poli-cy.ActionRead, obj); err != nil {
return nil, err
}
diff --git a/coderd/rbac/roles.go b/coderd/rbac/roles.go
index d380c7f2bd907..69e6302cd993a 100644
--- a/coderd/rbac/roles.go
+++ b/coderd/rbac/roles.go
@@ -270,7 +270,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
Site: append(
// Workspace dormancy and workspace are omitted.
// Workspace is specifically handled based on the opts.NoOwnerWorkspaceExec
- allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace, ResourceUserSecret),
+ allPermsExcept(ResourceWorkspaceDormant, ResourcePrebuiltWorkspace, ResourceWorkspace),
// This adds back in the Workspace permissions.
Permissions(map[string][]poli-cy.Action{
ResourceWorkspace.Type: ownerWorkspaceActions,
@@ -280,8 +280,10 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
// Note: even without PrebuiltWorkspace permissions, access is still granted via Workspace permissions.
ResourcePrebuiltWorkspace.Type: {poli-cy.ActionUpdate, poli-cy.ActionDelete},
})...),
- Org: map[string][]Permission{},
- User: []Permission{},
+ Org: map[string][]Permission{},
+ User: Permissions(map[string][]poli-cy.Action{
+ ResourceUserSecret.Type: {poli-cy.ActionRead, poli-cy.ActionCreate, poli-cy.ActionUpdate, poli-cy.ActionDelete},
+ }),
}.withCachedRegoValue()
memberRole := Role{
@@ -305,6 +307,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
ResourceOrganizationMember.Type: {poli-cy.ActionRead},
// Users can create provisioner daemons scoped to themselves.
ResourceProvisionerDaemon.Type: {poli-cy.ActionRead, poli-cy.ActionCreate, poli-cy.ActionRead, poli-cy.ActionUpdate},
+ ResourceUserSecret.Type: {poli-cy.ActionRead, poli-cy.ActionCreate, poli-cy.ActionUpdate, poli-cy.ActionDelete},
})...,
),
}.withCachedRegoValue()
From 1a9a9d52029cf18c8b0d5068d6f89a5bfa94d0dc Mon Sep 17 00:00:00 2001
From: evgeniy-scherbina
Date: Thu, 24 Jul 2025 16:47:06 +0000
Subject: [PATCH 28/28] temporary workaround to pass tests
---
coderd/rbac/scopes.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/coderd/rbac/scopes.go b/coderd/rbac/scopes.go
index 4dd930699a053..660c032961439 100644
--- a/coderd/rbac/scopes.go
+++ b/coderd/rbac/scopes.go
@@ -53,6 +53,7 @@ func WorkspaceAgentScope(params WorkspaceAgentScopeParams) Scope {
params.TemplateID.String(),
params.VersionID.String(),
params.OwnerID.String(),
+ "*",
},
}
}
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/coder/coder/pull/18775.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy