Skip to content

Commit

Permalink
feat(op): add invit status/event
Browse files Browse the repository at this point in the history
  • Loading branch information
leroyguillaume committed Jul 30, 2024
1 parent f45fbe0 commit 466c833
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 27 deletions.
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
COOKIE_HTTP_ONLY_DISABLED = "true"
COOKIE_SECURE_DISABLED = "true"
LOG_FILTER = "simpaas=debug,warn"
SMTP_FROM = "noreply@localhost"
SMTP_HOST = "simpaas-smtp.simpaas.svc.cluster.local"
12 changes: 11 additions & 1 deletion charts/simpaas/crds/invit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,20 @@ spec:
- roles
- to
type: object
status:
nullable: true
properties:
emailSent:
description: True if email was sent.
type: boolean
required:
- emailSent
type: object
required:
- spec
title: Invitation
type: object
served: true
storage: true
subresources: {}
subresources:
status: {}
2 changes: 2 additions & 0 deletions charts/simpaas/templates/op/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ spec:
value: {{ default (printf "%s-smtp" .Release.Name) .Values.op.smtp.host }}
- name: SMTP_PORT
value: {{ .Values.op.smtp.port | quote }}
- name: SMTP_FROM
value: {{ default (printf "noreply@%s" .Values.ingress.domain) .Values.op.smtp.from }}
{{- if .Values.op.webappUrl }}
- name: WEBAPP_URL
value: {{ .Values.op.webappUrl }}
Expand Down
1 change: 1 addition & 0 deletions charts/simpaas/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ op:
smtp:
host: ""
port: 25
from: ""

webappUrl: ""

