Skip to content

Add PodListeners #644

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Sep 15, 2023
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ All notable changes to this project will be documented in this file.

### Added

- `PodListeners` CRD ([#644]).
- Add support for tls pkcs12 password to secret operator volume builder ([#645]).

### Changed

- Derive `Eq` and `Copy` where applicable for listener CRDs ([#644]).
- Bump `kube` to `0.86.0` and Kubernetes version to `1.28` ([#648]).

[#644]: https://github.com/stackabletech/operator-rs/pull/644
[#645]: https://github.com/stackabletech/operator-rs/pull/645
[#648]: https://github.com/stackabletech/operator-rs/pull/648

Expand Down
101 changes: 93 additions & 8 deletions src/commons/listener.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
//! This modules provides resource types used to interact with [listener-operator](https://docs.stackable.tech/listener-operator/stable/index.html)
//!
//! # Custom Resources
//!
//! ## [`Listener`]
//!
//! Exposes a set of pods, either internally to the cluster or to the outside world. The mechanism for how it is exposed
//! is managed by the [`ListenerClass`].
//!
//! It can be either created manually by the application administrator (for applications that expose a single load-balanced endpoint),
//! or automatically when mounting a [listener volume](`ListenerOperatorVolumeSourceBuilder`) (for applications that expose a separate endpoint
//! per replica).
//!
//! All exposed pods *must* have a mounted [listener volume](`ListenerOperatorVolumeSourceBuilder`), regardless of whether the [`Listener`] is created automatically.
//!
//! ## [`ListenerClass`]
//!
//! Declares a policy for how [`Listener`]s are exposed to users.
//!
//! It is created by the cluster administrator.
//!
//! ## [`PodListeners`]
//!
//! Informs users and other operators about the state of all [`Listener`]s associated with a [`Pod`].
//!
//! It is created by the Stackable Secret Operator, and always named `pod-{pod.metadata.uid}`.

use std::collections::BTreeMap;

Expand All @@ -7,10 +32,15 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[cfg(doc)]
use k8s_openapi::api::core::v1::{Node, Pod, Service, Volume};
use k8s_openapi::api::core::v1::{
Node, PersistentVolume, PersistentVolumeClaim, Pod, Service, Volume,
};

#[cfg(doc)]
use crate::builder::ListenerOperatorVolumeSourceBuilder;

/// Defines a policy for how [`Listener`]s should be exposed.
#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema)]
#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[kube(
group = "listeners.stackable.tech",
version = "v1alpha1",
Expand All @@ -25,7 +55,7 @@ pub struct ListenerClassSpec {
}

/// The method used to access the services.
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema, PartialEq, Eq)]
pub enum ServiceType {
/// Reserve a port on each node.
NodePort,
Expand All @@ -37,11 +67,13 @@ pub enum ServiceType {

/// Exposes a set of pods to the outside world.
///
/// Essentially a Stackable extension of a Kubernetes [`Service`]. Compared to [`Service`], [`Listener`] changes two things:
/// Essentially a Stackable extension of a Kubernetes [`Service`]. Compared to [`Service`], [`Listener`] changes three things:
/// 1. It uses a cluster-level policy object ([`ListenerClass`]) to define how exactly the exposure works
/// 2. It has a consistent API for reading back the exposed address(es) of the service
/// 3. The [`Pod`] must mount a [`Volume`] referring to the `Listener`, which also allows us to control stickiness
#[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default)]
#[derive(
CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq,
)]
#[kube(
group = "listeners.stackable.tech",
version = "v1alpha1",
Expand Down Expand Up @@ -69,7 +101,7 @@ impl ListenerSpec {
}
}

#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ListenerPort {
/// The name of the port.
Expand All @@ -83,7 +115,7 @@ pub struct ListenerPort {
}

/// Informs users about how to reach the [`Listener`].
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ListenerStatus {
/// The backing Kubernetes [`Service`].
Expand All @@ -98,11 +130,64 @@ pub struct ListenerStatus {
}

/// One address that a [`Listener`] is accessible from.
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ListenerIngress {
/// The hostname or IP address to the [`Listener`].
pub address: String,
/// The type of address (`Hostname` or `IP`).
pub address_type: AddressType,
/// Port mapping table.
pub ports: BTreeMap<String, i32>,
}

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub enum AddressType {
Hostname,
#[serde(rename = "IP")]
Ip,
}

/// Informs users about [`Listener`]s that are bound by a given [`Pod`].
///
/// This is not expected to be created or modified by users. It will be created by
/// the Stackable Listener Operator when mounting the listener volume, and is always
/// named `pod-{pod.metadata.uid}`.
#[derive(
CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq,
)]
#[kube(
group = "listeners.stackable.tech",
version = "v1alpha1",
kind = "PodListeners",
namespaced,
plural = "podlisteners"
)]
#[serde(rename_all = "camelCase")]
pub struct PodListenersSpec {
/// All listeners currently bound by the [`Pod`].
///
/// Indexed by [`Volume`] name (not [`PersistentVolume`] or [`PersistentVolumeClaim`]).
pub listeners: BTreeMap<String, PodListener>,
}

#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PodListener {
/// `Node` if this address only allows access to [`Pod`]s hosted on a specific Kubernetes [`Node`], otherwise `Cluster`.
pub scope: PodListenerScope,
/// Addresses allowing access to this [`Pod`].
///
/// Compared to [`ListenerStatus::ingress_addresses`], this list is restricted to addresses that can access this [`Pod`].
///
/// This field is intended to be equivalent to the files mounted into the listener volume.
pub ingress_addresses: Option<Vec<ListenerIngress>>,
}

#[derive(Serialize, Deserialize, Clone, Copy, Debug, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub enum PodListenerScope {
Node,
Cluster,
}