Skip to content

Commit 9c7b784

Browse files
Deployment env update (#29)
* feat(terraform): add Cloudinary environment variables for question service * feat(terraform): add environment variables for admin credentials and update .gitignore * feat(deployment): enhance environment variable handling for Cloud Run deployments in cd-deploy.yml and cd-preview.yml * feat(deployment): add environment variable support for matching service in cd-deploy.yml and cd-preview.yml * refactor(execution-service): restructure main function to start HTTP server before worker initialization for GCP deployments * feat(deployment): enhance environment variable configuration for user service in cd-preview.yml and server.js * feat(question-service): add health check endpoint and improve MongoDB connection handling; update environment variable configuration in cd-preview.yml * chore(workflows): remove obsolete CD workflows for preview deployments * fix: remove cd-deploy.yml
1 parent fcb9b54 commit 9c7b784

File tree

9 files changed

+219
-51
lines changed

9 files changed

+219
-51
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@ terraform.rc
4949

5050
# Optional: ignore plan files saved before destroying Terraform configuration
5151
# Uncomment the line below if you want to ignore planout files.
52-
# planout
52+
# planout
53+
54+
# Service account keys
55+
key.json
56+
*.json.key
57+
*-key.json
58+
service-account-key*.json

backend/execution-service/src/index.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,38 @@ const PORT = process.env.PORT || 8080;
77

88
async function main() {
99
let worker;
10-
11-
if (useGcp) {
12-
console.log("Starting Pub/Sub worker...");
13-
worker = new PubSubWorker();
14-
} else {
15-
console.log("Starting RabbitMQ worker...");
16-
worker = new RabbitMQWorker();
17-
}
18-
19-
await worker.start();
20-
2110
let server = null;
11+
12+
// Start HTTP server first for Cloud Run health checks
2213
if (useGcp) {
23-
// Create a minimal HTTP server for GCP Cloud Run health checks
2414
server = http.createServer((req, res) => {
2515
res.writeHead(200, { 'Content-Type': 'application/json' });
2616
res.end(JSON.stringify({
2717
data: 'Hello World!',
18+
status: 'ok',
2819
}));
2920
});
30-
server.listen(PORT);
21+
server.listen(PORT, () => {
22+
console.log(`HTTP server listening on port ${PORT}`);
23+
});
24+
}
25+
26+
27+
try {
28+
if (useGcp) {
29+
console.log("Starting Pub/Sub worker...");
30+
worker = new PubSubWorker();
31+
} else {
32+
console.log("Starting RabbitMQ worker...");
33+
worker = new RabbitMQWorker();
34+
}
35+
36+
37+
worker.start().catch((error) => {
38+
console.error("Failed to start worker:", error);
39+
});
40+
} catch (error) {
41+
console.error("Error initializing worker:", error);
3142
}
3243

3344
// Graceful shutdown

backend/question-service/src/config/db.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ const connectDB = async () => {
6565
console.log(`Database contains ${questionCount} questions`)
6666
}
6767
} catch (error) {
68-
console.log(error)
69-
process.exit(1)
68+
console.log("MongoDB connection error:", error)
69+
throw error
7070
}
7171
}
7272

backend/question-service/src/index.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ const connectDB = require('./config/db')
55
const { kafkaManager } = require('./config/kafka')
66
const { pubsubManager } = require('./config/pubsub')
77

8-
connectDB()
9-
108
const app = express()
119

