first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -1,150 +1,21 @@
|
||||
use galaxy_server_client::cloud_object::Owner;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
auth::AuthStateProvider,
|
||||
cloud_object::{
|
||||
model::{
|
||||
generic_string_model::{GenericStringModel, GenericStringObjectId, StringModel},
|
||||
json_model::{JsonModel, JsonSerializer},
|
||||
persistence::CloudModel,
|
||||
},
|
||||
GenericCloudObject, GenericStringObjectFormat, GenericStringObjectUniqueKey,
|
||||
JsonObjectType, Revision, ServerCloudObject,
|
||||
},
|
||||
server::{ids::SyncId, sync_queue::QueueItem},
|
||||
workspaces::user_workspaces::UserWorkspaces,
|
||||
// Some of these re-exported types aren't used in the wasm build, so we suppress this
|
||||
// warning.
|
||||
#[cfg_attr(target_family = "wasm", expect(unused_imports))]
|
||||
pub use cloud_object_models::{
|
||||
AmbientAgentEnvironment, AwsProviderConfig, BaseImage, CloudAmbientAgentEnvironment,
|
||||
CloudAmbientAgentEnvironmentModel, GcpProviderConfig, GithubRepo, ProvidersConfig, SourceRepo,
|
||||
};
|
||||
use cloud_objects::cloud_object::Owner;
|
||||
use galaxyui::{AppContext, SingletonEntity as _};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GithubRepo {
|
||||
/// Repository owner (e.g. "warpdotdev")
|
||||
pub owner: String,
|
||||
/// Repository name (e.g. "warp-internal")
|
||||
pub repo: String,
|
||||
}
|
||||
|
||||
impl GithubRepo {
|
||||
pub fn new(owner: String, repo: String) -> Self {
|
||||
Self { owner, repo }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GithubRepo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}/{}", self.owner, self.repo)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BaseImage {
|
||||
DockerImage(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for BaseImage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BaseImage::DockerImage(s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GcpProviderConfig {
|
||||
pub project_number: String,
|
||||
pub workload_identity_federation_pool_id: String,
|
||||
pub workload_identity_federation_provider_id: String,
|
||||
/// Service account email for impersonation. When set, the federated token
|
||||
/// is exchanged for a service account access token.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub service_account_email: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AwsProviderConfig {
|
||||
pub role_arn: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct ProvidersConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub gcp: Option<GcpProviderConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub aws: Option<AwsProviderConfig>,
|
||||
}
|
||||
|
||||
impl ProvidersConfig {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.gcp.is_none() && self.aws.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
/// An AmbientAgentEnvironment represents an environment that we would run a Warp agent in.
|
||||
pub struct AmbientAgentEnvironment {
|
||||
/// Environment name
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
/// Optional description of the environment (max 240 characters)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
/// List of GitHub repositories
|
||||
#[serde(default)]
|
||||
pub github_repos: Vec<GithubRepo>,
|
||||
/// Base image specification
|
||||
#[serde(flatten)]
|
||||
pub base_image: BaseImage,
|
||||
/// List of setup commands to run after cloning
|
||||
#[serde(default)]
|
||||
pub setup_commands: Vec<String>,
|
||||
/// Optional cloud provider configurations for automatic auth.
|
||||
#[serde(default, skip_serializing_if = "ProvidersConfig::is_empty")]
|
||||
pub providers: ProvidersConfig,
|
||||
}
|
||||
|
||||
pub type CloudAmbientAgentEnvironment =
|
||||
GenericCloudObject<GenericStringObjectId, CloudAmbientAgentEnvironmentModel>;
|
||||
pub type CloudAmbientAgentEnvironmentModel =
|
||||
GenericStringModel<AmbientAgentEnvironment, JsonSerializer>;
|
||||
|
||||
impl CloudAmbientAgentEnvironment {
|
||||
pub fn get_all(app: &AppContext) -> Vec<CloudAmbientAgentEnvironment> {
|
||||
CloudModel::as_ref(app)
|
||||
.get_all_objects_of_type::<GenericStringObjectId, CloudAmbientAgentEnvironmentModel>()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_by_id<'a>(
|
||||
sync_id: &'a SyncId,
|
||||
app: &'a AppContext,
|
||||
) -> Option<&'a CloudAmbientAgentEnvironment> {
|
||||
CloudModel::as_ref(app)
|
||||
.get_object_of_type::<GenericStringObjectId, CloudAmbientAgentEnvironmentModel>(sync_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl AmbientAgentEnvironment {
|
||||
pub fn new(
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
github_repos: Vec<GithubRepo>,
|
||||
docker_image: String,
|
||||
setup_commands: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description,
|
||||
github_repos,
|
||||
base_image: BaseImage::DockerImage(docker_image),
|
||||
setup_commands,
|
||||
providers: ProvidersConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::auth::AuthStateProvider;
|
||||
use crate::cloud_object::model::generic_string_model::StringModel;
|
||||
use crate::cloud_object::model::json_model::JsonModel;
|
||||
use crate::cloud_object::{
|
||||
GenericStringObjectFormat, GenericStringObjectUniqueKey, JsonObjectType, Revision,
|
||||
};
|
||||
use crate::server::sync_queue::QueueItem;
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
|
||||
impl StringModel for AmbientAgentEnvironment {
|
||||
type CloudObjectType = CloudAmbientAgentEnvironment;
|
||||
@@ -181,14 +52,6 @@ impl StringModel for AmbientAgentEnvironment {
|
||||
None
|
||||
}
|
||||
|
||||
fn new_from_server_update(&self, server_cloud_object: &ServerCloudObject) -> Option<Self> {
|
||||
if let ServerCloudObject::AmbientAgentEnvironment(server_environment) = server_cloud_object
|
||||
{
|
||||
return Some(server_environment.model.clone().string_model);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn should_show_activity_toasts() -> bool {
|
||||
false
|
||||
}
|
||||
@@ -226,7 +89,3 @@ pub fn owner_for_new_personal_environment(ctx: &AppContext) -> Option<Owner> {
|
||||
let user_id = AuthStateProvider::as_ref(ctx).get().user_id()?;
|
||||
Some(Owner::User { user_uid: user_id })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "mod_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn deserialize_legacy_environment_without_providers() {
|
||||
let json = serde_json::json!({
|
||||
"name": "my-env",
|
||||
"github_repos": [{"owner": "warpdotdev", "repo": "warp"}],
|
||||
"docker_image": "ubuntu:latest",
|
||||
"setup_commands": ["echo hello"]
|
||||
});
|
||||
|
||||
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(env.name, "my-env");
|
||||
assert_eq!(env.providers, ProvidersConfig::default());
|
||||
assert_eq!(env.github_repos.len(), 1);
|
||||
assert_eq!(
|
||||
env.base_image,
|
||||
BaseImage::DockerImage("ubuntu:latest".into())
|
||||
);
|
||||
assert_eq!(env.setup_commands, vec!["echo hello"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_with_aws_provider() {
|
||||
let json = serde_json::json!({
|
||||
"name": "aws-env",
|
||||
"github_repos": [],
|
||||
"docker_image": "node:18",
|
||||
"providers": {
|
||||
"aws": {
|
||||
"role_arn": "arn:aws:iam::123456789012:role/my-role"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(env.name, "aws-env");
|
||||
let providers = env.providers;
|
||||
assert_eq!(providers.gcp, None);
|
||||
let aws = providers.aws.unwrap();
|
||||
assert_eq!(aws.role_arn, "arn:aws:iam::123456789012:role/my-role");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_with_gcp_provider() {
|
||||
let json = serde_json::json!({
|
||||
"name": "gcp-env",
|
||||
"github_repos": [],
|
||||
"docker_image": "node:18",
|
||||
"providers": {
|
||||
"gcp": {
|
||||
"project_number": "123456",
|
||||
"workload_identity_federation_pool_id": "pool-1",
|
||||
"workload_identity_federation_provider_id": "provider-1"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
|
||||
let gcp = env.providers.gcp.unwrap();
|
||||
assert_eq!(gcp.project_number, "123456");
|
||||
assert_eq!(gcp.workload_identity_federation_pool_id, "pool-1");
|
||||
assert_eq!(gcp.workload_identity_federation_provider_id, "provider-1");
|
||||
assert_eq!(gcp.service_account_email, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_with_gcp_provider_service_account() {
|
||||
let json = serde_json::json!({
|
||||
"name": "gcp-sa-env",
|
||||
"github_repos": [],
|
||||
"docker_image": "node:18",
|
||||
"providers": {
|
||||
"gcp": {
|
||||
"project_number": "123456",
|
||||
"workload_identity_federation_pool_id": "pool-1",
|
||||
"workload_identity_federation_provider_id": "provider-1",
|
||||
"service_account_email": "sa@project.iam.gserviceaccount.com"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
|
||||
let gcp = env.providers.gcp.unwrap();
|
||||
assert_eq!(gcp.project_number, "123456");
|
||||
assert_eq!(
|
||||
gcp.service_account_email.as_deref(),
|
||||
Some("sa@project.iam.gserviceaccount.com")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_with_both_providers() {
|
||||
let json = serde_json::json!({
|
||||
"name": "both-env",
|
||||
"github_repos": [],
|
||||
"docker_image": "node:18",
|
||||
"providers": {
|
||||
"gcp": {
|
||||
"project_number": "123456",
|
||||
"workload_identity_federation_pool_id": "pool-1",
|
||||
"workload_identity_federation_provider_id": "provider-1"
|
||||
},
|
||||
"aws": {
|
||||
"role_arn": "arn:aws:iam::123456789012:role/my-role"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
|
||||
let providers = env.providers;
|
||||
assert!(providers.gcp.is_some());
|
||||
assert!(providers.aws.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_with_providers_none_omits_field() {
|
||||
let env = AmbientAgentEnvironment::new(
|
||||
"test-env".into(),
|
||||
None,
|
||||
vec![],
|
||||
"ubuntu:latest".into(),
|
||||
vec![],
|
||||
);
|
||||
|
||||
let json = serde_json::to_value(&env).unwrap();
|
||||
assert!(!json.as_object().unwrap().contains_key("providers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_with_providers_includes_field() {
|
||||
let mut env = AmbientAgentEnvironment::new(
|
||||
"test-env".into(),
|
||||
None,
|
||||
vec![],
|
||||
"ubuntu:latest".into(),
|
||||
vec![],
|
||||
);
|
||||
env.providers = ProvidersConfig {
|
||||
gcp: None,
|
||||
aws: Some(AwsProviderConfig {
|
||||
role_arn: "arn:aws:iam::123456789012:role/test".into(),
|
||||
}),
|
||||
};
|
||||
|
||||
let json = serde_json::to_value(&env).unwrap();
|
||||
let providers = json.get("providers").unwrap();
|
||||
assert!(providers.get("aws").is_some());
|
||||
assert!(providers.get("gcp").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_serde_with_providers() {
|
||||
let mut env = AmbientAgentEnvironment::new(
|
||||
"rt-env".into(),
|
||||
Some("desc".into()),
|
||||
vec![GithubRepo::new("owner".into(), "repo".into())],
|
||||
"alpine:latest".into(),
|
||||
vec!["make build".into()],
|
||||
);
|
||||
env.providers = ProvidersConfig {
|
||||
gcp: Some(GcpProviderConfig {
|
||||
project_number: "999".into(),
|
||||
workload_identity_federation_pool_id: "p".into(),
|
||||
workload_identity_federation_provider_id: "pr".into(),
|
||||
service_account_email: Some("sa@proj.iam.gserviceaccount.com".into()),
|
||||
}),
|
||||
aws: Some(AwsProviderConfig {
|
||||
role_arn: "arn:aws:iam::1:role/r".into(),
|
||||
}),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&env).unwrap();
|
||||
let deserialized: AmbientAgentEnvironment = serde_json::from_str(&serialized).unwrap();
|
||||
assert_eq!(env, deserialized);
|
||||
}
|
||||
Reference in New Issue
Block a user