Added ownerRefs
This commit is contained in:
parent
39bab0cb3a
commit
1a7dfa7acd
60
src/main.rs
60
src/main.rs
|
@ -12,14 +12,16 @@ use kube::runtime::wait::await_condition;
|
||||||
use kube::runtime::watcher::Config;
|
use kube::runtime::watcher::Config;
|
||||||
use kube::Client;
|
use kube::Client;
|
||||||
use kube::CustomResourceExt;
|
use kube::CustomResourceExt;
|
||||||
|
use kube::Resource;
|
||||||
use kube::ResourceExt;
|
use kube::ResourceExt;
|
||||||
use kube::{runtime::controller::Action, runtime::Controller};
|
use kube::{runtime::controller::Action, runtime::Controller};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
pub mod crd;
|
|
||||||
pub mod minio;
|
mod crd;
|
||||||
pub mod neon;
|
mod minio;
|
||||||
|
mod neon;
|
||||||
|
|
||||||
struct ContextData {
|
struct ContextData {
|
||||||
pub kube_client: Client,
|
pub kube_client: Client,
|
||||||
|
@ -40,9 +42,11 @@ pub enum Error {
|
||||||
#[from]
|
#[from]
|
||||||
source: kube::Error,
|
source: kube::Error,
|
||||||
},
|
},
|
||||||
/// Error in user input or Echo resource definition, typically missing fields.
|
/// Error in user input or NeonDatabase resource definition, typically missing fields.
|
||||||
#[error("Invalid Neon CRD: {0}")]
|
#[error("Invalid NeonDatabase CRD: {0}")]
|
||||||
UserInputError(String),
|
UserInputError(String),
|
||||||
|
#[error("General error: {0}")]
|
||||||
|
GeneralError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -55,7 +59,7 @@ async fn main() {
|
||||||
let crd_api: Api<NeonDatabase> = Api::all(kube_client.clone());
|
let crd_api: Api<NeonDatabase> = Api::all(kube_client.clone());
|
||||||
let context: Arc<ContextData> = Arc::new(ContextData::new(kube_client.clone()));
|
let context: Arc<ContextData> = Arc::new(ContextData::new(kube_client.clone()));
|
||||||
|
|
||||||
let ssapply = PatchParams::apply("crd_apply_example").force();
|
let ssapply = PatchParams::apply("neon_operator").force();
|
||||||
let crds: Api<CustomResourceDefinition> = Api::all(kube_client.clone());
|
let crds: Api<CustomResourceDefinition> = Api::all(kube_client.clone());
|
||||||
info!(
|
info!(
|
||||||
"Creating crd: {}",
|
"Creating crd: {}",
|
||||||
|
@ -77,7 +81,7 @@ async fn main() {
|
||||||
);
|
);
|
||||||
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), establish)
|
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), establish)
|
||||||
.await
|
.await
|
||||||
.expect("Waiting failed");
|
.expect("Timeout waiting for CRD to establish");
|
||||||
|
|
||||||
info!("CRD established, starting controller");
|
info!("CRD established, starting controller");
|
||||||
Controller::new(crd_api.clone(), Config::default())
|
Controller::new(crd_api.clone(), Config::default())
|
||||||
|
@ -100,16 +104,44 @@ async fn reconcile(neon: Arc<NeonDatabase>, context: Arc<ContextData>) -> Result
|
||||||
let client = context.kube_client.clone();
|
let client = context.kube_client.clone();
|
||||||
|
|
||||||
let namespace: String = match neon.namespace() {
|
let namespace: String = match neon.namespace() {
|
||||||
Some(ns) => ns.to_string(),
|
Some(ns) => ns,
|
||||||
None => return Err(Error::UserInputError("Missing namespace".to_string())),
|
None => return Err(Error::UserInputError("Missing namespace".to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
minio::create_deployment(client.clone(), &namespace).await?;
|
let owner_ref = neon
|
||||||
minio::create_service(client.clone(), &namespace).await?;
|
.controller_owner_ref(&())
|
||||||
neon::reconcile_storage_broker(client.clone(), &namespace).await?;
|
.ok_or(Error::GeneralError("Missing owner ref".to_string()))?;
|
||||||
neon::reconcile_page_server(client.clone(), &namespace).await?;
|
|
||||||
neon::reconcile_safe_keepers(client.clone(), &namespace).await?;
|
minio::create_deployment(client.clone(), &owner_ref, &namespace).await?;
|
||||||
neon::reconcile_compute(client.clone(), &namespace, &neon.spec.compute_image_ref).await?;
|
minio::create_service(client.clone(), &owner_ref, &namespace).await?;
|
||||||
|
neon::reconcile_storage_broker(
|
||||||
|
client.clone(),
|
||||||
|
&owner_ref,
|
||||||
|
&namespace,
|
||||||
|
&neon.spec.neon_image_ref,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
neon::reconcile_page_server(
|
||||||
|
client.clone(),
|
||||||
|
&owner_ref,
|
||||||
|
&namespace,
|
||||||
|
&neon.spec.neon_image_ref,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
neon::reconcile_safe_keepers(
|
||||||
|
client.clone(),
|
||||||
|
&owner_ref,
|
||||||
|
&namespace,
|
||||||
|
&neon.spec.neon_image_ref,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
neon::reconcile_compute(
|
||||||
|
client.clone(),
|
||||||
|
&owner_ref,
|
||||||
|
&namespace,
|
||||||
|
&neon.spec.compute_image_ref,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(Action::requeue(Duration::from_secs(5)))
|
Ok(Action::requeue(Duration::from_secs(5)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
49
src/minio.rs
49
src/minio.rs
|
@ -1,16 +1,33 @@
|
||||||
use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec};
|
use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec};
|
||||||
use k8s_openapi::api::core::v1::{
|
use k8s_openapi::api::core::v1::{
|
||||||
Container, ContainerPort, EnvVar, PodSpec, PodTemplateSpec, Service, ServicePort, ServiceSpec,
|
Container, ContainerPort, EnvVar, HTTPGetAction, PodSpec, PodTemplateSpec, Probe, Service,
|
||||||
|
ServicePort, ServiceSpec,
|
||||||
};
|
};
|
||||||
use k8s_openapi::apimachinery::pkg::apis::meta::v1::LabelSelector;
|
use k8s_openapi::apimachinery::pkg::apis::meta::v1::{LabelSelector, OwnerReference};
|
||||||
|
use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString;
|
||||||
use kube::api::{ListParams, ObjectMeta, Patch, PatchParams};
|
use kube::api::{ListParams, ObjectMeta, Patch, PatchParams};
|
||||||
use kube::{Api, Client, Error};
|
use kube::{Api, Client, Error};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use tracing::{info, instrument};
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
const NAME: &str = "minio";
|
// Minio settings to lift out
|
||||||
|
const NAME: &str = "minio"; // In a real world scenario, we should support multiple minio instances
|
||||||
|
const MINIO_ROOT_USER: &str = "minio";
|
||||||
|
const MINIO_ROOT_PASSWORD: &str = "password";
|
||||||
|
const MINIO_IMAGE: &str = "quay.io/minio/minio:RELEASE.2022-10-20T00-55-09Z";
|
||||||
|
const MINIO_PORT: i32 = 9000;
|
||||||
|
const MINIO_CONSOLE_PORT: i32 = 9001;
|
||||||
|
|
||||||
pub async fn create_deployment(client: Client, namespace: &str) -> Result<(), Error> {
|
// Workflow:
|
||||||
|
// 1. Create minio deployment
|
||||||
|
// 2. Create minio service
|
||||||
|
// 3. Ensure bucket exists
|
||||||
|
|
||||||
|
pub async fn create_deployment(
|
||||||
|
client: Client,
|
||||||
|
owner_ref: &OwnerReference,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let minio_container = Container {
|
let minio_container = Container {
|
||||||
name: NAME.to_string(),
|
name: NAME.to_string(),
|
||||||
env: Some(vec![
|
env: Some(vec![
|
||||||
|
@ -49,20 +66,20 @@ pub async fn create_deployment(client: Client, namespace: &str) -> Result<(), Er
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.collect::<Vec<String>>(),
|
.collect::<Vec<String>>(),
|
||||||
),
|
),
|
||||||
liveness_probe: Some(k8s_openapi::api::core::v1::Probe {
|
liveness_probe: Some(Probe {
|
||||||
http_get: Some(k8s_openapi::api::core::v1::HTTPGetAction {
|
http_get: Some(HTTPGetAction {
|
||||||
path: Some("/minio/health/live".to_string()),
|
path: Some("/minio/health/live".to_string()),
|
||||||
port: k8s_openapi::apimachinery::pkg::util::intstr::IntOrString::Int(9000),
|
port: IntOrString::Int(9000),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
initial_delay_seconds: Some(10),
|
initial_delay_seconds: Some(10),
|
||||||
period_seconds: Some(30),
|
period_seconds: Some(30),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
readiness_probe: Some(k8s_openapi::api::core::v1::Probe {
|
readiness_probe: Some(Probe {
|
||||||
http_get: Some(k8s_openapi::api::core::v1::HTTPGetAction {
|
http_get: Some(HTTPGetAction {
|
||||||
path: Some("/minio/health/ready".to_string()),
|
path: Some("/minio/health/ready".to_string()),
|
||||||
port: k8s_openapi::apimachinery::pkg::util::intstr::IntOrString::Int(9000),
|
port: IntOrString::Int(9000),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
initial_delay_seconds: Some(10),
|
initial_delay_seconds: Some(10),
|
||||||
|
@ -100,6 +117,7 @@ pub async fn create_deployment(client: Client, namespace: &str) -> Result<(), Er
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some(NAME.to_string()),
|
name: Some(NAME.to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
labels: Some(BTreeMap::from([("app".to_string(), NAME.to_string())])),
|
labels: Some(BTreeMap::from([("app".to_string(), NAME.to_string())])),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
@ -131,7 +149,7 @@ pub async fn create_deployment(client: Client, namespace: &str) -> Result<(), Er
|
||||||
deployment_api
|
deployment_api
|
||||||
.patch(
|
.patch(
|
||||||
NAME,
|
NAME,
|
||||||
&PatchParams::apply("neon-operator"),
|
&PatchParams::apply("neon_operator"),
|
||||||
&Patch::Apply(deployment),
|
&Patch::Apply(deployment),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -139,12 +157,17 @@ pub async fn create_deployment(client: Client, namespace: &str) -> Result<(), Er
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_service(client: Client, namespace: &str) -> Result<(), Error> {
|
pub async fn create_service(
|
||||||
|
client: Client,
|
||||||
|
owner_ref: &OwnerReference,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let service = Service {
|
let service = Service {
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some(NAME.to_string()),
|
name: Some(NAME.to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
labels: Some(BTreeMap::from([("app".to_string(), NAME.to_string())])),
|
labels: Some(BTreeMap::from([("app".to_string(), NAME.to_string())])),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
spec: Some(ServiceSpec {
|
spec: Some(ServiceSpec {
|
||||||
|
@ -162,7 +185,7 @@ pub async fn create_service(client: Client, namespace: &str) -> Result<(), Error
|
||||||
Api::<Service>::namespaced(client, namespace)
|
Api::<Service>::namespaced(client, namespace)
|
||||||
.patch(
|
.patch(
|
||||||
NAME,
|
NAME,
|
||||||
&PatchParams::apply("neon-operator"),
|
&PatchParams::apply("neon_operator"),
|
||||||
&Patch::Apply(service),
|
&Patch::Apply(service),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
39
src/neon.rs
39
src/neon.rs
|
@ -2,21 +2,22 @@ use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec, StatefulSet};
|
||||||
use k8s_openapi::api::core::v1::{
|
use k8s_openapi::api::core::v1::{
|
||||||
Container, ContainerPort, EnvVar, PodSpec, PodTemplateSpec, Service, ServicePort, ServiceSpec,
|
Container, ContainerPort, EnvVar, PodSpec, PodTemplateSpec, Service, ServicePort, ServiceSpec,
|
||||||
};
|
};
|
||||||
use k8s_openapi::apimachinery::pkg::apis::meta::v1::LabelSelector;
|
use k8s_openapi::apimachinery::pkg::apis::meta::v1::{LabelSelector, OwnerReference};
|
||||||
use kube::api::{ObjectMeta, Patch, PatchParams};
|
use kube::api::{ObjectMeta, Patch, PatchParams};
|
||||||
use kube::{Api, Client, Error};
|
use kube::{Api, Client, Error};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
// Workflow:
|
pub async fn reconcile_storage_broker(
|
||||||
// 1. Create minio deployment
|
client: Client,
|
||||||
// 2. Create minio service
|
owner_ref: &OwnerReference,
|
||||||
// 3. Ensure bucket exists
|
namespace: &str,
|
||||||
|
neon_image_ref: &str,
|
||||||
pub async fn reconcile_storage_broker(client: Client, namespace: &str) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let storage_broker = Deployment {
|
let storage_broker = Deployment {
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some("storage-broker".to_string()),
|
name: Some("storage-broker".to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
spec: Some(DeploymentSpec {
|
spec: Some(DeploymentSpec {
|
||||||
|
@ -39,7 +40,7 @@ pub async fn reconcile_storage_broker(client: Client, namespace: &str) -> Result
|
||||||
spec: Some(PodSpec {
|
spec: Some(PodSpec {
|
||||||
containers: vec![Container {
|
containers: vec![Container {
|
||||||
name: "storage-broker".to_string(),
|
name: "storage-broker".to_string(),
|
||||||
image: Some("neondatabase/neon".to_string()),
|
image: Some(neon_image_ref.to_string()),
|
||||||
ports: Some(vec![ContainerPort {
|
ports: Some(vec![ContainerPort {
|
||||||
container_port: 50051,
|
container_port: 50051,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -104,11 +105,17 @@ pub async fn reconcile_storage_broker(client: Client, namespace: &str) -> Result
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reconcile_page_server(client: Client, namespace: &str) -> Result<(), Error> {
|
pub async fn reconcile_page_server(
|
||||||
|
client: Client,
|
||||||
|
owner_ref: &OwnerReference,
|
||||||
|
namespace: &str,
|
||||||
|
neon_image_ref: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let deployment = Deployment {
|
let deployment = Deployment {
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some("pageserver".to_string()),
|
name: Some("pageserver".to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
spec: Some(DeploymentSpec {
|
spec: Some(DeploymentSpec {
|
||||||
|
@ -131,7 +138,7 @@ pub async fn reconcile_page_server(client: Client, namespace: &str) -> Result<()
|
||||||
spec: Some(PodSpec {
|
spec: Some(PodSpec {
|
||||||
containers: vec![Container {
|
containers: vec![Container {
|
||||||
name: "pageserver".to_string(),
|
name: "pageserver".to_string(),
|
||||||
image: Some("neondatabase/neon".to_string()),
|
image: Some(neon_image_ref.to_string()),
|
||||||
ports: Some(vec![ContainerPort {
|
ports: Some(vec![ContainerPort {
|
||||||
container_port: 9898,
|
container_port: 9898,
|
||||||
name: Some("http".to_string()),
|
name: Some("http".to_string()),
|
||||||
|
@ -235,11 +242,17 @@ pub async fn reconcile_page_server(client: Client, namespace: &str) -> Result<()
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reconcile_safe_keepers(client: Client, namespace: &str) -> Result<(), Error> {
|
pub async fn reconcile_safe_keepers(
|
||||||
|
client: Client,
|
||||||
|
owner_ref: &OwnerReference,
|
||||||
|
namespace: &str,
|
||||||
|
neon_image_ref: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let stateful_set = StatefulSet {
|
let stateful_set = StatefulSet {
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some("safe-keeper".to_string()),
|
name: Some("safe-keeper".to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
spec: Some(k8s_openapi::api::apps::v1::StatefulSetSpec {
|
spec: Some(k8s_openapi::api::apps::v1::StatefulSetSpec {
|
||||||
|
@ -263,7 +276,7 @@ pub async fn reconcile_safe_keepers(client: Client, namespace: &str) -> Result<(
|
||||||
spec: Some(PodSpec {
|
spec: Some(PodSpec {
|
||||||
containers: vec![Container {
|
containers: vec![Container {
|
||||||
name: "safe-keeper".to_string(),
|
name: "safe-keeper".to_string(),
|
||||||
image: Some("neondatabase/neon".to_string()),
|
image: Some(neon_image_ref.to_string()),
|
||||||
ports: Some(vec![ContainerPort {
|
ports: Some(vec![ContainerPort {
|
||||||
container_port: 7676,
|
container_port: 7676,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -375,6 +388,7 @@ pub async fn reconcile_safe_keepers(client: Client, namespace: &str) -> Result<(
|
||||||
|
|
||||||
pub async fn reconcile_compute(
|
pub async fn reconcile_compute(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
owner_ref: &OwnerReference,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
compute_image: &str,
|
compute_image: &str,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
@ -382,6 +396,7 @@ pub async fn reconcile_compute(
|
||||||
metadata: ObjectMeta {
|
metadata: ObjectMeta {
|
||||||
name: Some("compute".to_string()),
|
name: Some("compute".to_string()),
|
||||||
namespace: Some(namespace.to_string()),
|
namespace: Some(namespace.to_string()),
|
||||||
|
owner_references: Some(vec![owner_ref.clone()]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
spec: Some(DeploymentSpec {
|
spec: Some(DeploymentSpec {
|
||||||
|
|
Loading…
Reference in New Issue