1210
app.use(
1311
cors({
14-
origin: process.env.WEB_BASE_URL,
12+
origin: process.env.WEB_BASE_URL || "*", // Allow all origins if not set
1513
credentials: true,
1614
optionsSuccessStatus: 200,
1715
}),
@@ -20,16 +18,36 @@ app.use(
2018
app.use(express.json())
2119
app.use(express.urlencoded({ extended: false }))
2220

21+
// Health check endpoint
22+
app.get('/health', (req, res) => {
23+
res.json({ status: 'ok', service: 'question-service' })
24+
})
25+
2326
const PORT = process.env.PORT || 8003
2427
const useGcp = !!process.env.PUBSUB_PROJECT_ID;
2528

2629
app.listen(PORT, async () => {
2730
console.log(`Question service is running on port ${PORT}...`)
2831

29-
if (useGcp) {
30-
await pubsubManager.setupSubscribers();
32+
33+
if (process.env.MONGO_URI) {
34+
connectDB().catch((err) => {
35+
console.log("MongoDB connection error:", err);
36+
console.log("Server is running but MongoDB is not connected");
37+
});
3138
} else {
32-
await kafkaManager.setupSubscribers();
39+
console.log("MONGO_URI not set, skipping MongoDB connection");
40+
}
41+
42+
// Setup subscribers
43+
try {
44+
if (useGcp) {
45+
await pubsubManager.setupSubscribers();
46+
} else {
47+
await kafkaManager.setupSubscribers();
48+
}
49+
} catch (error) {
50+
console.error("Error setting up subscribers:", error);
3351
}
3452
})
3553

backend/user-service/src/server.js

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,54 @@ app.use(
2020
);
2121
app.use(express.json());
2222

23+
// Health check endpoint
24+
app.get("/health", (req, res) => {
25+
res.json({ status: "ok", service: "user-service" });
26+
});
27+
2328
// Routes
2429
app.use("/v1", authRoutes);
2530

26-
// Connect to MongoDB
27-
mongoose
28-
.connect(process.env.MONGO_URI)
29-
.then(() => console.log("MongoDB connected"))
30-
.then(async () => {
31-
console.log("MongoDB connected");
32-
33-
const adminUsername = process.env.ADMIN_USERNAME;
34-
const adminEmail = process.env.ADMIN_EMAIL;
35-
const adminPassword = process.env.ADMIN_PW;
31+
app.listen(PORT, () => {
32+
console.log(`Server started on port ${PORT}`);
33+
34+
if (process.env.MONGO_URI) {
35+
mongoose
36+
.connect(process.env.MONGO_URI)
37+
.then(() => {
38+
console.log("MongoDB connected");
39+
40+
// Create admin user if needed
41+
const adminUsername = process.env.ADMIN_USERNAME;
42+
const adminEmail = process.env.ADMIN_EMAIL;
43+
const adminPassword = process.env.ADMIN_PW;
3644

37-
const existingAdmin = await User.findOne({ username: adminUsername });
38-
39-
if (!existingAdmin) {
40-
const hashedPassword = await bcrypt.hash(adminPassword, 10);
41-
const admin = new User({
42-
username: adminUsername,
43-
email: adminEmail,
44-
password: hashedPassword,
45-
role: "admin",
46-
verified: true
45+
if (adminUsername && adminEmail && adminPassword) {
46+
User.findOne({ username: adminUsername })
47+
.then(async (existingAdmin) => {
48+
if (!existingAdmin) {
49+
const hashedPassword = await bcrypt.hash(adminPassword, 10);
50+
const admin = new User({
51+
username: adminUsername,
52+
email: adminEmail,
53+
password: hashedPassword,
54+
role: "admin",
55+
verified: true
56+
});
57+
await admin.save();
58+
console.log("Default admin user created");
59+
} else {
60+
console.log("Admin user already exists");
61+
}
62+
})
63+
.catch((err) => console.log("Error creating admin user:", err));
64+
}
65+
})
66+
.catch((err) => {
67+
console.log("MongoDB connection error:", err);
68+
console.log("Server is running but MongoDB is not connected");
4769
});
48-
await admin.save();
49-
console.log("Default admin user created");
50-
} else {
51-
console.log("Admin user already exists");
52-
}
53-
})
54-
.catch((err) => console.log("MongoDB connection error:", err));
55-
56-
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
70+
} else {
71+
console.log("MONGO_URI not set, skipping MongoDB connection");
72+
}
73+
});

setup-iam.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
$PROJECT_ID = "your-project-id"
2+
$SA_EMAIL = "peerprep-cd-sa@${PROJECT_ID}.iam.gserviceaccount.com"
3+
4+
Write-Host "Setting up IAM for project: $PROJECT_ID" -ForegroundColor Green
5+
Write-Host "Service Account: $SA_EMAIL" -ForegroundColor Green
6+
7+
# Cloud Run Admin
8+
Write-Host "`nGranting Cloud Run Admin role..." -ForegroundColor Yellow
9+
gcloud projects add-iam-policy-binding $PROJECT_ID `
10+
--member="serviceAccount:$SA_EMAIL" `
11+
--role="roles/run.admin"
12+
13+
# Artifact Registry Writer
14+
Write-Host "`nGranting Artifact Registry Writer role..." -ForegroundColor Yellow
15+
gcloud projects add-iam-policy-binding $PROJECT_ID `
16+
--member="serviceAccount:$SA_EMAIL" `
17+
--role="roles/artifactregistry.writer"
18+
19+
# Service Account User (to use the service account)
20+
Write-Host "`nGranting Service Account User role..." -ForegroundColor Yellow
21+
gcloud projects add-iam-policy-binding $PROJECT_ID `
22+
--member="serviceAccount:$SA_EMAIL" `
23+
--role="roles/iam.serviceAccountUser"
24+
25+
Write-Host "`nIAM setup complete!" -ForegroundColor Green
26+
Write-Host "`nNext step: Create service account key with:" -ForegroundColor Cyan
27+
Write-Host " gcloud iam service-accounts keys create key.json --iam-account=$SA_EMAIL" -ForegroundColor White
28+

setup-iam.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
PROJECT_ID="your-project-id"
2+
SA_EMAIL="peerprep-cd-sa@${PROJECT_ID}.iam.gserviceaccount.com"
3+
4+
# Cloud Run Admin
5+
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
6+
--member="serviceAccount:${SA_EMAIL}" \
7+
--role="roles/run.admin"
8+
9+
# Artifact Registry Writer
10+
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
11+
--member="serviceAccount:${SA_EMAIL}" \
12+
--role="roles/artifactregistry.writer"
13+
14+
# Service Account User (to use the service account)
15+
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
16+
--member="serviceAccount:${SA_EMAIL}" \
17+
--role="roles/iam.serviceAccountUser"

terraform/.terraform.lock.hcl

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

terraform/main.tf

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,36 @@ resource "google_cloud_run_v2_service" "user_service" {
238238
}
239239
}
240240

241+
env {
242+
name = "ADMIN_EMAIL"
243+
value_source {
244+
secret_key_ref {
245+
secret = "admin_email"
246+
version = "latest"
247+
}
248+
}
249+
}
250+
251+
env {
252+
name = "ADMIN_USERNAME"
253+
value_source {
254+
secret_key_ref {
255+
secret = "admin_username"
256+
version = "latest"
257+
}
258+
}
259+
}
260+
261+
env {
262+
name = "ADMIN_PW"
263+
value_source {
264+
secret_key_ref {
265+
secret = "admin_pw"
266+
version = "latest"
267+
}
268+
}
269+
}
270+
241271
resources {
242272
limits = {
243273
cpu = "1"
@@ -296,6 +326,46 @@ resource "google_cloud_run_v2_service" "question_service" {
296326
value = var.project_id
297327
}
298328

329+
env {
330+
name = "CLOUDINARY_CLOUD_NAME"
331+
value_source {
332+
secret_key_ref {
333+
secret = "cloudinary_cloud_name"
334+
version = "latest"
335+
}
336+
}
337+
}
338+
339+
env {
340+
name = "CLOUDINARY_API_KEY"
341+
value_source {
342+
secret_key_ref {
343+
secret = "cloudinary_api_key"
344+
version = "latest"
345+
}
346+
}
347+
}
348+
349+
env {
350+
name = "CLOUDINARY_API_SECRET"
351+
value_source {
352+
secret_key_ref {
353+
secret = "cloudinary_api_secret"
354+
version = "latest"
355+
}
356+
}
357+
}
358+
359+
env {
360+
name = "JWT_SECRET"
361+
value_source {
362+
secret_key_ref {
363+
secret = "jwt_secret"
364+
version = "latest"
365+
}
366+
}
367+
}
368+
299369
resources {
300370
limits = {
301371
cpu = "1"

0 commit comments

Comments
 (0)