Skip to content

Commit

Permalink
Adding feature cluster role binding. Fixes #52
Browse files Browse the repository at this point in the history
  • Loading branch information
shinusuresh committed Aug 25, 2022
1 parent 3a64b77 commit db63fe6
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 6 deletions.
18 changes: 16 additions & 2 deletions src/app/mod.rs
Expand Up @@ -40,7 +40,7 @@ use self::{
pods::{KubeContainer, KubePod},
replicasets::KubeReplicaSet,
replication_controllers::KubeReplicationController,
roles::{KubeClusterRoles, KubeRoles},
roles::{KubeClusterRoleBinding, KubeClusterRoles, KubeRoles},
secrets::KubeSecret,
statefulsets::KubeStatefulSet,
storageclass::KubeStorageClass,
Expand Down Expand Up @@ -76,6 +76,7 @@ pub enum ActiveBlock {
StorageClasses,
Roles,
ClusterRoles,
ClusterRoleBinding,
More,
}

Expand Down Expand Up @@ -133,6 +134,7 @@ pub struct Data {
pub storage_classes: StatefulTable<KubeStorageClass>,
pub roles: StatefulTable<KubeRoles>,
pub clusterroles: StatefulTable<KubeClusterRoles>,
pub clusterrolebinding: StatefulTable<KubeClusterRoleBinding>,
}

/// selected data items
Expand Down Expand Up @@ -208,6 +210,7 @@ impl Default for Data {
storage_classes: StatefulTable::new(),
roles: StatefulTable::new(),
clusterroles: StatefulTable::new(),
clusterrolebinding: StatefulTable::new(),
}
}
}
Expand Down Expand Up @@ -335,7 +338,10 @@ impl Default for App {
("Roles".into(), ActiveBlock::Roles),
// ("Role Bindings".into(), ActiveBlock::RplCtrl),
("Cluster Roles".into(), ActiveBlock::ClusterRoles),
// ("Cluster Role Bindings".into(), ActiveBlock::RplCtrl),
(
"Cluster Role Bindings".into(),
ActiveBlock::ClusterRoleBinding,
),
// ("Service Accounts".into(), ActiveBlock::RplCtrl),
// ("Ingresses".into(), ActiveBlock::RplCtrl),
// ("Network Policies".into(), ActiveBlock::RplCtrl),
Expand Down Expand Up @@ -531,6 +537,7 @@ impl App {
self.dispatch(IoEvent::GetStorageClasses).await;
self.dispatch(IoEvent::GetRoles).await;
self.dispatch(IoEvent::GetClusterRoles).await;
self.dispatch(IoEvent::GetClusterRoleBinding).await;
self.dispatch(IoEvent::GetMetrics).await;
}

Expand Down Expand Up @@ -578,6 +585,9 @@ impl App {
ActiveBlock::ClusterRoles => {
self.dispatch(IoEvent::GetClusterRoles).await;
}
ActiveBlock::ClusterRoleBinding => {
self.dispatch(IoEvent::GetClusterRoleBinding).await;
}
ActiveBlock::Logs => {
if !self.is_streaming {
// do not tail to avoid duplicates
Expand Down Expand Up @@ -737,6 +747,10 @@ mod tests {
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetStorageClasses);
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetRoles);
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetClusterRoles);
assert_eq!(
sync_io_rx.recv().await.unwrap(),
IoEvent::GetClusterRoleBinding
);
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetMetrics);
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetNamespaces);
assert_eq!(sync_io_rx.recv().await.unwrap(), IoEvent::GetNodes);
Expand Down
55 changes: 52 additions & 3 deletions src/app/roles.rs
@@ -1,4 +1,6 @@
use k8s_openapi::{api::rbac::v1::ClusterRole, api::rbac::v1::Role, chrono::Utc};
use k8s_openapi::{
api::rbac::v1::ClusterRole, api::rbac::v1::ClusterRoleBinding, api::rbac::v1::Role, chrono::Utc,
};

use super::{models::KubeResource, utils};

Expand All @@ -17,6 +19,14 @@ pub struct KubeClusterRoles {
k8s_obj: ClusterRole,
}

#[derive(Clone, Debug, PartialEq)]
pub struct KubeClusterRoleBinding {
pub name: String,
pub role: String,
pub age: String,
k8s_obj: ClusterRoleBinding,
}

