alien_bindings/providers/build/
grpc.rs1use crate::{
2 error::{Error, ErrorData},
3 grpc::build_service::alien_bindings::build::{
4 build_service_client::BuildServiceClient, BuildConfig as ProtoBuildConfig,
5 ComputeType as ProtoComputeType, GetBuildStatusRequest, StartBuildRequest,
6 StopBuildRequest,
7 },
8 grpc::status_conversion::status_to_alien_error,
9 traits::Build,
10};
11use alien_core::{BuildConfig, BuildExecution, BuildStatus};
12use alien_error::{AlienError, Context, IntoAlienError};
13use async_trait::async_trait;
14use tonic::{transport::Channel, Request, Status};
15
16#[derive(Debug)]
21pub struct GrpcBuild {
22 client: BuildServiceClient<Channel>,
23 binding_name: String,
24}
25
26impl GrpcBuild {
27 pub async fn new(binding_name: String, grpc_address: String) -> Result<Self, Error> {
29 let channel = crate::providers::grpc_provider::create_grpc_channel(grpc_address).await?;
30 Self::new_from_channel(channel, binding_name).await
31 }
32
33 pub async fn new_from_channel(channel: Channel, binding_name: String) -> Result<Self, Error> {
35 let client = BuildServiceClient::new(channel);
36
37 Ok(Self {
38 client,
39 binding_name,
40 })
41 }
42
43 fn client(&self) -> BuildServiceClient<Channel> {
44 self.client.clone()
45 }
46}
47
48fn map_alien_config_to_proto(config: BuildConfig) -> ProtoBuildConfig {
49 let compute_type = match config.compute_type {
50 alien_core::ComputeType::Small => ProtoComputeType::Small,
51 alien_core::ComputeType::Medium => ProtoComputeType::Medium,
52 alien_core::ComputeType::Large => ProtoComputeType::Large,
53 alien_core::ComputeType::XLarge => ProtoComputeType::Xlarge,
54 };
55
56 let monitoring = if let Some(alien_monitoring) = config.monitoring {
58 Some(
59 crate::grpc::build_service::alien_bindings::build::MonitoringConfig {
60 endpoint: alien_monitoring.endpoint,
61 headers: alien_monitoring.headers,
62 logs_uri: alien_monitoring.logs_uri,
63 tls_enabled: alien_monitoring.tls_enabled,
64 tls_verify: alien_monitoring.tls_verify,
65 },
66 )
67 } else {
68 None
69 };
70
71 ProtoBuildConfig {
72 script: config.script,
73 environment: config.environment,
74 compute_type: compute_type.into(),
75 timeout_seconds: Some(config.timeout_seconds as i32),
76 monitoring,
77 }
78}
79
80fn map_proto_execution_to_alien(
81 execution: crate::grpc::build_service::alien_bindings::build::BuildExecution,
82) -> BuildExecution {
83 let status = match execution.status() {
84 crate::grpc::build_service::alien_bindings::build::BuildStatus::Unspecified => {
85 BuildStatus::Failed
86 }
87 crate::grpc::build_service::alien_bindings::build::BuildStatus::Queued => {
88 BuildStatus::Queued
89 }
90 crate::grpc::build_service::alien_bindings::build::BuildStatus::Running => {
91 BuildStatus::Running
92 }
93 crate::grpc::build_service::alien_bindings::build::BuildStatus::Succeeded => {
94 BuildStatus::Succeeded
95 }
96 crate::grpc::build_service::alien_bindings::build::BuildStatus::Failed => {
97 BuildStatus::Failed
98 }
99 crate::grpc::build_service::alien_bindings::build::BuildStatus::Cancelled => {
100 BuildStatus::Cancelled
101 }
102 crate::grpc::build_service::alien_bindings::build::BuildStatus::TimedOut => {
103 BuildStatus::TimedOut
104 }
105 };
106
107 BuildExecution {
108 id: execution.id,
109 status,
110 start_time: execution.start_time,
111 end_time: execution.end_time,
112 }
113}
114
115#[async_trait]
116impl Build for GrpcBuild {
117 async fn start_build(&self, config: BuildConfig) -> Result<BuildExecution, Error> {
118 let mut client = self.client();
119
120 let proto_config = map_alien_config_to_proto(config);
121 let request = StartBuildRequest {
122 binding_name: self.binding_name.clone(),
123 config: Some(proto_config),
124 };
125
126 let response = client
127 .start_build(Request::new(request))
128 .await
129 .map_err(|e| status_to_alien_error(e, "start_build"))?
130 .into_inner();
131
132 let execution = response.execution.ok_or_else(|| {
133 AlienError::new(ErrorData::UnexpectedResponseFormat {
134 provider: "grpc".to_string(),
135 binding_name: self.binding_name.clone(),
136 field: "execution".to_string(),
137 response_json: "missing execution field".to_string(),
138 })
139 })?;
140
141 Ok(map_proto_execution_to_alien(execution))
142 }
143
144 async fn get_build_status(&self, build_id: &str) -> Result<BuildExecution, Error> {
145 let mut client = self.client();
146
147 let request = GetBuildStatusRequest {
148 binding_name: self.binding_name.clone(),
149 build_id: build_id.to_string(),
150 };
151
152 let response = client
153 .get_build_status(Request::new(request))
154 .await
155 .map_err(|e| status_to_alien_error(e, "get_build_status"))?
156 .into_inner();
157
158 let execution = response.execution.ok_or_else(|| {
159 AlienError::new(ErrorData::UnexpectedResponseFormat {
160 provider: "grpc".to_string(),
161 binding_name: self.binding_name.clone(),
162 field: "execution".to_string(),
163 response_json: "missing execution field".to_string(),
164 })
165 })?;
166
167 Ok(map_proto_execution_to_alien(execution))
168 }
169
170 async fn stop_build(&self, build_id: &str) -> Result<(), Error> {
171 let mut client = self.client();
172
173 let request = StopBuildRequest {
174 binding_name: self.binding_name.clone(),
175 build_id: build_id.to_string(),
176 };
177
178 client
179 .stop_build(Request::new(request))
180 .await
181 .map_err(|e| status_to_alien_error(e, "stop_build"))?;
182
183 Ok(())
184 }
185}
186
187impl crate::traits::Binding for GrpcBuild {}