Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 147 additions & 31 deletions bin/core/src/api/write/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use komodo_client::{
api::{read::ExportAllResourcesToToml, write::*},
entities::{
self, Operation, RepoExecutionArgs, ResourceTarget,
ResourceTargetVariant,
action::Action,
alert::{Alert, AlertData, SeverityLevel},
alerter::Alerter,
Expand All @@ -36,6 +37,8 @@ use komodo_client::{
use mogh_resolver::Resolve;
use tracing::Instrument;

use komodo_client::api::read::ExportResourcesToToml;

use crate::{
alert::send_alerts,
api::read::ReadArgs,
Expand Down Expand Up @@ -450,17 +453,23 @@ impl Resolve<WriteArgs> for CommitSync {
fields(
operator = args.user.id,
sync = self.sync,
resource_type = format!("{:?}", self.resource_type),
resources = format!("{:?}", self.resources),
)
)]
async fn resolve(
self,
args: &WriteArgs,
) -> mogh_error::Result<Update> {
let WriteArgs { user } = args;
let CommitSync {
sync,
resource_type,
resources,
} = self;

let sync = get_check_permissions::<entities::sync::ResourceSync>(
&self.sync,
user,
&sync,
&args.user,
PermissionLevel::Write.into(),
)
.await?;
Expand Down Expand Up @@ -517,26 +526,47 @@ impl Resolve<WriteArgs> for CommitSync {
};

// Get the latest existing resources to preserve any meta values
let RemoteResources { resources, .. } =
let RemoteResources {
resources: existing_resources,
..
} =
crate::sync::remote::get_remote_resources(&sync, repo.as_ref())
.await
.context("failed to get remote resources")?;

let res = ExportAllResourcesToToml {
include_resources: sync.config.include_resources,
tags: sync.config.match_tags.clone(),
include_variables: sync.config.include_variables,
include_user_groups: sync.config.include_user_groups,
existing: resources
.inspect_err(|e| warn!("Existing resource TOML is unavailable, resource meta will not be preserved | ERROR: {e:#}"))
.ok(),
}
.resolve(&ReadArgs {
user: sync_user().to_owned(),
})
.await?;
let res = if resource_type.is_some() || resources.is_some() {
let targets =
build_resource_targets(resource_type, resources.as_deref());
ExportResourcesToToml {
targets,
user_groups: vec![],
include_variables: false,
existing: existing_resources
.inspect_err(|e| warn!("Existing resource TOML is unavailable, resource meta will not be preserved | ERROR: {e:#}"))
.ok(),
}
.resolve(&ReadArgs {
user: sync_user().to_owned(),
})
.await?
} else {
ExportAllResourcesToToml {
include_resources: sync.config.include_resources,
tags: sync.config.match_tags.clone(),
include_variables: sync.config.include_variables,
include_user_groups: sync.config.include_user_groups,
existing: existing_resources
.inspect_err(|e| warn!("Existing resource TOML is unavailable, resource meta will not be preserved | ERROR: {e:#}"))
.ok(),
}
.resolve(&ReadArgs {
user: sync_user().to_owned(),
})
.await?
};

let mut update = make_update(&sync, Operation::CommitSync, user);
let mut update =
make_update(&sync, Operation::CommitSync, &args.user);
update.id = add_update(update.clone()).await?;

update.logs.push(Log::simple("Resources", res.toml.clone()));
Expand Down Expand Up @@ -567,20 +597,33 @@ impl Resolve<WriteArgs> for CommitSync {
add_update(update.clone()).await?;
return Ok(update);
} else {
update.push_simple_log(
"Write contents",
format!("File contents written to {file_path:?}"),
);
let log_msg = match (resource_type, resources.as_deref()) {
(Some(rtype), Some(names)) if names.len() == 1 => {
format!("{} \"{}\" to file", rtype, names[0])
}
(Some(rtype), Some(names)) => {
format!("{} [{}] to file", rtype, names.join(", "))
}
_ => format!("All resources to file"),
};
update.push_simple_log("Commit", log_msg);
}
} else if let Some(repo) = &repo {
let Some(resource_path) = resource_path else {
// Resource path checked above for repo mode.
unreachable!()
};
let args: RepoExecutionArgs = repo.into();
if let Err(e) =
commit_git_sync(args, &resource_path, &res.toml, &mut update)
.await
let repo_args: RepoExecutionArgs = repo.into();
if let Err(e) = commit_git_sync(
repo_args,
&resource_path,
&res.toml,
&mut update,
resource_type.as_ref().map(|t| t.as_ref()),
resources.as_deref(),
&args.user.username,
)
.await
{
update.push_error_log(
"Write resource file",
Expand All @@ -595,10 +638,17 @@ impl Resolve<WriteArgs> for CommitSync {
// Resource path checked above for repo mode.
unreachable!()
};
let args: RepoExecutionArgs = (&sync).into();
if let Err(e) =
commit_git_sync(args, &resource_path, &res.toml, &mut update)
.await
let sync_args: RepoExecutionArgs = (&sync).into();
if let Err(e) = commit_git_sync(
sync_args,
&resource_path,
&res.toml,
&mut update,
resource_type.as_ref().map(|t| t.as_ref()),
resources.as_deref(),
&args.user.username,
)
.await
{
update.push_error_log(
"Write resource file",
Expand Down Expand Up @@ -627,6 +677,17 @@ impl Resolve<WriteArgs> for CommitSync {
update.finalize();
add_update(update.clone()).await?;
return Ok(update);
} else {
let log_msg = match (resource_type, resources.as_deref()) {
(Some(rtype), Some(names)) if names.len() == 1 => {
format!("{} \"{}\" to database", rtype, names[0])
}
(Some(rtype), Some(names)) => {
format!("{} [{}] to database", rtype, names.join(", "))
}
_ => "All resources to database".to_string(),
};
update.push_simple_log("Commit", log_msg);
}

if let Err(e) = (RefreshResourceSyncPending { sync: sync.name })
Expand All @@ -652,6 +713,9 @@ async fn commit_git_sync(
resource_path: &Path,
toml: &str,
update: &mut Update,
resource_type: Option<&str>,
resource_names: Option<&[String]>,
username: &str,
) -> anyhow::Result<()> {
let root = args.unique_path(&core_config().repo_directory)?;
args.destination = Some(root.display().to_string());
Expand All @@ -677,8 +741,20 @@ async fn commit_git_sync(
return Ok(());
}

let commit_msg = match (resource_type, resource_names) {
(Some(rtype), Some(names)) if names.len() == 1 => {
format!("{}: {} \"{}\"", username, rtype, names[0])
}
(Some(rtype), Some(names)) if names.len() > 1 => {
format!("{}: {} [{}]", username, rtype, names.join(", "))
}
_ => {
format!("{}: Commit Resource File", username)
}
};

let res = git::write_commit_file(
"Commit Sync",
&commit_msg,
&root,
resource_path,
toml,
Expand All @@ -690,6 +766,46 @@ async fn commit_git_sync(
Ok(())
}

fn build_resource_targets(
resource_type: Option<ResourceTargetVariant>,
resources: Option<&[String]>,
) -> Vec<ResourceTarget> {
let mut targets = Vec::new();
let resources = resources.unwrap_or(&[]);

macro_rules! push_targets {
($(($Variant:ident, $field:ident)),* $(,)?) => {
$(
if resource_type == Some(ResourceTargetVariant::$Variant) {
for name_or_id in resources {
targets.push(ResourceTarget::$Variant(name_or_id.clone()));
}
}
)*
};
}

push_targets!(
(Server, servers),
(Swarm, swarms),
(Stack, stacks),
(Deployment, deployments),
(Build, builds),
(Repo, repos),
(Procedure, procedures),
(Action, actions),
(Builder, builders),
(Alerter, alerters),
(ResourceSync, resource_syncs),
);

if resource_type.is_none() && resources.is_empty() {
return vec![];
}

targets
}

impl Resolve<WriteArgs> for RefreshResourceSyncPending {
async fn resolve(
self,
Expand Down
9 changes: 9 additions & 0 deletions client/core/rs/src/api/write/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;

use crate::entities::{
ResourceTargetVariant,
sync::{_PartialResourceSyncConfig, ResourceSync},
update::Update,
};
Expand Down Expand Up @@ -250,6 +251,14 @@ pub struct CommitSync {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub sync: String,
/// Only commit a specific resource type.
/// Combine with `resources` to specify specific resources.
#[serde(default)]
pub resource_type: Option<ResourceTargetVariant>,
/// Only commit specific resources by id or name.
/// Combine with `resource_type` to specify resources.
#[serde(default)]
pub resources: Option<Vec<String>>,
}

//
Expand Down
10 changes: 10 additions & 0 deletions client/core/ts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6290,6 +6290,16 @@ export interface CloseAlert {
export interface CommitSync {
/** Id or name */
sync: string;
/**
* Only commit a specific resource type.
* Combine with `resources` to specify specific resources.
*/
resource_type?: ResourceTarget["type"];
/**
* Only commit specific resources by id or name.
* Combine with `resource_type` to specify resources.
*/
resources?: string[];
}

/**
Expand Down
25 changes: 23 additions & 2 deletions ui/src/resources/sync/pending.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useExecute, usePermissions, useRead } from "@/lib/hooks";
import { useExecute, usePermissions, useRead, useWrite } from "@/lib/hooks";
import { ReactNode } from "react";
import { useFullResourceSync } from ".";
import { useResourceSyncTabsView } from "./hooks";
Expand All @@ -13,6 +13,7 @@ import { ConfirmButton } from "mogh_ui";
import { SquarePlay } from "lucide-react";
import { MonacoDiffEditor, MonacoEditor } from "mogh_ui";
import { diffTypeIntention } from "@/lib/color";
import { ICONS } from "@/theme/icons";

export default function ResourceSyncPending({
id,
Expand All @@ -25,9 +26,10 @@ export default function ResourceSyncPending({
?.syncing;
const sync = useFullResourceSync(id);
const { view } = useResourceSyncTabsView(sync);
const { canExecute } = usePermissions({ type: "ResourceSync", id });
const { canExecute, canWrite } = usePermissions({ type: "ResourceSync", id });
const { mutate: runSync, isPending } = useExecute("RunSync");
const enableFancyToml = useRead("GetCoreInfo", {}).data?.enable_fancy_toml;
const { mutate: commitHunk, isPending: committing } = useWrite("CommitSync");
const loading = isPending || syncing;

return (
Expand Down Expand Up @@ -179,6 +181,25 @@ export default function ResourceSyncPending({
Execute Change
</ConfirmButton>
)}
{canWrite && view === "Commit" && (
<ConfirmButton
icon={<ICONS.Commit size="1rem" />}
onClick={() =>
commitHunk({
sync: id,
resource_type: update.target.type,
resources: [
update.data.type === "Create"
? update.data.data.name!
: update.target.id,
],
})
}
loading={committing}
>
Commit Change
</ConfirmButton>
)}
</Group>

{/* CONTENT */}
Expand Down