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