import { exec } from '../proc/shell'; export enum ServiceType { serviceEnv = 'serviceEnv', serviceCatalogEnv = 'serviceCatalogEnv', } export interface ServiceInstance { name: string; namespace: string; class: string; plan: string; status: string; } interface ServiceInstanceMap { [name: string]: ServiceInstance; } /** * Gets available service instances deployed to cluster. * @returns A list of ServiceInstance objects. */ export class ServiceCatalog { private serviceInstanceNames: string[] = []; private serviceInstanceList: ServiceInstance[] = []; private serviceInstanceMap: ServiceInstanceMap = {}; private ns: string | undefined; public get namespace(): string { return this.ns; } public set namespace(value: string) { this.ns = value; } async getServiceInstances( namespace?: string, ): Promise { // If we've already got service instances, just return those. let ns = namespace; if (!this.ns && !namespace) { // TODO restrict to relevant ns ns = '--all-namespaces'; } if ( this.serviceInstanceNames.length !== 0 && Object.keys(this.serviceInstanceMap).length !== 0 ) { return this.serviceInstanceList.filter( (SERVICE_INSTANCE_LIST) => SERVICE_INSTANCE_LIST.namespace === this.ns, ); } try { // svcat use the api server and preferred way to work with the service catalog let results = await exec(`svcat get instances -n ${ns}`, true); if (results.code !== 0) { // fallback results = await exec(`kubectl get serviceInstance -n ${ns}`, true); if (results.code !== 0) { return undefined; } } return this.parseServiceInstances(results.stdout); } catch (e) { return undefined; } } resultsPos = ( results: string, ): { namespacePos: number; namePos: number; sliceResults: number } => { const anchor = '-------+-------'; let sliceResults; let namePos; let namespacePos; if (results.includes(anchor)) { sliceResults = 2; namePos = 0; namespacePos = 1; } else { sliceResults = 1; namePos = 1; namespacePos = 0; } return { sliceResults, namePos, namespacePos }; }; parseServiceInstances(results: string): ServiceInstance[] { const serviceInstances = Array.of(); const { sliceResults, namePos, namespacePos } = this.resultsPos(results); // Remove headers + empty lines. const splitResults = results .split('\n') .slice(sliceResults) .filter((s) => s.length !== 0); for (const line of splitResults) { const fields = line.split(' ').filter((s) => s.length !== 0); const serviceInstance = { name: fields[namePos], namespace: fields[namespacePos], class: fields[2], plan: fields[3], status: fields[4], }; // Service instance name -> service instance map. this.serviceInstanceMap[serviceInstance.name] = serviceInstance; // All available service instance names. this.serviceInstanceNames.push(serviceInstance.name); this.serviceInstanceList.push(serviceInstance); serviceInstances.push(serviceInstance); } return serviceInstances; } }