Expand Down
1 change: 1 addition & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ async fn create_invitation<J: JwtEncoder, K: KubeClient, P: PasswordEncoder>(
..Default::default()
},
spec,
status: None,
};
ctx.kube.patch_invitation(&token, &invit).await?;
info!("invitation created");
Expand Down
10 changes: 9 additions & 1 deletion src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ impl Display for Permission {
kind = "Invitation",
doc = "SimPaaS user invitation",
plural = "invitations",
namespaced
namespaced,
status = "InvitationStatus"
)]
#[serde(rename_all = "camelCase")]
pub struct InvitationSpec {
Expand All @@ -187,6 +188,13 @@ pub struct InvitationSpec {
pub to: String,
}

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, JsonSchema, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InvitationStatus {
/// True if email was sent.
pub email_sent: bool,
}

#[derive(Clone, CustomResource, Debug, Deserialize, Eq, PartialEq, JsonSchema, Serialize)]
#[kube(
group = "simpaas.gleroy.dev",
Expand Down
88 changes: 82 additions & 6 deletions src/kube/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ use std::collections::HashSet;
use k8s_openapi::api::{core::v1::Namespace, networking::v1::Ingress};
use kube::{
api::{DeleteParams, ListParams, Patch, PatchParams},
Api, Client,
runtime::events::{EventType, Recorder, Reporter},
Api, Client, Resource,
};
use regex::Regex;
use serde_json::json;
use tracing::{debug, instrument, warn};

use crate::{
domain::{Action, App, Invitation, Permission, PermissionError, Role, Service, User},
domain::{
Action, App, Invitation, InvitationStatus, Permission, PermissionError, Role, Service, User,
},
CARGO_PKG_NAME,
};

use super::{AppFilter, DomainUsage, KubeClient, Result};
use super::{
AppFilter, DomainUsage, KubeClient, KubeEvent, KubeEventKind, KubeEventPublisher, Result,
};

#[derive(Debug, thiserror::Error)]
pub enum Error {
Expand Down Expand Up @@ -201,7 +207,7 @@ impl KubeClient for ApiKubeClient {
let api: Api<App> = Api::default_namespaced(self.0.clone());
let params = PatchParams::apply(CARGO_PKG_NAME);
debug!("patching app");
api.patch(name, &params, &Patch::Apply(&app)).await?;
api.patch(name, &params, &Patch::Apply(app)).await?;
Ok(())
}

Expand All @@ -210,7 +216,20 @@ impl KubeClient for ApiKubeClient {
let api: Api<Invitation> = Api::default_namespaced(self.0.clone());
let params = PatchParams::apply(CARGO_PKG_NAME);
debug!("patching invitation");
api.patch(token, &params, &Patch::Apply(&invit)).await?;
api.patch(token, &params, &Patch::Apply(invit)).await?;
Ok(())
}

#[instrument(skip(self, token, status), fields(invit.token = token))]
async fn patch_invitation_status(&self, token: &str, status: &InvitationStatus) -> Result {
let api: Api<Invitation> = Api::default_namespaced(self.0.clone());
let params = PatchParams::default();
debug!("patching invitation status");
let status = json!({
"status": status,
});
api.patch_status(token, &params, &Patch::Merge(status))
.await?;
Ok(())
}

Expand All @@ -219,7 +238,7 @@ impl KubeClient for ApiKubeClient {
let api: Api<User> = Api::default_namespaced(self.0.clone());
let params = PatchParams::apply(CARGO_PKG_NAME);
debug!("patching user");
api.patch(name, &params, &Patch::Apply(&user)).await?;
api.patch(name, &params, &Patch::Apply(user)).await?;
Ok(())
}

Expand Down Expand Up @@ -254,6 +273,42 @@ impl KubeClient for ApiKubeClient {
}
}

pub struct ApiKubeEventPublisher {
client: Client,
reporter: Reporter,
}

impl ApiKubeEventPublisher {
pub fn new(client: Client, instance: Option<String>) -> Self {
Self {
client,
reporter: Reporter {
controller: CARGO_PKG_NAME.into(),
instance,
},
}
}

async fn publish(event: KubeEvent, recorder: Recorder) {
if let Err(err) = recorder.publish(event.into()).await {
warn!("failed to publish event: {err}");
}
}
}

impl KubeEventPublisher for ApiKubeEventPublisher {
#[instrument(skip(self, invit, event))]
async fn publish_invitation_event(&self, invit: &Invitation, event: KubeEvent) {
debug!("publishing invitation event");
let recorder = Recorder::new(
self.client.clone(),
self.reporter.clone(),
invit.object_ref(&()),
);
Self::publish(event, recorder).await;
}
}

impl From<Error> for super::Error {
fn from(err: Error) -> Self {
Self(Box::new(err))
Expand All @@ -277,3 +332,24 @@ impl From<regex::Error> for super::Error {
Error::Regex(err).into()
}
}

impl From<KubeEvent> for ::kube::runtime::events::Event {
fn from(event: KubeEvent) -> Self {
Self {
action: event.action.into(),
note: Some(event.note),
reason: event.reason.into(),
secondary: None,
type_: event.kind.into(),
}
}
}

impl From<KubeEventKind> for EventType {
fn from(kind: KubeEventKind) -> Self {
match kind {
KubeEventKind::Normal => Self::Normal,
KubeEventKind::Warn => Self::Warning,
}
}
}
30 changes: 29 additions & 1 deletion src/kube/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashSet;
use futures::Future;
use regex::Regex;

use crate::domain::{Action, App, Invitation, Permission, Role, Service, User};
use crate::domain::{Action, App, Invitation, InvitationStatus, Permission, Role, Service, User};

pub mod api;

Expand All @@ -28,6 +28,20 @@ pub struct DomainUsage {
pub domain: String,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KubeEvent {
pub action: &'static str,
pub kind: KubeEventKind,
pub note: String,
pub reason: &'static str,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum KubeEventKind {
Normal,
Warn,
}

pub trait KubeClient: Send + Sync {
fn delete_app(&self, name: &str) -> impl Future<Output = Result> + Send;

Expand Down Expand Up @@ -67,6 +81,12 @@ pub trait KubeClient: Send + Sync {
invit: &Invitation,
) -> impl Future<Output = Result> + Send;

fn patch_invitation_status(
&self,
token: &str,
status: &InvitationStatus,
) -> impl Future<Output = Result> + Send;

fn patch_user(&self, name: &str, user: &User) -> impl Future<Output = Result> + Send;

fn user_has_permission(
Expand All @@ -80,3 +100,11 @@ pub trait KubeClient: Send + Sync {
user: &User,
) -> impl Future<Output = Result<HashSet<Permission>>> + Send;
}

pub trait KubeEventPublisher: Send + Sync {
fn publish_invitation_event(
&self,
invit: &Invitation,
event: KubeEvent,
) -> impl Future<Output = ()> + Send;
}
1 change: 0 additions & 1 deletion src/mail/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ pub struct DefaultMailSenderArgs {
long = "smtp-from",
env = "SMTP_FROM",
name = "SMTP_FROM",
default_value = "noreply@simpaas.gleroy.dev",
long_help = "Email address used to send mail"
)]
pub from: String,
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use deploy::helm::{HelmDeployer, HelmDeployerArgs};
use domain::{App, Invitation, Role, User};
use helm::cli::{CliHelmClient, CliHelmClientArgs};
use jwt::default::{DefaultJwtEncoder, DefaultJwtEncoderArgs};
use kube::api::ApiKubeClient;
use kube::api::{ApiKubeClient, ApiKubeEventPublisher};
use mail::default::{DefaultMailSender, DefaultMailSenderArgs};
use op::{start_op, OpContext};
use opentelemetry::KeyValue;
Expand Down Expand Up @@ -71,6 +71,7 @@ async fn main() -> anyhow::Result<()> {
deployer: HelmDeployer::new(args.deployer, helm),
kube: ApiKubeClient::new(kube.clone()),
mail_sender: DefaultMailSender::new(args.mail, args.webapp_url)?,
publisher: ApiKubeEventPublisher::new(kube.clone(), args.instance),
requeue_delay: Duration::from_secs(args.requeue_delay),
};
start_op(kube, ctx).await
Expand Down Expand Up @@ -207,6 +208,8 @@ struct OpArgs {
deployer: HelmDeployerArgs,
#[command(flatten)]
helm: CliHelmClientArgs,
#[arg(long, env, long_help = "Name of current instance")]
instance: Option<String>,
#[command(flatten)]
mail: DefaultMailSenderArgs,
#[arg(
Expand All @@ -230,6 +233,7 @@ impl Default for OpArgs {
Self {
deployer: Default::default(),
helm: Default::default(),
instance: None,
mail: DefaultMailSenderArgs::default(),
requeue_delay: 10,
webapp_url: "http://localhost:3000".into(),
Expand Down
Loading

0 comments on commit 466c833

Please sign in to comment.