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