From 8382ff4c27268c25d75083684ec41137e6ffad86 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Sat, 8 Jan 2022 07:39:21 +0000 Subject: [PATCH 1/4] =?UTF-8?q?[server]=C2=A0allow=20to=20fetch=20owner=20?= =?UTF-8?q?token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitpod-protocol/src/gitpod-service.ts | 6 +++++ components/server/src/auth/rate-limiter.ts | 1 + .../src/workspace/gitpod-server-impl.ts | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index f659fe54ce29c5..b1a51a895681f3 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -71,8 +71,14 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, getWorkspaceOwner(workspaceId: string): Promise; getWorkspaceUsers(workspaceId: string): Promise; getFeaturedRepositories(): Promise; + /** + * **Security:** + * Sensitive information like an owner token is erased, since it allows access for all team members. + * If you need to access an owner token use `getOwnerToken` instead. + */ getWorkspace(id: string): Promise; isWorkspaceOwner(workspaceId: string): Promise; + getOwnerToken(workspaceId: string): Promise; /** * Creates and starts a workspace for the given context URL. diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts index f946e65136a5bb..3440fc220ab3cd 100644 --- a/components/server/src/auth/rate-limiter.ts +++ b/components/server/src/auth/rate-limiter.ts @@ -57,6 +57,7 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig { "getFeaturedRepositories": { group: "default", points: 1 }, "getWorkspace": { group: "default", points: 1 }, "isWorkspaceOwner": { group: "default", points: 1 }, + "getOwnerToken": { group: "default", points: 1 }, "createWorkspace": { group: "default", points: 1 }, "startWorkspace": { group: "default", points: 1 }, "stopWorkspace": { group: "default", points: 1 }, diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 3afaf2b1995ef4..5023a29215cbd5 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -443,6 +443,28 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { }; } + public async getOwnerToken(ctx: TraceContext, workspaceId: string): Promise { + traceAPIParams(ctx, { workspaceId }); + traceWI(ctx, { workspaceId }); + + this.checkUser('getOwnerToken'); + + const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId); + if (!workspace) { + throw new Error('owner token not found'); + } + await this.guardAccess({ kind: "workspace", subject: workspace }, "get"); + + const latestInstance = await this.workspaceDb.trace(ctx).findCurrentInstance(workspaceId); + this.guardAccess({ kind: "workspaceInstance", subject: latestInstance, workspace}, "get"); + + const ownerToken = latestInstance?.status.ownerToken; + if (!ownerToken) { + throw new Error('owner token not found'); + } + return ownerToken; + } + public async startWorkspace(ctx: TraceContext, workspaceId: string, options: GitpodServer.StartWorkspaceOptions): Promise { traceAPIParams(ctx, { workspaceId, options }); traceWI(ctx, { workspaceId }); From 332fff52e698a61de0a973621e6b751907824146 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Sat, 8 Jan 2022 07:39:47 +0000 Subject: [PATCH 2/4] [server] add JB Gateway plugin as ouath2 client --- components/server/src/oauth-server/db.ts | 45 +++++++++++++++++------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/components/server/src/oauth-server/db.ts b/components/server/src/oauth-server/db.ts index 4eed0cc74f97f4..fddc8bd8278966 100644 --- a/components/server/src/oauth-server/db.ts +++ b/components/server/src/oauth-server/db.ts @@ -16,15 +16,6 @@ export interface InMemory { scopes: { [id: string]: OAuthScope }; } -// Scopes -const scopes: OAuthScope[] = [ - { name: "function:getGitpodTokenScopes" }, - { name: "function:getWorkspace" }, - { name: "function:getWorkspaces" }, - { name: "function:listenForWorkspaceInstanceUpdates" }, - { name: "resource:default" } -]; - // Clients const localAppClientID = 'gplctl-1.0'; const localClient: OAuthClient = { @@ -35,16 +26,46 @@ const localClient: OAuthClient = { // NOTE: these need to be kept in sync with the port range in the local app redirectUris: Array.from({ length: 10 }, (_, i) => 'http://127.0.0.1:' + (63110 + i)), allowedGrants: ['authorization_code'], - scopes, + scopes: [ + { name: "function:getGitpodTokenScopes" }, + { name: "function:getWorkspace" }, + { name: "function:getWorkspaces" }, + { name: "function:listenForWorkspaceInstanceUpdates" }, + { name: "resource:default" } + ], +} + +const jetBrainsGateway: OAuthClient = { + id: 'jetbrains-gateway-gitpod-plugin', + name: 'JetBrains Gateway Gitpod Plugin', + // Set of valid redirect URIs + // NOTE: these need to be kept in sync with the port range in + // https://github.com/JetBrains/intellij-community/blob/8f07b83138bcb8a98a031e4508080c849a735644/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java#L34 + redirectUris: Array.from({ length: 20 }, (_, i) => `http://127.0.0.1:${63342 + i}/api/gitpod/oauth/authorization_code`), + allowedGrants: ['authorization_code'], + scopes: [ + { name: "function:getGitpodTokenScopes" }, + { name: "function:getIDEOptions" }, + { name: "function:getOwnerToken" }, + { name: "function:getWorkspace" }, + { name: "function:getWorkspaces" }, + { name: "function:listenForWorkspaceInstanceUpdates" }, + { name: "resource:default" } + ], } + export const inMemoryDatabase: InMemory = { clients: { [localClient.id]: localClient, + [jetBrainsGateway.id]: jetBrainsGateway }, tokens: {}, scopes: {}, }; -for (const scope of scopes) { - inMemoryDatabase.scopes[scope.name] = scope; +for (const clientId in inMemoryDatabase.clients) { + const client = inMemoryDatabase.clients[clientId]; + for (const scope of client.scopes) { + inMemoryDatabase.scopes[scope.name] = scope; + } } From d81517dd39b4d3bfbc84e86f6cd1e40c09cee2c8 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Thu, 20 Jan 2022 09:00:09 +0000 Subject: [PATCH 3/4] [server] add referrer prefix to control IDE option --- chart/templates/server-ide-configmap.yaml | 5 + components/gitpod-protocol/src/context-url.ts | 4 +- .../gitpod-protocol/src/ide-protocol.ts | 17 ++ components/gitpod-protocol/src/protocol.ts | 12 ++ .../ee/src/workspace/workspace-starter.ts | 5 +- components/server/src/container-module.ts | 2 + components/server/src/ide-config.ts | 30 +++- .../referrer-prefix-context-parser.ts | 28 +++ .../server/src/workspace/workspace-starter.ts | 163 ++++++++++++------ .../pkg/components/server/ide/configmap.go | 19 +- installer/pkg/components/server/ide/types.go | 7 + 11 files changed, 230 insertions(+), 62 deletions(-) create mode 100644 components/server/src/workspace/referrer-prefix-context-parser.ts diff --git a/chart/templates/server-ide-configmap.yaml b/chart/templates/server-ide-configmap.yaml index 9583bc787db303..32450a8e129f81 100644 --- a/chart/templates/server-ide-configmap.yaml +++ b/chart/templates/server-ide-configmap.yaml @@ -104,6 +104,11 @@ options: defaultIde: "code" defaultDesktopIde: "code-desktop" + +clients: + jetbrains-gateway: + defaultDesktopIDE: "intellij" + desktopIDEs: ["intellij", "goland", "pycharm", "phpstorm"] {{ end }} {{- if $comp.serverIdeConfigDeploy.enabled }} diff --git a/components/gitpod-protocol/src/context-url.ts b/components/gitpod-protocol/src/context-url.ts index dd83762e5d3513..fc5a9aa43545e8 100644 --- a/components/gitpod-protocol/src/context-url.ts +++ b/components/gitpod-protocol/src/context-url.ts @@ -8,6 +8,7 @@ export namespace ContextURL { export const INCREMENTAL_PREBUILD_PREFIX = "incremental-prebuild"; export const PREBUILD_PREFIX = "prebuild"; export const IMAGEBUILD_PREFIX = "imagebuild"; + export const REFERRER_PREFIX = 'referrer:'; /** * The field "contextUrl" might contain prefixes like: @@ -37,7 +38,8 @@ export namespace ContextURL { const firstSegment = segments[0]; if (firstSegment === PREBUILD_PREFIX || firstSegment === INCREMENTAL_PREBUILD_PREFIX || - firstSegment === IMAGEBUILD_PREFIX) { + firstSegment === IMAGEBUILD_PREFIX || + firstSegment.startsWith(REFERRER_PREFIX)) { return segmentsToURL(1); } diff --git a/components/gitpod-protocol/src/ide-protocol.ts b/components/gitpod-protocol/src/ide-protocol.ts index 760b10bc90b8ea..c1281f490d327d 100644 --- a/components/gitpod-protocol/src/ide-protocol.ts +++ b/components/gitpod-protocol/src/ide-protocol.ts @@ -29,6 +29,23 @@ export interface IDEOptions { * The default desktop IDE when the user has not specified one. */ defaultDesktopIde: string; + + /** + * Client specific IDE options. + */ + clients?: { [id: string]: IDEClient }; +} + +export interface IDEClient { + /** + * The default desktop IDE when the user has not specified one. + */ + defaultDesktopIDE?: string; + + /** + * Desktop IDEs supported by the client. + */ + desktopIDEs?: string[] } export interface IDEOption { diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index 514fcbbad91f71..3b0415ebc6155e 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -891,6 +891,18 @@ export namespace PrebuiltWorkspaceContext { } } +export interface WithReferrerContext extends WorkspaceContext { + referrer: string + referrerIde?: string +} + +export namespace WithReferrerContext { + export function is(context: any): context is WithReferrerContext { + return context + && 'referrer' in context; + } +} + export interface WithEnvvarsContext extends WorkspaceContext { envvars: EnvVarWithValue[]; } diff --git a/components/server/ee/src/workspace/workspace-starter.ts b/components/server/ee/src/workspace/workspace-starter.ts index 2472e599719703..66360d5125b93d 100644 --- a/components/server/ee/src/workspace/workspace-starter.ts +++ b/components/server/ee/src/workspace/workspace-starter.ts @@ -5,6 +5,7 @@ */ import { Workspace, User, WorkspaceInstance, WorkspaceInstanceConfiguration, NamedWorkspaceFeatureFlag } from "@gitpod/gitpod-protocol"; +import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { inject, injectable } from "inversify"; import { IDEConfig } from "../../../src/ide-config"; import { WorkspaceStarter } from "../../../src/workspace/workspace-starter"; @@ -19,8 +20,8 @@ export class WorkspaceStarterEE extends WorkspaceStarter { * * @param workspace the workspace to create an instance for */ - protected async newInstance(workspace: Workspace, user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig): Promise { - const instance = await super.newInstance(workspace, user, excludeFeatureFlags, ideConfig); + protected async newInstance(ctx: TraceContext, workspace: Workspace, user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig): Promise { + const instance = await super.newInstance(ctx, workspace, user, excludeFeatureFlags, ideConfig); if (await this.eligibilityService.hasFixedWorkspaceResources(user)) { const config: WorkspaceInstanceConfiguration = instance.configuration!; const ff = (config.featureFlags || []); diff --git a/components/server/src/container-module.ts b/components/server/src/container-module.ts index 5ff576d6ae7b54..66f463449599f5 100644 --- a/components/server/src/container-module.ts +++ b/components/server/src/container-module.ts @@ -79,6 +79,7 @@ import { IClientCallMetrics } from '@gitpod/content-service/lib/client-call-metr import { DebugApp } from './debug-app'; import { LocalMessageBroker, LocalRabbitMQBackedMessageBroker } from './messaging/local-message-broker'; import { contentServiceBinder } from '@gitpod/content-service/lib/sugar'; +import { ReferrerPrefixParser } from './workspace/referrer-prefix-context-parser'; export const productionContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { bind(Config).toConstantValue(ConfigFile.fromFile()); @@ -147,6 +148,7 @@ export const productionContainerModule = new ContainerModule((bind, unbind, isBo bind(ContextParser).toSelf().inSingletonScope(); bind(SnapshotContextParser).toSelf().inSingletonScope(); bind(IContextParser).to(SnapshotContextParser).inSingletonScope(); + bind(IPrefixContextParser).to(ReferrerPrefixParser).inSingletonScope(); bind(IPrefixContextParser).to(EnvvarPrefixParser).inSingletonScope(); bind(IPrefixContextParser).to(ImageBuildPrefixContextParser).inSingletonScope(); bind(IPrefixContextParser).to(AdditionalContentPrefixContextParser).inSingletonScope(); diff --git a/components/server/src/ide-config.ts b/components/server/src/ide-config.ts index 47c628d647aec8..aa1911209f1a0d 100644 --- a/components/server/src/ide-config.ts +++ b/components/server/src/ide-config.ts @@ -6,7 +6,7 @@ import { Disposable, DisposableCollection, Emitter } from '@gitpod/gitpod-protocol'; import { filePathTelepresenceAware } from '@gitpod/gitpod-protocol/lib/env'; -import { IDEOptions } from '@gitpod/gitpod-protocol/lib/ide-protocol'; +import { IDEClient, IDEOptions } from '@gitpod/gitpod-protocol/lib/ide-protocol'; import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; import { repeat } from '@gitpod/gitpod-protocol/lib/util/repeat'; import * as Ajv from 'ajv'; @@ -19,6 +19,7 @@ import debounce = require('lodash.debounce') export interface IDEConfig { supervisorImage: string; ideOptions: IDEOptions; + clients?: { [id: string]: IDEClient }; } const scheme = { @@ -56,13 +57,23 @@ const scheme = { }, "defaultIde": { "type": "string" }, "defaultDesktopIde": { "type": "string" }, + "clients": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "defaultDesktopIDE": { "type": "string" }, + "desktopIDEs": { "type": "array", "items": { "type": "string" } }, + } + } + } }, "required": [ "options", "defaultIde", "defaultDesktopIde", ], - }, + } }, "required": [ "supervisorImage", @@ -143,6 +154,21 @@ export class IDEConfigService { throw new Error(`invalid: Editor (desktop), '${newValue.ideOptions.defaultDesktopIde}' needs to be of type 'desktop' but is '${newValue.ideOptions.options[newValue.ideOptions.defaultIde].type}'.`); } + if (newValue.ideOptions.clients) { + for (const [clientId, client] of Object.entries(newValue.ideOptions.clients)) { + if (client.defaultDesktopIDE && !(client.defaultDesktopIDE in newValue.ideOptions.options)) { + throw new Error(`${clientId} client: there is no option entry for editor '${client.defaultDesktopIDE}'.`); + } + if (client.desktopIDEs) { + for (const ide of client.desktopIDEs) { + if (!(ide in newValue.ideOptions.options)) { + throw new Error(`${clientId} client: there is no option entry for editor '${ide}'.`); + } + } + } + } + } + value = newValue; } diff --git a/components/server/src/workspace/referrer-prefix-context-parser.ts b/components/server/src/workspace/referrer-prefix-context-parser.ts new file mode 100644 index 00000000000000..03b16d93343d27 --- /dev/null +++ b/components/server/src/workspace/referrer-prefix-context-parser.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2020 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + + import { IPrefixContextParser } from "./context-parser"; + import { User, WorkspaceContext, WithReferrerContext } from "@gitpod/gitpod-protocol"; + import { injectable } from "inversify"; + + +@injectable() +export class ReferrerPrefixParser implements IPrefixContextParser { + + private readonly prefix = /^\/?referrer:([^\/:]*)(?::([^\/]*))?\// + + findPrefix(_: User, context: string): string | undefined { + return this.prefix.exec(context)?.[0] || undefined; + } + + async handle(_: User, prefix: string, context: WorkspaceContext): Promise { + const matches = this.prefix.exec(prefix); + const referrer = matches?.[1]; + const referrerIde = matches?.[2]; + return referrer ? {...context, referrer, referrerIde} : context; + } + +} \ No newline at end of file diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index d1624cc9f9780c..24b7c5bcf20154 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -32,6 +32,8 @@ import * as path from 'path'; import * as grpc from "@grpc/grpc-js"; import { IDEConfig, IDEConfigService } from "../ide-config"; import { EnvVarWithValue } from "@gitpod/gitpod-protocol/src/protocol"; +import { WithReferrerContext } from "@gitpod/gitpod-protocol/lib/protocol"; +import { IDEOption } from "@gitpod/gitpod-protocol/lib/ide-protocol"; export interface StartWorkspaceOptions { rethrow?: boolean; @@ -88,9 +90,9 @@ export class WorkspaceStarter { req.setAuth(auth); const client = this.imagebuilderClientProvider.getDefault(); - const res = await client.resolveBaseImage({span}, req); + const res = await client.resolveBaseImage({ span }, req); workspace.imageSource = { - baseImageResolved: res.getRef() + baseImageResolved: res.getRef() } } @@ -101,7 +103,7 @@ export class WorkspaceStarter { const ideConfig = await this.ideConfigService.config; // create and store instance - let instance = await this.workspaceDb.trace({ span }).storeInstance(await this.newInstance(workspace, user, options.excludeFeatureFlags || [], ideConfig)); + let instance = await this.workspaceDb.trace({ span }).storeInstance(await this.newInstance(ctx, workspace, user, options.excludeFeatureFlags || [], ideConfig)); span.log({ "newInstance": instance.id }); const forceRebuild = !!workspace.context.forceImageBuild; @@ -194,12 +196,12 @@ export class WorkspaceStarter { } // start that thing - log.info({instanceId: instance.id}, 'starting instance'); + log.info({ instanceId: instance.id }, 'starting instance'); resp = (await manager.startWorkspace({ span }, startRequest)).toObject(); break; } catch (err: any) { if ('code' in err && err.code !== grpc.status.OK && lastInstallation !== "") { - log.error({instanceId: instance.id}, "cannot start workspace on cluster, might retry", err, {cluster: lastInstallation}); + log.error({ instanceId: instance.id }, "cannot start workspace on cluster, might retry", err, { cluster: lastInstallation }); exceptInstallation.push(lastInstallation); } else { throw err; @@ -240,7 +242,7 @@ export class WorkspaceStarter { if (rethrow) { throw err; } else { - log.error("error starting instance", err, { instanceId: instance.id}); + log.error("error starting instance", err, { instanceId: instance.id }); } return { instanceID: instance.id }; @@ -251,9 +253,9 @@ export class WorkspaceStarter { protected async notifyOnPrebuildQueued(ctx: TraceContext, workspaceId: string) { const span = TraceContext.startSpan("notifyOnPrebuildQueued", ctx); - const prebuild = await this.workspaceDb.trace({span}).findPrebuildByWorkspaceID(workspaceId); + const prebuild = await this.workspaceDb.trace({ span }).findPrebuildByWorkspaceID(workspaceId); if (prebuild) { - const info = (await this.workspaceDb.trace({span}).findPrebuildInfos([prebuild.id]))[0]; + const info = (await this.workspaceDb.trace({ span }).findPrebuildInfos([prebuild.id]))[0]; if (info) { this.messageBus.notifyOnPrebuildUpdate({ info, status: "queued" }); } @@ -281,21 +283,21 @@ export class WorkspaceStarter { // If we just attempted to start a workspace for a prebuild - and that failed, we have to fail the prebuild itself. if (workspace.type === 'prebuild') { - const prebuild = await this.workspaceDb.trace({span}).findPrebuildByWorkspaceID(workspace.id); + const prebuild = await this.workspaceDb.trace({ span }).findPrebuildByWorkspaceID(workspace.id); if (prebuild && prebuild.state !== 'aborted') { prebuild.state = "aborted"; prebuild.error = err.toString(); await this.workspaceDb.trace({ span }).storePrebuiltWorkspace(prebuild) - await this.messageBus.notifyHeadlessUpdate({span}, workspace.ownerId, workspace.id, { + await this.messageBus.notifyHeadlessUpdate({ span }, workspace.ownerId, workspace.id, { type: HeadlessWorkspaceEventType.Aborted, // TODO: `workspaceID: workspace.id` not needed here? (found in ee/src/prebuilds/prebuild-queue-maintainer.ts and ee/src/bridge.ts) }); } } } catch (err) { - TraceContext.setError({span}, err); - log.error({workspaceId: workspace.id, instanceId: instance.id, userId: workspace.ownerId}, "cannot properly fail workspace instance during start", err); + TraceContext.setError({ span }, err); + log.error({ workspaceId: workspace.id, instanceId: instance.id, userId: workspace.ownerId }, "cannot properly fail workspace instance during start", err); } } @@ -304,7 +306,7 @@ export class WorkspaceStarter { * * @param workspace the workspace to create an instance for */ - protected async newInstance(workspace: Workspace, user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig): Promise { + protected async newInstance(ctx: TraceContext, workspace: Workspace, user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig): Promise { // TODO(cw): once we allow changing the IDE in the workspace config (i.e. .gitpod.yml), we must // give that value precedence over the default choice. const configuration: WorkspaceInstanceConfiguration = { @@ -324,17 +326,34 @@ export class WorkspaceStarter { } } - const useDesktopIdeChoice = user.additionalData?.ideSettings?.useDesktopIde || false; - if (useDesktopIdeChoice) { - const desktopIdeChoice = user.additionalData?.ideSettings?.defaultDesktopIde; - if (!!desktopIdeChoice) { - const mappedImage = ideConfig.ideOptions.options[desktopIdeChoice]; - if (!!mappedImage && mappedImage.image) { - configuration.desktopIdeImage = mappedImage.image; - } else if (this.authService.hasPermission(user, "ide-settings")) { - // if the IDE choice isn't one of the preconfiured choices, we assume its the image name. - // For now, this feature requires special permissions. - configuration.desktopIdeImage = desktopIdeChoice; + const referrerIde = this.resolveReferrerIDE(workspace, user, ideConfig); + if (referrerIde) { + configuration.desktopIdeImage = referrerIde.option.image; + if (!user.additionalData?.ideSettings) { + // A user does not have IDE settings configured yet configure it with a referrer ide as default. + const additionalData = user?.additionalData || {}; + const settings = additionalData.ideSettings || {}; + settings.useDesktopIde = true; + settings.defaultDesktopIde = referrerIde.id; + additionalData.ideSettings = settings; + user.additionalData = additionalData; + this.userDB.trace(ctx).updateUserPartial(user).catch(e => { + log.error({ userId: user.id }, "cannot configure default desktop ide", e); + }); + } + } else { + const useDesktopIdeChoice = user.additionalData?.ideSettings?.useDesktopIde || false; + if (useDesktopIdeChoice) { + const desktopIdeChoice = user.additionalData?.ideSettings?.defaultDesktopIde; + if (!!desktopIdeChoice) { + const mappedImage = ideConfig.ideOptions.options[desktopIdeChoice]; + if (!!mappedImage && mappedImage.image) { + configuration.desktopIdeImage = mappedImage.image; + } else if (this.authService.hasPermission(user, "ide-settings")) { + // if the IDE choice isn't one of the preconfiured choices, we assume its the image name. + // For now, this feature requires special permissions. + configuration.desktopIdeImage = desktopIdeChoice; + } } } } @@ -373,10 +392,48 @@ export class WorkspaceStarter { }, configuration } + if (WithReferrerContext.is(workspace.context)) { + this.analytics.track({ userId: user.id, event: "ide_referrer", properties: { + workspaceId: workspace.id, + instanceId: instance.id, + referrer: workspace.context.referrer, + referrerIde: workspace.context.referrerIde + }}); + } return instance; } - protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable}> { + protected resolveReferrerIDE(workspace: Workspace, user: User, ideConfig: IDEConfig): { id: string, option: IDEOption } | undefined { + if (!WithReferrerContext.is(workspace.context)) { + return undefined; + } + const referrer = ideConfig.ideOptions.clients?.[workspace.context.referrer]; + if (!referrer) { + return undefined; + } + + const providedIde = workspace.context.referrerIde; + const providedOption = providedIde && ideConfig.ideOptions.options[providedIde]; + if (providedOption && referrer.desktopIDEs?.some(ide => ide === providedIde)) { + return { id: providedIde, option: providedOption }; + } + + const defaultDesktopIde = user.additionalData?.ideSettings?.defaultDesktopIde; + const userOption = defaultDesktopIde && ideConfig.ideOptions.options[defaultDesktopIde]; + if (userOption && referrer.desktopIDEs?.some(ide => ide === defaultDesktopIde)) { + return { id: defaultDesktopIde, option: userOption }; + } + + const defaultIde = referrer.defaultDesktopIDE; + const defaultOption = defaultIde && ideConfig.ideOptions.options[defaultIde]; + if (defaultOption) { + return { id: defaultIde, option: defaultOption } + } + + return undefined; + } + + protected async prepareBuildRequest(ctx: TraceContext, workspace: Workspace, imgsrc: WorkspaceImageSource, user: User, ignoreBaseImageresolvedAndRebuildBase: boolean = false): Promise<{ src: BuildSource, auth: BuildRegistryAuth, disposable?: Disposable }> { const span = TraceContext.startSpan("prepareBuildRequest", ctx); try { @@ -384,7 +441,7 @@ export class WorkspaceStarter { // and dismiss the original image source. if (workspace.baseImageNameResolved && !ignoreBaseImageresolvedAndRebuildBase) { span.setTag("hasBaseImageNameResolved", true); - span.log({"baseImageNameResolved": workspace.baseImageNameResolved}); + span.log({ "baseImageNameResolved": workspace.baseImageNameResolved }); const ref = new BuildSourceReference(); ref.setRef(workspace.baseImageNameResolved); @@ -403,7 +460,7 @@ export class WorkspaceStarter { const auth = new BuildRegistryAuth(); auth.setSelective(nauth); - return {src, auth}; + return { src, auth }; } const auth = new BuildRegistryAuth(); @@ -424,7 +481,7 @@ export class WorkspaceStarter { if (!AdditionalContentContext.hasDockerConfig(workspace.context, workspace.config) && imgsrc.dockerFileSource) { // TODO(se): we cannot change this initializer structure now because it is part of how baserefs are computed in image-builder. // Image builds should however just use the initialization if the workspace they are running for (i.e. the one from above). - const { git, disposable } = await this.createGitInitializer({span}, workspace, { + const { git, disposable } = await this.createGitInitializer({ span }, workspace, { ...imgsrc.dockerFileSource, title: "irrelevant", ref: undefined, @@ -437,7 +494,7 @@ export class WorkspaceStarter { source = new WorkspaceInitializer(); source.setGit(git); } else { - const {initializer, disposable} = await this.createInitializer({span}, workspace, workspace.context, user, false); + const { initializer, disposable } = await this.createInitializer({ span }, workspace, workspace.context, user, false); source = initializer; disp.push(disposable); } @@ -454,7 +511,7 @@ export class WorkspaceStarter { const src = new BuildSource(); src.setFile(file); - return {src, auth, disposable: disp}; + return { src, auth, disposable: disp }; } if (WorkspaceImageSourceReference.is(imgsrc)) { const ref = new BuildSourceReference(); @@ -462,7 +519,7 @@ export class WorkspaceStarter { const src = new BuildSource(); src.setRef(ref); - return {src, auth}; + return { src, auth }; } throw new Error("unknown workspace image source"); @@ -478,7 +535,7 @@ export class WorkspaceStarter { const span = TraceContext.startSpan("needsImageBuild", ctx); try { const client = this.imagebuilderClientProvider.getDefault(); - const {src, auth, disposable} = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user); const req = new ResolveWorkspaceImageRequest(); req.setSource(src); @@ -504,7 +561,7 @@ export class WorkspaceStarter { try { // Start build... const client = this.imagebuilderClientProvider.getDefault(); - const {src, auth, disposable} = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); + const { src, auth, disposable } = await this.prepareBuildRequest({ span }, workspace, workspace.imageSource!, user, ignoreBaseImageresolvedAndRebuildBase || forceRebuild); const req = new BuildRequest(); req.setSource(src); @@ -515,7 +572,7 @@ export class WorkspaceStarter { // Update the workspace now that we know what the name of the workspace image will be (which doubles as buildID) workspace.imageNameResolved = result.ref; - span.log({"ref": workspace.imageNameResolved}); + span.log({ "ref": workspace.imageNameResolved }); await this.workspaceDb.trace({ span }).store(workspace); // Update workspace instance to tell the world we're building an image @@ -550,7 +607,7 @@ export class WorkspaceStarter { // Note: it's intentional that we overwrite existing baseImageNameResolved values here so that one by one the refs here become absolute (i.e. digested form). // This prevents the "rebuilds" for old workspaces. if (!!buildResult.getBaseRef() && buildResult.getBaseRef() != workspace.baseImageNameResolved) { - span.log({"oldBaseRef": workspace.baseImageNameResolved, "newBaseRef": buildResult.getBaseRef()}); + span.log({ "oldBaseRef": workspace.baseImageNameResolved, "newBaseRef": buildResult.getBaseRef() }); workspace.baseImageNameResolved = buildResult.getBaseRef(); await this.workspaceDb.trace({ span }).store(workspace); @@ -572,11 +629,11 @@ export class WorkspaceStarter { return msg.startsWith("build failed:"); }; if (looksLikeUserError(message)) { - log.debug({instanceId: instance.id, userId: user.id, workspaceId: workspace.id}, `workspace image build failed: ${message}`); + log.debug({ instanceId: instance.id, userId: user.id, workspaceId: workspace.id }, `workspace image build failed: ${message}`); } else { - log.warn({instanceId: instance.id, userId: user.id, workspaceId: workspace.id}, `workspace image build failed: ${message}`); + log.warn({ instanceId: instance.id, userId: user.id, workspaceId: workspace.id }, `workspace image build failed: ${message}`); } - this.analytics.track({ userId: user.id, event: "imagebuild-failed", properties: { workspaceId: workspace.id, instanceId: instance.id, contextURL: workspace.contextURL, }}); + this.analytics.track({ userId: user.id, event: "imagebuild-failed", properties: { workspaceId: workspace.id, instanceId: instance.id, contextURL: workspace.contextURL, } }); throw err; } finally { @@ -703,7 +760,7 @@ export class WorkspaceStarter { const portIndex = new Set(); const ports = (workspace.config.ports || []).map(p => { if (portIndex.has(p.port)) { - log.debug({instanceId: instance.id, workspaceId: workspace.id, userId: user.id}, `duplicate port in user config: ${p.port}`); + log.debug({ instanceId: instance.id, workspaceId: workspace.id, userId: user.id }, `duplicate port in user config: ${p.port}`); return undefined; } portIndex.add(p.port); @@ -797,18 +854,18 @@ export class WorkspaceStarter { "function:deleteEnvVar", "function:trackEvent", - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "workspace", subjectID: workspace.id, operations: ["get", "update"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "workspaceInstance", subjectID: instance.id, operations: ["get", "update", "delete"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "snapshot", subjectID: ScopedResourceGuard.SNAPSHOT_WORKSPACE_SUBJECT_ID_PREFIX + workspace.id, operations: ["create"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "gitpodToken", subjectID: "*", operations: ["create"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "userStorage", subjectID: "*", operations: ["create", "get", "update"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "token", subjectID: "*", operations: ["get"]}), - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "contentBlob", subjectID: "*", operations: ["create", "get"]}), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "workspace", subjectID: workspace.id, operations: ["get", "update"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "workspaceInstance", subjectID: instance.id, operations: ["get", "update", "delete"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "snapshot", subjectID: ScopedResourceGuard.SNAPSHOT_WORKSPACE_SUBJECT_ID_PREFIX + workspace.id, operations: ["create"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "gitpodToken", subjectID: "*", operations: ["create"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "userStorage", subjectID: "*", operations: ["create", "get", "update"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "token", subjectID: "*", operations: ["get"] }), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "contentBlob", subjectID: "*", operations: ["create", "get"] }), ]; if (CommitContext.is(workspace.context)) { const subjectID = workspace.context.repository.owner + '/' + workspace.context.repository.name; scopes.push( - "resource:"+ScopedResourceGuard.marshalResourceScope({kind: "envVar", subjectID, operations: ["create", "get", "update", "delete"]}), + "resource:" + ScopedResourceGuard.marshalResourceScope({ kind: "envVar", subjectID, operations: ["create", "get", "update", "delete"] }), ); } return scopes; @@ -838,7 +895,7 @@ export class WorkspaceStarter { return gitSpec; } - protected async createInitializer(traceCtx: TraceContext, workspace: Workspace, context: WorkspaceContext, user: User, mustHaveBackup: boolean): Promise<{initializer: WorkspaceInitializer, disposable: Disposable}> { + protected async createInitializer(traceCtx: TraceContext, workspace: Workspace, context: WorkspaceContext, user: User, mustHaveBackup: boolean): Promise<{ initializer: WorkspaceInitializer, disposable: Disposable }> { let result = new WorkspaceInitializer(); const disp = new DisposableCollection(); @@ -870,10 +927,10 @@ export class WorkspaceStarter { throw new Error("cannot create initializer for unkown context type"); } if (AdditionalContentContext.is(context)) { - const additionalInit = new FileDownloadInitializer(); + const additionalInit = new FileDownloadInitializer(); const getDigest = (contents: string) => { - return 'sha256:'+crypto.createHash('sha256').update(contents).digest('hex'); + return 'sha256:' + crypto.createHash('sha256').update(contents).digest('hex'); } const tokenExpirationTime = new Date(); @@ -900,10 +957,10 @@ export class WorkspaceStarter { composite.addInitializer(wsInitializerForDownload); result = newRoot; } - return {initializer: result, disposable: disp}; + return { initializer: result, disposable: disp }; } - protected async createGitInitializer(traceCtx: TraceContext, workspace: Workspace, context: CommitContext, user: User): Promise<{git: GitInitializer, disposable: Disposable}> { + protected async createGitInitializer(traceCtx: TraceContext, workspace: Workspace, context: CommitContext, user: User): Promise<{ git: GitInitializer, disposable: Disposable }> { if (!CommitContext.is(context)) { throw new Error("Unknown workspace context"); } @@ -929,7 +986,7 @@ export class WorkspaceStarter { tokenOTS = res.token; disposable = res.disposable; } catch (error) { // no token - log.error({workspaceId: workspace.id, userId: workspace.ownerId}, "cannot authenticate user for Git initializer", error); + log.error({ workspaceId: workspace.id, userId: workspace.ownerId }, "cannot authenticate user for Git initializer", error); throw new Error("User is unauthorized!"); } const cloneUrl = context.repository.cloneUrl || context.cloneUrl!; diff --git a/installer/pkg/components/server/ide/configmap.go b/installer/pkg/components/server/ide/configmap.go index a0fbd736799fe6..364c050468bc65 100644 --- a/installer/pkg/components/server/ide/configmap.go +++ b/installer/pkg/components/server/ide/configmap.go @@ -23,9 +23,20 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { } typeBrowser := "browser" typeDesktop := "desktop" + + intellij := "intellij" + goland := "goland" + pycharm := "pycharm" + phpstorm := "phpstorm" idecfg := IDEConfig{ SupervisorImage: common.ImageName(ctx.Config.Repository, workspace.SupervisorImage, ctx.VersionManifest.Components.Workspace.Supervisor.Version), IDEOptions: IDEOptions{ + IDEClients: map[string]IDEClient{ + "jetbrains-gateway": { + DefaultDesktopIDE: intellij, + DesktopIDEs: []string{intellij, goland, pycharm, phpstorm}, + }, + }, Options: map[string]IDEOption{ "code": { OrderKey: pointer.String("00"), @@ -60,7 +71,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Label: pointer.String("Insiders"), Image: common.ImageName(ctx.Config.Repository, ide.CodeDesktopInsidersIDEImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.CodeDesktopImageInsiders.Version), }, - "intellij": { + intellij: { OrderKey: pointer.String("04"), Title: "IntelliJ IDEA", Type: typeDesktop, @@ -68,7 +79,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Notes: []string{"While in beta, when you open a workspace with IntelliJ IDEA you will need to use the password “gitpod”."}, Image: common.ImageName(ctx.Config.Repository, ide.IntelliJDesktopIDEImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.IntelliJImage.Version), }, - "goland": { + goland: { OrderKey: pointer.String("05"), Title: "GoLand", Type: typeDesktop, @@ -76,7 +87,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Notes: []string{"While in beta, when you open a workspace with GoLand you will need to use the password “gitpod”."}, Image: common.ImageName(ctx.Config.Repository, ide.GoLandDesktopIdeImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.GoLandImage.Version), }, - "pycharm": { + pycharm: { OrderKey: pointer.String("06"), Title: "PyCharm", Type: typeDesktop, @@ -84,7 +95,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Notes: []string{"While in beta, when you open a workspace with PyCharm you will need to use the password “gitpod”."}, Image: common.ImageName(ctx.Config.Repository, ide.PyCharmDesktopIdeImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.PyCharmImage.Version), }, - "phpstorm": { + phpstorm: { OrderKey: pointer.String("07"), Title: "PhpStorm", Type: typeDesktop, diff --git a/installer/pkg/components/server/ide/types.go b/installer/pkg/components/server/ide/types.go index 4cd3cd631ae673..b63078d29e44c3 100644 --- a/installer/pkg/components/server/ide/types.go +++ b/installer/pkg/components/server/ide/types.go @@ -17,6 +17,7 @@ type IDEOptions struct { Options map[string]IDEOption `json:"options"` DefaultIDE string `json:"defaultIde"` DefaultDesktopIDE string `json:"defaultDesktopIde"` + IDEClients map[string]IDEClient `json:"clients,omitempty"` } // IDEOption interface from components/gitpod-protocol/src/ide-protocol.ts @@ -32,3 +33,9 @@ type IDEOption struct { Image string `json:"image"` ResolveImageDigest *bool `json:"resolveImageDigest,omitempty"` } + +// IDEClient interface from components/gitpod-protocol/src/ide-protocol.ts +type IDEClient struct { + DefaultDesktopIDE string `json:"defaultDesktopIDE,omitempty"` + DesktopIDEs []string `json:"desktopIDEs,omitempty"` +} From ac97da9071436755304bddd8ac2cd9057d997202 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Thu, 20 Jan 2022 16:56:25 +0000 Subject: [PATCH 4/4] [ide] desktop client installation steps --- chart/templates/server-ide-configmap.yaml | 15 + components/dashboard/src/start/StartPage.tsx | 2 +- .../dashboard/src/start/StartWorkspace.tsx | 40 +- .../gitpod-protocol/src/ide-protocol.ts | 5 + components/ide/code-desktop/status/main.go | 1 + components/supervisor-api/go/info.pb.gw.go | 2 +- components/supervisor-api/go/info_grpc.pb.go | 2 +- .../supervisor-api/go/notification.pb.gw.go | 2 +- .../supervisor-api/go/notification_grpc.pb.go | 2 +- components/supervisor-api/go/port.pb.gw.go | 2 +- components/supervisor-api/go/port_grpc.pb.go | 2 +- components/supervisor-api/go/status.pb.go | 356 +++++++++--------- components/supervisor-api/go/status.pb.gw.go | 2 +- .../supervisor-api/go/status_grpc.pb.go | 2 +- .../supervisor-api/go/terminal.pb.gw.go | 2 +- .../supervisor-api/go/terminal_grpc.pb.go | 2 +- components/supervisor-api/go/token.pb.gw.go | 2 +- components/supervisor-api/go/token_grpc.pb.go | 2 +- .../java/io/gitpod/supervisor/api/Info.java | 2 +- .../supervisor/api/InfoServiceGrpc.java | 2 +- .../gitpod/supervisor/api/Notification.java | 2 +- .../api/NotificationServiceGrpc.java | 2 +- .../java/io/gitpod/supervisor/api/Port.java | 2 +- .../supervisor/api/PortServiceGrpc.java | 2 +- .../java/io/gitpod/supervisor/api/Status.java | 284 ++++++++++---- .../supervisor/api/StatusServiceGrpc.java | 2 +- .../supervisor/api/TerminalOuterClass.java | 2 +- .../supervisor/api/TerminalServiceGrpc.java | 2 +- .../java/io/gitpod/supervisor/api/Token.java | 2 +- .../supervisor/api/TokenServiceGrpc.java | 2 +- components/supervisor-api/status.proto | 1 + components/supervisor/frontend/src/index.ts | 20 +- .../supervisor/pkg/supervisor/services.go | 6 +- .../pkg/components/server/ide/configmap.go | 26 +- installer/pkg/components/server/ide/types.go | 1 + 35 files changed, 524 insertions(+), 279 deletions(-) diff --git a/chart/templates/server-ide-configmap.yaml b/chart/templates/server-ide-configmap.yaml index 32450a8e129f81..3ec8a51dc431ee 100644 --- a/chart/templates/server-ide-configmap.yaml +++ b/chart/templates/server-ide-configmap.yaml @@ -106,9 +106,24 @@ defaultIde: "code" defaultDesktopIde: "code-desktop" clients: + vscode: + defaultDesktopIDE: "code-desktop" + desktopIDEs: ["code-desktop"] + installationSteps: [ + "If you don't see an open dialog by the browser, make sure you have VS Code installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + ] + vscode-insiders: + defaultDesktopIDE: "code-desktop-insiders" + desktopIDEs: ["code-desktop-insiders"] + installationSteps: [ + "If you don't see an open dialog by the browser, make sure you have VS Code Insiders installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + ] jetbrains-gateway: defaultDesktopIDE: "intellij" desktopIDEs: ["intellij", "goland", "pycharm", "phpstorm"] + installationSteps: [ + "If you don't see an open dialog by the browser, make sure you have JetBrains Gateway with Gitpod Plugin installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + ] {{ end }} {{- if $comp.serverIdeConfigDeploy.enabled }} diff --git a/components/dashboard/src/start/StartPage.tsx b/components/dashboard/src/start/StartPage.tsx index 77e67ebe07f304..9f86c156cd6afb 100644 --- a/components/dashboard/src/start/StartPage.tsx +++ b/components/dashboard/src/start/StartPage.tsx @@ -34,7 +34,7 @@ function getPhaseTitle(phase?: StartPhase, error?: StartWorkspaceError) { case StartPhase.Running: return "Starting"; case StartPhase.IdeReady: - return "Your Workspace is Ready!"; + return "Running"; case StartPhase.Stopping: return "Stopping"; case StartPhase.Stopped: diff --git a/components/dashboard/src/start/StartWorkspace.tsx b/components/dashboard/src/start/StartWorkspace.tsx index da9a94a0ba938b..f517d431b5b29d 100644 --- a/components/dashboard/src/start/StartWorkspace.tsx +++ b/components/dashboard/src/start/StartWorkspace.tsx @@ -5,6 +5,7 @@ */ import { ContextURL, DisposableCollection, WithPrebuild, Workspace, WorkspaceImageBuild, WorkspaceInstance } from "@gitpod/gitpod-protocol"; +import { IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol"; import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import EventEmitter from "events"; import React, { Suspense, useEffect } from "react"; @@ -32,7 +33,9 @@ export interface StartWorkspaceState { desktopIde?: { link: string label: string + clientID?: string } + ideOptions?: IDEOptions } export default class StartWorkspace extends React.Component { @@ -54,7 +57,8 @@ export default class StartWorkspace extends React.Component this.setState({ ideOptions })) } componentWillUnmount() { @@ -238,6 +243,7 @@ export default class StartWorkspace extends React.ComponentPreparing workspace …

; + const contextURL = this.state.workspace?.context.normalizedContextURL || ContextURL.parseToURL(this.state.workspace?.contextURL)?.toString(); switch (this.state?.workspaceInstance?.status.phase) { // unknown indicates an issue within the system in that it cannot determine the actual phase of @@ -281,17 +287,26 @@ export default class StartWorkspace extends React.ComponentOpening IDE …

; + statusMessage =

Opening Workspace …

; } else { phase = StartPhase.IdeReady; + const openLink = this.state.desktopIde.link; + const openLinkLabel = this.state.desktopIde.label; + const clientID = this.state.desktopIde.clientID + const client = clientID ? this.state.ideOptions?.clients?.[clientID] : undefined; + const installationSteps = client?.installationSteps?.length &&
+ {client.installationSteps.map(step =>
)} +
statusMessage =
+

Opening Workspace …

 
-

{this.state.workspaceInstance.workspaceId}

-

{this.state.workspace?.contextURL}

+

{this.state.workspaceInstance.workspaceId}

+

{contextURL}

+ {installationSteps}
- +
These IDE options are based on your user preferences.
; @@ -336,7 +362,7 @@ export default class StartWorkspace extends React.Component 

{this.state.workspaceInstance.workspaceId}

-

{this.state.workspace?.contextURL}

+

{contextURL}

@@ -363,7 +389,7 @@ export default class StartWorkspace extends React.Component 

{this.state.workspaceInstance.workspaceId}

-

{this.state.workspace?.contextURL}

+

{contextURL}

diff --git a/components/gitpod-protocol/src/ide-protocol.ts b/components/gitpod-protocol/src/ide-protocol.ts index c1281f490d327d..6e3c9980932670 100644 --- a/components/gitpod-protocol/src/ide-protocol.ts +++ b/components/gitpod-protocol/src/ide-protocol.ts @@ -46,6 +46,11 @@ export interface IDEClient { * Desktop IDEs supported by the client. */ desktopIDEs?: string[] + + /** + * Steps to install the client on user machine. + */ + installationSteps?: string[] } export interface IDEOption { diff --git a/components/ide/code-desktop/status/main.go b/components/ide/code-desktop/status/main.go index 34ab1dedc11ca6..11f89631daeb0e 100644 --- a/components/ide/code-desktop/status/main.go +++ b/components/ide/code-desktop/status/main.go @@ -72,6 +72,7 @@ func main() { response := make(map[string]string) response["link"] = link.String() response["label"] = label + response["clientID"] = schema w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) }) diff --git a/components/supervisor-api/go/info.pb.gw.go b/components/supervisor-api/go/info.pb.gw.go index 7d05fe385d9835..bbb171b6c2ce31 100644 --- a/components/supervisor-api/go/info.pb.gw.go +++ b/components/supervisor-api/go/info.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/info_grpc.pb.go b/components/supervisor-api/go/info_grpc.pb.go index c1d602c8cc09fc..a6ba0486b1d054 100644 --- a/components/supervisor-api/go/info_grpc.pb.go +++ b/components/supervisor-api/go/info_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/notification.pb.gw.go b/components/supervisor-api/go/notification.pb.gw.go index 473fc0e8d96cf0..1f02ee798a0ce8 100644 --- a/components/supervisor-api/go/notification.pb.gw.go +++ b/components/supervisor-api/go/notification.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/notification_grpc.pb.go b/components/supervisor-api/go/notification_grpc.pb.go index 13b467b3b1a505..781237778f80f9 100644 --- a/components/supervisor-api/go/notification_grpc.pb.go +++ b/components/supervisor-api/go/notification_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/port.pb.gw.go b/components/supervisor-api/go/port.pb.gw.go index 8fcf868a2ac9ae..b34ddc80540c7f 100644 --- a/components/supervisor-api/go/port.pb.gw.go +++ b/components/supervisor-api/go/port.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/port_grpc.pb.go b/components/supervisor-api/go/port_grpc.pb.go index a3f3a35f500a96..71c1ccae8af106 100644 --- a/components/supervisor-api/go/port_grpc.pb.go +++ b/components/supervisor-api/go/port_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/status.pb.go b/components/supervisor-api/go/status.pb.go index e596f7335610d7..823637c74e98f2 100644 --- a/components/supervisor-api/go/status.pb.go +++ b/components/supervisor-api/go/status.pb.go @@ -1225,8 +1225,9 @@ type IDEStatusResponse_DesktopStatus struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Link string `protobuf:"bytes,1,opt,name=link,proto3" json:"link,omitempty"` - Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` + Link string `protobuf:"bytes,1,opt,name=link,proto3" json:"link,omitempty"` + Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` + ClientID string `protobuf:"bytes,3,opt,name=clientID,proto3" json:"clientID,omitempty"` } func (x *IDEStatusResponse_DesktopStatus) Reset() { @@ -1275,6 +1276,13 @@ func (x *IDEStatusResponse_DesktopStatus) GetLabel() string { return "" } +func (x *IDEStatusResponse_DesktopStatus) GetClientID() string { + if x != nil { + return x.ClientID + } + return "" +} + var File_status_proto protoreflect.FileDescriptor var file_status_proto_rawDesc = []byte{ @@ -1289,186 +1297,188 @@ var file_status_proto_rawDesc = []byte{ 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0x26, 0x0a, 0x10, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, - 0x61, 0x69, 0x74, 0x22, 0xa5, 0x01, 0x0a, 0x11, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x61, 0x69, 0x74, 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x45, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, - 0x1a, 0x39, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x1a, 0x55, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x2a, 0x0a, 0x14, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x77, 0x61, 0x69, 0x74, 0x22, 0x68, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x31, - 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x22, 0x15, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x41, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x29, 0x0a, 0x10, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x63, 0x61, 0x6e, 0x61, - 0x72, 0x79, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x2e, 0x0a, 0x12, 0x50, - 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x44, 0x0a, 0x13, 0x50, - 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, - 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, - 0x73, 0x22, 0x9f, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6c, 0x12, 0x3e, 0x0a, 0x0a, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, - 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x4f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x70, 0x6f, 0x73, - 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x6f, - 0x73, 0x65, 0x64, 0x22, 0xf1, 0x01, 0x0a, 0x10, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, - 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x76, 0x69, 0x73, - 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, - 0x6c, 0x56, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, - 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 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, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb4, 0x02, 0x0a, 0x0b, 0x50, 0x6f, 0x72, 0x74, - 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, - 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x35, - 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x45, 0x78, 0x70, - 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x65, 0x78, - 0x70, 0x6f, 0x73, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x65, 0x78, - 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x75, - 0x74, 0x6f, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, - 0x45, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x74, 0x75, 0x6e, 0x6e, - 0x65, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x75, 0x70, - 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, - 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, - 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x2e, - 0x0a, 0x12, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x43, - 0x0a, 0x13, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, - 0x72, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x74, 0x61, - 0x73, 0x6b, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x15, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, - 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x40, 0x0a, 0x0c, 0x70, - 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, - 0x61, 0x73, 0x6b, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0c, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, - 0x10, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x2a, 0x43, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x0a, - 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, - 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x10, 0x01, 0x12, 0x11, 0x0a, - 0x0d, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x10, 0x02, - 0x2a, 0x29, 0x0a, 0x0e, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x00, 0x12, - 0x0a, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x10, 0x01, 0x2a, 0x65, 0x0a, 0x13, 0x4f, - 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x10, 0x00, 0x12, 0x10, - 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x10, 0x01, - 0x12, 0x10, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, - 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x10, 0x03, 0x12, 0x12, - 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x10, 0x04, 0x2a, 0x39, 0x0a, 0x10, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x78, - 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x72, 0x79, 0x69, 0x6e, 0x67, - 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x10, - 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x2a, 0x31, 0x0a, - 0x09, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x6f, 0x70, - 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, - 0x6e, 0x67, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x10, 0x02, - 0x32, 0xcb, 0x06, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x7c, 0x0a, 0x10, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x2e, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, - 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, - 0x12, 0x83, 0x01, 0x0a, 0x09, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x44, 0x45, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x73, - 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x33, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, - 0x69, 0x64, 0x65, 0x5a, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x2f, 0x69, 0x64, 0x65, 0x2f, 0x77, 0x61, 0x69, 0x74, 0x2f, 0x7b, 0x77, 0x61, 0x69, 0x74, - 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, 0x12, 0x97, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, - 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x22, 0x2a, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x77, 0x61, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, + 0x61, 0x69, 0x74, 0x22, 0x68, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x3b, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5a, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2f, - 0x77, 0x61, 0x69, 0x74, 0x2f, 0x7b, 0x77, 0x61, 0x69, 0x74, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, - 0x12, 0x6c, 0x0a, 0x0c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x95, - 0x01, 0x0a, 0x0b, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, - 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, - 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5a, 0x29, 0x12, 0x27, 0x2f, 0x76, 0x31, - 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2f, 0x6f, 0x62, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x2f, 0x7b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x3d, 0x74, - 0x72, 0x75, 0x65, 0x7d, 0x30, 0x01, 0x12, 0x95, 0x01, 0x0a, 0x0b, 0x54, 0x61, 0x73, 0x6b, 0x73, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x12, - 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, - 0x73, 0x5a, 0x29, 0x12, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, - 0x74, 0x61, 0x73, 0x6b, 0x73, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x2f, 0x7b, 0x6f, - 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, 0x30, 0x01, 0x42, 0x46, - 0x0a, 0x18, 0x69, 0x6f, 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, - 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, - 0x6f, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x15, 0x0a, + 0x13, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x41, 0x0a, 0x14, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, + 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x63, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x2e, 0x0a, 0x12, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x44, 0x0a, 0x13, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, + 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x9f, 0x01, + 0x0a, 0x0f, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, + 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, + 0x3e, 0x0a, 0x0a, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, + 0x2e, 0x4f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x22, + 0xf1, 0x01, 0x0a, 0x10, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x56, 0x69, 0x73, + 0x69, 0x62, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x12, 0x43, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, + 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 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, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xb4, 0x02, 0x0a, 0x0b, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, + 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x07, 0x65, 0x78, + 0x70, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x75, + 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x65, + 0x64, 0x12, 0x41, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, + 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x78, + 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x52, 0x0c, 0x61, 0x75, 0x74, 0x6f, 0x45, 0x78, 0x70, 0x6f, + 0x73, 0x75, 0x72, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, + 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x12, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x43, 0x0a, 0x13, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x61, + 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x22, + 0xa7, 0x01, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x40, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x50, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x70, 0x72, 0x65, + 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, 0x10, 0x54, 0x61, 0x73, + 0x6b, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x70, + 0x65, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, + 0x70, 0x65, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x2a, 0x43, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x66, 0x72, 0x6f, 0x6d, + 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x66, 0x72, 0x6f, + 0x6d, 0x5f, 0x70, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x10, 0x02, 0x2a, 0x29, 0x0a, 0x0e, + 0x50, 0x6f, 0x72, 0x74, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x0b, + 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x10, 0x01, 0x2a, 0x65, 0x0a, 0x13, 0x4f, 0x6e, 0x50, 0x6f, 0x72, + 0x74, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, + 0x0a, 0x06, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x6f, 0x70, + 0x65, 0x6e, 0x5f, 0x62, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, + 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x10, 0x02, 0x12, 0x0a, + 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x10, 0x04, 0x2a, 0x39, + 0x0a, 0x10, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x75, + 0x72, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x74, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x0d, + 0x0a, 0x09, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0a, 0x0a, + 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x02, 0x2a, 0x31, 0x0a, 0x09, 0x54, 0x61, 0x73, + 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, + 0x67, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x01, + 0x12, 0x0a, 0x0a, 0x06, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x10, 0x02, 0x32, 0xcb, 0x06, 0x0a, + 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7c, + 0x0a, 0x10, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x23, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, + 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, + 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x12, 0x83, 0x01, 0x0a, + 0x09, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, + 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x44, 0x45, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x33, 0x12, + 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x69, 0x64, 0x65, 0x5a, + 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x69, 0x64, + 0x65, 0x2f, 0x77, 0x61, 0x69, 0x74, 0x2f, 0x7b, 0x77, 0x61, 0x69, 0x74, 0x3d, 0x74, 0x72, 0x75, + 0x65, 0x7d, 0x12, 0x97, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, + 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, + 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x41, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x3b, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5a, 0x25, 0x12, 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2f, 0x77, 0x61, 0x69, 0x74, + 0x2f, 0x7b, 0x77, 0x61, 0x69, 0x74, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, 0x12, 0x6c, 0x0a, 0x0c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x73, + 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x95, 0x01, 0x0a, 0x0b, 0x50, + 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x75, 0x70, + 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x3d, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5a, 0x29, 0x12, 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x2f, 0x7b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, + 0x30, 0x01, 0x12, 0x95, 0x01, 0x0a, 0x0b, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, + 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2e, + 0x54, 0x61, 0x73, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x12, 0x10, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x5a, 0x29, 0x12, + 0x27, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x74, 0x61, 0x73, 0x6b, + 0x73, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x2f, 0x7b, 0x6f, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x7d, 0x30, 0x01, 0x42, 0x46, 0x0a, 0x18, 0x69, 0x6f, + 0x2e, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, + 0x6f, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x69, 0x74, + 0x70, 0x6f, 0x64, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/components/supervisor-api/go/status.pb.gw.go b/components/supervisor-api/go/status.pb.gw.go index cef3d2004593c3..310fb074c88116 100644 --- a/components/supervisor-api/go/status.pb.gw.go +++ b/components/supervisor-api/go/status.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/status_grpc.pb.go b/components/supervisor-api/go/status_grpc.pb.go index b9cf45b9104284..f67278ca34c6f7 100644 --- a/components/supervisor-api/go/status_grpc.pb.go +++ b/components/supervisor-api/go/status_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/terminal.pb.gw.go b/components/supervisor-api/go/terminal.pb.gw.go index 9d93c8d91543f2..e9521171e23bd5 100644 --- a/components/supervisor-api/go/terminal.pb.gw.go +++ b/components/supervisor-api/go/terminal.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/terminal_grpc.pb.go b/components/supervisor-api/go/terminal_grpc.pb.go index 1a662e505e3409..fa2c37e903cef9 100644 --- a/components/supervisor-api/go/terminal_grpc.pb.go +++ b/components/supervisor-api/go/terminal_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/token.pb.gw.go b/components/supervisor-api/go/token.pb.gw.go index 6198985ec6c41e..f3e9f17ac7b7ce 100644 --- a/components/supervisor-api/go/token.pb.gw.go +++ b/components/supervisor-api/go/token.pb.gw.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/go/token_grpc.pb.go b/components/supervisor-api/go/token_grpc.pb.go index 41c96edc9b639a..6d87c9eea6b0e5 100644 --- a/components/supervisor-api/go/token_grpc.pb.go +++ b/components/supervisor-api/go/token_grpc.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Info.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Info.java index fda4e22aec5c5a..86cbe8ce0f0dc6 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Info.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Info.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/InfoServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/InfoServiceGrpc.java index eead5000e9ef4d..3a0745aefe4637 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/InfoServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/InfoServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Notification.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Notification.java index a31d858e3444d8..816ca822921c07 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Notification.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Notification.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/NotificationServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/NotificationServiceGrpc.java index 092a0decc2f05d..5b547cc8a28e60 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/NotificationServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/NotificationServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Port.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Port.java index 0cde0049cb8f48..ec8d07f14981b8 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Port.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Port.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/PortServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/PortServiceGrpc.java index d9a91d1bf8875e..08235cfa9b0d02 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/PortServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/PortServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Status.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Status.java index de63efc212c39f..d2e5f9d086710d 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Status.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Status.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. @@ -2180,6 +2180,18 @@ public interface DesktopStatusOrBuilder extends */ com.google.protobuf.ByteString getLabelBytes(); + + /** + * string clientID = 3; + * @return The clientID. + */ + java.lang.String getClientID(); + /** + * string clientID = 3; + * @return The bytes for clientID. + */ + com.google.protobuf.ByteString + getClientIDBytes(); } /** * Protobuf type {@code supervisor.IDEStatusResponse.DesktopStatus} @@ -2196,6 +2208,7 @@ private DesktopStatus(com.google.protobuf.GeneratedMessageV3.Builder builder) private DesktopStatus() { link_ = ""; label_ = ""; + clientID_ = ""; } @java.lang.Override @@ -2240,6 +2253,12 @@ private DesktopStatus( label_ = s; break; } + case 26: { + java.lang.String s = input.readStringRequireUtf8(); + + clientID_ = s; + break; + } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -2348,6 +2367,44 @@ public java.lang.String getLabel() { } } + public static final int CLIENTID_FIELD_NUMBER = 3; + private volatile java.lang.Object clientID_; + /** + * string clientID = 3; + * @return The clientID. + */ + @java.lang.Override + public java.lang.String getClientID() { + java.lang.Object ref = clientID_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + clientID_ = s; + return s; + } + } + /** + * string clientID = 3; + * @return The bytes for clientID. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getClientIDBytes() { + java.lang.Object ref = clientID_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clientID_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -2368,6 +2425,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(label_)) { com.google.protobuf.GeneratedMessageV3.writeString(output, 2, label_); } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(clientID_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 3, clientID_); + } unknownFields.writeTo(output); } @@ -2383,6 +2443,9 @@ public int getSerializedSize() { if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(label_)) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, label_); } + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(clientID_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, clientID_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -2402,6 +2465,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getLink())) return false; if (!getLabel() .equals(other.getLabel())) return false; + if (!getClientID() + .equals(other.getClientID())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -2417,6 +2482,8 @@ public int hashCode() { hash = (53 * hash) + getLink().hashCode(); hash = (37 * hash) + LABEL_FIELD_NUMBER; hash = (53 * hash) + getLabel().hashCode(); + hash = (37 * hash) + CLIENTID_FIELD_NUMBER; + hash = (53 * hash) + getClientID().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -2554,6 +2621,8 @@ public Builder clear() { label_ = ""; + clientID_ = ""; + return this; } @@ -2582,6 +2651,7 @@ public io.gitpod.supervisor.api.Status.IDEStatusResponse.DesktopStatus buildPart io.gitpod.supervisor.api.Status.IDEStatusResponse.DesktopStatus result = new io.gitpod.supervisor.api.Status.IDEStatusResponse.DesktopStatus(this); result.link_ = link_; result.label_ = label_; + result.clientID_ = clientID_; onBuilt(); return result; } @@ -2638,6 +2708,10 @@ public Builder mergeFrom(io.gitpod.supervisor.api.Status.IDEStatusResponse.Deskt label_ = other.label_; onChanged(); } + if (!other.getClientID().isEmpty()) { + clientID_ = other.clientID_; + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -2818,6 +2892,82 @@ public Builder setLabelBytes( onChanged(); return this; } + + private java.lang.Object clientID_ = ""; + /** + * string clientID = 3; + * @return The clientID. + */ + public java.lang.String getClientID() { + java.lang.Object ref = clientID_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + clientID_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string clientID = 3; + * @return The bytes for clientID. + */ + public com.google.protobuf.ByteString + getClientIDBytes() { + java.lang.Object ref = clientID_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + clientID_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string clientID = 3; + * @param value The clientID to set. + * @return This builder for chaining. + */ + public Builder setClientID( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + clientID_ = value; + onChanged(); + return this; + } + /** + * string clientID = 3; + * @return This builder for chaining. + */ + public Builder clearClientID() { + + clientID_ = getDefaultInstance().getClientID(); + onChanged(); + return this; + } + /** + * string clientID = 3; + * @param value The bytes for clientID to set. + * @return This builder for chaining. + */ + public Builder setClientIDBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + clientID_ = value; + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { @@ -13758,72 +13908,72 @@ public io.gitpod.supervisor.api.Status.TaskPresentation getDefaultInstanceForTyp "nnotations.proto\032\nport.proto\"\031\n\027Supervis" + "orStatusRequest\"&\n\030SupervisorStatusRespo" + "nse\022\n\n\002ok\030\001 \001(\010\" \n\020IDEStatusRequest\022\014\n\004w" + - "ait\030\001 \001(\010\"\213\001\n\021IDEStatusResponse\022\n\n\002ok\030\001 " + + "ait\030\001 \001(\010\"\235\001\n\021IDEStatusResponse\022\n\n\002ok\030\001 " + "\001(\010\022<\n\007desktop\030\002 \001(\0132+.supervisor.IDESta" + - "tusResponse.DesktopStatus\032,\n\rDesktopStat" + - "us\022\014\n\004link\030\001 \001(\t\022\r\n\005label\030\002 \001(\t\"$\n\024Conte" + - "ntStatusRequest\022\014\n\004wait\030\001 \001(\010\"U\n\025Content" + - "StatusResponse\022\021\n\tavailable\030\001 \001(\010\022)\n\006sou" + - "rce\030\002 \001(\0162\031.supervisor.ContentSource\"\025\n\023" + - "BackupStatusRequest\"0\n\024BackupStatusRespo" + - "nse\022\030\n\020canary_available\030\001 \001(\010\"%\n\022PortsSt" + - "atusRequest\022\017\n\007observe\030\001 \001(\010\"=\n\023PortsSta" + - "tusResponse\022&\n\005ports\030\001 \003(\0132\027.supervisor." + - "PortsStatus\"\203\001\n\017ExposedPortInfo\022.\n\nvisib" + - "ility\030\001 \001(\0162\032.supervisor.PortVisibility\022" + - "\013\n\003url\030\002 \001(\t\0223\n\non_exposed\030\003 \001(\0162\037.super" + - "visor.OnPortExposedAction\"\304\001\n\020TunneledPo" + - "rtInfo\022\023\n\013target_port\030\001 \001(\r\022/\n\nvisibilit" + - "y\030\002 \001(\0162\033.supervisor.TunnelVisiblity\022:\n\007" + - "clients\030\003 \003(\0132).supervisor.TunneledPortI" + - "nfo.ClientsEntry\032.\n\014ClientsEntry\022\013\n\003key\030" + - "\001 \001(\t\022\r\n\005value\030\002 \001(\r:\0028\001\"\355\001\n\013PortsStatus" + - "\022\022\n\nlocal_port\030\001 \001(\r\022\016\n\006served\030\004 \001(\010\022,\n\007" + - "exposed\030\005 \001(\0132\033.supervisor.ExposedPortIn" + - "fo\0223\n\rauto_exposure\030\007 \001(\0162\034.supervisor.P" + - "ortAutoExposure\022.\n\010tunneled\030\006 \001(\0132\034.supe" + - "rvisor.TunneledPortInfo\022\023\n\013description\030\010" + - " \001(\t\022\014\n\004name\030\t \001(\tJ\004\010\002\020\003\"%\n\022TasksStatusR" + - "equest\022\017\n\007observe\030\001 \001(\010\"<\n\023TasksStatusRe" + - "sponse\022%\n\005tasks\030\001 \003(\0132\026.supervisor.TaskS" + - "tatus\"\204\001\n\nTaskStatus\022\n\n\002id\030\001 \001(\t\022$\n\005stat" + - "e\030\002 \001(\0162\025.supervisor.TaskState\022\020\n\010termin" + - "al\030\003 \001(\t\0222\n\014presentation\030\004 \001(\0132\034.supervi" + - "sor.TaskPresentation\"D\n\020TaskPresentation" + - "\022\014\n\004name\030\001 \001(\t\022\017\n\007open_in\030\002 \001(\t\022\021\n\topen_" + - "mode\030\003 \001(\t*C\n\rContentSource\022\016\n\nfrom_othe" + - "r\020\000\022\017\n\013from_backup\020\001\022\021\n\rfrom_prebuild\020\002*" + - "?\n\016PortVisibility\022\026\n\022private_visibility\020" + - "\000\022\025\n\021public_visibility\020\001*e\n\023OnPortExpose" + - "dAction\022\n\n\006ignore\020\000\022\020\n\014open_browser\020\001\022\020\n" + - "\014open_preview\020\002\022\n\n\006notify\020\003\022\022\n\016notify_pr" + - "ivate\020\004*9\n\020PortAutoExposure\022\n\n\006trying\020\000\022" + - "\r\n\tsucceeded\020\001\022\n\n\006failed\020\002*1\n\tTaskState\022" + - "\013\n\007opening\020\000\022\013\n\007running\020\001\022\n\n\006closed\020\0022\313\006" + - "\n\rStatusService\022|\n\020SupervisorStatus\022#.su" + - "pervisor.SupervisorStatusRequest\032$.super" + - "visor.SupervisorStatusResponse\"\035\202\323\344\223\002\027\022\025" + - "/v1/status/supervisor\022\203\001\n\tIDEStatus\022\034.su" + - "pervisor.IDEStatusRequest\032\035.supervisor.I" + - "DEStatusResponse\"9\202\323\344\223\0023\022\016/v1/status/ide" + - "Z!\022\037/v1/status/ide/wait/{wait=true}\022\227\001\n\r" + - "ContentStatus\022 .supervisor.ContentStatus" + - "Request\032!.supervisor.ContentStatusRespon" + - "se\"A\202\323\344\223\002;\022\022/v1/status/contentZ%\022#/v1/st" + - "atus/content/wait/{wait=true}\022l\n\014BackupS" + - "tatus\022\037.supervisor.BackupStatusRequest\032 " + - ".supervisor.BackupStatusResponse\"\031\202\323\344\223\002\023" + - "\022\021/v1/status/backup\022\225\001\n\013PortsStatus\022\036.su" + - "pervisor.PortsStatusRequest\032\037.supervisor" + - ".PortsStatusResponse\"C\202\323\344\223\002=\022\020/v1/status" + - "/portsZ)\022\'/v1/status/ports/observe/{obse" + - "rve=true}0\001\022\225\001\n\013TasksStatus\022\036.supervisor" + - ".TasksStatusRequest\032\037.supervisor.TasksSt" + - "atusResponse\"C\202\323\344\223\002=\022\020/v1/status/tasksZ)" + - "\022\'/v1/status/tasks/observe/{observe=true" + - "}0\001BF\n\030io.gitpod.supervisor.apiZ*github." + - "com/gitpod-io/gitpod/supervisor/apib\006pro" + - "to3" + "tusResponse.DesktopStatus\032>\n\rDesktopStat" + + "us\022\014\n\004link\030\001 \001(\t\022\r\n\005label\030\002 \001(\t\022\020\n\010clien" + + "tID\030\003 \001(\t\"$\n\024ContentStatusRequest\022\014\n\004wai" + + "t\030\001 \001(\010\"U\n\025ContentStatusResponse\022\021\n\tavai" + + "lable\030\001 \001(\010\022)\n\006source\030\002 \001(\0162\031.supervisor" + + ".ContentSource\"\025\n\023BackupStatusRequest\"0\n" + + "\024BackupStatusResponse\022\030\n\020canary_availabl" + + "e\030\001 \001(\010\"%\n\022PortsStatusRequest\022\017\n\007observe" + + "\030\001 \001(\010\"=\n\023PortsStatusResponse\022&\n\005ports\030\001" + + " \003(\0132\027.supervisor.PortsStatus\"\203\001\n\017Expose" + + "dPortInfo\022.\n\nvisibility\030\001 \001(\0162\032.supervis" + + "or.PortVisibility\022\013\n\003url\030\002 \001(\t\0223\n\non_exp" + + "osed\030\003 \001(\0162\037.supervisor.OnPortExposedAct" + + "ion\"\304\001\n\020TunneledPortInfo\022\023\n\013target_port\030" + + "\001 \001(\r\022/\n\nvisibility\030\002 \001(\0162\033.supervisor.T" + + "unnelVisiblity\022:\n\007clients\030\003 \003(\0132).superv" + + "isor.TunneledPortInfo.ClientsEntry\032.\n\014Cl" + + "ientsEntry\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030\002 \001(\r:\002" + + "8\001\"\355\001\n\013PortsStatus\022\022\n\nlocal_port\030\001 \001(\r\022\016" + + "\n\006served\030\004 \001(\010\022,\n\007exposed\030\005 \001(\0132\033.superv" + + "isor.ExposedPortInfo\0223\n\rauto_exposure\030\007 " + + "\001(\0162\034.supervisor.PortAutoExposure\022.\n\010tun" + + "neled\030\006 \001(\0132\034.supervisor.TunneledPortInf" + + "o\022\023\n\013description\030\010 \001(\t\022\014\n\004name\030\t \001(\tJ\004\010\002" + + "\020\003\"%\n\022TasksStatusRequest\022\017\n\007observe\030\001 \001(" + + "\010\"<\n\023TasksStatusResponse\022%\n\005tasks\030\001 \003(\0132" + + "\026.supervisor.TaskStatus\"\204\001\n\nTaskStatus\022\n" + + "\n\002id\030\001 \001(\t\022$\n\005state\030\002 \001(\0162\025.supervisor.T" + + "askState\022\020\n\010terminal\030\003 \001(\t\0222\n\014presentati" + + "on\030\004 \001(\0132\034.supervisor.TaskPresentation\"D" + + "\n\020TaskPresentation\022\014\n\004name\030\001 \001(\t\022\017\n\007open" + + "_in\030\002 \001(\t\022\021\n\topen_mode\030\003 \001(\t*C\n\rContentS" + + "ource\022\016\n\nfrom_other\020\000\022\017\n\013from_backup\020\001\022\021" + + "\n\rfrom_prebuild\020\002*?\n\016PortVisibility\022\026\n\022p" + + "rivate_visibility\020\000\022\025\n\021public_visibility" + + "\020\001*e\n\023OnPortExposedAction\022\n\n\006ignore\020\000\022\020\n" + + "\014open_browser\020\001\022\020\n\014open_preview\020\002\022\n\n\006not" + + "ify\020\003\022\022\n\016notify_private\020\004*9\n\020PortAutoExp" + + "osure\022\n\n\006trying\020\000\022\r\n\tsucceeded\020\001\022\n\n\006fail" + + "ed\020\002*1\n\tTaskState\022\013\n\007opening\020\000\022\013\n\007runnin" + + "g\020\001\022\n\n\006closed\020\0022\313\006\n\rStatusService\022|\n\020Sup" + + "ervisorStatus\022#.supervisor.SupervisorSta" + + "tusRequest\032$.supervisor.SupervisorStatus" + + "Response\"\035\202\323\344\223\002\027\022\025/v1/status/supervisor\022" + + "\203\001\n\tIDEStatus\022\034.supervisor.IDEStatusRequ" + + "est\032\035.supervisor.IDEStatusResponse\"9\202\323\344\223" + + "\0023\022\016/v1/status/ideZ!\022\037/v1/status/ide/wai" + + "t/{wait=true}\022\227\001\n\rContentStatus\022 .superv" + + "isor.ContentStatusRequest\032!.supervisor.C" + + "ontentStatusResponse\"A\202\323\344\223\002;\022\022/v1/status" + + "/contentZ%\022#/v1/status/content/wait/{wai" + + "t=true}\022l\n\014BackupStatus\022\037.supervisor.Bac" + + "kupStatusRequest\032 .supervisor.BackupStat" + + "usResponse\"\031\202\323\344\223\002\023\022\021/v1/status/backup\022\225\001" + + "\n\013PortsStatus\022\036.supervisor.PortsStatusRe" + + "quest\032\037.supervisor.PortsStatusResponse\"C" + + "\202\323\344\223\002=\022\020/v1/status/portsZ)\022\'/v1/status/p" + + "orts/observe/{observe=true}0\001\022\225\001\n\013TasksS" + + "tatus\022\036.supervisor.TasksStatusRequest\032\037." + + "supervisor.TasksStatusResponse\"C\202\323\344\223\002=\022\020" + + "/v1/status/tasksZ)\022\'/v1/status/tasks/obs" + + "erve/{observe=true}0\001BF\n\030io.gitpod.super" + + "visor.apiZ*github.com/gitpod-io/gitpod/s" + + "upervisor/apib\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -13860,7 +14010,7 @@ public io.gitpod.supervisor.api.Status.TaskPresentation getDefaultInstanceForTyp internal_static_supervisor_IDEStatusResponse_DesktopStatus_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_supervisor_IDEStatusResponse_DesktopStatus_descriptor, - new java.lang.String[] { "Link", "Label", }); + new java.lang.String[] { "Link", "Label", "ClientID", }); internal_static_supervisor_ContentStatusRequest_descriptor = getDescriptor().getMessageTypes().get(4); internal_static_supervisor_ContentStatusRequest_fieldAccessorTable = new diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/StatusServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/StatusServiceGrpc.java index 839fd68daae264..397266de54b431 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/StatusServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/StatusServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalOuterClass.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalOuterClass.java index bc26546c122449..8169d0798541be 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalOuterClass.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalOuterClass.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalServiceGrpc.java index 328b5af6480fa6..146dbd8c3e6bad 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TerminalServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Token.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Token.java index e6f68b54209dfa..8a5bea0aad54ad 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Token.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/Token.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TokenServiceGrpc.java b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TokenServiceGrpc.java index 300af3cc0e76f0..51aadf0d13941a 100644 --- a/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TokenServiceGrpc.java +++ b/components/supervisor-api/java/src/main/java/io/gitpod/supervisor/api/TokenServiceGrpc.java @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. // Licensed under the GNU Affero General Public License (AGPL). // See License-AGPL.txt in the project root for license information. diff --git a/components/supervisor-api/status.proto b/components/supervisor-api/status.proto index a312b68f6a3026..0b16a90a0da789 100644 --- a/components/supervisor-api/status.proto +++ b/components/supervisor-api/status.proto @@ -90,6 +90,7 @@ message IDEStatusResponse { message DesktopStatus { string link = 1; string label = 2; + string clientID = 3; } DesktopStatus desktop = 2; diff --git a/components/supervisor/frontend/src/index.ts b/components/supervisor/frontend/src/index.ts index 9b75fe222b595e..ffa333f2c497d3 100644 --- a/components/supervisor/frontend/src/index.ts +++ b/components/supervisor/frontend/src/index.ts @@ -102,11 +102,12 @@ const toStop = new DisposableCollection(); toStop.push({ dispose: () => window.removeEventListener('message', hideDesktopIdeEventListener) }); let isDesktopIde: undefined | boolean = undefined; - let ideStatus: undefined | { desktop: { link: string, label: string } } = undefined; + let ideStatus: undefined | { desktop: { link: string, label: string, clientID?: string } } = undefined; //#region current-frame let current: HTMLElement = loading.frame; let stopped = false; + let desktopRedirected = false; const nextFrame = () => { const instance = gitpodServiceClient.info.latestInstance; if (instance) { @@ -118,9 +119,22 @@ const toStop = new DisposableCollection(); if (isDesktopIde && !!ideStatus) { loading.setState({ desktopIdeLink: ideStatus.desktop.link, - desktopIdeLabel: ideStatus.desktop.label || "Open Desktop IDE" + desktopIdeLabel: ideStatus.desktop.label || "Open Desktop IDE", + desktopIdeClientID: ideStatus.desktop.clientID, }); - // window.open(ideStatus.desktop.link); + if (!desktopRedirected) { + desktopRedirected = true; + try { + const desktopLink = new URL(ideStatus.desktop.link) + // redirect only if points to desktop application + // don't navigate browser to another page + if (desktopLink.protocol != 'http:' && desktopLink.protocol != 'https:') { + window.location.href = ideStatus.desktop.link; + } + } catch (e) { + console.error('invalid desktop link:', e) + } + } return loading.frame; } } diff --git a/components/supervisor/pkg/supervisor/services.go b/components/supervisor/pkg/supervisor/services.go index 09fd8a599e1dde..7cfec9700886d6 100644 --- a/components/supervisor/pkg/supervisor/services.go +++ b/components/supervisor/pkg/supervisor/services.go @@ -43,8 +43,9 @@ type RegisterableRESTService interface { } type DesktopIDEStatus struct { - Link string `json:"link"` - Label string `json:"label"` + Link string `json:"link"` + Label string `json:"label"` + ClientID string `json:"clientID,omitempty"` } type ideReadyState struct { @@ -148,6 +149,7 @@ func (s *statusService) IDEStatus(ctx context.Context, req *api.IDEStatusRequest if i != nil { desktopStatus.Link = i.Link desktopStatus.Label = i.Label + desktopStatus.ClientID = i.ClientID } ok = ok && okR } diff --git a/installer/pkg/components/server/ide/configmap.go b/installer/pkg/components/server/ide/configmap.go index 364c050468bc65..fae96277b7b86b 100644 --- a/installer/pkg/components/server/ide/configmap.go +++ b/installer/pkg/components/server/ide/configmap.go @@ -24,6 +24,9 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { typeBrowser := "browser" typeDesktop := "desktop" + codeDesktop := "code-desktop" + codeDesktopInsiders := "code-desktop-insiders" + intellij := "intellij" goland := "goland" pycharm := "pycharm" @@ -32,9 +35,26 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { SupervisorImage: common.ImageName(ctx.Config.Repository, workspace.SupervisorImage, ctx.VersionManifest.Components.Workspace.Supervisor.Version), IDEOptions: IDEOptions{ IDEClients: map[string]IDEClient{ + "vscode": { + DefaultDesktopIDE: codeDesktop, + DesktopIDEs: []string{codeDesktop}, + InstallationSteps: []string{ + "If you don't see an open dialog by the browser, make sure you have VS Code installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + }, + }, + "vscode-insiders": { + DefaultDesktopIDE: codeDesktopInsiders, + DesktopIDEs: []string{codeDesktopInsiders}, + InstallationSteps: []string{ + "If you don't see an open dialog by the browser, make sure you have VS Code Insiders installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + }, + }, "jetbrains-gateway": { DefaultDesktopIDE: intellij, DesktopIDEs: []string{intellij, goland, pycharm, phpstorm}, + InstallationSteps: []string{ + "If you don't see an open dialog by the browser, make sure you have JetBrains Gateway with Gitpod Plugin installed on your machine, and then click ${OPEN_LINK_LABEL} below.", + }, }, }, Options: map[string]IDEOption{ @@ -55,14 +75,14 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Image: common.ImageName(ctx.Config.Repository, ide.CodeIDEImage, ctx.VersionManifest.Components.Workspace.CodeImage.Version), ResolveImageDigest: pointer.Bool(true), }, - "code-desktop": { + codeDesktop: { OrderKey: pointer.String("02"), Title: "VS Code", Type: typeDesktop, Logo: getIdeLogoPath("vscode"), Image: common.ImageName(ctx.Config.Repository, ide.CodeDesktopIDEImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.CodeDesktopImage.Version), }, - "code-desktop-insiders": { + codeDesktopInsiders: { OrderKey: pointer.String("03"), Title: "VS Code", Type: typeDesktop, @@ -105,7 +125,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { }, }, DefaultIDE: "code", - DefaultDesktopIDE: "code-desktop", + DefaultDesktopIDE: codeDesktop, }, } diff --git a/installer/pkg/components/server/ide/types.go b/installer/pkg/components/server/ide/types.go index b63078d29e44c3..68114a7b1312d8 100644 --- a/installer/pkg/components/server/ide/types.go +++ b/installer/pkg/components/server/ide/types.go @@ -38,4 +38,5 @@ type IDEOption struct { type IDEClient struct { DefaultDesktopIDE string `json:"defaultDesktopIDE,omitempty"` DesktopIDEs []string `json:"desktopIDEs,omitempty"` + InstallationSteps []string `json:"installationSteps,omitempty"` }