diff --git a/CHANGELOG.md b/CHANGELOG.md index e7b7f43da..972eacc4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- More CRD documentation ([#697]). + +[#697]: https://github.com/stackabletech/operator-rs/pull/697 + ## [0.58.0] - 2023-12-04 ### Added diff --git a/src/commons/authentication/ldap.rs b/src/commons/authentication/ldap.rs index 45daf70e4..069140b23 100644 --- a/src/commons/authentication/ldap.rs +++ b/src/commons/authentication/ldap.rs @@ -13,28 +13,28 @@ use crate::{ #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AuthenticationProvider { - /// Hostname of the LDAP server + /// Hostname of the LDAP server, for example: `my.ldap.server`. pub hostname: String, - /// Port of the LDAP server. If TLS is used defaults to 636 otherwise to 389 + /// Port of the LDAP server. If TLS is used defaults to 636 otherwise to 389. port: Option<u16>, - /// LDAP search base + /// LDAP search base, for example: `ou=users,dc=example,dc=org`. #[serde(default)] pub search_base: String, - /// LDAP query to filter users + /// LDAP query to filter users, for example: `(memberOf=cn=myTeam,ou=teams,dc=example,dc=org)`. #[serde(default)] pub search_filter: String, - /// The name of the LDAP object fields + /// The name of the LDAP object fields. #[serde(default)] pub ldap_field_names: FieldNames, - /// In case you need a special account for searching the LDAP server you can specify it here + /// In case you need a special account for searching the LDAP server you can specify it here. bind_credentials: Option<SecretClassVolume>, - /// Use a TLS connection. If not specified no TLS will be used + /// Use a TLS connection. If not specified no TLS will be used. #[serde(flatten)] pub tls: TlsClientDetails, } diff --git a/src/commons/authentication/mod.rs b/src/commons/authentication/mod.rs index 7203196e5..1837870c2 100644 --- a/src/commons/authentication/mod.rs +++ b/src/commons/authentication/mod.rs @@ -15,6 +15,11 @@ pub mod tls; pub(crate) const SECRET_BASE_PATH: &str = "/stackable/secrets"; +/// The Stackable Platform uses the AuthenticationClass as a central mechanism to handle user authentication across supported products. +/// The authentication mechanism needs to be configured only in the AuthenticationClass which is then referenced in the product. +/// Multiple different authentication providers are supported. +/// Learn more in the [authentication concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/authentication) and the +/// [Authentication with OpenLDAP tutorial](DOCS_BASE_URL_PLACEHOLDER/tutorials/authentication_with_openldap). #[derive(Clone, CustomResource, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[kube( group = "authentication.stackable.tech", @@ -29,7 +34,7 @@ pub(crate) const SECRET_BASE_PATH: &str = "/stackable/secrets"; )] #[serde(rename_all = "camelCase")] pub struct AuthenticationClassSpec { - /// Provider used for authentication like LDAP or Kerberos + /// Provider used for authentication like LDAP or Kerberos. pub provider: AuthenticationClassProvider, } @@ -37,9 +42,20 @@ pub struct AuthenticationClassSpec { #[serde(rename_all = "camelCase")] #[allow(clippy::large_enum_variant)] pub enum AuthenticationClassProvider { + /// The [static provider](https://DOCS_BASE_URL_PLACEHOLDER/concepts/authentication#_static) is used to configure a + /// static set of users, identified by username and password. Static(static_::AuthenticationProvider), + + /// The [LDAP provider](DOCS_BASE_URL_PLACEHOLDER/concepts/authentication#_ldap). + /// There is also the ["Authentication with LDAP" tutorial](DOCS_BASE_URL_PLACEHOLDER/tutorials/authentication_with_openldap) + /// where you can learn to configure Superset and Trino with OpenLDAP. Ldap(ldap::AuthenticationProvider), + + /// The OIDC provider can be used to configure OpenID Connect. Oidc(oidc::AuthenticationProvider), + + /// The [TLS provider](DOCS_BASE_URL_PLACEHOLDER/concepts/authentication#_tls). + /// The TLS AuthenticationClass is used when users should authenticate themselves with a TLS certificate. Tls(tls::AuthenticationProvider), } diff --git a/src/commons/authentication/oidc.rs b/src/commons/authentication/oidc.rs index 8d2e3413e..1581ae2f4 100644 --- a/src/commons/authentication/oidc.rs +++ b/src/commons/authentication/oidc.rs @@ -41,8 +41,8 @@ pub struct AuthenticationProvider { /// Hostname of the identity provider, e.g. `my.keycloak.corp`. hostname: String, - /// Port of the identity provider. If TLS is used defaults to `443`, - /// otherwise to `80`. + /// Port of the identity provider. If TLS is used defaults to 443, + /// otherwise to 80. port: Option<u16>, /// Root HTTP path of the identity provider. Defaults to `/`. @@ -74,9 +74,9 @@ pub struct AuthenticationProvider { pub scopes: Vec<String>, /// This is a hint about which identity provider is used by the - /// [`AuthenticationClass`]. Operators *can* opt to use this + /// AuthenticationClass. Operators *can* opt to use this /// value to enable known quirks around OIDC / OAuth authentication. - /// [`None`] means there is no hint and OIDC should be used as it is + /// Not providing a hint means there is no hint and OIDC should be used as it is /// intended to be used (via the `.well-known` discovery). #[serde(default)] pub provider_hint: Option<IdentityProviderHint>, diff --git a/src/commons/authentication/static_.rs b/src/commons/authentication/static_.rs index 19deaa9ee..153d61185 100644 --- a/src/commons/authentication/static_.rs +++ b/src/commons/authentication/static_.rs @@ -25,8 +25,8 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AuthenticationProvider { - /// Secret providing the usernames and password. - /// The secret must contain an entry for every user, with the key being the username and the value the password in plain text. + /// Secret providing the usernames and passwords. + /// The Secret must contain an entry for every user, with the key being the username and the value the password in plain text. /// It must be located in the same namespace as the product using it. pub user_credentials_secret: UserCredentialsSecretRef, } @@ -34,6 +34,6 @@ pub struct AuthenticationProvider { #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct UserCredentialsSecretRef { - /// Name of the secret + /// Name of the Secret. pub name: String, } diff --git a/src/commons/authentication/tls.rs b/src/commons/authentication/tls.rs index dfa7060e6..be51ca24b 100644 --- a/src/commons/authentication/tls.rs +++ b/src/commons/authentication/tls.rs @@ -10,9 +10,9 @@ use crate::{ #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct AuthenticationProvider { - /// See `<https://docs.stackable.tech/home/contributor/adr/ADR016-tls-authentication.html>`. + /// See [ADR017: TLS authentication](DOCS_BASE_URL_PLACEHOLDER/contributor/adr/adr017-tls_authentication). /// If `client_cert_secret_class` is not set, the TLS settings may also be used for client authentication. - /// If `client_cert_secret_class` is set, the [SecretClass](https://docs.stackable.tech/secret-operator/secretclass.html) + /// If `client_cert_secret_class` is set, the [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) /// will be used to provision client certificates. pub client_cert_secret_class: Option<String>, } @@ -20,7 +20,7 @@ pub struct AuthenticationProvider { #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TlsClientDetails { - /// Use a TLS connection. If not specified no TLS will be used + /// Use a TLS connection. If not specified no TLS will be used. pub tls: Option<Tls>, } @@ -108,36 +108,36 @@ impl TlsClientDetails { #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Tls { - /// The verification method used to verify the certificates of the server and/or the client + /// The verification method used to verify the certificates of the server and/or the client. pub verification: TlsVerification, } #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub enum TlsVerification { - /// Use TLS but don't verify certificates + /// Use TLS but don't verify certificates. None {}, - /// Use TLS and ca certificate to verify the server + /// Use TLS and a CA certificate to verify the server. Server(TlsServerVerification), } #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct TlsServerVerification { - /// Ca cert to verify the server + /// CA cert to verify the server. pub ca_cert: CaCert, } #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub enum CaCert { - /// Use TLS and the ca certificates trusted by the common web browsers to verify the server. + /// Use TLS and the CA certificates trusted by the common web browsers to verify the server. /// This can be useful when you e.g. use public AWS S3 or other public available services. WebPki {}, - /// Name of the SecretClass which will provide the ca cert. - /// Note that a SecretClass does not need to have a key but can also work with just a ca cert. - /// So if you got provided with a ca cert but don't have access to the key you can still use this method. + /// Name of the [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) which will provide the CA certificate. + /// Note that a SecretClass does not need to have a key but can also work with just a CA certificate, + /// so if you got provided with a CA cert but don't have access to the key you can still use this method. SecretClass(String), } diff --git a/src/commons/listener.rs b/src/commons/listener.rs index ac69aa8a8..911b41a06 100644 --- a/src/commons/listener.rs +++ b/src/commons/listener.rs @@ -39,7 +39,9 @@ use k8s_openapi::api::core::v1::{ #[cfg(doc)] use crate::builder::ListenerOperatorVolumeSourceBuilder; -/// Defines a policy for how [`Listener`]s should be exposed. +/// Defines a policy for how [Listeners](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listener) should be exposed. +/// Read the [ListenerClass documentation](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass) +/// for more information. #[derive(CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)] #[kube( group = "listeners.stackable.tech", @@ -49,7 +51,8 @@ use crate::builder::ListenerOperatorVolumeSourceBuilder; #[serde(rename_all = "camelCase")] pub struct ListenerClassSpec { pub service_type: ServiceType, - /// Annotations that should be added to the [`Service`] object. + + /// Annotations that should be added to the Service object. #[serde(default)] pub service_annotations: BTreeMap<String, String>, } @@ -67,10 +70,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 three things: -/// 1. It uses a cluster-level policy object ([`ListenerClass`]) to define how exactly the exposure works +/// Essentially a Stackable extension of a Kubernetes Service. Compared to a Service, a 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 +/// 3. The Pod must mount a Volume referring to the Listener, which also allows +/// ["sticky" scheduling](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listener#_sticky_scheduling). +/// +/// Learn more in the [Listener documentation](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listener). #[derive( CustomResource, Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq, )] @@ -83,14 +89,17 @@ pub enum ServiceType { )] #[serde(rename_all = "camelCase")] pub struct ListenerSpec { - /// The name of the [`ListenerClass`]. + /// The name of the [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass). pub class_name: Option<String>, - /// Extra labels that the [`Pod`]s must match in order to be exposed. They must _also_ still have a `Volume` referring to the listener. + + /// Extra labels that the Pods must match in order to be exposed. They must _also_ still have a Volume referring to the Listener. #[serde(default)] pub extra_pod_selector_labels: BTreeMap<String, String>, + /// Ports that should be exposed. pub ports: Option<Vec<ListenerPort>>, - /// Whether incoming traffic should also be directed to `Pod`s that are not `Ready`. + + /// Whether incoming traffic should also be directed to Pods that are not `Ready`. #[schemars(default = "Self::default_publish_not_ready_addresses")] pub publish_not_ready_addresses: Option<bool>, } @@ -106,7 +115,7 @@ impl ListenerSpec { pub struct ListenerPort { /// The name of the port. /// - /// The name of each port *must* be unique within a single [`Listener`]. + /// The name of each port *must* be unique within a single Listener. pub name: String, /// The port number. pub port: i32, @@ -114,26 +123,26 @@ pub struct ListenerPort { pub protocol: Option<String>, } -/// Informs users about how to reach the [`Listener`]. +/// Informs users about how to reach the Listener. #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ListenerStatus { - /// The backing Kubernetes [`Service`]. + /// The backing Kubernetes Service. pub service_name: Option<String>, - /// All addresses that the [`Listener`] is currently reachable from. + /// All addresses that the Listener is currently reachable from. pub ingress_addresses: Option<Vec<ListenerIngress>>, - /// Port mappings for accessing the [`Listener`] on each [`Node`] that the [`Pod`]s are currently running on. + /// Port mappings for accessing the Listener on each Node that the Pods are currently running on. /// - /// This is only intended for internal use by listener-operator itself. This will be left unset if using a [`ListenerClass`] that does - /// not require [`Node`]-local access. + /// This is only intended for internal use by listener-operator itself. This will be left unset if using a ListenerClass that does + /// not require Node-local access. pub node_ports: Option<BTreeMap<String, i32>>, } -/// One address that a [`Listener`] is accessible from. +/// One address that a Listener is accessible from. #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ListenerIngress { - /// The hostname or IP address to the [`Listener`]. + /// The hostname or IP address to the Listener. pub address: String, /// The type of address (`Hostname` or `IP`). pub address_type: AddressType, @@ -149,7 +158,7 @@ pub enum AddressType { Ip, } -/// Informs users about [`Listener`]s that are bound by a given [`Pod`]. +/// Informs users about Listeners 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 @@ -166,22 +175,22 @@ pub enum AddressType { )] #[serde(rename_all = "camelCase")] pub struct PodListenersSpec { - /// All listeners currently bound by the [`Pod`]. + /// All Listeners currently bound by the Pod. /// - /// Indexed by [`Volume`] name (not [`PersistentVolume`] or [`PersistentVolumeClaim`]). + /// 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`. + /// `Node` if this address only allows access to Pods hosted on a specific Kubernetes Node, otherwise `Cluster`. pub scope: PodListenerScope, - /// Addresses allowing access to this [`Pod`]. + /// Addresses allowing access to this Pod. /// - /// Compared to [`ListenerStatus::ingress_addresses`], this list is restricted to addresses that can access this [`Pod`]. + /// Compared to `ingress_addresses` on the Listener status, 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. + /// This field is intended to be equivalent to the files mounted into the Listener volume. pub ingress_addresses: Option<Vec<ListenerIngress>>, } diff --git a/src/commons/s3.rs b/src/commons/s3.rs index e65bf65a8..7c7a80750 100644 --- a/src/commons/s3.rs +++ b/src/commons/s3.rs @@ -14,8 +14,8 @@ use crate::{ error::{self, OperatorResult}, }; -/// S3 bucket specification containing only the bucket name and an inlined or -/// referenced connection specification. +/// S3 bucket specification containing the bucket name and an inlined or referenced connection specification. +/// Learn more on the [S3 concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/s3). #[derive( Clone, CustomResource, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize, )] @@ -153,7 +153,8 @@ impl S3ConnectionDef { } } -/// S3 connection definition as CRD. +/// S3 connection definition as a resource. +/// Learn more on the [S3 concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/s3). #[derive( CustomResource, Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize, )] @@ -189,7 +190,8 @@ pub struct S3ConnectionSpec { pub access_style: Option<S3AccessStyle>, /// If the S3 uses authentication you have to specify you S3 credentials. - /// In the most cases a SecretClass providing `accessKey` and `secretKey` is sufficient. + /// In the most cases a [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) + /// providing `accessKey` and `secretKey` is sufficient. #[serde(default, skip_serializing_if = "Option::is_none")] pub credentials: Option<SecretClassVolume>, diff --git a/src/commons/secret_class.rs b/src/commons/secret_class.rs index 93ee1ed01..b8fc56afc 100644 --- a/src/commons/secret_class.rs +++ b/src/commons/secret_class.rs @@ -6,9 +6,10 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SecretClassVolume { - /// [SecretClass](https://docs.stackable.tech/secret-operator/secretclass.html) containing the LDAP bind credentials + /// [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) containing the LDAP bind credentials. pub secret_class: String, - /// [Scope](https://docs.stackable.tech/secret-operator/scope.html) of the [SecretClass](https://docs.stackable.tech/secret-operator/secretclass.html) + /// [Scope](DOCS_BASE_URL_PLACEHOLDER/secret-operator/scope) of the + /// [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass). pub scope: Option<SecretClassVolumeScope>, } @@ -42,10 +43,18 @@ impl SecretClassVolume { #[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct SecretClassVolumeScope { + /// The pod scope is resolved to the name of the Kubernetes Pod. + /// This allows the secret to differentiate between StatefulSet replicas. #[serde(default)] pub pod: bool, + + /// The node scope is resolved to the name of the Kubernetes Node object that the Pod is running on. + /// This will typically be the DNS name of the node. #[serde(default)] pub node: bool, + + /// The service scope allows Pod objects to specify custom scopes. + /// This should typically correspond to Service objects that the Pod participates in. #[serde(default)] pub services: Vec<String>, } diff --git a/src/role_utils.rs b/src/role_utils.rs index 7f83a9fb0..f9072f7eb 100644 --- a/src/role_utils.rs +++ b/src/role_utils.rs @@ -147,11 +147,13 @@ fn config_schema_default() -> serde_json::Value { serde_json::json!({}) } -/// This struct represents a role - e.g. HDFS datanodes or Trino workers. It has a [`HashMap`] containing +/// This struct represents a role - e.g. HDFS datanodes or Trino workers. It has a key-value-map containing /// all the roleGroups that are part of this role. Additionally, there is a `config`, which is configurable /// at the role *and* roleGroup level. Everything at roleGroup level is merged on top of what is configured -/// on role level using the [`Merge`] trait. There is also a second form of config, which can only be configured +/// on role level. There is also a second form of config, which can only be configured /// at role level, the `roleConfig`. +/// You can learn more about this in the +/// [Roles and role group concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/roles-and-role-groups). // // Everything below is only a "normal" comment, not rustdoc - so we don't bloat the CRD documentation // with technical (Rust) details.