1use crate::{bindings::binding_env_var_name, ErrorData, Platform, ResourceRef, Result};
2use alien_error::AlienError;
3use std::collections::HashMap;
4
5pub const ENV_ALIEN_CURRENT_WORKER_BINDING_NAME: &str = "ALIEN_CURRENT_WORKER_BINDING_NAME";
6pub const ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME: &str = "ALIEN_CURRENT_CONTAINER_BINDING_NAME";
7pub const ENV_OPERATOR_BASE_PLATFORM: &str = "OPERATOR_BASE_PLATFORM";
8pub const ENV_ALIEN_DEPLOYMENT_TYPE: &str = "ALIEN_DEPLOYMENT_TYPE";
9pub const ENV_ALIEN_LAMBDA_MODE: &str = "ALIEN_LAMBDA_MODE";
10pub const ENV_ALIEN_RUNTIME_SEND_OTLP: &str = "ALIEN_RUNTIME_SEND_OTLP";
11pub const ENV_ALIEN_RUNTIME_SECRETS: &str = "ALIEN_RUNTIME_SECRETS";
12pub const ENV_ALIEN_SECRETS: &str = "ALIEN_SECRETS";
13pub const ENV_ALIEN_SECRET_ENV_REVISION: &str = "ALIEN_SECRET_ENV_REVISION";
16pub const ENV_ALIEN_TRANSPORT: &str = "ALIEN_TRANSPORT";
17pub const ENV_ALIEN_DEPLOYMENT_ID: &str = "ALIEN_DEPLOYMENT_ID";
18pub const ENV_ALIEN_DEPLOYMENT_NAME: &str = "ALIEN_DEPLOYMENT_NAME";
19pub const ENV_ALIEN_RESOURCE_ID: &str = "ALIEN_RESOURCE_ID";
23pub const ENV_ALIEN_PUBLIC_ENDPOINTS_JSON: &str = "ALIEN_PUBLIC_ENDPOINTS_JSON";
24pub const ENV_ALIEN_COMMANDS_TOKEN: &str = "ALIEN_COMMANDS_TOKEN";
25pub const ENV_ALIEN_COMMANDS_TOKEN_FILE: &str = "ALIEN_COMMANDS_TOKEN_FILE";
29pub const ENV_ALIEN_COMMANDS_LEASE_SECONDS: &str = "ALIEN_COMMANDS_LEASE_SECONDS";
31pub const ENV_ALIEN_COMMANDS_MAX_LEASES: &str = "ALIEN_COMMANDS_MAX_LEASES";
33pub const ENV_ALIEN_COMMANDS_POLL_INTERVAL_MS: &str = "ALIEN_COMMANDS_POLL_INTERVAL_MS";
35pub const ENV_ALIEN_COMMANDS_POLL_MAX_INTERVAL_MS: &str = "ALIEN_COMMANDS_POLL_MAX_INTERVAL_MS";
37pub const ENV_ALIEN_COMMANDS_POLL_JITTER: &str = "ALIEN_COMMANDS_POLL_JITTER";
39pub const ENV_ALIEN_COMMANDS_DRAIN_TIMEOUT_MS: &str = "ALIEN_COMMANDS_DRAIN_TIMEOUT_MS";
41pub const ENV_ALIEN_COMMANDS_TARGET_RESOURCE_ID: &str = "ALIEN_COMMANDS_TARGET_RESOURCE_ID";
43pub const ENV_ALIEN_COMMANDS_URL: &str = "ALIEN_COMMANDS_URL";
50pub const ENV_ALIEN_COMMANDS_TARGET_RESOURCE_TYPE: &str = "ALIEN_COMMANDS_TARGET_RESOURCE_TYPE";
56pub const ENV_ALIEN_MANAGER_URL: &str = "ALIEN_MANAGER_URL";
58pub const ENV_ALIEN_DEPLOYMENT_TOKEN: &str = "ALIEN_DEPLOYMENT_TOKEN";
60pub const ENV_ALIEN_WORKER_GRPC_ADDRESS: &str = "ALIEN_WORKER_GRPC_ADDRESS";
65pub const ENV_ALIEN_WORKER_TIMEOUT_SECONDS: &str = "ALIEN_WORKER_TIMEOUT_SECONDS";
69pub const ENV_AWS_ACCOUNT_ID: &str = "AWS_ACCOUNT_ID";
70pub const ENV_AWS_REGION: &str = "AWS_REGION";
71pub const ENV_AZURE_CLIENT_ID: &str = "AZURE_CLIENT_ID";
72pub const ENV_AZURE_REGION: &str = "AZURE_REGION";
73pub const ENV_AZURE_SUBSCRIPTION_ID: &str = "AZURE_SUBSCRIPTION_ID";
74pub const ENV_AZURE_TENANT_ID: &str = "AZURE_TENANT_ID";
75pub const ENV_GCP_PROJECT_ID: &str = "GCP_PROJECT_ID";
76pub const ENV_GCP_REGION: &str = "GCP_REGION";
77pub const ENV_GOOGLE_CLOUD_PROJECT: &str = "GOOGLE_CLOUD_PROJECT";
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum RuntimeEnvironmentValue {
81 Literal(&'static str),
82 AwsAccountId,
83 AwsRegion,
84 AzureClientId,
85 AzureRegion,
86 AzureSubscriptionId,
87 AzureTenantId,
88 BasePlatform,
89 CurrentContainerBindingName,
90 CurrentWorkerBindingName,
91 GcpProjectId,
92 GcpRegion,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub struct RuntimeEnvironmentEntry {
97 pub name: &'static str,
98 pub value: RuntimeEnvironmentValue,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
102pub enum RuntimeEnvironmentBindingSource {
103 LinkedResource(ResourceRef),
104 CurrentContainer,
105 CurrentWorker,
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct RuntimeEnvironmentBindingEntry {
110 pub env_name: String,
111 pub binding_name: String,
112 pub source: RuntimeEnvironmentBindingSource,
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116pub enum RuntimeEnvironmentPlanEntry {
117 Scalar(RuntimeEnvironmentEntry),
118 Binding(RuntimeEnvironmentBindingEntry),
119}
120
121#[derive(Debug, Clone, Default, PartialEq, Eq)]
122pub struct RuntimeEnvironmentPlan {
123 entries: Vec<RuntimeEnvironmentPlanEntry>,
124}
125
126impl RuntimeEnvironmentPlan {
127 pub fn new() -> Self {
128 Self::default()
129 }
130
131 pub fn add_scalar_entries(
132 mut self,
133 entries: impl IntoIterator<Item = RuntimeEnvironmentEntry>,
134 ) -> Self {
135 self.entries
136 .extend(entries.into_iter().map(RuntimeEnvironmentPlanEntry::Scalar));
137 self
138 }
139
140 pub fn add_linked_bindings(mut self, links: &[ResourceRef]) -> Self {
141 self.entries.extend(links.iter().cloned().map(|link| {
142 let binding_name = link.id().to_string();
143 RuntimeEnvironmentPlanEntry::Binding(RuntimeEnvironmentBindingEntry {
144 env_name: binding_env_var_name(&binding_name),
145 binding_name,
146 source: RuntimeEnvironmentBindingSource::LinkedResource(link),
147 })
148 }));
149 self
150 }
151
152 pub fn add_current_worker_binding(mut self, worker_id: &str) -> Self {
153 self.entries.push(RuntimeEnvironmentPlanEntry::Binding(
154 RuntimeEnvironmentBindingEntry {
155 env_name: binding_env_var_name(worker_id),
156 binding_name: worker_id.to_string(),
157 source: RuntimeEnvironmentBindingSource::CurrentWorker,
158 },
159 ));
160 self
161 }
162
163 pub fn add_current_container_binding(mut self, container_id: &str) -> Self {
164 self.entries.push(RuntimeEnvironmentPlanEntry::Binding(
165 RuntimeEnvironmentBindingEntry {
166 env_name: binding_env_var_name(container_id),
167 binding_name: container_id.to_string(),
168 source: RuntimeEnvironmentBindingSource::CurrentContainer,
169 },
170 ));
171 self
172 }
173
174 pub fn entries(&self) -> &[RuntimeEnvironmentPlanEntry] {
175 &self.entries
176 }
177}
178
179pub trait RuntimeEnvironmentRenderer {
180 type Value;
181
182 fn render_runtime_environment_value(
183 &self,
184 value: RuntimeEnvironmentValue,
185 ) -> Result<Option<Self::Value>>;
186
187 fn render_runtime_environment_binding(
188 &self,
189 entry: &RuntimeEnvironmentBindingEntry,
190 ) -> Result<Option<Self::Value>>;
191}
192
193pub fn standard_runtime_environment_plan(platform: Platform) -> Vec<RuntimeEnvironmentEntry> {
194 let mut entries = vec![RuntimeEnvironmentEntry {
195 name: ENV_ALIEN_DEPLOYMENT_TYPE,
196 value: RuntimeEnvironmentValue::Literal(platform.as_str()),
197 }];
198
199 match platform {
200 Platform::Aws => entries.push(RuntimeEnvironmentEntry {
201 name: ENV_AWS_ACCOUNT_ID,
202 value: RuntimeEnvironmentValue::AwsAccountId,
203 }),
204 Platform::Gcp => entries.extend([
205 RuntimeEnvironmentEntry {
206 name: ENV_GOOGLE_CLOUD_PROJECT,
207 value: RuntimeEnvironmentValue::GcpProjectId,
208 },
209 RuntimeEnvironmentEntry {
210 name: ENV_GCP_PROJECT_ID,
211 value: RuntimeEnvironmentValue::GcpProjectId,
212 },
213 RuntimeEnvironmentEntry {
214 name: ENV_GCP_REGION,
215 value: RuntimeEnvironmentValue::GcpRegion,
216 },
217 ]),
218 Platform::Azure => entries.extend([
219 RuntimeEnvironmentEntry {
220 name: ENV_AZURE_SUBSCRIPTION_ID,
221 value: RuntimeEnvironmentValue::AzureSubscriptionId,
222 },
223 RuntimeEnvironmentEntry {
224 name: ENV_AZURE_TENANT_ID,
225 value: RuntimeEnvironmentValue::AzureTenantId,
226 },
227 RuntimeEnvironmentEntry {
228 name: ENV_AZURE_REGION,
229 value: RuntimeEnvironmentValue::AzureRegion,
230 },
231 ]),
232 Platform::Kubernetes => entries.push(RuntimeEnvironmentEntry {
233 name: ENV_OPERATOR_BASE_PLATFORM,
234 value: RuntimeEnvironmentValue::BasePlatform,
235 }),
236 Platform::Machines | Platform::Local | Platform::Test => {}
237 }
238
239 entries
240}
241
242pub fn kubernetes_base_platform_runtime_environment_plan(
243 base_platform: Option<Platform>,
244) -> Vec<RuntimeEnvironmentEntry> {
245 match base_platform {
246 Some(Platform::Aws) => vec![
247 RuntimeEnvironmentEntry {
248 name: ENV_AWS_ACCOUNT_ID,
249 value: RuntimeEnvironmentValue::AwsAccountId,
250 },
251 RuntimeEnvironmentEntry {
252 name: ENV_AWS_REGION,
253 value: RuntimeEnvironmentValue::AwsRegion,
254 },
255 ],
256 Some(Platform::Gcp) => vec![
257 RuntimeEnvironmentEntry {
258 name: ENV_GOOGLE_CLOUD_PROJECT,
259 value: RuntimeEnvironmentValue::GcpProjectId,
260 },
261 RuntimeEnvironmentEntry {
262 name: ENV_GCP_PROJECT_ID,
263 value: RuntimeEnvironmentValue::GcpProjectId,
264 },
265 RuntimeEnvironmentEntry {
266 name: ENV_GCP_REGION,
267 value: RuntimeEnvironmentValue::GcpRegion,
268 },
269 ],
270 Some(Platform::Azure) => vec![
271 RuntimeEnvironmentEntry {
272 name: ENV_AZURE_SUBSCRIPTION_ID,
273 value: RuntimeEnvironmentValue::AzureSubscriptionId,
274 },
275 RuntimeEnvironmentEntry {
276 name: ENV_AZURE_TENANT_ID,
277 value: RuntimeEnvironmentValue::AzureTenantId,
278 },
279 RuntimeEnvironmentEntry {
280 name: ENV_AZURE_REGION,
281 value: RuntimeEnvironmentValue::AzureRegion,
282 },
283 RuntimeEnvironmentEntry {
284 name: ENV_AZURE_CLIENT_ID,
285 value: RuntimeEnvironmentValue::AzureClientId,
286 },
287 ],
288 _ => Vec::new(),
289 }
290}
291
292pub fn worker_transport_runtime_environment_plan(
293 platform: Platform,
294) -> Vec<RuntimeEnvironmentEntry> {
295 match platform {
296 Platform::Aws => vec![
297 RuntimeEnvironmentEntry {
298 name: ENV_ALIEN_TRANSPORT,
299 value: RuntimeEnvironmentValue::Literal("lambda"),
300 },
301 RuntimeEnvironmentEntry {
302 name: ENV_ALIEN_LAMBDA_MODE,
303 value: RuntimeEnvironmentValue::Literal("buffered"),
304 },
305 ],
306 Platform::Gcp => vec![RuntimeEnvironmentEntry {
307 name: ENV_ALIEN_TRANSPORT,
308 value: RuntimeEnvironmentValue::Literal("cloud-run"),
309 }],
310 Platform::Azure => vec![RuntimeEnvironmentEntry {
311 name: ENV_ALIEN_TRANSPORT,
312 value: RuntimeEnvironmentValue::Literal("container-app"),
313 }],
314 Platform::Kubernetes | Platform::Machines => vec![RuntimeEnvironmentEntry {
315 name: ENV_ALIEN_TRANSPORT,
316 value: RuntimeEnvironmentValue::Literal("http"),
317 }],
318 Platform::Local | Platform::Test => vec![RuntimeEnvironmentEntry {
319 name: ENV_ALIEN_TRANSPORT,
320 value: RuntimeEnvironmentValue::Literal("local"),
326 }],
327 }
328}
329
330pub fn worker_runtime_environment_plan(platform: Platform) -> Vec<RuntimeEnvironmentEntry> {
331 let mut entries = standard_runtime_environment_plan(platform);
332 entries.extend(worker_transport_runtime_environment_plan(platform));
333 entries.push(RuntimeEnvironmentEntry {
334 name: ENV_ALIEN_RUNTIME_SEND_OTLP,
335 value: RuntimeEnvironmentValue::Literal("true"),
336 });
337 entries.push(RuntimeEnvironmentEntry {
338 name: ENV_ALIEN_CURRENT_WORKER_BINDING_NAME,
339 value: RuntimeEnvironmentValue::CurrentWorkerBindingName,
340 });
341 if platform == Platform::Azure {
342 entries.push(RuntimeEnvironmentEntry {
343 name: ENV_AZURE_CLIENT_ID,
344 value: RuntimeEnvironmentValue::AzureClientId,
345 });
346 }
347 entries
348}
349
350pub fn worker_runtime_environment_contract(
351 platform: Platform,
352 worker_id: &str,
353 links: &[ResourceRef],
354) -> RuntimeEnvironmentPlan {
355 RuntimeEnvironmentPlan::new()
356 .add_scalar_entries(worker_runtime_environment_plan(platform))
357 .add_linked_bindings(links)
358 .add_current_worker_binding(worker_id)
359}
360
361pub fn container_runtime_environment_plan(platform: Platform) -> Vec<RuntimeEnvironmentEntry> {
362 let mut entries = standard_runtime_environment_plan(platform);
363 entries.push(RuntimeEnvironmentEntry {
366 name: ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME,
367 value: RuntimeEnvironmentValue::CurrentContainerBindingName,
368 });
369 entries
370}
371
372pub fn container_runtime_environment_contract(
373 platform: Platform,
374 container_id: &str,
375 links: &[ResourceRef],
376) -> RuntimeEnvironmentPlan {
377 RuntimeEnvironmentPlan::new()
378 .add_scalar_entries(container_runtime_environment_plan(platform))
379 .add_linked_bindings(links)
380 .add_current_container_binding(container_id)
381}
382
383pub fn daemon_runtime_environment_plan(platform: Platform) -> Vec<RuntimeEnvironmentEntry> {
384 standard_runtime_environment_plan(platform)
389}
390
391pub fn daemon_runtime_environment_contract(
392 platform: Platform,
393 links: &[ResourceRef],
394) -> RuntimeEnvironmentPlan {
395 RuntimeEnvironmentPlan::new()
396 .add_scalar_entries(daemon_runtime_environment_plan(platform))
397 .add_linked_bindings(links)
398}
399
400pub fn render_runtime_environment_entries<R>(
401 entries: impl IntoIterator<Item = RuntimeEnvironmentEntry>,
402 renderer: &R,
403) -> Result<Vec<(&'static str, R::Value)>>
404where
405 R: RuntimeEnvironmentRenderer,
406{
407 let mut rendered = Vec::new();
408 for entry in entries {
409 if let Some(value) = renderer.render_runtime_environment_value(entry.value)? {
410 rendered.push((entry.name, value));
411 }
412 }
413 Ok(rendered)
414}
415
416pub fn render_runtime_environment_plan<R>(
417 plan: &RuntimeEnvironmentPlan,
418 renderer: &R,
419) -> Result<Vec<(String, R::Value)>>
420where
421 R: RuntimeEnvironmentRenderer,
422{
423 let mut rendered = Vec::new();
424 for entry in plan.entries() {
425 match entry {
426 RuntimeEnvironmentPlanEntry::Scalar(entry) => {
427 if let Some(value) = renderer.render_runtime_environment_value(entry.value)? {
428 rendered.push((entry.name.to_string(), value));
429 }
430 }
431 RuntimeEnvironmentPlanEntry::Binding(entry) => {
432 if let Some(value) = renderer.render_runtime_environment_binding(entry)? {
433 rendered.push((entry.env_name.clone(), value));
434 }
435 }
436 }
437 }
438 Ok(rendered)
439}
440
441pub fn is_runtime_environment_contract_name(name: &str) -> bool {
442 matches!(
443 name,
444 ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME
445 | ENV_ALIEN_CURRENT_WORKER_BINDING_NAME
446 | ENV_OPERATOR_BASE_PLATFORM
447 | ENV_ALIEN_DEPLOYMENT_TYPE
448 | ENV_ALIEN_LAMBDA_MODE
449 | ENV_ALIEN_RUNTIME_SEND_OTLP
450 | ENV_ALIEN_TRANSPORT
451 | ENV_AWS_ACCOUNT_ID
452 | ENV_AWS_REGION
453 | ENV_AZURE_CLIENT_ID
454 | ENV_AZURE_REGION
455 | ENV_AZURE_SUBSCRIPTION_ID
456 | ENV_AZURE_TENANT_ID
457 | ENV_GCP_PROJECT_ID
458 | ENV_GCP_REGION
459 | ENV_GOOGLE_CLOUD_PROJECT
460 ) || (name.starts_with("ALIEN_") && name.ends_with("_BINDING"))
461}
462
463pub fn is_reserved_runtime_environment_name(name: &str) -> bool {
464 is_runtime_environment_contract_name(name)
465 || matches!(
466 name,
467 ENV_ALIEN_WORKER_GRPC_ADDRESS
468 | ENV_ALIEN_WORKER_TIMEOUT_SECONDS
469 | ENV_ALIEN_COMMANDS_TOKEN
470 | ENV_ALIEN_COMMANDS_TOKEN_FILE
471 | ENV_ALIEN_COMMANDS_LEASE_SECONDS
472 | ENV_ALIEN_COMMANDS_MAX_LEASES
473 | ENV_ALIEN_COMMANDS_POLL_INTERVAL_MS
474 | ENV_ALIEN_COMMANDS_POLL_MAX_INTERVAL_MS
475 | ENV_ALIEN_COMMANDS_POLL_JITTER
476 | ENV_ALIEN_COMMANDS_DRAIN_TIMEOUT_MS
477 | ENV_ALIEN_COMMANDS_TARGET_RESOURCE_ID
478 | ENV_ALIEN_COMMANDS_TARGET_RESOURCE_TYPE
479 | ENV_ALIEN_COMMANDS_URL
480 | ENV_ALIEN_DEPLOYMENT_ID
481 | ENV_ALIEN_DEPLOYMENT_NAME
482 | "ALIEN_DEPLOYMENT_SERVICE_ACCOUNT"
483 | ENV_ALIEN_DEPLOYMENT_TOKEN
484 | ENV_ALIEN_MANAGER_URL
485 | ENV_ALIEN_RESOURCE_ID
486 | ENV_ALIEN_PUBLIC_ENDPOINTS_JSON
487 | ENV_ALIEN_RUNTIME_SECRETS
488 | ENV_ALIEN_SECRET_ENV_REVISION
489 | ENV_ALIEN_SECRETS
490 )
491 || name.starts_with("ALIEN_BINDING_")
492}
493
494pub fn validate_runtime_environment_user_vars<'a>(
495 names: impl IntoIterator<Item = &'a str>,
496) -> Result<()> {
497 let reserved: Vec<String> = names
498 .into_iter()
499 .filter(|name| is_reserved_runtime_environment_name(name))
500 .map(ToString::to_string)
501 .collect();
502 if reserved.is_empty() {
503 return Ok(());
504 }
505
506 Err(AlienError::new(ErrorData::GenericError {
507 message: format!(
508 "Environment variables use reserved Alien runtime names: {}",
509 reserved.join(", ")
510 ),
511 }))
512}
513
514pub fn validate_runtime_environment_user_map(env: &HashMap<String, String>) -> Result<()> {
515 validate_runtime_environment_user_vars(env.keys().map(String::as_str))
516}
517
518pub fn validate_prepared_runtime_environment_vars<'a>(
519 names: impl IntoIterator<Item = &'a str>,
520) -> Result<()> {
521 let reserved: Vec<String> = names
522 .into_iter()
523 .filter(|name| is_runtime_environment_contract_name(name))
524 .map(ToString::to_string)
525 .collect();
526 if reserved.is_empty() {
527 return Ok(());
528 }
529
530 Err(AlienError::new(ErrorData::GenericError {
531 message: format!(
532 "Environment variables collide with Alien runtime contract names: {}",
533 reserved.join(", ")
534 ),
535 }))
536}
537
538pub fn validate_prepared_runtime_environment_map(env: &HashMap<String, String>) -> Result<()> {
539 validate_prepared_runtime_environment_vars(env.keys().map(String::as_str))
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545
546 #[test]
547 fn reserves_builtin_and_binding_environment_names() {
548 assert!(is_reserved_runtime_environment_name(ENV_ALIEN_TRANSPORT));
549 assert!(is_reserved_runtime_environment_name(
550 ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME
551 ));
552 assert!(is_reserved_runtime_environment_name(
553 ENV_OPERATOR_BASE_PLATFORM
554 ));
555 assert!(is_reserved_runtime_environment_name(ENV_ALIEN_SECRETS));
556 assert!(is_reserved_runtime_environment_name(
557 ENV_ALIEN_SECRET_ENV_REVISION
558 ));
559 assert!(is_reserved_runtime_environment_name(
560 ENV_ALIEN_WORKER_GRPC_ADDRESS
561 ));
562 assert_eq!(ENV_ALIEN_WORKER_GRPC_ADDRESS, "ALIEN_WORKER_GRPC_ADDRESS");
563 assert!(is_reserved_runtime_environment_name(
564 ENV_ALIEN_WORKER_TIMEOUT_SECONDS
565 ));
566 assert_eq!(
567 ENV_ALIEN_WORKER_TIMEOUT_SECONDS,
568 "ALIEN_WORKER_TIMEOUT_SECONDS"
569 );
570 assert!(is_reserved_runtime_environment_name(
571 "ALIEN_STORAGE_BINDING"
572 ));
573 assert!(is_reserved_runtime_environment_name(
574 "ALIEN_BINDING_STORAGE_URL"
575 ));
576 assert!(!is_reserved_runtime_environment_name("USER_DEFINED"));
577 }
578
579 #[test]
580 fn reserves_deployment_identity_names() {
581 assert!(is_reserved_runtime_environment_name(ENV_ALIEN_MANAGER_URL));
582 assert!(is_reserved_runtime_environment_name(
583 ENV_ALIEN_DEPLOYMENT_TOKEN
584 ));
585 assert!(is_reserved_runtime_environment_name(
586 "ALIEN_DEPLOYMENT_SERVICE_ACCOUNT"
587 ));
588 assert!(is_reserved_runtime_environment_name(ENV_ALIEN_RESOURCE_ID));
589 assert_eq!(ENV_ALIEN_MANAGER_URL, "ALIEN_MANAGER_URL");
590 assert_eq!(ENV_ALIEN_DEPLOYMENT_TOKEN, "ALIEN_DEPLOYMENT_TOKEN");
591 assert_eq!(ENV_ALIEN_RESOURCE_ID, "ALIEN_RESOURCE_ID");
592 }
593
594 #[test]
595 fn reserves_commands_target_resource_id() {
596 assert!(is_reserved_runtime_environment_name(
597 ENV_ALIEN_COMMANDS_TARGET_RESOURCE_ID
598 ));
599 assert_eq!(
600 ENV_ALIEN_COMMANDS_TARGET_RESOURCE_ID,
601 "ALIEN_COMMANDS_TARGET_RESOURCE_ID"
602 );
603 }
604
605 #[test]
606 fn reserves_command_receiver_names() {
607 for (constant, expected) in [
608 (ENV_ALIEN_COMMANDS_URL, "ALIEN_COMMANDS_URL"),
609 (ENV_ALIEN_COMMANDS_TOKEN_FILE, "ALIEN_COMMANDS_TOKEN_FILE"),
610 (
611 ENV_ALIEN_COMMANDS_LEASE_SECONDS,
612 "ALIEN_COMMANDS_LEASE_SECONDS",
613 ),
614 (ENV_ALIEN_COMMANDS_MAX_LEASES, "ALIEN_COMMANDS_MAX_LEASES"),
615 (
616 ENV_ALIEN_COMMANDS_POLL_INTERVAL_MS,
617 "ALIEN_COMMANDS_POLL_INTERVAL_MS",
618 ),
619 (
620 ENV_ALIEN_COMMANDS_POLL_MAX_INTERVAL_MS,
621 "ALIEN_COMMANDS_POLL_MAX_INTERVAL_MS",
622 ),
623 (ENV_ALIEN_COMMANDS_POLL_JITTER, "ALIEN_COMMANDS_POLL_JITTER"),
624 (
625 ENV_ALIEN_COMMANDS_DRAIN_TIMEOUT_MS,
626 "ALIEN_COMMANDS_DRAIN_TIMEOUT_MS",
627 ),
628 (
629 ENV_ALIEN_COMMANDS_TARGET_RESOURCE_TYPE,
630 "ALIEN_COMMANDS_TARGET_RESOURCE_TYPE",
631 ),
632 ] {
633 assert_eq!(constant, expected);
634 assert!(is_reserved_runtime_environment_name(constant));
635 }
636 }
637
638 #[test]
639 fn rejects_reserved_user_environment_names() {
640 let error = validate_runtime_environment_user_vars(["USER_DEFINED", ENV_ALIEN_TRANSPORT])
641 .unwrap_err();
642
643 assert!(error.to_string().contains(ENV_ALIEN_TRANSPORT));
644 }
645
646 #[test]
647 fn prepared_environment_allows_deployment_managed_names() {
648 validate_prepared_runtime_environment_vars([
649 ENV_ALIEN_SECRETS,
650 ENV_ALIEN_DEPLOYMENT_ID,
651 ENV_ALIEN_DEPLOYMENT_NAME,
652 ENV_ALIEN_PUBLIC_ENDPOINTS_JSON,
653 ])
654 .unwrap();
655
656 let error =
657 validate_prepared_runtime_environment_vars([ENV_ALIEN_SECRETS, ENV_ALIEN_TRANSPORT])
658 .unwrap_err();
659
660 assert!(error.to_string().contains(ENV_ALIEN_TRANSPORT));
661 assert!(!error.to_string().contains(ENV_ALIEN_SECRETS));
662 }
663
664 #[test]
665 fn kubernetes_standard_environment_declares_base_platform() {
666 let entries = standard_runtime_environment_plan(Platform::Kubernetes);
667
668 assert!(entries.iter().any(|entry| {
669 entry.name == ENV_OPERATOR_BASE_PLATFORM
670 && entry.value == RuntimeEnvironmentValue::BasePlatform
671 }));
672 }
673
674 #[test]
675 fn kubernetes_gcp_base_environment_declares_gcp_identity() {
676 let entries = kubernetes_base_platform_runtime_environment_plan(Some(Platform::Gcp));
677
678 assert!(entries.iter().any(|entry| {
679 entry.name == ENV_GOOGLE_CLOUD_PROJECT
680 && entry.value == RuntimeEnvironmentValue::GcpProjectId
681 }));
682 assert!(entries.iter().any(|entry| {
683 entry.name == ENV_GCP_PROJECT_ID && entry.value == RuntimeEnvironmentValue::GcpProjectId
684 }));
685 assert!(entries.iter().any(|entry| {
686 entry.name == ENV_GCP_REGION && entry.value == RuntimeEnvironmentValue::GcpRegion
687 }));
688 }
689
690 #[test]
691 fn kubernetes_worker_environment_uses_http_proxy_transport() {
692 let entries = worker_transport_runtime_environment_plan(Platform::Kubernetes);
693
694 assert!(entries.iter().any(|entry| {
695 entry.name == ENV_ALIEN_TRANSPORT
696 && entry.value == RuntimeEnvironmentValue::Literal("http")
697 }));
698 }
699
700 #[test]
701 fn container_environment_does_not_inject_worker_transport() {
702 for platform in [
703 Platform::Local,
704 Platform::Kubernetes,
705 Platform::Aws,
706 Platform::Gcp,
707 Platform::Azure,
708 Platform::Test,
709 ] {
710 let entries = container_runtime_environment_plan(platform);
711 assert!(
712 !entries
713 .iter()
714 .any(|entry| entry.name == ENV_ALIEN_TRANSPORT),
715 "container plan for {platform:?} must not set ALIEN_TRANSPORT"
716 );
717 assert!(
719 entries
720 .iter()
721 .any(|entry| entry.name == ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME),
722 "container plan for {platform:?} must still declare the binding-name var"
723 );
724 }
725 }
726
727 #[test]
728 fn daemon_environment_is_standard_identity_only() {
729 for platform in [
732 Platform::Local,
733 Platform::Kubernetes,
734 Platform::Aws,
735 Platform::Gcp,
736 Platform::Azure,
737 Platform::Test,
738 ] {
739 let entries = daemon_runtime_environment_plan(platform);
740 assert!(
741 !entries
742 .iter()
743 .any(|entry| entry.name == ENV_ALIEN_TRANSPORT),
744 "daemon plan for {platform:?} must not set ALIEN_TRANSPORT"
745 );
746 assert!(
747 !entries
748 .iter()
749 .any(|entry| entry.name == ENV_ALIEN_CURRENT_CONTAINER_BINDING_NAME),
750 "daemon plan for {platform:?} must not set the container self-binding var"
751 );
752 assert!(
754 entries
755 .iter()
756 .any(|entry| entry.name == ENV_ALIEN_DEPLOYMENT_TYPE),
757 "daemon plan for {platform:?} must declare ALIEN_DEPLOYMENT_TYPE"
758 );
759 }
760 }
761
762 #[test]
763 fn worker_local_transport_uses_local_proxy() {
764 for platform in [Platform::Local, Platform::Test] {
767 let entries = worker_transport_runtime_environment_plan(platform);
768 assert!(entries.iter().any(|entry| {
769 entry.name == ENV_ALIEN_TRANSPORT
770 && entry.value == RuntimeEnvironmentValue::Literal("local")
771 }));
772 }
773 }
774}