diff --git a/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts b/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts index df5006cfb..43674de59 100644 --- a/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts +++ b/packages/teleterm/src/services/tshd/v1/cluster_pb.d.ts @@ -29,6 +29,12 @@ export class Cluster extends jspb.Message { setLoggedInUser(value?: LoggedInUser): Cluster; + hasFeatures(): boolean; + clearFeatures(): void; + getFeatures(): Features | undefined; + setFeatures(value?: Features): Cluster; + + serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): Cluster.AsObject; static toObject(includeInstance: boolean, msg: Cluster): Cluster.AsObject; @@ -47,6 +53,7 @@ export namespace Cluster { connected: boolean, leaf: boolean, loggedInUser?: LoggedInUser.AsObject, + features?: Features.AsObject, } } @@ -233,3 +240,24 @@ export namespace ResourceAccess { pb_delete: boolean, } } + +export class Features extends jspb.Message { + getAdvancedAccessWorkflows(): boolean; + setAdvancedAccessWorkflows(value: boolean): Features; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Features.AsObject; + static toObject(includeInstance: boolean, msg: Features): Features.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Features, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Features; + static deserializeBinaryFromReader(message: Features, reader: jspb.BinaryReader): Features; +} + +export namespace Features { + export type AsObject = { + advancedAccessWorkflows: boolean, + } +} diff --git a/packages/teleterm/src/services/tshd/v1/cluster_pb.js b/packages/teleterm/src/services/tshd/v1/cluster_pb.js index 4e88512ed..c8f481c31 100644 --- a/packages/teleterm/src/services/tshd/v1/cluster_pb.js +++ b/packages/teleterm/src/services/tshd/v1/cluster_pb.js @@ -17,6 +17,7 @@ var global = (function() { return this || window || global || self || Function(' goog.exportSymbol('proto.teleport.terminal.v1.ACL', null, global); goog.exportSymbol('proto.teleport.terminal.v1.Cluster', null, global); +goog.exportSymbol('proto.teleport.terminal.v1.Features', null, global); goog.exportSymbol('proto.teleport.terminal.v1.LoggedInUser', null, global); goog.exportSymbol('proto.teleport.terminal.v1.ResourceAccess', null, global); /** @@ -103,6 +104,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.teleport.terminal.v1.ResourceAccess.displayName = 'proto.teleport.terminal.v1.ResourceAccess'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.teleport.terminal.v1.Features = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.teleport.terminal.v1.Features, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.teleport.terminal.v1.Features.displayName = 'proto.teleport.terminal.v1.Features'; +} @@ -140,7 +162,8 @@ proto.teleport.terminal.v1.Cluster.toObject = function(includeInstance, msg) { proxyHost: jspb.Message.getFieldWithDefault(msg, 3, ""), connected: jspb.Message.getBooleanFieldWithDefault(msg, 4, false), leaf: jspb.Message.getBooleanFieldWithDefault(msg, 5, false), - loggedInUser: (f = msg.getLoggedInUser()) && proto.teleport.terminal.v1.LoggedInUser.toObject(includeInstance, f) + loggedInUser: (f = msg.getLoggedInUser()) && proto.teleport.terminal.v1.LoggedInUser.toObject(includeInstance, f), + features: (f = msg.getFeatures()) && proto.teleport.terminal.v1.Features.toObject(includeInstance, f) }; if (includeInstance) { @@ -202,6 +225,11 @@ proto.teleport.terminal.v1.Cluster.deserializeBinaryFromReader = function(msg, r reader.readMessage(value,proto.teleport.terminal.v1.LoggedInUser.deserializeBinaryFromReader); msg.setLoggedInUser(value); break; + case 8: + var value = new proto.teleport.terminal.v1.Features; + reader.readMessage(value,proto.teleport.terminal.v1.Features.deserializeBinaryFromReader); + msg.setFeatures(value); + break; default: reader.skipField(); break; @@ -274,6 +302,14 @@ proto.teleport.terminal.v1.Cluster.serializeBinaryToWriter = function(message, w proto.teleport.terminal.v1.LoggedInUser.serializeBinaryToWriter ); } + f = message.getFeatures(); + if (f != null) { + writer.writeMessage( + 8, + f, + proto.teleport.terminal.v1.Features.serializeBinaryToWriter + ); + } }; @@ -404,6 +440,43 @@ proto.teleport.terminal.v1.Cluster.prototype.hasLoggedInUser = function() { }; +/** + * optional Features features = 8; + * @return {?proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Cluster.prototype.getFeatures = function() { + return /** @type{?proto.teleport.terminal.v1.Features} */ ( + jspb.Message.getWrapperField(this, proto.teleport.terminal.v1.Features, 8)); +}; + + +/** + * @param {?proto.teleport.terminal.v1.Features|undefined} value + * @return {!proto.teleport.terminal.v1.Cluster} returns this +*/ +proto.teleport.terminal.v1.Cluster.prototype.setFeatures = function(value) { + return jspb.Message.setWrapperField(this, 8, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.teleport.terminal.v1.Cluster} returns this + */ +proto.teleport.terminal.v1.Cluster.prototype.clearFeatures = function() { + return this.setFeatures(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.teleport.terminal.v1.Cluster.prototype.hasFeatures = function() { + return jspb.Message.getField(this, 8) != null; +}; + + /** * List of repeated fields within this message type. @@ -1701,4 +1774,134 @@ proto.teleport.terminal.v1.ResourceAccess.prototype.setDelete = function(value) }; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.teleport.terminal.v1.Features.prototype.toObject = function(opt_includeInstance) { + return proto.teleport.terminal.v1.Features.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.teleport.terminal.v1.Features} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.terminal.v1.Features.toObject = function(includeInstance, msg) { + var f, obj = { + advancedAccessWorkflows: jspb.Message.getBooleanFieldWithDefault(msg, 1, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Features.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.teleport.terminal.v1.Features; + return proto.teleport.terminal.v1.Features.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.teleport.terminal.v1.Features} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.teleport.terminal.v1.Features} + */ +proto.teleport.terminal.v1.Features.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setAdvancedAccessWorkflows(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.teleport.terminal.v1.Features.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.teleport.terminal.v1.Features.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.teleport.terminal.v1.Features} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.teleport.terminal.v1.Features.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAdvancedAccessWorkflows(); + if (f) { + writer.writeBool( + 1, + f + ); + } +}; + + +/** + * optional bool advanced_access_workflows = 1; + * @return {boolean} + */ +proto.teleport.terminal.v1.Features.prototype.getAdvancedAccessWorkflows = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.teleport.terminal.v1.Features} returns this + */ +proto.teleport.terminal.v1.Features.prototype.setAdvancedAccessWorkflows = function(value) { + return jspb.Message.setProto3BooleanField(this, 1, value); +}; + + goog.object.extend(exports, proto.teleport.terminal.v1); diff --git a/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js b/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js index 16721498c..f2734c4bf 100644 --- a/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js +++ b/packages/teleterm/src/services/tshd/v1/service_grpc_pb.js @@ -603,6 +603,7 @@ function deserialize_teleport_terminal_v1_SetGatewayTargetSubresourceNameRequest // TerminalService describes Teleterm service var TerminalServiceService = exports.TerminalServiceService = { // ListRootClusters lists root clusters +// Does not include detailed cluster information that would require a network request. listRootClusters: { path: '/teleport.terminal.v1.TerminalService/ListRootClusters', requestStream: false, @@ -615,6 +616,7 @@ listRootClusters: { responseDeserialize: deserialize_teleport_terminal_v1_ListClustersResponse, }, // ListLeafClusters lists leaf clusters +// Does not include detailed cluster information that would require a network request. listLeafClusters: { path: '/teleport.terminal.v1.TerminalService/ListLeafClusters', requestStream: false, diff --git a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx index fb243d2b0..65f444c97 100644 --- a/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx +++ b/packages/teleterm/src/ui/TopBar/NavigationMenu/NavigationMenu.tsx @@ -59,8 +59,9 @@ export function NavigationMenu() { const [isPopoverOpened, setIsPopoverOpened] = useState(false); const selectorRef = useRef(); - if (!activeRootCluster) { - return <>; + const shouldShowMenu = !!activeRootCluster?.features?.advancedAccessWorkflows; + if (!shouldShowMenu) { + return null; } return ( diff --git a/packages/teleterm/src/ui/TopBar/TopBar.tsx b/packages/teleterm/src/ui/TopBar/TopBar.tsx index f4d53b634..fada9a694 100644 --- a/packages/teleterm/src/ui/TopBar/TopBar.tsx +++ b/packages/teleterm/src/ui/TopBar/TopBar.tsx @@ -20,8 +20,8 @@ export function TopBar() { - + ); diff --git a/packages/teleterm/src/ui/services/clusters/clustersService.ts b/packages/teleterm/src/ui/services/clusters/clustersService.ts index 96500a47f..2f78c111d 100644 --- a/packages/teleterm/src/ui/services/clusters/clustersService.ts +++ b/packages/teleterm/src/ui/services/clusters/clustersService.ts @@ -72,7 +72,6 @@ export class ClustersService extends ImmutableStore { async logout(clusterUri: string) { // TODO(gzdunek): logout and removeCluster should be combined into a single acton in tshd await this.client.logout(clusterUri); - await this.syncClusterInfo(clusterUri); this.removeResources(clusterUri); await this.removeCluster(clusterUri); await this.removeClusterKubeConfigs(clusterUri); @@ -705,8 +704,6 @@ export class ClustersService extends ImmutableStore { return useStore(this).state; } - // Note: client.getCluster ultimately reads data from the disk, so syncClusterInfo will not fail - // with a retryable error in case the certs have expired. private async syncClusterInfo(clusterUri: string) { const cluster = await this.client.getCluster(clusterUri); const assumedRequests = cluster.loggedInUser