Skip to content

Commit dd74e3a

Browse files
authored
GCP utils (#16)
### What changes were proposed in this pull request? Introduces a new `GCPUtils` class that provides a unified interface for interacting with various Google Cloud Platform services: - Secret Manager operations for managing secrets - Cloud Storage methods for file upload/download - Cloud Scheduler functionality for job creation - Cloud Run service deployment capabilities - Cloud SQL database management - Helper methods for resource verification ### Why are the changes needed? To centralize and standardize GCP service interactions within the application, reducing code duplication and providing a consistent interface for common cloud operations. This utility class will make it easier to manage cloud resources and perform common GCP operations throughout the application. ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? Each GCP service method includes error handling and follows GCP best practices. Integration tests should be added to verify interactions with each cloud service in isolation.
1 parent 151f0a1 commit dd74e3a

File tree

3 files changed

+1672
-110
lines changed

3 files changed

+1672
-110
lines changed

core/utils/gcpUtils.ts

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
2+
import { CloudSchedulerClient } from "@google-cloud/scheduler";
3+
import { Storage } from "@google-cloud/storage";
4+
import { CloudRunClient } from "@google-cloud/run";
5+
import { Compute } from "@google-cloud/compute";
6+
7+
export class GCPUtils {
8+
private secretManager: SecretManagerServiceClient;
9+
private scheduler: CloudSchedulerClient;
10+
private storage: Storage;
11+
private cloudRun: CloudRunClient;
12+
private compute: Compute;
13+
private projectId: string;
14+
15+
constructor(projectId: string) {
16+
this.projectId = projectId;
17+
this.secretManager = new SecretManagerServiceClient();
18+
this.scheduler = new CloudSchedulerClient();
19+
this.storage = new Storage();
20+
this.cloudRun = new CloudRunClient();
21+
this.compute = new Compute();
22+
}
23+
24+
// Secret Manager Methods
25+
async getSecret(secretName: string, version = "latest"): Promise<string> {
26+
try {
27+
const name = `projects/${this.projectId}/secrets/${secretName}/versions/${version}`;
28+
const [response] = await this.secretManager.accessSecretVersion({ name });
29+
return response.payload?.data?.toString() || "";
30+
} catch (error) {
31+
throw new Error(`Failed to get secret: ${error}`);
32+
}
33+
}
34+
35+
async createSecret(secretId: string, payload: string): Promise<void> {
36+
try {
37+
const parent = `projects/${this.projectId}`;
38+
await this.secretManager.createSecret({
39+
parent,
40+
secretId,
41+
secret: {
42+
replication: {
43+
automatic: {},
44+
},
45+
},
46+
});
47+
await this.secretManager.addSecretVersion({
48+
parent: `projects/${this.projectId}/secrets/${secretId}`,
49+
payload: {
50+
data: Buffer.from(payload, "utf8"),
51+
},
52+
});
53+
} catch (error) {
54+
throw new Error(`Failed to create secret: ${error}`);
55+
}
56+
}
57+
58+
// Cloud Storage Methods
59+
async uploadFile(
60+
bucketName: string,
61+
filePath: string,
62+
destination: string,
63+
): Promise<void> {
64+
try {
65+
const bucket = this.storage.bucket(bucketName);
66+
await bucket.upload(filePath, {
67+
destination,
68+
});
69+
} catch (error) {
70+
throw new Error(`Failed to upload file: ${error}`);
71+
}
72+
}
73+
74+
async downloadFile(
75+
bucketName: string,
76+
fileName: string,
77+
destination: string,
78+
): Promise<void> {
79+
try {
80+
const bucket = this.storage.bucket(bucketName);
81+
await bucket.file(fileName).download({
82+
destination,
83+
});
84+
} catch (error) {
85+
throw new Error(`Failed to download file: ${error}`);
86+
}
87+
}
88+
89+
// Cloud Scheduler Methods
90+
async createJob(
91+
jobName: string,
92+
schedule: string,
93+
targetUrl: string,
94+
): Promise<void> {
95+
try {
96+
const parent = `projects/${this.projectId}/locations/us-central1`;
97+
await this.scheduler.createJob({
98+
parent,
99+
job: {
100+
name: `${parent}/jobs/${jobName}`,
101+
httpTarget: {
102+
uri: targetUrl,
103+
httpMethod: "POST",
104+
},
105+
schedule,
106+
timeZone: "UTC",
107+
},
108+
});
109+
} catch (error) {
110+
throw new Error(`Failed to create job: ${error}`);
111+
}
112+
}
113+
114+
// Cloud Run Methods
115+
async deployService(
116+
serviceName: string,
117+
image: string,
118+
envVars: Record<string, string> = {},
119+
): Promise<void> {
120+
try {
121+
const parent = `projects/${this.projectId}/locations/us-central1`;
122+
await this.cloudRun.createService({
123+
parent,
124+
service: {
125+
name: `${parent}/services/${serviceName}`,
126+
template: {
127+
containers: [
128+
{
129+
image,
130+
env: Object.entries(envVars).map(([key, value]) => ({
131+
name: key,
132+
value,
133+
})),
134+
},
135+
],
136+
},
137+
},
138+
});
139+
} catch (error) {
140+
throw new Error(`Failed to deploy service: ${error}`);
141+
}
142+
}
143+
144+
// Helper Methods
145+
async checkResourceExists(resourcePath: string): Promise<boolean> {
146+
try {
147+
await this.secretManager.getProjectParent({
148+
name: resourcePath,
149+
});
150+
return true;
151+
} catch {
152+
return false;
153+
}
154+
}
155+
}
156+
157+
// Export a singleton instance
158+
export const gcpUtils = new GCPUtils(process.env.GOOGLE_CLOUD_PROJECT || "");

0 commit comments

Comments
 (0)