impl KubeResource<Role> for KubeRoles {
fn from_api(role: &Role) -> Self {
KubeRoles {
Expand Down Expand Up @@ -46,9 +56,31 @@ impl KubeResource<ClusterRole> for KubeClusterRoles {
}
}

impl KubeResource<ClusterRoleBinding> for KubeClusterRoleBinding {
fn from_api(clusterrolebinding: &ClusterRoleBinding) -> Self {
KubeClusterRoleBinding {
name: clusterrolebinding.metadata.name.clone().unwrap_or_default(),
role: format!(
"{}/{}",
clusterrolebinding.role_ref.kind.clone(),
clusterrolebinding.role_ref.name.clone()
),
age: utils::to_age(
clusterrolebinding.metadata.creation_timestamp.as_ref(),
Utc::now(),
),
k8s_obj: clusterrolebinding.to_owned(),
}
}

fn get_k8s_obj(&self) -> &ClusterRoleBinding {
&self.k8s_obj
}
}

#[cfg(test)]
mod tests {
use crate::app::roles::{KubeClusterRoles, KubeRoles};
use crate::app::roles::{KubeClusterRoleBinding, KubeClusterRoles, KubeRoles};
use crate::app::test_utils::{convert_resource_from_file, get_time};
use crate::app::utils;
use k8s_openapi::chrono::Utc;
Expand All @@ -70,7 +102,7 @@ mod tests {
}

#[test]
fn test_cluster_roles_binding_from_rbac_api() {
fn test_cluster_roles_from_rbac_api() {
let (clusterroles, cluster_roles_list): (Vec<KubeClusterRoles>, Vec<_>) =
convert_resource_from_file("clusterroles");

Expand All @@ -84,4 +116,21 @@ mod tests {
}
)
}

#[test]
fn test_cluster_role_bindings_from_rbac_api() {
let (clusterrolebinding, cluster_role_bindings_list): (Vec<KubeClusterRoleBinding>, Vec<_>) =
convert_resource_from_file("clusterrole_binding");

assert_eq!(clusterrolebinding.len(), 2);
assert_eq!(
clusterrolebinding[0],
KubeClusterRoleBinding {
name: "admin-user".into(),
role: "ClusterRole/cluster-admin".into(),
age: utils::to_age(Some(&get_time("2022-03-02T16:50:53Z")), Utc::now()),
k8s_obj: cluster_role_bindings_list[0].clone(),
}
)
}
}
16 changes: 16 additions & 0 deletions src/handlers/mod.rs
Expand Up @@ -490,6 +490,21 @@ async fn handle_route_events(key: Key, app: &mut App) {
.await;
}
}
ActiveBlock::ClusterRoleBinding => {
if let Some(res) = handle_block_action(key, &mut app.data.clusterrolebinding) {
let _ok = handle_describe_decode_or_yaml_action(
key,
app,
&res,
IoCmdEvent::GetDescribe {
kind: "clusterrolebinding".to_owned(),
value: res.name.to_owned(),
ns: None,
},
)
.await;
}
}
ActiveBlock::Contexts | ActiveBlock::Utilization | ActiveBlock::Help => { /* Do nothing */ }
}
}
Expand Down Expand Up @@ -555,6 +570,7 @@ async fn handle_block_scroll(app: &mut App, up: bool, is_mouse: bool, page: bool
ActiveBlock::StorageClasses => app.data.storage_classes.handle_scroll(up, page),
ActiveBlock::Roles => app.data.roles.handle_scroll(up, page),
ActiveBlock::ClusterRoles => app.data.clusterroles.handle_scroll(up, page),
ActiveBlock::ClusterRoleBinding => app.data.clusterrolebinding.handle_scroll(up, page),
ActiveBlock::Contexts => app.data.contexts.handle_scroll(up, page),
ActiveBlock::Utilization => app.data.metrics.handle_scroll(up, page),
ActiveBlock::Help => app.help_docs.handle_scroll(up, page),
Expand Down
11 changes: 10 additions & 1 deletion src/network/kube_api.rs
Expand Up @@ -28,7 +28,7 @@ use crate::app::{
pods::KubePod,
replicasets::KubeReplicaSet,
replication_controllers::KubeReplicationController,
roles::{KubeClusterRoles, KubeRoles},
roles::{KubeClusterRoleBinding, KubeClusterRoles, KubeRoles},
secrets::KubeSecret,
statefulsets::KubeStatefulSet,
storageclass::KubeStorageClass,
Expand Down Expand Up @@ -327,6 +327,15 @@ impl<'a> Network<'a> {
app.data.clusterroles.set_items(items);
}

pub async fn get_cluster_role_binding(&self) {
let items: Vec<KubeClusterRoleBinding> = self
.get_namespaced_resources(|it| KubeClusterRoleBinding::from_api(it))
.await;

let mut app = self.app.lock().await;
app.data.clusterrolebinding.set_items(items);
}

/// calls the kubernetes API to list the given resource for either selected namespace or all namespaces
async fn get_namespaced_resources<K: ApiResource, T, F>(&self, map_fn: F) -> Vec<T>
where
Expand Down
4 changes: 4 additions & 0 deletions src/network/mod.rs
Expand Up @@ -30,6 +30,7 @@ pub enum IoEvent {
GetStorageClasses,
GetRoles,
GetClusterRoles,
GetClusterRoleBinding,
GetMetrics,
RefreshClient,
}
Expand Down Expand Up @@ -166,6 +167,9 @@ impl<'a> Network<'a> {
IoEvent::GetClusterRoles => {
self.get_cluster_roles().await;
}
IoEvent::GetClusterRoleBinding => {
self.get_cluster_role_binding().await;
}
};

let mut app = self.app.lock().await;
Expand Down
56 changes: 56 additions & 0 deletions src/ui/resource_tabs.rs
Expand Up @@ -37,6 +37,7 @@ static RPL_CTRL_TITLE: &str = "ReplicationControllers";
static STORAGE_CLASSES_LABEL: &str = "StorageClasses";
static ROLES_TITLE: &str = "Roles";
static CLUSTER_ROLES_TITLE: &str = "ClusterRoles";
static CLUSTER_ROLES_BINDING_TITLE: &str = "ClusterRoleBinding";
static DESCRIBE_ACTIVE: &str = "-> Describe ";
static YAML_ACTIVE: &str = "-> YAML ";

Expand Down Expand Up @@ -88,6 +89,7 @@ fn draw_more<B: Backend>(block: ActiveBlock, f: &mut Frame<'_, B>, app: &mut App
ActiveBlock::StorageClasses => draw_storage_classes_tab(block, f, app, area),
ActiveBlock::Roles => draw_roles_tab(block, f, app, area),
ActiveBlock::ClusterRoles => draw_cluster_roles_tab(block, f, app, area),
ActiveBlock::ClusterRoleBinding => draw_cluster_role_binding_tab(block, f, app, area),
ActiveBlock::Describe | ActiveBlock::Yaml => {
let mut prev_route = app.get_prev_route();
if prev_route.active_block == block {
Expand All @@ -100,6 +102,7 @@ fn draw_more<B: Backend>(block: ActiveBlock, f: &mut Frame<'_, B>, app: &mut App
ActiveBlock::StorageClasses => draw_storage_classes_tab(block, f, app, area),
ActiveBlock::Roles => draw_roles_tab(block, f, app, area),
ActiveBlock::ClusterRoles => draw_cluster_roles_tab(block, f, app, area),
ActiveBlock::ClusterRoleBinding => draw_cluster_role_binding_tab(block, f, app, area),
_ => { /* do nothing */ }
}
}
Expand Down Expand Up @@ -1110,6 +1113,59 @@ fn draw_cluster_roles_block<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, are
);
}

fn draw_cluster_role_binding_tab<B: Backend>(
block: ActiveBlock,
f: &mut Frame<'_, B>,
app: &mut App,
area: Rect,
) {
draw_resource_tab!(
CLUSTER_ROLES_BINDING_TITLE,
block,
f,
app,
area,
draw_cluster_role_binding_tab,
draw_cluster_role_binding_block,
app.data.clusterrolebinding
);
}

fn draw_cluster_role_binding_block<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, area: Rect) {
let title = get_resource_title(
app,
CLUSTER_ROLES_BINDING_TITLE,
"",
app.data.clusterrolebinding.items.len(),
);

draw_resource_block(
f,
area,
ResourceTableProps {
title,
inline_help: DESCRIBE_YAML_AND_ESC_HINT.into(),
resource: &mut app.data.clusterrolebinding,
table_headers: vec!["Name", "Role", "Age"],
column_widths: vec![
Constraint::Percentage(40),
Constraint::Percentage(40),
Constraint::Percentage(20),
],
},
|c| {
Row::new(vec![
Cell::from(c.name.to_owned()),
Cell::from(c.role.to_owned()),
Cell::from(c.age.to_owned()),
])
.style(style_primary(app.light_theme))
},
app.light_theme,
app.is_loading,
);
}

/// common for all resources
fn draw_describe_block<B: Backend>(
f: &mut Frame<'_, B>,
Expand Down
46 changes: 46 additions & 0 deletions test_data/clusterrole_binding.yaml
@@ -0,0 +1,46 @@
apiVersion: v1
items:
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
creationTimestamp: "2022-03-02T16:50:53Z"
name: admin-user
resourceVersion: "70549225"
uid: e86a4046-d74b-457e-9e93-6269a675284d
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: power-user
namespace: kube-system
- kind: ServiceAccount
name: admin-user
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"aws-node"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"aws-node"},"subjects":[{"kind":"ServiceAccount","name":"aws-node","namespace":"kube-system"}]}
meta.helm.sh/release-name: aws-cni
meta.helm.sh/release-namespace: kube-system
creationTimestamp: "2022-03-02T16:42:18Z"
labels:
app.kubernetes.io/managed-by: Helm
name: aws-node
resourceVersion: "70549265"
uid: f6dd32c5-f853-4a8b-a98b-0726d4702cbe
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: aws-node
subjects:
- kind: ServiceAccount
name: aws-node
namespace: kube-system
kind: List
metadata:
resourceVersion: ""
selfLink: ""

0 comments on commit db63fe6

Please sign in to comment.