import Compute from '@google-cloud/compute';
import { Storage } from '@google-cloud/storage';
// Note: Cloud Functions client requires @google-cloud/functions package
// For now, we'll use a simplified approach
import type { CloudCredentials, GCPComputeInstance, GCPStorageBucket, GCPCloudFunction, ResourceStatus } from '../types/index.js';
import { credentialManager } from '../utils/credential-manager.js';
export class GCPAdapter {
private compute: any | null = null; // Compute type from @google-cloud/compute
private storage: Storage | null = null;
private credentials: CloudCredentials | null = null;
private projectId: string;
private region: string;
constructor(projectId?: string, region: string = 'us-central1') {
this.projectId = projectId || '';
this.region = region;
}
/**
* Initialize GCP clients with credentials
*/
private async initializeClients(): Promise<void> {
if (!this.credentials) {
this.credentials = await credentialManager.getCredentials('gcp');
if (!this.credentials) {
this.credentials = credentialManager.loadFromEnvironment('gcp');
}
}
if (!this.projectId && this.credentials?.projectId) {
this.projectId = this.credentials.projectId;
}
if (!this.projectId) {
// Try to get from environment
this.projectId = process.env.GCP_PROJECT_ID || process.env.GOOGLE_CLOUD_PROJECT || '';
}
if (!this.projectId) {
throw new Error('GCP Project ID is required');
}
// Initialize clients
// GCP SDK automatically uses Application Default Credentials
// (GOOGLE_APPLICATION_CREDENTIALS env var or gcloud auth)
// @ts-ignore - Compute is a default export object, not a class
this.compute = Compute({ projectId: this.projectId });
this.storage = new Storage({ projectId: this.projectId });
// Cloud Functions client initialization would go here
// this.functionsClient = new CloudFunctionsServiceClient();
}
/**
* List Compute Engine instances
*/
async listComputeInstances(): Promise<GCPComputeInstance[]> {
await this.initializeClients();
if (!this.compute) throw new Error('Compute client not initialized');
try {
const instances: GCPComputeInstance[] = [];
const [zones] = await this.compute.getZones();
for (const zone of zones) {
const zoneName = zone.name || '';
const vm = this.compute.zone(zoneName);
const [vms] = await vm.getVMs();
for (const instance of vms) {
const networkInterfaces = instance.networkInterfaces || [];
const privateIp = networkInterfaces[0]?.networkIP;
const publicIp = networkInterfaces[0]?.accessConfigs?.[0]?.natIP;
instances.push({
id: instance.id?.toString() || instance.name || '',
type: 'instance',
name: instance.name || '',
projectId: this.projectId,
zone: zoneName,
region: this.extractRegion(zoneName),
status: this.mapInstanceStatus(instance.status),
machineType: this.extractMachineType(instance.machineType || ''),
image: this.extractImage(instance.disks?.[0]?.initializeParams?.sourceImage || ''),
privateIp,
publicIp,
creationTimestamp: instance.creationTimestamp ? new Date(instance.creationTimestamp) : undefined,
labels: instance.labels,
});
}
}
return instances;
} catch (error) {
throw new Error(`Failed to list compute instances: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* List Cloud Storage buckets
*/
async listStorageBuckets(): Promise<GCPStorageBucket[]> {
await this.initializeClients();
if (!this.storage) throw new Error('Storage client not initialized');
try {
const [buckets] = await this.storage.getBuckets();
const bucketList: GCPStorageBucket[] = [];
for (const bucket of buckets) {
const [metadata] = await bucket.getMetadata();
bucketList.push({
id: bucket.name,
type: 'storage',
name: bucket.name,
projectId: this.projectId,
location: metadata.location || this.region,
status: 'running',
bucketName: bucket.name,
storageClass: metadata.storageClass,
labels: metadata.labels ? Object.fromEntries(
Object.entries(metadata.labels).map(([k, v]) => [k, v || ''])
) : undefined,
});
}
return bucketList;
} catch (error) {
throw new Error(`Failed to list storage buckets: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* List Cloud Functions
*/
async listCloudFunctions(): Promise<GCPCloudFunction[]> {
await this.initializeClients();
// Simplified implementation - would use Cloud Functions API in production
// For now, return empty array as Cloud Functions client requires additional setup
try {
// In production, would use:
// const { CloudFunctionsServiceClient } = await import('@google-cloud/functions');
// this.functionsClient = new CloudFunctionsServiceClient();
// const parent = `projects/${this.projectId}/locations/-`;
// const [functionList] = await this.functionsClient.listFunctions({ parent });
return [];
} catch (error) {
throw new Error(`Failed to list cloud functions: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Helper: Extract region from zone
*/
private extractRegion(zone: string): string {
// Zones are like us-central1-a, extract us-central1
const match = zone.match(/^([a-z]+-[a-z]+[0-9]+)/);
return match ? match[1] : this.region;
}
/**
* Helper: Extract machine type from full path
*/
private extractMachineType(machineType: string): string {
// Machine types are like projects/.../machineTypes/n1-standard-1
const match = machineType.match(/machineTypes\/([^\/]+)$/);
return match ? match[1] : machineType;
}
/**
* Helper: Extract image from full path
*/
private extractImage(imagePath: string): string {
// Images are like projects/.../images/ubuntu-2004
const match = imagePath.match(/images\/([^\/]+)$/);
return match ? match[1] : imagePath;
}
/**
* Helper: Map GCP instance status
*/
private mapInstanceStatus(status?: string): ResourceStatus {
const statusMap: Record<string, ResourceStatus> = {
RUNNING: 'running',
STOPPED: 'stopped',
STOPPING: 'stopped',
STARTING: 'pending',
PROVISIONING: 'pending',
TERMINATED: 'terminated',
};
return statusMap[status || ''] || 'unknown';
}
}