Skip to content

Commit 7a7ddec

Browse files
committed
OpenShift compatibility (#352)
1 parent 14f81fd commit 7a7ddec

30 files changed

Lines changed: 565 additions & 137 deletions

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616
This change is breaking, because - for security reasons - we default to the `cluster-internal` `ListenerClass`.
1717
If you need your cluster to be accessible from outside of Kubernetes you need to set `clusterConfig.listenerClass`
1818
to `external-unstable` or `external-stable` ([#350]).
19-
- `operator-rs` `0.31.0` -> `0.35.0` ([#322], [#326]).
19+
- `operator-rs` `0.31.0` -> `0.35.0` -> `0.40.2` ([#322], [#326], [#352]).
2020
- Bumped stackable image versions to "23.4.0-rc2" ([#322], [#326]).
2121
- Fragmented `SupersetConfig` ([#323]).
2222
- Restructured documentation ([#344]).
23+
- Create `ServiceAccount` for Superset clusters. Use `build_rbac_resources()` from operator-rs ([#352])
2324

2425
[#322]: https://github.com/stackabletech/superset-operator/pull/322
2526
[#323]: https://github.com/stackabletech/superset-operator/pull/323
@@ -29,6 +30,7 @@
2930
[#348]: https://github.com/stackabletech/superset-operator/pull/348
3031
[#349]: https://github.com/stackabletech/superset-operator/pull/349
3132
[#350]: https://github.com/stackabletech/superset-operator/pull/350
33+
[#352]: https://github.com/stackabletech/superset-operator/pull/352
3234

3335
## [23.1.0] - 2023-01-23
3436

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deploy/helm/superset-operator/templates/roles.yaml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,88 @@ rules:
110110
- get
111111
- list
112112
- watch
113+
- apiGroups:
114+
- rbac.authorization.k8s.io
115+
resources:
116+
- clusterroles
117+
verbs:
118+
- bind
119+
resourceNames:
120+
- {{ include "operator.name" . }}-clusterrole
121+
122+
{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }}
123+
---
124+
apiVersion: security.openshift.io/v1
125+
kind: SecurityContextConstraints
126+
metadata:
127+
name: superset-scc
128+
annotations:
129+
kubernetes.io/description: |-
130+
This resource is derived from hostmount-anyuid. It provides all the features of the
131+
restricted SCC but allows host mounts and any UID by a pod. This is primarily
132+
used by the persistent volume recycler. WARNING: this SCC allows host file
133+
system access as any UID, including UID 0. Grant with caution.
134+
release.openshift.io/create-only: "true"
135+
allowHostDirVolumePlugin: true
136+
allowHostIPC: false
137+
allowHostNetwork: false
138+
allowHostPID: false
139+
allowHostPorts: false
140+
allowPrivilegeEscalation: true
141+
allowPrivilegedContainer: false
142+
allowedCapabilities: null
143+
defaultAddCapabilities: null
144+
fsGroup:
145+
type: RunAsAny
146+
groups: []
147+
priority: null
148+
readOnlyRootFilesystem: false
149+
requiredDropCapabilities:
150+
- MKNOD
151+
runAsUser:
152+
type: RunAsAny
153+
seLinuxContext:
154+
type: MustRunAs
155+
supplementalGroups:
156+
type: RunAsAny
157+
volumes:
158+
- configMap
159+
- downwardAPI
160+
- emptyDir
161+
- hostPath
162+
- nfs
163+
- persistentVolumeClaim
164+
- projected
165+
- secret
166+
- ephemeral
167+
{{ end }}
168+
---
169+
apiVersion: rbac.authorization.k8s.io/v1
170+
kind: ClusterRole
171+
metadata:
172+
name: {{ include "operator.name" . }}-clusterrole
173+
rules:
174+
- apiGroups:
175+
- ""
176+
resources:
177+
- configmaps
178+
- secrets
179+
- serviceaccounts
180+
verbs:
181+
- get
182+
- apiGroups:
183+
- events.k8s.io
184+
resources:
185+
- events
186+
verbs:
187+
- create
188+
{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }}
189+
- apiGroups:
190+
- security.openshift.io
191+
resources:
192+
- securitycontextconstraints
193+
resourceNames:
194+
- superset-scc
195+
verbs:
196+
- use
197+
{{ end }}

rust/crd/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ publish = false
1111
[dependencies]
1212
serde = "1.0"
1313
serde_json = "1.0"
14-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.39.0" }
14+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.40.2" }
1515
strum = { version = "0.24", features = ["derive"] }
1616
snafu = "0.7"
1717
tracing = "0.1"

rust/operator-binary/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ futures = { version = "0.3", features = ["compat"] }
1616
indoc = "1.0"
1717
serde = "1.0"
1818
snafu = "0.7"
19-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.39.0" }
19+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.40.2" }
2020
stackable-superset-crd = { path = "../crd" }
2121
strum = { version = "0.24", features = ["derive"] }
2222
tokio = { version = "1.25", features = ["macros", "rt-multi-thread"] }
2323
tracing = "0.1"
2424

2525
[build-dependencies]
2626
built = { version = "0.5", features = ["chrono", "git2"] }
27-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.39.0" }
27+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.40.2" }
2828
stackable-superset-crd = { path = "../crd" }

rust/operator-binary/src/druid_connection_controller.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::util::{get_job_state, JobState};
22

3-
use crate::superset_controller::DOCKER_IMAGE_BASE_NAME;
3+
use crate::{rbac, superset_controller::DOCKER_IMAGE_BASE_NAME, APP_NAME};
44
use snafu::{OptionExt, ResultExt, Snafu};
55
use stackable_operator::commons::product_image_selection::ResolvedProductImage;
66
use stackable_operator::{
7-
builder::{ContainerBuilder, ObjectMetaBuilder},
7+
builder::{ContainerBuilder, ObjectMetaBuilder, PodSecurityContextBuilder},
88
client::Client,
99
k8s_openapi::api::{
1010
batch::v1::{Job, JobSpec},
@@ -72,6 +72,14 @@ pub enum Error {
7272
source: stackable_superset_crd::druidconnection::Error,
7373
druid_connection: ObjectRef<DruidConnection>,
7474
},
75+
#[snafu(display("failed to patch service account"))]
76+
ApplyServiceAccount {
77+
source: stackable_operator::error::Error,
78+
},
79+
#[snafu(display("failed to patch role binding"))]
80+
ApplyRoleBinding {
81+
source: stackable_operator::error::Error,
82+
},
7583
}
7684

7785
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -96,6 +104,8 @@ impl ReconcilerError for Error {
96104
Error::DruidConnectionNoNamespace {
97105
druid_connection, ..
98106
} => Some(druid_connection.clone().erase()),
107+
Error::ApplyServiceAccount { .. } => None,
108+
Error::ApplyRoleBinding { .. } => None,
99109
}
100110
}
101111
}
@@ -108,6 +118,21 @@ pub async fn reconcile_druid_connection(
108118

109119
let client = &ctx.client;
110120

121+
let (rbac_sa, rbac_rolebinding) =
122+
rbac::build_rbac_resources(druid_connection.as_ref(), APP_NAME);
123+
client
124+
.apply_patch(DRUID_CONNECTION_CONTROLLER_NAME, &rbac_sa, &rbac_sa)
125+
.await
126+
.context(ApplyServiceAccountSnafu)?;
127+
client
128+
.apply_patch(
129+
DRUID_CONNECTION_CONTROLLER_NAME,
130+
&rbac_rolebinding,
131+
&rbac_rolebinding,
132+
)
133+
.await
134+
.context(ApplyRoleBindingSnafu)?;
135+
111136
if let Some(ref s) = druid_connection.status {
112137
match s.condition {
113138
DruidConnectionStatusCondition::Pending => {
@@ -172,6 +197,7 @@ pub async fn reconcile_druid_connection(
172197
&druid_connection,
173198
&resolved_product_image,
174199
&sqlalchemy_str,
200+
&rbac_sa.name_any(),
175201
)
176202
.await?;
177203
client
@@ -266,6 +292,7 @@ async fn build_import_job(
266292
druid_connection: &DruidConnection,
267293
resolved_product_image: &ResolvedProductImage,
268294
sqlalchemy_str: &str,
295+
sa_name: &str,
269296
) -> Result<Job> {
270297
let mut commands = vec![];
271298

@@ -301,6 +328,13 @@ async fn build_import_job(
301328
containers: vec![container],
302329
image_pull_secrets: resolved_product_image.pull_secrets.clone(),
303330
restart_policy: Some("Never".to_string()),
331+
service_account: Some(sa_name.to_string()),
332+
security_context: Some(
333+
PodSecurityContextBuilder::new()
334+
.run_as_user(1000)
335+
.run_as_group(0)
336+
.build(),
337+
),
304338
..Default::default()
305339
}),
306340
};

rust/operator-binary/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod config;
22
mod controller_commons;
33
mod druid_connection_controller;
44
mod product_logging;
5+
mod rbac;
56
mod superset_controller;
67
mod superset_db_controller;
78
mod util;

rust/operator-binary/src/rbac.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use stackable_operator::builder::ObjectMetaBuilder;
2+
use stackable_operator::k8s_openapi::api::core::v1::ServiceAccount;
3+
use stackable_operator::k8s_openapi::api::rbac::v1::{RoleBinding, RoleRef, Subject};
4+
use stackable_operator::kube::{Resource, ResourceExt};
5+
6+
/// Obsolete: only used in the DB controller for historical reasons. Scheduled for deletion
7+
/// once the entire DB controller is deleted as discused here: <https://github.com/stackabletech/superset-operator/issues/351>
8+
///
9+
/// Build RBAC objects for the product workloads.
10+
/// The `rbac_prefix` is meant to be the product name, for example: zookeeper, airflow, etc.
11+
/// and it is a assumed that a ClusterRole named `{rbac_prefix}-clusterrole` exists.
12+
pub fn build_rbac_resources<T: Resource>(
13+
resource: &T,
14+
rbac_prefix: &str,
15+
) -> (ServiceAccount, RoleBinding) {
16+
let sa_name = format!("{rbac_prefix}-sa");
17+
let service_account = ServiceAccount {
18+
metadata: ObjectMetaBuilder::new()
19+
.name_and_namespace(resource)
20+
.name(sa_name.clone())
21+
.build(),
22+
..ServiceAccount::default()
23+
};
24+
25+
let role_binding = RoleBinding {
26+
metadata: ObjectMetaBuilder::new()
27+
.name_and_namespace(resource)
28+
.name(format!("{rbac_prefix}-rolebinding"))
29+
.build(),
30+
role_ref: RoleRef {
31+
kind: "ClusterRole".to_string(),
32+
name: format!("{rbac_prefix}-clusterrole"),
33+
api_group: "rbac.authorization.k8s.io".to_string(),
34+
},
35+
subjects: Some(vec![Subject {
36+
kind: "ServiceAccount".to_string(),
37+
name: sa_name,
38+
namespace: resource.namespace(),
39+
..Subject::default()
40+
}]),
41+
};
42+
43+
(service_account, role_binding)
44+
}

0 commit comments

Comments
 (0)