@@ -23,7 +23,12 @@ import { VsCommandError, vsCommand } from '../vscommand';
2323import { OpenShiftTerminalManager } from '../webview/openshift-terminal/openShiftTerminal' ;
2424import OpenShiftItem , { clusterRequired } from './openshiftItem' ;
2525import fetch = require( 'make-fetch-happen' ) ;
26- import { Cluster as KcuCluster } from '@kubernetes/client-node/dist/config_types' ;
26+ import { Cluster as KcuCluster , Context as KcuContext } from '@kubernetes/client-node/dist/config_types' ;
27+
28+ export interface QuickPickItemExt extends QuickPickItem {
29+ name : string ,
30+ cluster : string
31+ }
2732
2833export class Cluster extends OpenShiftItem {
2934
@@ -152,6 +157,15 @@ export class Cluster extends OpenShiftItem {
152157 } ) ;
153158 }
154159
160+ private static getProjectLabel ( ctx : KcuContext ) : string {
161+ const k8sConfig = new KubeConfigUtils ( ) ;
162+ const pn = k8sConfig . extractProjectNameFromContextName ( ctx . name ) || '' ;
163+ const ns = ctx . namespace || pn ;
164+ let label = ns . length > 0 ? ns : '[default]' ;
165+ if ( ns !== pn && pn . length > 0 ) label = `${ label } (${ pn } )` ;
166+ return label ;
167+ }
168+
155169 @vsCommand ( 'openshift.explorer.switchContext' )
156170 static async switchContext ( ) : Promise < string > {
157171 return new Promise < string > ( ( resolve , reject ) => {
@@ -161,8 +175,19 @@ export class Cluster extends OpenShiftItem {
161175 ) ;
162176 const deleteBtn = new quickBtn ( new ThemeIcon ( 'trash' ) , 'Delete' ) ;
163177 const quickPick = window . createQuickPick ( ) ;
164- const contextNames : QuickPickItem [ ] = contexts . map ( ( ctx ) => ( {
165- label : `${ ctx . name } ` ,
178+ const contextNames : QuickPickItemExt [ ] = contexts
179+ . map ( ( ctx ) => {
180+ return {
181+ ...ctx ,
182+ label : Cluster . getProjectLabel ( ctx )
183+ }
184+ } )
185+ . map ( ( ctx ) => ( {
186+ name : `${ ctx . name } ` ,
187+ cluster : `${ ctx . cluster } ` ,
188+ label : `${ ctx . label } ` ,
189+ description : `on ${ ctx . cluster } ` ,
190+ detail : `User: ${ ctx . user } ` ,
166191 buttons : [ deleteBtn ] ,
167192 } ) ) ;
168193 quickPick . items = contextNames ;
@@ -188,11 +213,32 @@ export class Cluster extends OpenShiftItem {
188213 selection = selects ;
189214 } ) ;
190215 quickPick . onDidAccept ( ( ) => {
191- const choice = selection [ 0 ] ;
216+ const choice = selection [ 0 ] as QuickPickItemExt ;
192217 hideDisposable . dispose ( ) ;
193218 quickPick . hide ( ) ;
194- Oc . Instance . setContext ( choice . label )
195- . then ( ( ) => resolve ( `Cluster context is changed to: ${ choice . label } .` ) )
219+ Oc . Instance . setContext ( choice . name )
220+ . then ( async ( ) => {
221+ const clusterURL = k8sConfig . findClusterURL ( choice . cluster ) ;
222+ if ( await LoginUtil . Instance . requireLogin ( clusterURL ) ) {
223+ const status = await Cluster . login ( choice . name , true ) ;
224+ if ( status ) {
225+ if ( Cluster . isSandboxCluster ( clusterURL )
226+ && ! k8sConfig . equalsToCurrentContext ( choice . name ) ) {
227+ await window . showWarningMessage (
228+ 'The cluster appears to be a OpenShift Dev Sandbox cluster, \
229+ but the required project doesn\'t appear to be existing. \
230+ The cluster provided default project is selected instead. ' ,
231+ 'OK' ,
232+ ) ;
233+ }
234+ }
235+ }
236+ const kcu = new KubeConfigUtils ( ) ;
237+ const currentContext = kcu . findContext ( kcu . currentContext ) ;
238+ const pr = currentContext ? Cluster . getProjectLabel ( currentContext ) : choice . label ;
239+ const cl = currentContext ? currentContext . cluster : choice . description ;
240+ resolve ( `Cluster context is changed to ${ pr } on ${ cl } .` ) ;
241+ } )
196242 . catch ( reject ) ;
197243 } ) ;
198244 quickPick . onDidTriggerButton ( ( button ) => {
@@ -337,7 +383,7 @@ export class Cluster extends OpenShiftItem {
337383 * - `undefined` if user pressed `Back` button
338384 * @returns string contaning cluster login method name or null if cancelled or undefined if Back is pressed
339385 */
340- private static async getLoginMethod ( ) : Promise < string | null | undefined > {
386+ private static async getLoginMethod ( clusterURL : string ) : Promise < string | null | undefined > {
341387 return new Promise < string | null | undefined > ( ( resolve , reject ) => {
342388 const loginActions : QuickPickItem [ ] = [
343389 {
@@ -350,6 +396,7 @@ export class Cluster extends OpenShiftItem {
350396 }
351397 ] ;
352398 const quickPick = window . createQuickPick ( ) ;
399+ quickPick . placeholder = `Select the log in method for: ${ clusterURL } ` ;
353400 quickPick . items = [ ...loginActions ] ;
354401 const cancelBtn = new quickBtn ( new ThemeIcon ( 'close' ) , 'Cancel' ) ;
355402 quickPick . buttons = [ QuickInputButtons . Back , cancelBtn ] ;
@@ -376,18 +423,25 @@ export class Cluster extends OpenShiftItem {
376423
377424 /**
378425 * Checks if we're already logged in to a cluster.
379- * So, if we are, no need to re-enter User Credentials of Token
426+ * So, if we are, no need to re-enter User Credentials of Token.
427+ *
428+ * If contextName is specified and points to a cluster with the same URI as clusterURI,
429+ * the context is used as context to be switched to. Otherwise, we'll use the first
430+ * context found for the clusrtURI specified.
380431 *
381432 * @param clusterURI URI of the cluster to login
382433 * @returns true in case we should continue with asking for credentials,
383434 * false in case we're already logged in
384435 */
385- static async shouldAskForLoginCredentials ( clusterURI : string ) : Promise < boolean > {
436+ static async shouldAskForLoginCredentials ( clusterURI : string , contextName ?: string ) : Promise < boolean > {
386437 const kcu = new KubeConfigUtils ( ) ;
387438 const cluster : KcuCluster = kcu . findCluster ( clusterURI ) ;
388439 if ( ! cluster ) return true ;
389440
390- const context = kcu . findContext ( cluster . name ) ;
441+ let context : KcuContext = contextName && kcu . findContext ( contextName ) ;
442+ if ( ! context || context . cluster !== context . cluster ) {
443+ context = kcu . findContextForCluster ( cluster . name ) ;
444+ }
391445 if ( ! context ) return true ;
392446
393447 // Save `current-context`
@@ -402,13 +456,42 @@ export class Cluster extends OpenShiftItem {
402456 return true ;
403457 }
404458
459+ private static isOpenshiftLocalCluster ( clusterURL : string ) : boolean {
460+ try {
461+ return new URL ( clusterURL ) . hostname === 'api.crc.testing' ;
462+ } catch ( _ ) {
463+ return false ;
464+ }
465+ }
466+
467+ private static isSandboxCluster ( clusterURL : string ) : boolean {
468+ try {
469+ return / a p i \. s a n d b o x - .* o p e n s h i f t a p p s \. c o m / . test ( new URL ( clusterURL ) . hostname ) ;
470+ } catch ( _ ) {
471+ return false ;
472+ }
473+ }
474+
475+ /**
476+ * Login to a cluster
477+ *
478+ * @param context - Required context name
479+ * @param skipConfirmation - 'true' in case we don't need any confirmation, 'false' - otherwise
480+ * @returns Successful login message, otherwise - 'null'
481+ */
405482 @vsCommand ( 'openshift.explorer.login' )
406- static async login ( context ?: any , skipConfirmation = false ) : Promise < string > {
483+ static async login ( context ?: string , skipConfirmation = false ) : Promise < string > {
407484 const response = await Cluster . requestLoginConfirmation ( skipConfirmation ) ;
408485
409486 if ( response !== 'Yes' ) return null ;
410487
411488 let clusterURL : string ;
489+ if ( context ) {
490+ // If context is specified, we'll initialize clusterURL from it
491+ const kcu = new KubeConfigUtils ( ) ;
492+ const ctx = kcu . findContext ( context ) ;
493+ clusterURL = ctx && kcu . findClusterURL ( ctx . cluster ) ;
494+ }
412495
413496 enum Step {
414497 selectCluster = 'selectCluster' ,
@@ -423,8 +506,9 @@ export class Cluster extends OpenShiftItem {
423506 case Step . selectCluster : {
424507 let clusterIsUp = false ;
425508 do {
426- clusterURL = await Cluster . getUrl ( ) ;
427-
509+ if ( ! clusterURL ) {
510+ clusterURL = await Cluster . getUrl ( ) ;
511+ }
428512 if ( ! clusterURL ) return null ;
429513
430514 try {
@@ -439,14 +523,7 @@ export class Cluster extends OpenShiftItem {
439523 // so it's running
440524 clusterIsUp = true ;
441525 } catch ( e ) {
442- let clusterURLObj : any = undefined ;
443- try {
444- clusterURLObj = new URL ( clusterURL ) ;
445- } catch ( _ ) {
446- // Ignore
447- }
448- if ( clusterURLObj && clusterURLObj . hostname === 'api.crc.testing' ) {
449- const startCrc = 'Start OpenShift Local' ;
526+ if ( Cluster . isOpenshiftLocalCluster ( clusterURL ) ) { const startCrc = 'Start OpenShift Local' ;
450527 const promptResponse = await window . showWarningMessage (
451528 'The cluster appears to be a OpenShift Local cluster, but it isn\'t running' ,
452529 'Use a different cluster' ,
@@ -458,7 +535,7 @@ export class Cluster extends OpenShiftItem {
458535 // it will take the cluster a few minutes to stabilize
459536 return null ;
460537 }
461- } else if ( clusterURLObj && / a p i \. s a n d b o x - . * o p e n s h i f t a p p s \. c o m / . test ( clusterURLObj . hostname ) ) {
538+ } else if ( Cluster . isSandboxCluster ( clusterURL ) ) {
462539 const devSandboxSignup = 'Sign up for OpenShift Dev Sandbox' ;
463540 const promptResponse = await window . showWarningMessage (
464541 'The cluster appears to be a OpenShift Dev Sandbox cluster, but it isn\'t running' ,
@@ -480,14 +557,14 @@ export class Cluster extends OpenShiftItem {
480557 } while ( ! clusterIsUp ) ;
481558
482559 // contibue if cluster requires User Credentials/Token
483- if ( ! ( await Cluster . shouldAskForLoginCredentials ( clusterURL ) ) ) {
560+ if ( ! ( await Cluster . shouldAskForLoginCredentials ( clusterURL , context ) ) ) {
484561 return null ;
485562 }
486563 step = Step . selectLoginMethod ;
487564 break ;
488565 }
489566 case Step . selectLoginMethod : {
490- const result = await Cluster . getLoginMethod ( ) ;
567+ const result = await Cluster . getLoginMethod ( clusterURL ) ;
491568 if ( result === null ) { // User cancelled the operation
492569 return null ;
493570 } else if ( ! result ) { // Back button is hit
@@ -564,6 +641,7 @@ export class Cluster extends OpenShiftItem {
564641 const addUser : QuickPickItem = { label : addUserLabel } ;
565642
566643 const quickPick = window . createQuickPick ( ) ;
644+ quickPick . placeholder = `Select or add username for: ${ clusterURL } ` ;
567645 quickPick . items = [ addUser , ...users ] ;
568646 const cancelBtn = new quickBtn ( new ThemeIcon ( 'close' ) , 'Cancel' ) ;
569647 quickPick . buttons = [ QuickInputButtons . Back , cancelBtn ] ;
@@ -632,7 +710,8 @@ export class Cluster extends OpenShiftItem {
632710 if ( ! username ) {
633711 const prompt = 'Provide Username for basic authentication to the API server' ;
634712 const validateInput = ( value : string ) => NameValidator . emptyName ( 'User name cannot be empty' , value ? value : '' ) ;
635- const newUsername = await inputValue ( prompt , '' , false , validateInput ) ;
713+ const newUsername = await inputValue ( prompt , '' , false , validateInput ,
714+ `Provide Username for: ${ clusterURL } ` ) ;
636715
637716 if ( newUsername === null ) {
638717 return null ; // Cancel
@@ -650,7 +729,8 @@ export class Cluster extends OpenShiftItem {
650729 password = await TokenStore . getItem ( 'login' , username ) ;
651730 const prompt = 'Provide Password for basic authentication to the API server' ;
652731 const validateInput = ( value : string ) => NameValidator . emptyName ( 'Password cannot be empty' , value ? value : '' ) ;
653- const newPassword = await inputValue ( prompt , password , true , validateInput ) ;
732+ const newPassword = await inputValue ( prompt , password , true , validateInput ,
733+ `Provide Password for: ${ clusterURL } ` ) ;
654734
655735 if ( newPassword === null ) {
656736 return null ; // Cancel
@@ -747,7 +827,8 @@ export class Cluster extends OpenShiftItem {
747827 if ( ! userToken ) {
748828 const prompt = 'Provide Bearer token for authentication to the API server' ;
749829 const validateInput = ( value : string ) => NameValidator . emptyName ( 'Bearer token cannot be empty' , value ? value : '' ) ;
750- ocToken = await inputValue ( prompt , token ? token : '' , true , validateInput ) ;
830+ ocToken = await inputValue ( prompt , token ? token : '' , true , validateInput ,
831+ `Provide Bearer token for: ${ clusterURL } ` ) ;
751832 if ( ocToken === null ) {
752833 return null ; // Cancel
753834 } else if ( ! ocToken ) {
0 commit comments