From e94acb95a93fd9dc2bb734f172d72958d296b199 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 14 Nov 2022 12:10:19 +0100 Subject: [PATCH 01/59] docs(todo): describe LDAP usage refer to https://github.com/stackabletech/nifi-operator/pull/303/files#diff-ff1c06cfbfb2d6a67f8e0a1e5bd2b2d55f2e8e2704e360f73509318fa9d645bc --- docs/modules/ROOT/pages/usage.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index c394bb6f..4aaf13cf 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -307,7 +307,7 @@ spec: ==== LDAP -Currently not supported. +TODO: add more info here. === Authorization with Open Policy Agent (OPA) From be8f258bf0e516a9cdee8bd228fbd6577274813b Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 10 Jan 2023 13:43:50 +0100 Subject: [PATCH 02/59] test: add adjusted ldap integration test # Conflicts: # tests/test-definition.yaml --- .../kuttl/ldap-authentication/00-assert.yaml | 17 ++++ .../ldap-authentication/00-install-zk.yaml.j2 | 29 ++++++ .../kuttl/ldap-authentication/02-assert.yaml | 28 ++++++ .../02-install-hdfs.yaml.j2 | 29 ++++++ .../03-assert-openldap.yaml | 12 +++ .../03-install-openldap.yaml | 73 +++++++++++++++ .../03-openldap-secret-class.yaml | 23 +++++ .../04-assert-ldap-user.yaml | 6 ++ .../ldap-authentication/04-ldap-user.yaml | 8 ++ .../kuttl/ldap-authentication/05-assert.yaml | 44 +++++++++ .../05-install-druid.yaml.j2 | 93 +++++++++++++++++++ .../kuttl/ldap-authentication/06-assert.yaml | 12 +++ .../06-checks-container.yaml | 21 +++++ .../kuttl/ldap-authentication/07-assert.yaml | 6 ++ .../ldap-authentication/07-authcheck.yaml | 6 ++ .../kuttl/ldap-authentication/README.md | 5 + .../kuttl/ldap-authentication/authcheck.py | 57 ++++++++++++ .../ldap-authentication/create_ldap_user.sh | 15 +++ tests/test-definition.yaml | 11 +++ 19 files changed, 495 insertions(+) create mode 100644 tests/templates/kuttl/ldap-authentication/00-assert.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/00-install-zk.yaml.j2 create mode 100644 tests/templates/kuttl/ldap-authentication/02-assert.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 create mode 100644 tests/templates/kuttl/ldap-authentication/03-assert-openldap.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/03-openldap-secret-class.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/04-assert-ldap-user.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/04-ldap-user.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/05-assert.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 create mode 100644 tests/templates/kuttl/ldap-authentication/06-assert.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/06-checks-container.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/07-assert.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/07-authcheck.yaml create mode 100644 tests/templates/kuttl/ldap-authentication/README.md create mode 100755 tests/templates/kuttl/ldap-authentication/authcheck.py create mode 100644 tests/templates/kuttl/ldap-authentication/create_ldap_user.sh diff --git a/tests/templates/kuttl/ldap-authentication/00-assert.yaml b/tests/templates/kuttl/ldap-authentication/00-assert.yaml new file mode 100644 index 00000000..4998bcdd --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/00-assert.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: druid-zk-server-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: hdfs-znode diff --git a/tests/templates/kuttl/ldap-authentication/00-install-zk.yaml.j2 b/tests/templates/kuttl/ldap-authentication/00-install-zk.yaml.j2 new file mode 100644 index 00000000..9958d9fb --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/00-install-zk.yaml.j2 @@ -0,0 +1,29 @@ +--- +apiVersion: zookeeper.stackable.tech/v1alpha1 +kind: ZookeeperCluster +metadata: + name: druid-zk +spec: + image: + productVersion: "{{ test_scenario['values']['zookeeper-latest'].split('-stackable')[0] }}" + stackableVersion: "{{ test_scenario['values']['zookeeper-latest'].split('-stackable')[1] }}" + servers: + roleGroups: + default: + replicas: 1 +--- +apiVersion: zookeeper.stackable.tech/v1alpha1 +kind: ZookeeperZnode +metadata: + name: druid-znode +spec: + clusterRef: + name: druid-zk +--- +apiVersion: zookeeper.stackable.tech/v1alpha1 +kind: ZookeeperZnode +metadata: + name: hdfs-znode +spec: + clusterRef: + name: druid-zk diff --git a/tests/templates/kuttl/ldap-authentication/02-assert.yaml b/tests/templates/kuttl/ldap-authentication/02-assert.yaml new file mode 100644 index 00000000..7138c1b0 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/02-assert.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: druid-hdfs-namenode-default +status: + readyReplicas: 2 + replicas: 2 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: druid-hdfs-journalnode-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: druid-hdfs-datanode-default +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 b/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 new file mode 100644 index 00000000..a5db7007 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 @@ -0,0 +1,29 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: druid-hdfs +timeout: 600 +--- +apiVersion: hdfs.stackable.tech/v1alpha1 +kind: HdfsCluster +metadata: + name: druid-hdfs +spec: + image: + productVersion: "{{ test_scenario['values']['hadoop'].split('-stackable')[0] }}" + stackableVersion: "{{ test_scenario['values']['hadoop'].split('-stackable')[1] }}" + zookeeperConfigMapName: hdfs-znode + dfsReplication: 1 + nameNodes: + roleGroups: + default: + replicas: 2 + dataNodes: + roleGroups: + default: + replicas: 1 + journalNodes: + roleGroups: + default: + replicas: 1 \ No newline at end of file diff --git a/tests/templates/kuttl/ldap-authentication/03-assert-openldap.yaml b/tests/templates/kuttl/ldap-authentication/03-assert-openldap.yaml new file mode 100644 index 00000000..9bcbc0e0 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/03-assert-openldap.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: openldap +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml b/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml new file mode 100644 index 00000000..b5898764 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: openldap + labels: + app.kubernetes.io/name: openldap +spec: + selector: + matchLabels: + app.kubernetes.io/name: openldap + serviceName: openldap + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: openldap + spec: + containers: + - name: openldap + image: docker.io/bitnami/openldap:2.5 + env: + - name: LDAP_ADMIN_USERNAME + value: admin + - name: LDAP_ADMIN_PASSWORD + value: admin + - name: LDAP_ENABLE_TLS + value: "yes" + - name: LDAP_TLS_CERT_FILE + value: /tls/tls.crt + - name: LDAP_TLS_KEY_FILE + value: /tls/tls.key + - name: LDAP_TLS_CA_FILE + value: /tls/ca.crt + ports: + - name: ldap + containerPort: 1389 + - name: tls-ldap + containerPort: 1636 + volumeMounts: + - name: tls + mountPath: /tls + startupProbe: + tcpSocket: + port: 1389 + readinessProbe: + tcpSocket: + port: 1389 + volumes: + - name: tls + csi: + driver: secrets.stackable.tech + volumeAttributes: + secrets.stackable.tech/class: openldap-tls + secrets.stackable.tech/scope: pod +--- +apiVersion: v1 +kind: Service +metadata: + name: openldap + labels: + app.kubernetes.io/name: openldap +spec: + type: ClusterIP + ports: + - name: ldap + port: 1389 + targetPort: ldap + - name: tls-ldap + port: 1636 + targetPort: tls-ldap + selector: + app.kubernetes.io/name: openldap diff --git a/tests/templates/kuttl/ldap-authentication/03-openldap-secret-class.yaml b/tests/templates/kuttl/ldap-authentication/03-openldap-secret-class.yaml new file mode 100644 index 00000000..0da7016e --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/03-openldap-secret-class.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: openldap +commands: + # SecretClass requires an explicit namespace to work + - script: | + kubectl apply -n $NAMESPACE -f - < /dev/null + - script: kubectl exec -n $NAMESPACE openldap-0 -- bash -c LDAPTLS_CACERT=/tls/ca.crt ldapsearch -Z -H ldaps://localhost:1636 -D uid=admin,dc=example,dc=org -w admin -b ou=users,dc=example,dc=org > /dev/null diff --git a/tests/templates/kuttl/ldap-authentication/04-ldap-user.yaml b/tests/templates/kuttl/ldap-authentication/04-ldap-user.yaml new file mode 100644 index 00000000..463e57f6 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/04-ldap-user.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: create-ldap-user +commands: + - script: kubectl cp -n $NAMESPACE ./create_ldap_user.sh openldap-0:/tmp + - script: kubectl exec -n $NAMESPACE openldap-0 -- sh /tmp/create_ldap_user.sh diff --git a/tests/templates/kuttl/ldap-authentication/05-assert.yaml b/tests/templates/kuttl/ldap-authentication/05-assert.yaml new file mode 100644 index 00000000..a3331d5c --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/05-assert.yaml @@ -0,0 +1,44 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: derby-druid-broker-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: derby-druid-coordinator-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: derby-druid-historical-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: derby-druid-middlemanager-default +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: derby-druid-router-default +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 new file mode 100644 index 00000000..c66cc2ee --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 @@ -0,0 +1,93 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: install-druid +timeout: 600 +--- +apiVersion: authentication.stackable.tech/v1alpha1 +kind: AuthenticationClass +metadata: + name: druid-ldap-auth-class +spec: + provider: + ldap: + hostname: openldap + searchBase: ou=users,dc=example,dc=org + searchFilter: (uid=%s) +{% if test_scenario['values']['ldap-use-tls'] == 'false' %} + port: 1389 +{% else %} + port: 1636 + tls: + verification: + server: + caCert: + secretClass: openldap-tls +{% endif %} + bindCredentials: + secretClass: druid-ldap-secret +--- +apiVersion: secrets.stackable.tech/v1alpha1 +kind: SecretClass +metadata: + name: druid-ldap-secret +spec: + backend: + k8sSearch: + searchNamespace: + pod: {} +--- +apiVersion: v1 +kind: Secret +metadata: + name: druid-ldap-secret + labels: + secrets.stackable.tech/class: druid-ldap-secret +stringData: + username: uid=admin,dc=example,dc=org + password: admin +--- +apiVersion: druid.stackable.tech/v1alpha1 +kind: DruidCluster +metadata: + name: derby-druid +spec: + image: + productVersion: "{{ test_scenario['values']['druid'].split('-stackable')[0] }}" + stackableVersion: "{{ test_scenario['values']['druid'].split('-stackable')[1] }}" + clusterConfig: + authentication: + - authenticationClass: druid-ldap-auth-class + deepStorage: + hdfs: + configMapName: druid-hdfs + directory: /druid + metadataStorageDatabase: + dbType: derby + connString: jdbc:derby://localhost:1527/var/druid/metadata.db;create=true + host: localhost + port: 1527 + tls: + serverAndInternalSecretClass: null + zookeeperConfigMapName: druid-znode + brokers: + roleGroups: + default: + replicas: 1 + coordinators: + roleGroups: + default: + replicas: 1 + historicals: + roleGroups: + default: + replicas: 1 + middleManagers: + roleGroups: + default: + replicas: 1 + routers: + roleGroups: + default: + replicas: 1 diff --git a/tests/templates/kuttl/ldap-authentication/06-assert.yaml b/tests/templates/kuttl/ldap-authentication/06-assert.yaml new file mode 100644 index 00000000..dc085bb1 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/06-assert.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: checks +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/ldap-authentication/06-checks-container.yaml b/tests/templates/kuttl/ldap-authentication/06-checks-container.yaml new file mode 100644 index 00000000..e1cea63a --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/06-checks-container.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: checks + labels: + app: checks +spec: + replicas: 1 + selector: + matchLabels: + app: checks + template: + metadata: + labels: + app: checks + spec: + containers: + - name: checks + image: docker.stackable.tech/stackable/testing-tools:0.1.0-stackable0.1.0 + command: ["sleep", "infinity"] diff --git a/tests/templates/kuttl/ldap-authentication/07-assert.yaml b/tests/templates/kuttl/ldap-authentication/07-assert.yaml new file mode 100644 index 00000000..a3cc6c3f --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/07-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: + - script: kubectl exec -n $NAMESPACE checks-0 -- python /tmp/authcheck.py +timeout: 600 diff --git a/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml b/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml new file mode 100644 index 00000000..719f96a9 --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 600 +commands: + - script: kubectl cp -n $NAMESPACE ./authcheck.py checks-0:/tmp diff --git a/tests/templates/kuttl/ldap-authentication/README.md b/tests/templates/kuttl/ldap-authentication/README.md new file mode 100644 index 00000000..2df2377d --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/README.md @@ -0,0 +1,5 @@ +# LDAP Authenticator Test + +This test sets an LDAP user called 'alice'. + +See `authcheck.py` for examples of authorized access. diff --git a/tests/templates/kuttl/ldap-authentication/authcheck.py b/tests/templates/kuttl/ldap-authentication/authcheck.py new file mode 100755 index 00000000..592ce51f --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/authcheck.py @@ -0,0 +1,57 @@ +import requests +import sys +import logging + + +def main(): + result = 0 + + proto = "http" + druid_cluster_name = "derby-druid" + + druid_ports = { + "coordinator": 8081, + # "broker": 8082, + # "middlemanager": 8091, + # "historical": 8083, + # "router": 8888 + } + log_level = 'INFO' + logging.basicConfig(level=log_level, format='%(asctime)s %(levelname)s: %(message)s', stream=sys.stdout) + + for role, port in druid_ports.items(): + url = f"{proto}://{druid_cluster_name}-{role}-default:{port}/status" + # make an authorized request -> return 401 expected + logging.info(f"making unauthorized request to {role}.") + res = requests.get(url) + if res.status_code != 401: + logging.error(f"expected 401 but got {res.status_code}") + result = 1 + break + else: + logging.info("success") + # make an authorized request -> return 200 expected + logging.info(f"making request as LDAP user [alice] to {role}") + res = requests.get(url, auth=("alice", "alice")) + if res.status_code != 200: + logging.error(f"expected 200 but got {res.status_code}") + result = 1 + break + else: + logging.info("success") + # make an unauthorized request -> return 403 expected + # eve is not an ldap user + logging.info(f"making request as unknown user [eve] to {role}") + res = requests.get(url, auth=("eve", "eve")) + if res.status_code != 401: + logging.error(f"expected 401 but got {res.status_code}") + result = 1 + break + else: + logging.info("success") + + return result + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/templates/kuttl/ldap-authentication/create_ldap_user.sh b/tests/templates/kuttl/ldap-authentication/create_ldap_user.sh new file mode 100644 index 00000000..ef0c6ead --- /dev/null +++ b/tests/templates/kuttl/ldap-authentication/create_ldap_user.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +cat << 'EOF' | ldapadd -H ldap://localhost:1389 -D cn=admin,dc=example,dc=org -w admin +dn: uid=alice,ou=users,dc=example,dc=org +uid: alice +cn: alice +sn: alice +objectClass: top +objectClass: posixAccount +objectClass: inetOrgPerson +homeDirectory: /home/alice +uidNumber: 3 +gidNumber: 3 +userPassword: alice +EOF diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index faf084b9..3e33b5ef 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -29,6 +29,10 @@ dimensions: - "no-tls" - "internal-and-server-tls" - "internal-and-server-tls-and-tls-client-auth" + - name: ldap-use-tls + values: + #- "true" # commented out, as it is not yet functional + - "false" tests: - name: smoke dimensions: @@ -75,3 +79,10 @@ tests: - druid-latest - zookeeper-latest - tls-mode + - name: ldap-authentication + dimensions: + - druid + - zookeeper-latest + - opa + - hadoop + - ldap-use-tls \ No newline at end of file From 8b93abd252858073c6b0321b13ac6ef8b0843cb5 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 9 Jan 2023 13:34:25 +0100 Subject: [PATCH 03/59] feat: accept LDAP for authentication --- rust/crd/src/authentication.rs | 51 ++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/rust/crd/src/authentication.rs b/rust/crd/src/authentication.rs index deaab60b..d34e5ce1 100644 --- a/rust/crd/src/authentication.rs +++ b/rust/crd/src/authentication.rs @@ -9,7 +9,7 @@ use stackable_operator::{ use crate::DruidCluster; -const SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS: [&str; 1] = ["TLS"]; +const SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS: [&str; 2] = ["LDAP", "TLS"]; #[derive(Snafu, Debug)] pub enum Error { @@ -56,6 +56,10 @@ pub struct DruidAuthentication { /// /// Please note that the SecretClass used to authenticate users needs to be the same /// as the SecretClass used for internal communication. + /// + /// ## LDAP provider + /// + /// Only affects client connections. TODO pub authentication_class: String, } @@ -112,6 +116,12 @@ impl ResolvedAuthenticationClasses { .find(|auth| matches!(auth.spec.provider, AuthenticationClassProvider::Tls(_))) } + pub fn get_ldap_authentication_class(&self) -> Option<&AuthenticationClass> { + self.resolved_authentication_classes + .iter() + .find(|auth| matches!(auth.spec.provider, AuthenticationClassProvider::Ldap(_))) + } + /// Validates the resolved AuthenticationClasses. /// Currently errors out if: /// - More than one AuthenticationClass was provided @@ -124,6 +134,7 @@ impl ResolvedAuthenticationClasses { for auth_class in &self.resolved_authentication_classes { match &auth_class.spec.provider { AuthenticationClassProvider::Tls(_) => {} + AuthenticationClassProvider::Ldap(_) => {} _ => { return Err(Error::AuthenticationProviderNotSupported { authentication_class: ObjectRef::from_obj(auth_class), @@ -133,6 +144,8 @@ impl ResolvedAuthenticationClasses { } } + // TODO: verify LDAP case + if let Some(tls_auth_class) = self.get_tls_authentication_class() { match &server_and_internal_secret_class { Some(server_and_internal_secret_class) => { @@ -189,11 +202,8 @@ mod tests { let classes = ResolvedAuthenticationClasses::new(vec![get_ldap_authentication_class()]); assert!( - matches!( - classes.validate(None), - Err(Error::AuthenticationProviderNotSupported { provider, authentication_class }) if provider == "Ldap" && authentication_class.name == "ldap", - ), - "Not supported: No server tls, LDAP authentication class" + classes.validate(None).is_ok(), + "Supported: No server tls, LDAP authentication class" ); let classes = ResolvedAuthenticationClasses::new(vec![]); @@ -240,6 +250,18 @@ mod tests { ), "Not supported: Server tls, multiple authentication classes" ); + + let classes = ResolvedAuthenticationClasses::new(vec![ + get_tls_authentication_class_without_secret_class(), + get_ldap_authentication_class(), + ]); + assert!( + matches!( + classes.validate(Some("tls-druid".to_string())), + Err(Error::MultipleAuthenticationClassesProvided {}) + ), + "Not supported: Server tls, multiple authentication classes" + ); } #[test] @@ -259,6 +281,23 @@ mod tests { ); } + #[test] + fn test_get_ldap_authentication_class() { + let classes = ResolvedAuthenticationClasses::new(vec![ + get_ldap_authentication_class(), + get_tls_authentication_class_without_secret_class(), + get_tls_authentication_class_with_secret_class_druid_clients(), + ]); + + let ldap_authentication_class = classes.get_ldap_authentication_class(); + + // TODO Check deriving PartialEq for AuthenticationClass so that we can compare them directly instead of comparing the names + assert_eq!( + ldap_authentication_class.map(|class| class.name_unchecked()), + Some("ldap".to_string()) + ); + } + fn get_tls_authentication_class_without_secret_class() -> AuthenticationClass { let input = r#" apiVersion: authentication.stackable.tech/v1alpha1 From 75d54480ffe19f1c29cd17091a9fa1684f0f6588 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 9 Jan 2023 16:16:18 +0100 Subject: [PATCH 04/59] fix: secret name --- .../kuttl/ldap-authentication/05-install-druid.yaml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 index c66cc2ee..3ba5b230 100644 --- a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 +++ b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 @@ -45,7 +45,7 @@ metadata: labels: secrets.stackable.tech/class: druid-ldap-secret stringData: - username: uid=admin,dc=example,dc=org + user: uid=admin,dc=example,dc=org password: admin --- apiVersion: druid.stackable.tech/v1alpha1 From b4bdbc57601e99dec8ee0403ce1d1fc2a7c1ac6a Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 9 Jan 2023 16:16:49 +0100 Subject: [PATCH 05/59] wip: add ldap integration sketch --- rust/crd/src/ldap.rs | 283 +++++++++++++++++++ rust/operator-binary/src/druid_controller.rs | 7 + 2 files changed, 290 insertions(+) create mode 100644 rust/crd/src/ldap.rs diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs new file mode 100644 index 00000000..3cf60e35 --- /dev/null +++ b/rust/crd/src/ldap.rs @@ -0,0 +1,283 @@ +use std::collections::BTreeMap; +use std::string::FromUtf8Error; + +use snafu::Snafu; +use stackable_operator::commons::authentication::AuthenticationClassProvider; +use stackable_operator::commons::ldap::LdapAuthenticationProvider; +use stackable_operator::k8s_openapi::api::core::v1::Secret; +use stackable_operator::kube::runtime::reflector::ObjectRef; +use strum::{EnumDiscriminants, IntoStaticStr}; + +#[derive(Snafu, Debug, EnumDiscriminants)] +#[strum_discriminants(derive(IntoStaticStr))] +#[allow(clippy::enum_variant_names)] +pub enum Error { + #[snafu(display("invalid ldap settings"))] + InvalidLdapSettings, + #[snafu(display("missing ldap bind credentials"))] + MissingBindCredentials, + #[snafu(display("missing secret field"))] + MissingSecretField, + #[snafu(display("unable to parse key {} from {} as UTF8", key, secret))] + NonUtf8Secret { + source: FromUtf8Error, + key: String, + secret: ObjectRef, + }, + #[snafu(display("failed to find referenced {}", secret))] + MissingSecret { + source: stackable_operator::error::Error, + secret: ObjectRef, + }, + #[snafu(display( + "a required value was not found when parsing the authentication config: [{}]", + value + ))] + MissingRequiredValue { value: String }, +} + +const DEFAULT_LDAP_PORT: u16 = 1389; +const DEFAULT_LDAP_TLS_PORT: u16 = 1636; + +#[derive(Clone, Debug)] +pub struct DruidLdapSettings { + pub provider: LdapAuthenticationProvider, +} + +impl DruidLdapSettings { + pub async fn new_from( + resolved_authentication_config: &[AuthenticationClassProvider], + ) -> Result, Error> { + let maybe_provider = resolved_authentication_config + .iter() + .find_map(|acc| match acc { + AuthenticationClassProvider::Ldap(provider) => Some(provider), + _ => None, + }); + + if let Some(provider) = maybe_provider { + // create DruidLdapSettings with everything in it + Ok(Some(DruidLdapSettings { + provider: provider.clone(), + })) + } else { + Ok(None) + } + } + + pub fn generate_runtime_properties_config_lines(&self) -> BTreeMap> { + let mut lines: BTreeMap> = BTreeMap::new(); + + /* + NOTES on trying to get ldap and existing tls auth to work. + see https://druid.apache.org/docs/latest/configuration/index.html#authentication-and-authorization + + Below approaches did not work out: + + * adding "allowAll" to the authenticatorChain + * inserting the following line: + + ``` + lines.insert( + // TODO: maybe try removing this if basic above works + "druid.auth.authenticator.ldap.skipOnFailure".to_string(), + Some("true".to_string()), + ); + ``` + + NEXT: does it have to do with the escalator? + */ + lines.insert( + "druid.auth.authenticatorChain".to_string(), + Some(r#"["ldap"]"#.to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.type".to_string(), + Some("basic".to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.enableCacheNotifications".to_string(), + Some("true".to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.type".to_string(), + Some("ldap".to_string()), + ); + + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.url".to_string(), + Some(self.credentials_validator_url()), + ); + + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.baseDn".to_string(), + Some(self.provider.search_base.to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.userSearch".to_string(), + Some(self.provider.search_filter.to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.userAttribute".to_string(), + Some(self.provider.ldap_field_names.uid.to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.authorizeQueryContextParams".to_string(), + Some("true".to_string()), + ); + + lines.insert( + "druid.escalator.type".to_string(), + Some("basic".to_string()), + ); + + // NOTE: these placeholders will be replaced from a mounted secret on container startup + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.bindUser".to_string(), + Some("xxx_ldap_bind_user_xxx".to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.credentialsValidator.bindPassword".to_string(), + Some("xxx_ldap_bind_password_xxx".to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.initialAdminPassword".to_string(), + Some("xxx_ldap_bind_password_xxx".to_string()), + ); + lines.insert( + "druid.auth.authenticator.ldap.initialInternalClientPassword".to_string(), + Some("xxx_ldap_internal_password_xxx".to_string()), + ); + lines.insert( + "druid.escalator.internalClientUsername".to_string(), + Some("xxx_ldap_internal_user_xxx".to_string()), + ); + lines.insert( + "druid.escalator.internalClientPassword".to_string(), + Some("xxx_ldap_internal_password_xxx".to_string()), + ); + + lines + } + + fn is_ssl_enabled(&self) -> bool { + self.provider.tls.is_some() + } + + fn get_ldap_protocol_and_port(&self) -> (String, u16) { + let protocol = if self.is_ssl_enabled() { + "ldaps".to_string() + } else { + "ldap".to_string() + }; + + let port = if let Some(port) = self.provider.port { + port + } else if self.is_ssl_enabled() { + DEFAULT_LDAP_TLS_PORT + } else { + DEFAULT_LDAP_PORT + }; + + (protocol, port) + } + + fn credentials_validator_url(&self) -> String { + let (protocol, port) = self.get_ldap_protocol_and_port(); + format!("{}://{}:{}", protocol, self.provider.hostname, port,) + } +} + +#[cfg(test)] +mod test { + use super::*; + use stackable_operator::commons::ldap::LdapFieldNames; + + #[test] + fn test_ldap_settings_are_added() { + let ldap_settings = DruidLdapSettings { + provider: LdapAuthenticationProvider { + hostname: "openldap".to_string(), + port: None, + search_base: "ou=Users,dc=example,dc=org".to_string(), + search_filter: "(&(uid=%s)(objectClass=inetOrgPerson))".to_string(), + ldap_field_names: LdapFieldNames::default(), + bind_credentials: None, + tls: None, + }, + }; + + let expected: BTreeMap> = vec![ + ( + "druid.auth.authenticator.ldap.authorizeQueryContextParams".to_string(), + Some("true".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.baseDn".to_string(), + Some("ou=Users,dc=example,dc=org".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.bindPassword".to_string(), + Some("xxx_ldap_bind_password_xxx".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.bindUser".to_string(), + Some("xxx_ldap_bind_user_xxx".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.type".to_string(), + Some("ldap".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.url".to_string(), + Some("ldap://openldap:1389".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.userAttribute".to_string(), + Some("uid".to_string()), + ), + ( + "druid.auth.authenticator.ldap.credentialsValidator.userSearch".to_string(), + Some("(&(uid=%s)(objectClass=inetOrgPerson))".to_string()), + ), + ( + "druid.auth.authenticator.ldap.enableCacheNotifications".to_string(), + Some("true".to_string()), + ), + ( + "druid.auth.authenticator.ldap.initialAdminPassword".to_string(), + Some("xxx_ldap_bind_password_xxx".to_string()), + ), + ( + "druid.auth.authenticator.ldap.initialInternalClientPassword".to_string(), + Some("xxx_ldap_internal_password_xxx".to_string()), + ), + ( + "druid.auth.authenticator.ldap.type".to_string(), + Some("basic".to_string()), + ), + ( + "druid.auth.authenticatorChain".to_string(), + Some("[\"ldap\"]".to_string()), + ), + ( + "druid.escalator.internalClientPassword".to_string(), + Some("xxx_ldap_internal_password_xxx".to_string()), + ), + ( + "druid.escalator.internalClientUsername".to_string(), + Some("xxx_ldap_internal_user_xxx".to_string()), + ), + ( + "druid.escalator.type".to_string(), + Some("basic".to_string()), + ), + ] + .into_iter() + .collect(); + + let got = ldap_settings.generate_runtime_properties_config_lines(); + + assert_eq!(expected, got); + } +} diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index b8b9c90e..e39af2b3 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -260,6 +260,10 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< .await .context(FailedToInitializeSecurityContextSnafu)?; + let druid_ldap_settings = DruidLdapSettings::new_from_druid_cluster(client, &druid) + .await + .context(FailedToInitializeSecurityContextSnafu)?; + // False positive, auto-deref breaks type inference #[allow(clippy::explicit_auto_deref)] let role_config = transform_all_roles_to_config(&*druid, druid.build_role_properties()); @@ -295,6 +299,7 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< .add(client, &role_service) .await .context(ApplyRoleServiceSnafu)?; + for (rolegroup_name, rolegroup_config) in role_config.iter() { let rolegroup = RoleGroupRef { cluster: ObjectRef::from_obj(&*druid), @@ -322,6 +327,7 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< deep_storage_bucket_name.as_deref(), &resources, &druid_tls_security, + &druid_ldap_settings, )?; let rg_statefulset = build_rolegroup_statefulset( &druid, @@ -331,6 +337,7 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< s3_conn.as_ref(), &resources, &druid_tls_security, + &druid_ldap_settings, )?; cluster_resources .add(client, &rg_service) From 1d0b4cb8cd2ca112bb9ead9852248cb8692ed1a3 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 10 Jan 2023 13:31:03 +0100 Subject: [PATCH 06/59] wip: port and adjust ldap changes from old branch --- rust/crd/src/ldap.rs | 187 +++++++++++-------- rust/crd/src/lib.rs | 11 +- rust/crd/src/security.rs | 25 ++- rust/operator-binary/src/druid_controller.rs | 96 +++++++++- 4 files changed, 222 insertions(+), 97 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 3cf60e35..0709e05f 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -2,12 +2,16 @@ use std::collections::BTreeMap; use std::string::FromUtf8Error; use snafu::Snafu; -use stackable_operator::commons::authentication::AuthenticationClassProvider; +use stackable_operator::commons::authentication::{ + AuthenticationClass, AuthenticationClassProvider, +}; use stackable_operator::commons::ldap::LdapAuthenticationProvider; use stackable_operator::k8s_openapi::api::core::v1::Secret; use stackable_operator::kube::runtime::reflector::ObjectRef; use strum::{EnumDiscriminants, IntoStaticStr}; +use crate::authentication::ResolvedAuthenticationClasses; + #[derive(Snafu, Debug, EnumDiscriminants)] #[strum_discriminants(derive(IntoStaticStr))] #[allow(clippy::enum_variant_names)] @@ -45,118 +49,143 @@ pub struct DruidLdapSettings { } impl DruidLdapSettings { - pub async fn new_from( - resolved_authentication_config: &[AuthenticationClassProvider], - ) -> Result, Error> { - let maybe_provider = resolved_authentication_config - .iter() - .find_map(|acc| match acc { - AuthenticationClassProvider::Ldap(provider) => Some(provider), - _ => None, - }); + pub fn new_from( + resolved_authentication_config: ResolvedAuthenticationClasses, + ) -> Option { + let maybe_authentication_class = + resolved_authentication_config.get_ldap_authentication_class(); - if let Some(provider) = maybe_provider { - // create DruidLdapSettings with everything in it - Ok(Some(DruidLdapSettings { - provider: provider.clone(), - })) - } else { - Ok(None) + if let Some(authentication_class) = maybe_authentication_class { + if let AuthenticationClassProvider::Ldap(ref provider) = + authentication_class.spec.provider + { + return Some(DruidLdapSettings { + provider: provider.clone(), + }); + } } + None } - pub fn generate_runtime_properties_config_lines(&self) -> BTreeMap> { - let mut lines: BTreeMap> = BTreeMap::new(); - - /* - NOTES on trying to get ldap and existing tls auth to work. - see https://druid.apache.org/docs/latest/configuration/index.html#authentication-and-authorization - - Below approaches did not work out: - - * adding "allowAll" to the authenticatorChain - * inserting the following line: + fn add_druid_system_authenticator_lines(&self, lines: &mut BTreeMap>) { + lines.insert( + "druid.auth.authenticator.DruidSystemAuthenticator.type".to_string(), + Some("basic".to_string()), + ); + lines.insert( + "druid.auth.authenticator.DruidSystemAuthenticator.credentialsValidator.type" + .to_string(), + Some("metadata".to_string()), + ); - ``` - lines.insert( - // TODO: maybe try removing this if basic above works - "druid.auth.authenticator.ldap.skipOnFailure".to_string(), - Some("true".to_string()), - ); - ``` + // this line is left out, as we don't want to create an admin user + // # druid.auth.authenticator.DruidSystemAuthenticator.initialAdminPassword: XXX - NEXT: does it have to do with the escalator? - */ lines.insert( - "druid.auth.authenticatorChain".to_string(), - Some(r#"["ldap"]"#.to_string()), + "druid.auth.authenticator.DruidSystemAuthenticator.initialInternalClientPassword" + .to_string(), + Some("druid_system_pass".to_string()), // TODO: replace with sed placeholder + ); + lines.insert( + "druid.auth.authenticator.DruidSystemAuthenticator.authorizerName".to_string(), + Some("DruidSystemAuthorizer".to_string()), ); lines.insert( - "druid.auth.authenticator.ldap.type".to_string(), + "druid.auth.authenticator.DruidSystemAuthenticator.skipOnFailure".to_string(), + Some("true".to_string()), // TODO: is additional escaping necessary for "true"? + ); + } + + fn add_ldap_authenticator_lines(&self, lines: &mut BTreeMap>) { + lines.insert( + "druid.auth.authenticator.Ldap.type".to_string(), Some("basic".to_string()), ); lines.insert( - "druid.auth.authenticator.ldap.enableCacheNotifications".to_string(), - Some("true".to_string()), + "druid.auth.authenticator.Ldap.enableCacheNotifications".to_string(), + Some("true".to_string()), // TODO: is additional escaping necessary for "true"? ); lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.type".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.type".to_string(), Some("ldap".to_string()), ); - lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.url".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.url".to_string(), Some(self.credentials_validator_url()), ); - lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.baseDn".to_string(), - Some(self.provider.search_base.to_string()), + "druid.auth.authenticator.Ldap.credentialsValidator.bindUser".to_string(), + Some("xxx_ldap_bind_user_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.userSearch".to_string(), - Some(self.provider.search_filter.to_string()), + "druid.auth.authenticator.Ldap.credentialsValidator.bindPassword".to_string(), + Some("xxx_ldap_bind_password_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.userAttribute".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), + Some(self.provider.search_base.to_string()), + ); + lines.insert( + "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), Some(self.provider.ldap_field_names.uid.to_string()), ); lines.insert( - "druid.auth.authenticator.ldap.authorizeQueryContextParams".to_string(), - Some("true".to_string()), + "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), + Some(self.provider.search_filter.to_string()), + ); + lines.insert( + "druid.auth.authenticator.Ldap.authorizerName".to_string(), + Some("LdapAuthorizer".to_string()), ); + } + fn add_escalator_lines(&self, lines: &mut BTreeMap>) { lines.insert( "druid.escalator.type".to_string(), Some("basic".to_string()), ); - - // NOTE: these placeholders will be replaced from a mounted secret on container startup lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.bindUser".to_string(), - Some("xxx_ldap_bind_user_xxx".to_string()), + "druid.escalator.internalClientUsername".to_string(), + Some("druid_system".to_string()), // TODO: replace with sed-placeholder xxx_druid_system_internal_user_xxx + ); + lines.insert( + "druid.escalator.internalClientUsername".to_string(), + Some("druid_system_pass2".to_string()), // TODO: replace with sed-placeholder ); lines.insert( - "druid.auth.authenticator.ldap.credentialsValidator.bindPassword".to_string(), - Some("xxx_ldap_bind_password_xxx".to_string()), + "druid.escalator.authorizerName".to_string(), + Some("DruidSystemAuthorizer".to_string()), ); + } + + fn add_authorizer_lines(&self, lines: &mut BTreeMap>) { lines.insert( - "druid.auth.authenticator.ldap.initialAdminPassword".to_string(), - Some("xxx_ldap_bind_password_xxx".to_string()), + "druid.auth.authorizers".to_string(), + Some(r#"["LdapAuthorizer", "DruidSystemAuthorizer"]"#.to_string()), ); lines.insert( - "druid.auth.authenticator.ldap.initialInternalClientPassword".to_string(), - Some("xxx_ldap_internal_password_xxx".to_string()), + "druid.auth.authorizer.DruidSystemAuthorizer.type".to_string(), + Some(r#"allowAll"#.to_string()), ); lines.insert( - "druid.escalator.internalClientUsername".to_string(), - Some("xxx_ldap_internal_user_xxx".to_string()), + "druid.auth.authorizer.LdapAuthorizer.type".to_string(), + Some(r#"allowAll"#.to_string()), ); + } + + pub fn generate_runtime_properties_config_lines(&self) -> BTreeMap> { + let mut lines: BTreeMap> = BTreeMap::new(); + lines.insert( - "druid.escalator.internalClientPassword".to_string(), - Some("xxx_ldap_internal_password_xxx".to_string()), + "druid.auth.authenticatorChain".to_string(), + Some(r#"["DruidSystemAuthenticator", "Ldap"]"#.to_string()), ); + self.add_druid_system_authenticator_lines(&mut lines); + self.add_ldap_authenticator_lines(&mut lines); + self.add_escalator_lines(&mut lines); + self.add_authorizer_lines(&mut lines); + lines } @@ -209,51 +238,51 @@ mod test { let expected: BTreeMap> = vec![ ( - "druid.auth.authenticator.ldap.authorizeQueryContextParams".to_string(), + "druid.auth.authenticator.Ldap.authorizeQueryContextParams".to_string(), Some("true".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.baseDn".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), Some("ou=Users,dc=example,dc=org".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.bindPassword".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.bindPassword".to_string(), Some("xxx_ldap_bind_password_xxx".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.bindUser".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.bindUser".to_string(), Some("xxx_ldap_bind_user_xxx".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.type".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.type".to_string(), Some("ldap".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.url".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.url".to_string(), Some("ldap://openldap:1389".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.userAttribute".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), Some("uid".to_string()), ), ( - "druid.auth.authenticator.ldap.credentialsValidator.userSearch".to_string(), + "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), Some("(&(uid=%s)(objectClass=inetOrgPerson))".to_string()), ), ( - "druid.auth.authenticator.ldap.enableCacheNotifications".to_string(), + "druid.auth.authenticator.Ldap.enableCacheNotifications".to_string(), Some("true".to_string()), ), ( - "druid.auth.authenticator.ldap.initialAdminPassword".to_string(), + "druid.auth.authenticator.Ldap.initialAdminPassword".to_string(), Some("xxx_ldap_bind_password_xxx".to_string()), ), ( - "druid.auth.authenticator.ldap.initialInternalClientPassword".to_string(), + "druid.auth.authenticator.Ldap.initialInternalClientPassword".to_string(), Some("xxx_ldap_internal_password_xxx".to_string()), ), ( - "druid.auth.authenticator.ldap.type".to_string(), + "druid.auth.authenticator.Ldap.type".to_string(), Some("basic".to_string()), ), ( diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index 0755b8ed..ea3a9d23 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -1,5 +1,6 @@ pub mod authentication; pub mod authorization; +pub mod ldap; pub mod resource; pub mod security; pub mod storage; @@ -246,7 +247,11 @@ impl DruidRole { } /// Returns the start commands for the different server types. - pub fn get_command(&self, s3_connection: Option<&S3ConnectionSpec>) -> Vec { + pub fn get_command( + &self, + s3_connection: Option<&S3ConnectionSpec>, + ldap_auth_cmd: Vec, + ) -> Vec { let mut shell_cmd = vec![format!("keytool -importkeystore -srckeystore {SYSTEM_TRUST_STORE} -srcstoretype jks -srcstorepass {SYSTEM_TRUST_STORE_PASSWORD} -destkeystore {STACKABLE_TRUST_STORE} -deststoretype pkcs12 -deststorepass {STACKABLE_TRUST_STORE_PASSWORD} -noprompt")]; if let Some(s3_connection) = s3_connection { @@ -275,11 +280,13 @@ impl DruidRole { // copy hdfs config to RW_CONFIG_DIRECTORY folder (if available) shell_cmd.push(format!( - "cp -RL {hdfs_conf}/* {rw_conf} || :", + "cp -RL {hdfs_conf}/* {rw_conf} || :", // NOTE: the OR part is here because the command is not applicable sometimes, and would stop everything else from executing hdfs_conf = HDFS_CONFIG_DIRECTORY, rw_conf = RW_CONFIG_DIRECTORY, )); + shell_cmd.extend(ldap_auth_cmd); + shell_cmd.push(format!( "{} {} {}", "/stackable/druid/bin/run-druid", diff --git a/rust/crd/src/security.rs b/rust/crd/src/security.rs index b87b1e87..c3b20cbd 100644 --- a/rust/crd/src/security.rs +++ b/rust/crd/src/security.rs @@ -12,6 +12,7 @@ use stackable_operator::{ apimachinery::pkg::util::intstr::IntOrString, }, }; + use std::collections::BTreeMap; #[derive(Snafu, Debug)] @@ -26,6 +27,19 @@ pub struct DruidTlsSecurity { server_and_internal_secret_class: Option, } +pub async fn resolve_authentication_classes( + client: &Client, + druid: &DruidCluster, +) -> Result { + authentication::ResolvedAuthenticationClasses::from_references( + client, + druid, + &druid.spec.cluster_config.authentication, + ) + .await + .context(InvalidAuthenticationClassConfigurationSnafu) +} + impl DruidTlsSecurity { // Ports const ENABLE_PLAINTEXT_PORT: &str = "druid.enablePlaintextPort"; @@ -80,18 +94,11 @@ impl DruidTlsSecurity { /// Create a `DruidTlsSecurity` struct from the Druid custom resource and resolve /// all provided `AuthenticationClass` references. pub async fn new_from_druid_cluster( - client: &Client, druid: &DruidCluster, + resolved_authentication_classes: ResolvedAuthenticationClasses, ) -> Result { Ok(DruidTlsSecurity { - resolved_authentication_classes: - authentication::ResolvedAuthenticationClasses::from_references( - client, - druid, - &druid.spec.cluster_config.authentication, - ) - .await - .context(InvalidAuthenticationClassConfigurationSnafu)?, + resolved_authentication_classes, server_and_internal_secret_class: druid .spec .cluster_config diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index e39af2b3..c2d216dd 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -8,7 +8,10 @@ use crate::{ use crate::OPERATOR_NAME; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_druid_crd::{ - authorization::DruidAuthorization, build_string_list, security::DruidTlsSecurity, + authorization::DruidAuthorization, + build_string_list, + ldap::DruidLdapSettings, + security::{resolve_authentication_classes, DruidTlsSecurity}, DeepStorageSpec, DruidCluster, DruidRole, APP_NAME, AUTH_AUTHORIZER_OPA_URI, CERTS_DIR, CREDENTIALS_SECRET_PROPERTY, DRUID_CONFIG_DIRECTORY, DS_BUCKET, EXTENSIONS_LOADLIST, HDFS_CONFIG_DIRECTORY, JVM_CONFIG, LOG4J2_CONFIG, RUNTIME_PROPS, RW_CONFIG_DIRECTORY, @@ -18,7 +21,6 @@ use stackable_druid_crd::{ build_recommended_labels, resource::{self, RoleResource}, }; -use stackable_operator::commons::product_image_selection::ResolvedProductImage; use stackable_operator::{ builder::{ ConfigMapBuilder, ContainerBuilder, ObjectMetaBuilder, PodBuilder, @@ -48,6 +50,9 @@ use stackable_operator::{ product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config}, role_utils::RoleGroupRef, }; +use stackable_operator::{ + commons::product_image_selection::ResolvedProductImage, k8s_openapi::api::core::v1::Volume, +}; use std::{ collections::{BTreeMap, HashMap}, ops::Deref, @@ -183,6 +188,10 @@ pub enum Error { FailedToInitializeSecurityContext { source: stackable_druid_crd::security::Error, }, + #[snafu(display("failed to resolve ldap authentication settings"))] + LdapAuthorization { + source: stackable_druid_crd::ldap::Error, + }, } type Result = std::result::Result; @@ -256,13 +265,16 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< _ => None, }; - let druid_tls_security = DruidTlsSecurity::new_from_druid_cluster(client, &druid) + let resolved_authentication_classes = resolve_authentication_classes(client, &druid) .await .context(FailedToInitializeSecurityContextSnafu)?; - let druid_ldap_settings = DruidLdapSettings::new_from_druid_cluster(client, &druid) - .await - .context(FailedToInitializeSecurityContextSnafu)?; + let druid_tls_security = + DruidTlsSecurity::new_from_druid_cluster(&druid, resolved_authentication_classes.clone()) + .await + .context(FailedToInitializeSecurityContextSnafu)?; + + let druid_ldap_settings = DruidLdapSettings::new_from(resolved_authentication_classes); // False positive, auto-deref breaks type inference #[allow(clippy::explicit_auto_deref)] @@ -435,6 +447,7 @@ fn build_rolegroup_config_map( deep_storage_bucket_name: Option<&str>, resources: &RoleResource, druid_tls_security: &DruidTlsSecurity, + druid_ldap_settings: &Option, ) -> Result { let role = DruidRole::from_str(&rolegroup.role).unwrap(); let mut cm_conf_data = BTreeMap::new(); // filename -> filecontent @@ -498,6 +511,11 @@ fn build_rolegroup_config_map( // add tls encryption / auth properties druid_tls_security.add_tls_config_properties(&mut transformed_config, &role); + if let Some(ldap_settings) = druid_ldap_settings { + transformed_config + .extend(ldap_settings.generate_runtime_properties_config_lines()); + }; + let runtime_properties = stackable_operator::product_config::writer::to_java_properties_string( transformed_config.iter(), @@ -598,6 +616,7 @@ fn build_rolegroup_services( }) } +#[allow(clippy::too_many_arguments)] /// The rolegroup [`StatefulSet`] runs the rolegroup, as configured by the administrator. /// /// The [`Pod`](`stackable_operator::k8s_openapi::api::core::v1::Pod`)s are accessible through the corresponding [`Service`] (from [`build_rolegroup_services`]). @@ -609,6 +628,7 @@ fn build_rolegroup_statefulset( s3_conn: Option<&S3ConnectionSpec>, resources: &RoleResource, druid_tls_security: &DruidTlsSecurity, + maybe_ldap_settings: &Option, ) -> Result { let role = DruidRole::from_str(&rolegroup_ref.role).context(UnidentifiedDruidRoleSnafu { role: rolegroup_ref.role.to_string(), @@ -624,11 +644,15 @@ fn build_rolegroup_statefulset( let mut pb = PodBuilder::new(); pb.node_selector_opt(druid.node_selector(rolegroup_ref)); + let (ldap_auth_mounts, ldap_auth_cmd) = + get_ldap_secret_volume_and_volume_mounts_and_commands(maybe_ldap_settings); + // volume and volume mounts druid_tls_security .add_tls_volume_and_volume_mounts(&mut cb_prepare, &mut cb_druid, &mut pb) .context(FailedToInitializeSecurityContextSnafu)?; add_s3_volume_and_volume_mounts(s3_conn, &mut cb_druid, &mut pb)?; + add_ldap_secret_volume_mounts(&mut cb_druid, &mut pb, ldap_auth_mounts); add_config_volume_and_volume_mounts(rolegroup_ref, &mut cb_druid, &mut pb); add_hdfs_cm_volume_and_volume_mounts( &druid.spec.cluster_config.deep_storage, @@ -660,7 +684,7 @@ fn build_rolegroup_statefulset( cb_druid .image_from_product_image(resolved_product_image) - .command(role.get_command(s3_conn)) + .command(role.get_command(s3_conn, ldap_auth_cmd)) .add_env_vars(rest_env) .add_container_ports(druid_tls_security.container_ports(&role)) // 10s * 30 = 300s to come up @@ -745,6 +769,62 @@ fn add_hdfs_cm_volume_and_volume_mounts( } } +fn get_ldap_secret_volume_and_volume_mounts_and_commands( + maybe_ldap_settings: &Option, +) -> (BTreeMap, Vec) { + let mut volumes = BTreeMap::new(); + let mut commands = Vec::new(); + + if let Some(ldap_settings) = maybe_ldap_settings { + if let Some(credentials) = &ldap_settings.provider.bind_credentials { + let volume_name = credentials.secret_class.clone(); + let secret_volume = VolumeBuilder::new(&volume_name) + .ephemeral(SecretOperatorVolumeSourceBuilder::new(volume_name.clone()).build()) + .build(); + + volumes.insert( + volume_name.clone(), + (format!("/stackable/secrets/{volume_name}"), secret_volume), + ); + + let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/LDAP_BIND_USER)"); + let ldap_bind_password = + format!("$(cat /stackable/secrets/{volume_name}/LDAP_BIND_PASSWORD)"); + let ldap_internal_user = + format!("$(cat /stackable/secrets/{volume_name}/LDAP_INTERNAL_USER)"); + let ldap_internal_password = + format!("$(cat /stackable/secrets/{volume_name}/LDAP_INTERNAL_PASSWORD)"); + + const RUNTIME_PROPERTIES_PATH: &str = "/stackable/rwconfig/runtime.properties"; + commands + .push(r#"echo "Replacing LDAP placeholders with their proper values""#.to_string()); + commands.push(format!( + r#"sed "s/xxx_ldap_bind_user_xxx/{ldap_bind_user}/g" -i {RUNTIME_PROPERTIES_PATH}"# + )); + commands.push(format!( + r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {RUNTIME_PROPERTIES_PATH}"# + )); + commands.push(format!( + r#"sed "s/xxx_ldap_internal_user_xxx/{ldap_internal_user}/g" -i {RUNTIME_PROPERTIES_PATH}"# + )); + commands.push(format!(r#"sed "s/xxx_ldap_internal_password_xxx/{ldap_internal_password}/g" -i {RUNTIME_PROPERTIES_PATH}"#)); + } + } + + (volumes, commands) +} + +fn add_ldap_secret_volume_mounts( + cb_druid: &mut ContainerBuilder, + pb: &mut PodBuilder, + ldap_auth_volumes: BTreeMap, +) { + for (name, (path, volume)) in ldap_auth_volumes.iter() { + cb_druid.add_volume_mount(name, path); + pb.add_volume(volume.clone()); + } +} + fn add_config_volume_and_volume_mounts( rolegroup_ref: &RoleGroupRef, cb_druid: &mut ContainerBuilder, @@ -902,6 +982,7 @@ mod test { let resources = resource::resources(&druid, &DruidRole::Historical, &rolegroup_ref) .context(ResourceSnafu)?; + let maybe_ldap_settings: Option = None; let rg_configmap = build_rolegroup_config_map( &druid, @@ -914,6 +995,7 @@ mod test { None, &resources, &druid_tls_security, + &maybe_ldap_settings, ) .context(ControllerSnafu)?; From c02b0e708e72a578f9bb251e5e6f1e0497ef6e6f Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 10 Jan 2023 16:38:18 +0100 Subject: [PATCH 07/59] fix(test): ldap auth errors --- .../kuttl/ldap-authentication/04-assert-ldap-user.yaml | 4 ++-- .../kuttl/ldap-authentication/05-install-druid.yaml.j2 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/templates/kuttl/ldap-authentication/04-assert-ldap-user.yaml b/tests/templates/kuttl/ldap-authentication/04-assert-ldap-user.yaml index e0c8a04e..663a6f96 100644 --- a/tests/templates/kuttl/ldap-authentication/04-assert-ldap-user.yaml +++ b/tests/templates/kuttl/ldap-authentication/04-assert-ldap-user.yaml @@ -2,5 +2,5 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert commands: - - script: kubectl exec -n $NAMESPACE openldap-0 -- ldapsearch -H ldap://localhost:1389 -D uid=admin,dc=example,dc=org -w admin -b ou=users,dc=example,dc=org > /dev/null - - script: kubectl exec -n $NAMESPACE openldap-0 -- bash -c LDAPTLS_CACERT=/tls/ca.crt ldapsearch -Z -H ldaps://localhost:1636 -D uid=admin,dc=example,dc=org -w admin -b ou=users,dc=example,dc=org > /dev/null + - script: kubectl exec -n $NAMESPACE openldap-0 -- ldapsearch -H ldap://localhost:1389 -D cn=admin,dc=example,dc=org -w admin -b ou=users,dc=example,dc=org > /dev/null + - script: kubectl exec -n $NAMESPACE openldap-0 -- bash -c LDAPTLS_CACERT=/tls/ca.crt ldapsearch -Z -H ldaps://localhost:1636 -D cn=admin,dc=example,dc=org -w admin -b ou=users,dc=example,dc=org > /dev/null diff --git a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 index 3ba5b230..b0745fd4 100644 --- a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 +++ b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 @@ -45,7 +45,7 @@ metadata: labels: secrets.stackable.tech/class: druid-ldap-secret stringData: - user: uid=admin,dc=example,dc=org + user: cn=admin,dc=example,dc=org password: admin --- apiVersion: druid.stackable.tech/v1alpha1 From fff188c3a93a9657bd7157f01b48a03bbbb2a367 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 10 Jan 2023 16:38:36 +0100 Subject: [PATCH 08/59] test: simplify ldap test case --- rust/crd/src/ldap.rs | 79 +++----------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 0709e05f..3f7052c5 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -149,8 +149,8 @@ impl DruidLdapSettings { Some("druid_system".to_string()), // TODO: replace with sed-placeholder xxx_druid_system_internal_user_xxx ); lines.insert( - "druid.escalator.internalClientUsername".to_string(), - Some("druid_system_pass2".to_string()), // TODO: replace with sed-placeholder + "druid.escalator.internalClientPassword".to_string(), + Some("druid_system_pass".to_string()), // TODO: replace with sed-placeholder ); lines.insert( "druid.escalator.authorizerName".to_string(), @@ -228,85 +228,16 @@ mod test { provider: LdapAuthenticationProvider { hostname: "openldap".to_string(), port: None, - search_base: "ou=Users,dc=example,dc=org".to_string(), - search_filter: "(&(uid=%s)(objectClass=inetOrgPerson))".to_string(), + search_base: "ou=users,dc=example,dc=org".to_string(), + search_filter: "(uid=%s)".to_string(), ldap_field_names: LdapFieldNames::default(), bind_credentials: None, tls: None, }, }; - let expected: BTreeMap> = vec![ - ( - "druid.auth.authenticator.Ldap.authorizeQueryContextParams".to_string(), - Some("true".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), - Some("ou=Users,dc=example,dc=org".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.bindPassword".to_string(), - Some("xxx_ldap_bind_password_xxx".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.bindUser".to_string(), - Some("xxx_ldap_bind_user_xxx".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.type".to_string(), - Some("ldap".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.url".to_string(), - Some("ldap://openldap:1389".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), - Some("uid".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), - Some("(&(uid=%s)(objectClass=inetOrgPerson))".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.enableCacheNotifications".to_string(), - Some("true".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.initialAdminPassword".to_string(), - Some("xxx_ldap_bind_password_xxx".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.initialInternalClientPassword".to_string(), - Some("xxx_ldap_internal_password_xxx".to_string()), - ), - ( - "druid.auth.authenticator.Ldap.type".to_string(), - Some("basic".to_string()), - ), - ( - "druid.auth.authenticatorChain".to_string(), - Some("[\"ldap\"]".to_string()), - ), - ( - "druid.escalator.internalClientPassword".to_string(), - Some("xxx_ldap_internal_password_xxx".to_string()), - ), - ( - "druid.escalator.internalClientUsername".to_string(), - Some("xxx_ldap_internal_user_xxx".to_string()), - ), - ( - "druid.escalator.type".to_string(), - Some("basic".to_string()), - ), - ] - .into_iter() - .collect(); - let got = ldap_settings.generate_runtime_properties_config_lines(); - assert_eq!(expected, got); + assert!(got.contains_key("druid.auth.authenticator.Ldap.type")); } } From 5cc6ea14da82fb71750611ac676bc4f54a68aea5 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 10 Jan 2023 16:39:04 +0100 Subject: [PATCH 09/59] fix: skip obsolete secret replacement commands --- rust/operator-binary/src/druid_controller.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index c2d216dd..1ddae15a 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -787,13 +787,8 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( (format!("/stackable/secrets/{volume_name}"), secret_volume), ); - let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/LDAP_BIND_USER)"); - let ldap_bind_password = - format!("$(cat /stackable/secrets/{volume_name}/LDAP_BIND_PASSWORD)"); - let ldap_internal_user = - format!("$(cat /stackable/secrets/{volume_name}/LDAP_INTERNAL_USER)"); - let ldap_internal_password = - format!("$(cat /stackable/secrets/{volume_name}/LDAP_INTERNAL_PASSWORD)"); + let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/user)"); + let ldap_bind_password = format!("$(cat /stackable/secrets/{volume_name}/password)"); const RUNTIME_PROPERTIES_PATH: &str = "/stackable/rwconfig/runtime.properties"; commands @@ -804,10 +799,6 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( commands.push(format!( r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {RUNTIME_PROPERTIES_PATH}"# )); - commands.push(format!( - r#"sed "s/xxx_ldap_internal_user_xxx/{ldap_internal_user}/g" -i {RUNTIME_PROPERTIES_PATH}"# - )); - commands.push(format!(r#"sed "s/xxx_ldap_internal_password_xxx/{ldap_internal_password}/g" -i {RUNTIME_PROPERTIES_PATH}"#)); } } From b6d1433fef3531229eea60d3ac8a20f7f5f9950f Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Wed, 11 Jan 2023 11:20:29 +0100 Subject: [PATCH 10/59] fix: remove unused dependency --- rust/crd/src/ldap.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 3f7052c5..fa3d18ea 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -2,9 +2,7 @@ use std::collections::BTreeMap; use std::string::FromUtf8Error; use snafu::Snafu; -use stackable_operator::commons::authentication::{ - AuthenticationClass, AuthenticationClassProvider, -}; +use stackable_operator::commons::authentication::AuthenticationClassProvider; use stackable_operator::commons::ldap::LdapAuthenticationProvider; use stackable_operator::k8s_openapi::api::core::v1::Secret; use stackable_operator::kube::runtime::reflector::ObjectRef; From af7bf5b2c8aeeb62c6dbcd740105d8771e8bc781 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 08:17:19 +0100 Subject: [PATCH 11/59] refactor: remove unused error enum --- rust/crd/src/ldap.rs | 33 -------------------- rust/operator-binary/src/druid_controller.rs | 4 --- 2 files changed, 37 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index fa3d18ea..3262a778 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -1,43 +1,10 @@ use std::collections::BTreeMap; -use std::string::FromUtf8Error; -use snafu::Snafu; use stackable_operator::commons::authentication::AuthenticationClassProvider; use stackable_operator::commons::ldap::LdapAuthenticationProvider; -use stackable_operator::k8s_openapi::api::core::v1::Secret; -use stackable_operator::kube::runtime::reflector::ObjectRef; -use strum::{EnumDiscriminants, IntoStaticStr}; use crate::authentication::ResolvedAuthenticationClasses; -#[derive(Snafu, Debug, EnumDiscriminants)] -#[strum_discriminants(derive(IntoStaticStr))] -#[allow(clippy::enum_variant_names)] -pub enum Error { - #[snafu(display("invalid ldap settings"))] - InvalidLdapSettings, - #[snafu(display("missing ldap bind credentials"))] - MissingBindCredentials, - #[snafu(display("missing secret field"))] - MissingSecretField, - #[snafu(display("unable to parse key {} from {} as UTF8", key, secret))] - NonUtf8Secret { - source: FromUtf8Error, - key: String, - secret: ObjectRef, - }, - #[snafu(display("failed to find referenced {}", secret))] - MissingSecret { - source: stackable_operator::error::Error, - secret: ObjectRef, - }, - #[snafu(display( - "a required value was not found when parsing the authentication config: [{}]", - value - ))] - MissingRequiredValue { value: String }, -} - const DEFAULT_LDAP_PORT: u16 = 1389; const DEFAULT_LDAP_TLS_PORT: u16 = 1636; diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 1ddae15a..f3e0bc9b 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -188,10 +188,6 @@ pub enum Error { FailedToInitializeSecurityContext { source: stackable_druid_crd::security::Error, }, - #[snafu(display("failed to resolve ldap authentication settings"))] - LdapAuthorization { - source: stackable_druid_crd::ldap::Error, - }, } type Result = std::result::Result; From 64086b53ceb242c70ccfc7bd9a67d5bf3380e7ba Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 08:26:12 +0100 Subject: [PATCH 12/59] refactor: rename lines to config --- rust/crd/src/ldap.rs | 70 ++++++++++---------- rust/operator-binary/src/druid_controller.rs | 3 +- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 3262a778..9c9ae522 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -32,12 +32,12 @@ impl DruidLdapSettings { None } - fn add_druid_system_authenticator_lines(&self, lines: &mut BTreeMap>) { - lines.insert( + fn add_druid_system_authenticator_config(&self, config: &mut BTreeMap>) { + config.insert( "druid.auth.authenticator.DruidSystemAuthenticator.type".to_string(), Some("basic".to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.DruidSystemAuthenticator.credentialsValidator.type" .to_string(), Some("metadata".to_string()), @@ -46,112 +46,112 @@ impl DruidLdapSettings { // this line is left out, as we don't want to create an admin user // # druid.auth.authenticator.DruidSystemAuthenticator.initialAdminPassword: XXX - lines.insert( + config.insert( "druid.auth.authenticator.DruidSystemAuthenticator.initialInternalClientPassword" .to_string(), Some("druid_system_pass".to_string()), // TODO: replace with sed placeholder ); - lines.insert( + config.insert( "druid.auth.authenticator.DruidSystemAuthenticator.authorizerName".to_string(), Some("DruidSystemAuthorizer".to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.DruidSystemAuthenticator.skipOnFailure".to_string(), Some("true".to_string()), // TODO: is additional escaping necessary for "true"? ); } - fn add_ldap_authenticator_lines(&self, lines: &mut BTreeMap>) { - lines.insert( + fn add_ldap_authenticator_config(&self, config: &mut BTreeMap>) { + config.insert( "druid.auth.authenticator.Ldap.type".to_string(), Some("basic".to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.enableCacheNotifications".to_string(), Some("true".to_string()), // TODO: is additional escaping necessary for "true"? ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.type".to_string(), Some("ldap".to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.url".to_string(), Some(self.credentials_validator_url()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.bindUser".to_string(), Some("xxx_ldap_bind_user_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.bindPassword".to_string(), Some("xxx_ldap_bind_password_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), Some(self.provider.search_base.to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), Some(self.provider.ldap_field_names.uid.to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), Some(self.provider.search_filter.to_string()), ); - lines.insert( + config.insert( "druid.auth.authenticator.Ldap.authorizerName".to_string(), Some("LdapAuthorizer".to_string()), ); } - fn add_escalator_lines(&self, lines: &mut BTreeMap>) { - lines.insert( + fn add_escalator_config(&self, config: &mut BTreeMap>) { + config.insert( "druid.escalator.type".to_string(), Some("basic".to_string()), ); - lines.insert( + config.insert( "druid.escalator.internalClientUsername".to_string(), Some("druid_system".to_string()), // TODO: replace with sed-placeholder xxx_druid_system_internal_user_xxx ); - lines.insert( + config.insert( "druid.escalator.internalClientPassword".to_string(), Some("druid_system_pass".to_string()), // TODO: replace with sed-placeholder ); - lines.insert( + config.insert( "druid.escalator.authorizerName".to_string(), Some("DruidSystemAuthorizer".to_string()), ); } - fn add_authorizer_lines(&self, lines: &mut BTreeMap>) { - lines.insert( + fn add_authorizer_config(&self, config: &mut BTreeMap>) { + config.insert( "druid.auth.authorizers".to_string(), Some(r#"["LdapAuthorizer", "DruidSystemAuthorizer"]"#.to_string()), ); - lines.insert( + config.insert( "druid.auth.authorizer.DruidSystemAuthorizer.type".to_string(), Some(r#"allowAll"#.to_string()), ); - lines.insert( + config.insert( "druid.auth.authorizer.LdapAuthorizer.type".to_string(), Some(r#"allowAll"#.to_string()), ); } - pub fn generate_runtime_properties_config_lines(&self) -> BTreeMap> { - let mut lines: BTreeMap> = BTreeMap::new(); + pub fn generate_runtime_properties_config(&self) -> BTreeMap> { + let mut config: BTreeMap> = BTreeMap::new(); - lines.insert( + config.insert( "druid.auth.authenticatorChain".to_string(), Some(r#"["DruidSystemAuthenticator", "Ldap"]"#.to_string()), ); - self.add_druid_system_authenticator_lines(&mut lines); - self.add_ldap_authenticator_lines(&mut lines); - self.add_escalator_lines(&mut lines); - self.add_authorizer_lines(&mut lines); + self.add_druid_system_authenticator_config(&mut config); + self.add_ldap_authenticator_config(&mut config); + self.add_escalator_config(&mut config); + self.add_authorizer_config(&mut config); - lines + config } fn is_ssl_enabled(&self) -> bool { @@ -201,7 +201,7 @@ mod test { }, }; - let got = ldap_settings.generate_runtime_properties_config_lines(); + let got = ldap_settings.generate_runtime_properties_config(); assert!(got.contains_key("druid.auth.authenticator.Ldap.type")); } diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index f3e0bc9b..abf9d6b6 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -508,8 +508,7 @@ fn build_rolegroup_config_map( druid_tls_security.add_tls_config_properties(&mut transformed_config, &role); if let Some(ldap_settings) = druid_ldap_settings { - transformed_config - .extend(ldap_settings.generate_runtime_properties_config_lines()); + transformed_config.extend(ldap_settings.generate_runtime_properties_config()); }; let runtime_properties = From 19085a66fa711af0a2df033bc06dd8f4d8643c5f Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 08:28:00 +0100 Subject: [PATCH 13/59] refactor: rename provider to ldap --- rust/crd/src/ldap.rs | 18 +++++++++--------- rust/operator-binary/src/druid_controller.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 9c9ae522..d7adcee4 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -10,7 +10,7 @@ const DEFAULT_LDAP_TLS_PORT: u16 = 1636; #[derive(Clone, Debug)] pub struct DruidLdapSettings { - pub provider: LdapAuthenticationProvider, + pub ldap: LdapAuthenticationProvider, } impl DruidLdapSettings { @@ -25,7 +25,7 @@ impl DruidLdapSettings { authentication_class.spec.provider { return Some(DruidLdapSettings { - provider: provider.clone(), + ldap: provider.clone(), }); } } @@ -88,15 +88,15 @@ impl DruidLdapSettings { ); config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), - Some(self.provider.search_base.to_string()), + Some(self.ldap.search_base.to_string()), ); config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), - Some(self.provider.ldap_field_names.uid.to_string()), + Some(self.ldap.ldap_field_names.uid.to_string()), ); config.insert( "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), - Some(self.provider.search_filter.to_string()), + Some(self.ldap.search_filter.to_string()), ); config.insert( "druid.auth.authenticator.Ldap.authorizerName".to_string(), @@ -155,7 +155,7 @@ impl DruidLdapSettings { } fn is_ssl_enabled(&self) -> bool { - self.provider.tls.is_some() + self.ldap.tls.is_some() } fn get_ldap_protocol_and_port(&self) -> (String, u16) { @@ -165,7 +165,7 @@ impl DruidLdapSettings { "ldap".to_string() }; - let port = if let Some(port) = self.provider.port { + let port = if let Some(port) = self.ldap.port { port } else if self.is_ssl_enabled() { DEFAULT_LDAP_TLS_PORT @@ -178,7 +178,7 @@ impl DruidLdapSettings { fn credentials_validator_url(&self) -> String { let (protocol, port) = self.get_ldap_protocol_and_port(); - format!("{}://{}:{}", protocol, self.provider.hostname, port,) + format!("{}://{}:{}", protocol, self.ldap.hostname, port,) } } @@ -190,7 +190,7 @@ mod test { #[test] fn test_ldap_settings_are_added() { let ldap_settings = DruidLdapSettings { - provider: LdapAuthenticationProvider { + ldap: LdapAuthenticationProvider { hostname: "openldap".to_string(), port: None, search_base: "ou=users,dc=example,dc=org".to_string(), diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index abf9d6b6..21258d72 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -771,7 +771,7 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( let mut commands = Vec::new(); if let Some(ldap_settings) = maybe_ldap_settings { - if let Some(credentials) = &ldap_settings.provider.bind_credentials { + if let Some(credentials) = &ldap_settings.ldap.bind_credentials { let volume_name = credentials.secret_class.clone(); let secret_volume = VolumeBuilder::new(&volume_name) .ephemeral(SecretOperatorVolumeSourceBuilder::new(volume_name.clone()).build()) From 358d9a29d5217482a04c3b1bf78141ae2d6e3363 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 08:42:47 +0100 Subject: [PATCH 14/59] feat: add additional ldap cmd info Co-authored-by: Sebastian Bernauer --- rust/operator-binary/src/druid_controller.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 21258d72..84cae778 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -787,7 +787,7 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( const RUNTIME_PROPERTIES_PATH: &str = "/stackable/rwconfig/runtime.properties"; commands - .push(r#"echo "Replacing LDAP placeholders with their proper values""#.to_string()); + .push(r#"echo "Replacing LDAP placeholders with their proper values in {RUNTIME_PROPERTIES_FILE}""#.to_string()); commands.push(format!( r#"sed "s/xxx_ldap_bind_user_xxx/{ldap_bind_user}/g" -i {RUNTIME_PROPERTIES_PATH}"# )); From 0ded0a81956ec57cc6593e77bb381a9998a652e0 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 08:53:29 +0100 Subject: [PATCH 15/59] refactor: reuse consts to create config path --- rust/operator-binary/src/druid_controller.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 84cae778..59f50dc4 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -785,14 +785,14 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/user)"); let ldap_bind_password = format!("$(cat /stackable/secrets/{volume_name}/password)"); - const RUNTIME_PROPERTIES_PATH: &str = "/stackable/rwconfig/runtime.properties"; + let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); commands .push(r#"echo "Replacing LDAP placeholders with their proper values in {RUNTIME_PROPERTIES_FILE}""#.to_string()); commands.push(format!( - r#"sed "s/xxx_ldap_bind_user_xxx/{ldap_bind_user}/g" -i {RUNTIME_PROPERTIES_PATH}"# + r#"sed "s/xxx_ldap_bind_user_xxx/{ldap_bind_user}/g" -i {runtime_properties_file}"# )); commands.push(format!( - r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {RUNTIME_PROPERTIES_PATH}"# + r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {runtime_properties_file}"# )); } } From 17aff661b4d7f90e837c2a62fd53b0608dc0e9bd Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 09:04:19 +0100 Subject: [PATCH 16/59] refactor: move long config string prefixes into consts --- rust/crd/src/ldap.rs | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index d7adcee4..613a88f2 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -33,13 +33,11 @@ impl DruidLdapSettings { } fn add_druid_system_authenticator_config(&self, config: &mut BTreeMap>) { + const PREFIX: &str = "druid.auth.authenticator.DruidSystemAuthenticator"; + + config.insert(format!("{PREFIX}.type"), Some("basic".to_string())); config.insert( - "druid.auth.authenticator.DruidSystemAuthenticator.type".to_string(), - Some("basic".to_string()), - ); - config.insert( - "druid.auth.authenticator.DruidSystemAuthenticator.credentialsValidator.type" - .to_string(), + format!("{PREFIX}.credentialsValidator.type"), Some("metadata".to_string()), ); @@ -47,59 +45,57 @@ impl DruidLdapSettings { // # druid.auth.authenticator.DruidSystemAuthenticator.initialAdminPassword: XXX config.insert( - "druid.auth.authenticator.DruidSystemAuthenticator.initialInternalClientPassword" - .to_string(), + format!("{PREFIX}.initialInternalClientPassword"), Some("druid_system_pass".to_string()), // TODO: replace with sed placeholder ); config.insert( - "druid.auth.authenticator.DruidSystemAuthenticator.authorizerName".to_string(), + format!("{PREFIX}.authorizerName"), Some("DruidSystemAuthorizer".to_string()), ); config.insert( - "druid.auth.authenticator.DruidSystemAuthenticator.skipOnFailure".to_string(), + format!("{PREFIX}.skipOnFailure"), Some("true".to_string()), // TODO: is additional escaping necessary for "true"? ); } fn add_ldap_authenticator_config(&self, config: &mut BTreeMap>) { + const PREFIX: &str = "druid.auth.authenticator.Ldap"; + + config.insert(format!("{PREFIX}.type"), Some("basic".to_string())); config.insert( - "druid.auth.authenticator.Ldap.type".to_string(), - Some("basic".to_string()), - ); - config.insert( - "druid.auth.authenticator.Ldap.enableCacheNotifications".to_string(), + format!("{PREFIX}.enableCacheNotifications"), Some("true".to_string()), // TODO: is additional escaping necessary for "true"? ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.type".to_string(), + format!("{PREFIX}.credentialsValidator.type"), Some("ldap".to_string()), ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.url".to_string(), + format!("{PREFIX}.credentialsValidator.url"), Some(self.credentials_validator_url()), ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.bindUser".to_string(), + format!("{PREFIX}.credentialsValidator.bindUser"), Some("xxx_ldap_bind_user_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.bindPassword".to_string(), + format!("{PREFIX}.credentialsValidator.bindPassword"), Some("xxx_ldap_bind_password_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.baseDn".to_string(), + format!("{PREFIX}.credentialsValidator.baseDn"), Some(self.ldap.search_base.to_string()), ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.userAttribute".to_string(), + format!("{PREFIX}.credentialsValidator.userAttribute"), Some(self.ldap.ldap_field_names.uid.to_string()), ); config.insert( - "druid.auth.authenticator.Ldap.credentialsValidator.userSearch".to_string(), + format!("{PREFIX}.credentialsValidator.userSearch"), Some(self.ldap.search_filter.to_string()), ); config.insert( - "druid.auth.authenticator.Ldap.authorizerName".to_string(), + format!("{PREFIX}.authorizerName"), Some("LdapAuthorizer".to_string()), ); } From 868a432abd28fd6a184d7e4137ba47e5e90b9846 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 12 Jan 2023 09:06:08 +0100 Subject: [PATCH 17/59] style: remove unneeded comments --- rust/crd/src/ldap.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 613a88f2..b8ac84d2 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -52,10 +52,7 @@ impl DruidLdapSettings { format!("{PREFIX}.authorizerName"), Some("DruidSystemAuthorizer".to_string()), ); - config.insert( - format!("{PREFIX}.skipOnFailure"), - Some("true".to_string()), // TODO: is additional escaping necessary for "true"? - ); + config.insert(format!("{PREFIX}.skipOnFailure"), Some("true".to_string())); } fn add_ldap_authenticator_config(&self, config: &mut BTreeMap>) { @@ -64,7 +61,7 @@ impl DruidLdapSettings { config.insert(format!("{PREFIX}.type"), Some("basic".to_string())); config.insert( format!("{PREFIX}.enableCacheNotifications"), - Some("true".to_string()), // TODO: is additional escaping necessary for "true"? + Some("true".to_string()), ); config.insert( format!("{PREFIX}.credentialsValidator.type"), From 8cfb7884b867f244e173578c393ba7a835175359 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 16 Jan 2023 15:34:08 +0100 Subject: [PATCH 18/59] style: change argument order --- rust/operator-binary/src/druid_controller.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 59f50dc4..405dcc30 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -647,7 +647,7 @@ fn build_rolegroup_statefulset( .add_tls_volume_and_volume_mounts(&mut cb_prepare, &mut cb_druid, &mut pb) .context(FailedToInitializeSecurityContextSnafu)?; add_s3_volume_and_volume_mounts(s3_conn, &mut cb_druid, &mut pb)?; - add_ldap_secret_volume_mounts(&mut cb_druid, &mut pb, ldap_auth_mounts); + add_ldap_secret_volume_mounts(ldap_auth_mounts, &mut cb_druid, &mut pb); add_config_volume_and_volume_mounts(rolegroup_ref, &mut cb_druid, &mut pb); add_hdfs_cm_volume_and_volume_mounts( &druid.spec.cluster_config.deep_storage, @@ -801,9 +801,9 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( } fn add_ldap_secret_volume_mounts( + ldap_auth_volumes: BTreeMap, cb_druid: &mut ContainerBuilder, pb: &mut PodBuilder, - ldap_auth_volumes: BTreeMap, ) { for (name, (path, volume)) in ldap_auth_volumes.iter() { cb_druid.add_volume_mount(name, path); From f1bb08bb473bae9eaa81e30d3ed705383901d443 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 07:47:42 +0100 Subject: [PATCH 19/59] style: remove maybe_ prefix from variables --- rust/crd/src/ldap.rs | 5 ++--- rust/operator-binary/src/druid_controller.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index b8ac84d2..e4843d44 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -17,10 +17,9 @@ impl DruidLdapSettings { pub fn new_from( resolved_authentication_config: ResolvedAuthenticationClasses, ) -> Option { - let maybe_authentication_class = - resolved_authentication_config.get_ldap_authentication_class(); + let authentication_class = resolved_authentication_config.get_ldap_authentication_class(); - if let Some(authentication_class) = maybe_authentication_class { + if let Some(authentication_class) = authentication_class { if let AuthenticationClassProvider::Ldap(ref provider) = authentication_class.spec.provider { diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 405dcc30..630b63d6 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -623,7 +623,7 @@ fn build_rolegroup_statefulset( s3_conn: Option<&S3ConnectionSpec>, resources: &RoleResource, druid_tls_security: &DruidTlsSecurity, - maybe_ldap_settings: &Option, + ldap_settings: &Option, ) -> Result { let role = DruidRole::from_str(&rolegroup_ref.role).context(UnidentifiedDruidRoleSnafu { role: rolegroup_ref.role.to_string(), @@ -640,7 +640,7 @@ fn build_rolegroup_statefulset( pb.node_selector_opt(druid.node_selector(rolegroup_ref)); let (ldap_auth_mounts, ldap_auth_cmd) = - get_ldap_secret_volume_and_volume_mounts_and_commands(maybe_ldap_settings); + get_ldap_secret_volume_and_volume_mounts_and_commands(ldap_settings); // volume and volume mounts druid_tls_security @@ -765,12 +765,12 @@ fn add_hdfs_cm_volume_and_volume_mounts( } fn get_ldap_secret_volume_and_volume_mounts_and_commands( - maybe_ldap_settings: &Option, + ldap_settings: &Option, ) -> (BTreeMap, Vec) { let mut volumes = BTreeMap::new(); let mut commands = Vec::new(); - if let Some(ldap_settings) = maybe_ldap_settings { + if let Some(ldap_settings) = ldap_settings { if let Some(credentials) = &ldap_settings.ldap.bind_credentials { let volume_name = credentials.secret_class.clone(); let secret_volume = VolumeBuilder::new(&volume_name) @@ -968,7 +968,7 @@ mod test { let resources = resource::resources(&druid, &DruidRole::Historical, &rolegroup_ref) .context(ResourceSnafu)?; - let maybe_ldap_settings: Option = None; + let ldap_settings: Option = None; let rg_configmap = build_rolegroup_config_map( &druid, @@ -981,7 +981,7 @@ mod test { None, &resources, &druid_tls_security, - &maybe_ldap_settings, + &ldap_settings, ) .context(ControllerSnafu)?; From 1d99a33212a9b4f601eb534b90545c80c2d4d9d4 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 10:47:12 +0100 Subject: [PATCH 20/59] wip: towards replacing internal hard-coded secret --- Cargo.lock | 1 + rust/operator-binary/Cargo.toml | 1 + rust/operator-binary/src/druid_controller.rs | 17 ++- rust/operator-binary/src/internal_secret.rs | 103 +++++++++++++++++++ rust/operator-binary/src/main.rs | 1 + 5 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 rust/operator-binary/src/internal_secret.rs diff --git a/Cargo.lock b/Cargo.lock index cc24dedd..29520695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1845,6 +1845,7 @@ dependencies = [ "fnv", "futures 0.3.25", "indoc", + "openssl", "pin-project", "rstest", "semver", diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index cc6f4867..cad4f076 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -16,6 +16,7 @@ clap = "4.0" fnv = "1.0" futures = { version = "0.3", features = ["compat"] } indoc = "1.0.8" +openssl = "0.10.42" pin-project = "1.0" semver = "1.0" serde = "1.0" diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 630b63d6..00e36b0b 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -3,6 +3,10 @@ use crate::{ config::{get_jvm_config, get_log4j_config}, discovery::{self, build_discovery_configmaps}, extensions::get_extension_list, + internal_secret::{ + build_shared_internal_secret_name, create_shared_internal_secret, env_var_from_secret, + ENV_INTERNAL_SECRET, + }, }; use crate::OPERATOR_NAME; @@ -188,6 +192,10 @@ pub enum Error { FailedToInitializeSecurityContext { source: stackable_druid_crd::security::Error, }, + #[snafu(display("failed to retrieve secret for internal communications"))] + FailedInternalSecretCreation { + source: crate::internal_secret::Error, + }, } type Result = std::result::Result; @@ -308,6 +316,10 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< .await .context(ApplyRoleServiceSnafu)?; + create_shared_internal_secret(&druid, client, CONTROLLER_NAME) + .await + .context(FailedInternalSecretCreationSnafu)?; + for (rolegroup_name, rolegroup_config) in role_config.iter() { let rolegroup = RoleGroupRef { cluster: ObjectRef::from_obj(&*druid), @@ -665,7 +677,7 @@ fn build_rolegroup_statefulset( .build(); // rest of env - let rest_env = rolegroup_config + let mut rest_env = rolegroup_config .get(&PropertyNameKind::Env) .iter() .flat_map(|env_vars| env_vars.iter()) @@ -677,6 +689,9 @@ fn build_rolegroup_statefulset( }) .collect::>(); + let secret_name = build_shared_internal_secret_name(druid); + rest_env.push(env_var_from_secret(&secret_name, None, ENV_INTERNAL_SECRET)); + cb_druid .image_from_product_image(resolved_product_image) .command(role.get_command(s3_conn, ldap_auth_cmd)) diff --git a/rust/operator-binary/src/internal_secret.rs b/rust/operator-binary/src/internal_secret.rs new file mode 100644 index 00000000..7af30cf5 --- /dev/null +++ b/rust/operator-binary/src/internal_secret.rs @@ -0,0 +1,103 @@ +use snafu::{OptionExt, ResultExt, Snafu}; +use stackable_druid_crd::DruidCluster; +use stackable_operator::k8s_openapi::api::core::v1::{EnvVar, EnvVarSource, SecretKeySelector}; +use stackable_operator::kube::ResourceExt; +use stackable_operator::{ + builder::ObjectMetaBuilder, client::Client, k8s_openapi::api::core::v1::Secret, +}; +use std::collections::BTreeMap; +use strum::{EnumDiscriminants, IntoStaticStr}; + +pub const ENV_INTERNAL_SECRET: &str = "INTERNAL_SECRET"; + +#[derive(Snafu, Debug, EnumDiscriminants)] +#[strum_discriminants(derive(IntoStaticStr))] +#[allow(clippy::enum_variant_names)] +pub enum Error { + #[snafu(display("failed to apply internal secret"))] + ApplyInternalSecret { + source: stackable_operator::error::Error, + }, + #[snafu(display("failed to retrieve secret for internal communications"))] + FailedToRetrieveInternalSecret { + source: stackable_operator::error::Error, + }, + #[snafu(display("object defines no namespace"))] + ObjectHasNoNamespace, + #[snafu(display("object is missing metadata to build owner reference"))] + ObjectMissingMetadataForOwnerRef { + source: stackable_operator::error::Error, + }, +} + +pub async fn create_shared_internal_secret( + druid: &DruidCluster, + client: &Client, + controller_name: &str, +) -> Result<(), Error> { + let secret = build_shared_internal_secret(druid)?; + if client + .get_opt::( + &secret.name_any(), + secret + .namespace() + .as_deref() + .context(ObjectHasNoNamespaceSnafu)?, + ) + .await + .context(FailedToRetrieveInternalSecretSnafu)? + .is_none() + { + client + .apply_patch(controller_name, &secret, &secret) + .await + .context(ApplyInternalSecretSnafu)?; + } + + Ok(()) +} + +pub fn build_shared_internal_secret(druid: &DruidCluster) -> Result { + let mut internal_secret = BTreeMap::new(); + internal_secret.insert(ENV_INTERNAL_SECRET.to_string(), get_random_base64()); + + Ok(Secret { + immutable: Some(true), + metadata: ObjectMetaBuilder::new() + .name(build_shared_internal_secret_name(druid)) + .namespace_opt(druid.namespace()) + .ownerreference_from_resource(druid, None, Some(true)) + .context(ObjectMissingMetadataForOwnerRefSnafu)? + .build(), + string_data: Some(internal_secret), + ..Secret::default() + }) +} + +pub fn build_shared_internal_secret_name(druid: &DruidCluster) -> String { + format!("{}-internal-secret", druid.name_any()) +} + +fn get_random_base64() -> String { + let mut buf = [0; 512]; + openssl::rand::rand_bytes(&mut buf).unwrap(); + openssl::base64::encode_block(&buf) +} + +/// Give a secret name and an optional key in the secret to use. +/// The value from the key will be set into the given env var name. +/// If not secret key is given, the env var name will be used as the secret key. +pub fn env_var_from_secret(secret_name: &str, secret_key: Option<&str>, env_var: &str) -> EnvVar { + EnvVar { + name: env_var.to_string(), + value_from: Some(EnvVarSource { + secret_key_ref: Some(SecretKeySelector { + optional: Some(false), + name: Some(secret_name.to_string()), + key: secret_key.unwrap_or(env_var).to_string(), + }), + ..EnvVarSource::default() + }), + ..EnvVar::default() + } +} diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index 50e74d19..5a13ebab 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -2,6 +2,7 @@ mod config; mod discovery; mod druid_controller; mod extensions; +mod internal_secret; use std::sync::Arc; From afceef9911ab5ef34c315add01b974e797e93b69 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 10:50:35 +0100 Subject: [PATCH 21/59] wip: replace hard-coded secret with env var --- rust/crd/src/ldap.rs | 6 +++--- rust/operator-binary/src/druid_controller.rs | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index e4843d44..920b687f 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -45,7 +45,7 @@ impl DruidLdapSettings { config.insert( format!("{PREFIX}.initialInternalClientPassword"), - Some("druid_system_pass".to_string()), // TODO: replace with sed placeholder + Some("xxx_druid_system_internal_client_password_xxx".to_string()), ); config.insert( format!("{PREFIX}.authorizerName"), @@ -103,11 +103,11 @@ impl DruidLdapSettings { ); config.insert( "druid.escalator.internalClientUsername".to_string(), - Some("druid_system".to_string()), // TODO: replace with sed-placeholder xxx_druid_system_internal_user_xxx + Some("druid_system".to_string()), ); config.insert( "druid.escalator.internalClientPassword".to_string(), - Some("druid_system_pass".to_string()), // TODO: replace with sed-placeholder + Some("xxx_druid_system_internal_client_password_xxx".to_string()), ); config.insert( "druid.escalator.authorizerName".to_string(), diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 00e36b0b..eabacd99 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -799,6 +799,7 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/user)"); let ldap_bind_password = format!("$(cat /stackable/secrets/{volume_name}/password)"); + let internal_client_password = format!("$(echo ${ENV_INTERNAL_SECRET})"); let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); commands @@ -809,6 +810,9 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( commands.push(format!( r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {runtime_properties_file}"# )); + commands.push(format!( + r#"sed "s|xxx_druid_system_internal_client_password_xxx|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string + )); } } From c161c00e345bb367f23abad0d577b1870701c0e1 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 13:21:17 +0100 Subject: [PATCH 22/59] chore: clarify ldap tls test comment --- tests/test-definition.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index 3e33b5ef..b6e7f9f5 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -31,7 +31,7 @@ dimensions: - "internal-and-server-tls-and-tls-client-auth" - name: ldap-use-tls values: - #- "true" # commented out, as it is not yet functional + # - "true" # disabled, until ldaps is actually supported - "false" tests: - name: smoke @@ -85,4 +85,4 @@ tests: - zookeeper-latest - opa - hadoop - - ldap-use-tls \ No newline at end of file + - ldap-use-tls From 87fa14cf298b2fce93026afebea7e8630a46ddc8 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 13:31:57 +0100 Subject: [PATCH 23/59] docs: add to CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 479c7198..3f34fe4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - BREAKING: Support for TLS encryption (activated per default -> port changes) and TLS authentication ([#333]) - Use emptyDir for segment cache on historicals ([#342]) +- Add support for non-TLS LDAP authentication. ([#374]) ### Changed @@ -35,6 +36,7 @@ All notable changes to this project will be documented in this file. [#362]: https://github.com/stackabletech/druid-operator/pull/362 [#363]: https://github.com/stackabletech/druid-operator/pull/363 [#366]: https://github.com/stackabletech/druid-operator/pull/366 +[#374]: https://github.com/stackabletech/druid-operator/pull/374 ## [0.8.0] - 2022-11-07 From f5eb61209e96a974fb94508a0872d4ec2fb85710 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 13:48:25 +0100 Subject: [PATCH 24/59] docs: port and adjust previously written LDAP documentation --- .../code/druid-ldap-authentication.yaml | 43 +++++++++++++++++++ docs/modules/ROOT/pages/usage.adoc | 22 +++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml diff --git a/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml b/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml new file mode 100644 index 00000000..cacf9bee --- /dev/null +++ b/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: druid.stackable.tech/v1alpha1 +kind: DruidCluster +metadata: + name: derby-druid +spec: + clusterConfig: + authentication: + - authenticationClass: druid-ldap-auth-class +--- +apiVersion: authentication.stackable.tech/v1alpha1 +kind: AuthenticationClass +metadata: + name: druid-ldap-auth-class +spec: + provider: + ldap: + hostname: openldap + searchBase: ou=users,dc=example,dc=org + searchFilter: (uid=%s) + port: 1389 + bindCredentials: + secretClass: druid-ldap-secret +--- +apiVersion: secrets.stackable.tech/v1alpha1 +kind: SecretClass +metadata: + name: druid-ldap-secret +spec: + backend: + k8sSearch: + searchNamespace: + pod: {} +--- +apiVersion: v1 +kind: Secret +metadata: + name: druid-ldap-secret + labels: + secrets.stackable.tech/class: druid-ldap-secret +stringData: + user: uid=admin,ou=Users,dc=example,dc=org + password: admin diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 4aaf13cf..e1c16db9 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -307,7 +307,27 @@ spec: ==== LDAP -TODO: add more info here. + +Druid supports authentication of users against an LDAP server. +Have a look at https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication[the LDAP test] and the general xref:commons-operator::authenticationclass.adoc[Stackable Authentication] documentation on how to set it up. + +To use LDAP, you will need to specify a xref:commons-operator::authenticationclass.adoc[`AuthenticationClass`] which is used to authenticate the users. +Here is a reduced, non-functional YAML example, which shows the most essential parts which are needed to configure LDAP in the druid-operator: + +[source,yaml] +---- +include::example$code/druid-ldap-authentication.yaml[] +---- + +Refer to https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication/create_ldap_user.sh[the LDAP test user creation script] to see what LDAP settings are referred to in this config snippet. + +==== Current Limitations and Upcoming Work + +At the moment you can either use TLS authentication or LDAP authentication. Both methods together are not supported. + +Communication with a TLS-secured LDAP server is currently not implemented, but will be tackled in the near future. + +Authorization is done using the `allowAll` authorizer. In the future, support for `memberOf` and OPA will be added. === Authorization with Open Policy Agent (OPA) From a6af866b5c08878f243116edda61b3a20a99d854 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 14:59:25 +0100 Subject: [PATCH 25/59] docs: adjust LDAP documentation to refer to common concepts --- docs/modules/ROOT/pages/usage.adoc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index e1c16db9..4a5abf0a 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -307,11 +307,11 @@ spec: ==== LDAP +Druid supports xref:home:concepts:authentication.adoc[authentication] of users against an LDAP server. +This requires setting up an xref:home:concepts:authentication.adoc#authenticationclass[AuthenticationClass] for the LDAP server. -Druid supports authentication of users against an LDAP server. -Have a look at https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication[the LDAP test] and the general xref:commons-operator::authenticationclass.adoc[Stackable Authentication] documentation on how to set it up. +NOTE: You can follow the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to learn how to create an AuthenticationClass for an LDAP server. -To use LDAP, you will need to specify a xref:commons-operator::authenticationclass.adoc[`AuthenticationClass`] which is used to authenticate the users. Here is a reduced, non-functional YAML example, which shows the most essential parts which are needed to configure LDAP in the druid-operator: [source,yaml] @@ -319,15 +319,16 @@ Here is a reduced, non-functional YAML example, which shows the most essential p include::example$code/druid-ldap-authentication.yaml[] ---- -Refer to https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication/create_ldap_user.sh[the LDAP test user creation script] to see what LDAP settings are referred to in this config snippet. +You can see the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to see a complete example of how to set LDAP authentication for another Stackable operator. You can also consult the xref:home:reference:authenticationclass.adoc[] reference, or +https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication[the LDAP test] suite. ==== Current Limitations and Upcoming Work At the moment you can either use TLS authentication or LDAP authentication. Both methods together are not supported. -Communication with a TLS-secured LDAP server is currently not implemented, but will be tackled in the near future. +Communication with a TLS-secured LDAP server is currently not implemented, but will be tackled in the future. -Authorization is done using the `allowAll` authorizer. In the future, support for `memberOf` and OPA will be added. +Authorization is done using the `allowAll` authorizer. Supporting for `memberOf` and OPA authorization are planned. === Authorization with Open Policy Agent (OPA) From bcf3a62051e4860440f62bab64a8aa43567f0ed0 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 15:11:02 +0100 Subject: [PATCH 26/59] style: incorporate review suggestions --- rust/crd/src/ldap.rs | 6 +++--- rust/operator-binary/src/druid_controller.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 920b687f..7a91e10d 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -17,9 +17,9 @@ impl DruidLdapSettings { pub fn new_from( resolved_authentication_config: ResolvedAuthenticationClasses, ) -> Option { - let authentication_class = resolved_authentication_config.get_ldap_authentication_class(); - - if let Some(authentication_class) = authentication_class { + if let Some(authentication_class) = + resolved_authentication_config.get_ldap_authentication_class() + { if let AuthenticationClassProvider::Ldap(ref provider) = authentication_class.spec.provider { diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index eabacd99..5d392de7 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -824,7 +824,7 @@ fn add_ldap_secret_volume_mounts( cb_druid: &mut ContainerBuilder, pb: &mut PodBuilder, ) { - for (name, (path, volume)) in ldap_auth_volumes.iter() { + for (name, (path, volume)) in ldap_auth_volumes { cb_druid.add_volume_mount(name, path); pb.add_volume(volume.clone()); } From c1b6412ef77d297979afc475a56e36bd94f5cdf4 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 15:11:40 +0100 Subject: [PATCH 27/59] test: incorporate review suggestions --- .../kuttl/ldap-authentication/02-install-hdfs.yaml.j2 | 6 +++--- tests/test-definition.yaml | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 b/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 index a5db7007..d3729bca 100644 --- a/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 +++ b/tests/templates/kuttl/ldap-authentication/02-install-hdfs.yaml.j2 @@ -11,8 +11,8 @@ metadata: name: druid-hdfs spec: image: - productVersion: "{{ test_scenario['values']['hadoop'].split('-stackable')[0] }}" - stackableVersion: "{{ test_scenario['values']['hadoop'].split('-stackable')[1] }}" + productVersion: "{{ test_scenario['values']['hadoop-latest'].split('-stackable')[0] }}" + stackableVersion: "{{ test_scenario['values']['hadoop-latest'].split('-stackable')[1] }}" zookeeperConfigMapName: hdfs-znode dfsReplication: 1 nameNodes: @@ -26,4 +26,4 @@ spec: journalNodes: roleGroups: default: - replicas: 1 \ No newline at end of file + replicas: 1 diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index b6e7f9f5..fd5c8390 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -20,6 +20,9 @@ dimensions: - name: hadoop values: - 3.3.4-stackable0.2.0 + - name: hadoop-latest + values: + - 3.3.4-stackable0.2.0 - name: s3-use-tls values: - "true" @@ -84,5 +87,5 @@ tests: - druid - zookeeper-latest - opa - - hadoop + - hadoop-latest - ldap-use-tls From e60e2e8366303713d59b8d3d9f8ed1cdd2a4391c Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 15:20:21 +0100 Subject: [PATCH 28/59] docs: add note about OPA Co-authored-by: Sebastian Bernauer --- rust/crd/src/ldap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 7a91e10d..8d4115ac 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -126,7 +126,7 @@ impl DruidLdapSettings { ); config.insert( "druid.auth.authorizer.LdapAuthorizer.type".to_string(), - Some(r#"allowAll"#.to_string()), + Some(r#"allowAll"#.to_string()), // TODO: Respect opa setting ); } From 8a3d57aa91e617b7e9bbdf041351ed73a3895c12 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 19 Jan 2023 15:34:46 +0100 Subject: [PATCH 29/59] fix: add review suggestion regarding well-known ports --- rust/crd/src/ldap.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 8d4115ac..35308611 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -5,9 +5,6 @@ use stackable_operator::commons::ldap::LdapAuthenticationProvider; use crate::authentication::ResolvedAuthenticationClasses; -const DEFAULT_LDAP_PORT: u16 = 1389; -const DEFAULT_LDAP_TLS_PORT: u16 = 1636; - #[derive(Clone, Debug)] pub struct DruidLdapSettings { pub ldap: LdapAuthenticationProvider, @@ -159,10 +156,8 @@ impl DruidLdapSettings { let port = if let Some(port) = self.ldap.port { port - } else if self.is_ssl_enabled() { - DEFAULT_LDAP_TLS_PORT } else { - DEFAULT_LDAP_PORT + self.ldap.default_port() }; (protocol, port) From 69ee87c91393697f42ee1e6d79a229389755cd5c Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 10:53:39 +0100 Subject: [PATCH 30/59] docs: add LDAP info to doc comment --- rust/crd/src/authentication.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/crd/src/authentication.rs b/rust/crd/src/authentication.rs index d34e5ce1..e6f70a8b 100644 --- a/rust/crd/src/authentication.rs +++ b/rust/crd/src/authentication.rs @@ -59,7 +59,10 @@ pub struct DruidAuthentication { /// /// ## LDAP provider /// - /// Only affects client connections. TODO + /// Only affects client connections. This setting controls: + /// - If clients need to authenticate themselves against Druid via LDAP + /// - Which ca.crt to use when validating the provided client certs (currently not supported) + /// pub authentication_class: String, } From 0b2d67f789c0f1fe3bf4796ec390b3c27d829752 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 10:54:18 +0100 Subject: [PATCH 31/59] chore: add regenerated chart --- deploy/helm/druid-operator/crds/crds.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deploy/helm/druid-operator/crds/crds.yaml b/deploy/helm/druid-operator/crds/crds.yaml index 236ec3d8..fcb6fa07 100644 --- a/deploy/helm/druid-operator/crds/crds.yaml +++ b/deploy/helm/druid-operator/crds/crds.yaml @@ -374,6 +374,10 @@ spec: Only affects client connections. This setting controls: - If clients need to authenticate themselves against Druid via TLS - Which ca.crt to use when validating the provided client certs Please note that the SecretClass used to authenticate users needs to be the same as the SecretClass used for internal communication. + + ## LDAP provider + + Only affects client connections. This setting controls: - If clients need to authenticate themselves against Druid via LDAP - Which ca.crt to use when validating the provided client certs (currently not supported) type: string required: - authenticationClass From 3a7d0fa6dc14f9738c0d8d3380dc4f97b7a35492 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 15:25:46 +0100 Subject: [PATCH 32/59] docs: shorten and split ldap example code Co-authored-by: Felix Hennig --- .../code/druid-ldap-authentication.yaml | 53 ++++++------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml b/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml index cacf9bee..1aa3ad05 100644 --- a/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml +++ b/docs/modules/ROOT/examples/code/druid-ldap-authentication.yaml @@ -1,43 +1,22 @@ ---- -apiVersion: druid.stackable.tech/v1alpha1 -kind: DruidCluster -metadata: - name: derby-druid -spec: - clusterConfig: - authentication: - - authenticationClass: druid-ldap-auth-class ---- +# yamllint disable-file + +# tag::authclass[] apiVersion: authentication.stackable.tech/v1alpha1 kind: AuthenticationClass metadata: - name: druid-ldap-auth-class + name: ldap-auth spec: - provider: - ldap: - hostname: openldap - searchBase: ou=users,dc=example,dc=org - searchFilter: (uid=%s) - port: 1389 - bindCredentials: - secretClass: druid-ldap-secret ---- -apiVersion: secrets.stackable.tech/v1alpha1 -kind: SecretClass + [...] +#end::authclass[] + +# tag::druid[] +apiVersion: druid.stackable.tech/v1alpha1 +kind: DruidCluster metadata: - name: druid-ldap-secret + name: druid spec: - backend: - k8sSearch: - searchNamespace: - pod: {} ---- -apiVersion: v1 -kind: Secret -metadata: - name: druid-ldap-secret - labels: - secrets.stackable.tech/class: druid-ldap-secret -stringData: - user: uid=admin,ou=Users,dc=example,dc=org - password: admin + clusterConfig: + authentication: + - authenticationClass: ldap-auth + [...] +# end::druid[] From e8c4bbef98899e99500c57d74d4aca8becabd325 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 15:26:19 +0100 Subject: [PATCH 33/59] docs: include auth part of ldap example code Co-authored-by: Felix Hennig --- docs/modules/ROOT/pages/usage.adoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 4a5abf0a..b508e5f1 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -308,7 +308,12 @@ spec: ==== LDAP Druid supports xref:home:concepts:authentication.adoc[authentication] of users against an LDAP server. -This requires setting up an xref:home:concepts:authentication.adoc#authenticationclass[AuthenticationClass] for the LDAP server. +This requires setting up an xref:home:concepts:authentication.adoc#authenticationclass[AuthenticationClass] for the LDAP server: + +[source,yaml] +---- +include::example$code/druid-ldap-authentication.yaml[tag=authclass] +---- NOTE: You can follow the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to learn how to create an AuthenticationClass for an LDAP server. From 0f03177e8f470f3676b90075bc46d3c12d24f5ed Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 15:26:31 +0100 Subject: [PATCH 34/59] docs: include druid part of ldap example code Co-authored-by: Felix Hennig --- docs/modules/ROOT/pages/usage.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index b508e5f1..43bf0db9 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -317,11 +317,11 @@ include::example$code/druid-ldap-authentication.yaml[tag=authclass] NOTE: You can follow the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to learn how to create an AuthenticationClass for an LDAP server. -Here is a reduced, non-functional YAML example, which shows the most essential parts which are needed to configure LDAP in the druid-operator: +Reference the AuthenticationClass in your DruidCluster resource: [source,yaml] ---- -include::example$code/druid-ldap-authentication.yaml[] +include::example$code/druid-ldap-authentication.yaml[tag=druid] ---- You can see the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to see a complete example of how to set LDAP authentication for another Stackable operator. You can also consult the xref:home:reference:authenticationclass.adoc[] reference, or From c60579cf236cbb4155d7d3ed55ce43b167e669be Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 15:41:57 +0100 Subject: [PATCH 35/59] fix: adjust error message and TODO to reflect auth provider clash --- rust/crd/src/authentication.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/crd/src/authentication.rs b/rust/crd/src/authentication.rs index e6f70a8b..e6d883dc 100644 --- a/rust/crd/src/authentication.rs +++ b/rust/crd/src/authentication.rs @@ -18,8 +18,8 @@ pub enum Error { source: stackable_operator::error::Error, authentication_class: ObjectRef, }, - // TODO: Adapt message if multiple authentication classes are supported - #[snafu(display("only one authentication class is currently supported. Possible Authentication class providers are {SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS:?}"))] + // TODO: Adapt message if multiple authentication classes are supported simultaneously + #[snafu(display("only one authentication class is currently supported at a time. Possible Authentication class providers are {SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS:?}"))] MultipleAuthenticationClassesProvided, #[snafu(display( "failed to use authentication provider [{provider}] for authentication class [{authentication_class}] - supported providers: {SUPPORTED_AUTHENTICATION_CLASS_PROVIDERS:?}", From 5608804d52ca2b463cb5c707c9cff1f190256595 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 15:56:36 +0100 Subject: [PATCH 36/59] docs: fix ldap related sentence Co-authored-by: Malte Sander --- docs/modules/ROOT/pages/usage.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 43bf0db9..3df52ade 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -324,7 +324,7 @@ Reference the AuthenticationClass in your DruidCluster resource: include::example$code/druid-ldap-authentication.yaml[tag=druid] ---- -You can see the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to see a complete example of how to set LDAP authentication for another Stackable operator. You can also consult the xref:home:reference:authenticationclass.adoc[] reference, or +Check out the xref:home:tutorials:authentication_with_openldap.adoc[] tutorial to see a complete example of how to set LDAP authentication for another Stackable operator. You can also consult the xref:home:reference:authenticationclass.adoc[] reference, or https://github.com/stackabletech/druid-operator/tree/main/tests/templates/kuttl/ldap-authentication[the LDAP test] suite. ==== Current Limitations and Upcoming Work From 2b8511672cc91261a3139c03bca0a731de596e96 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 16:35:56 +0100 Subject: [PATCH 37/59] fix: suppress error output if cp fails as tolerated Co-authored-by: Malte Sander --- rust/crd/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index ea3a9d23..e04cb243 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -280,7 +280,7 @@ impl DruidRole { // copy hdfs config to RW_CONFIG_DIRECTORY folder (if available) shell_cmd.push(format!( - "cp -RL {hdfs_conf}/* {rw_conf} || :", // NOTE: the OR part is here because the command is not applicable sometimes, and would stop everything else from executing + "cp -RL {hdfs_conf}/* {rw_conf} 2>/dev/null || :", // NOTE: the OR part is here because the command is not applicable sometimes, and would stop everything else from executing hdfs_conf = HDFS_CONFIG_DIRECTORY, rw_conf = RW_CONFIG_DIRECTORY, )); From 6da5b45ff10e2026d7fd8b90ce0563cac86df323 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 16:38:06 +0100 Subject: [PATCH 38/59] fix: remove unnecessary async and Error type --- rust/crd/src/security.rs | 8 ++++---- rust/operator-binary/src/druid_controller.rs | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/rust/crd/src/security.rs b/rust/crd/src/security.rs index c3b20cbd..dc6ca4fb 100644 --- a/rust/crd/src/security.rs +++ b/rust/crd/src/security.rs @@ -93,11 +93,11 @@ impl DruidTlsSecurity { /// Create a `DruidTlsSecurity` struct from the Druid custom resource and resolve /// all provided `AuthenticationClass` references. - pub async fn new_from_druid_cluster( + pub fn new_from_druid_cluster( druid: &DruidCluster, resolved_authentication_classes: ResolvedAuthenticationClasses, - ) -> Result { - Ok(DruidTlsSecurity { + ) -> Self { + DruidTlsSecurity { resolved_authentication_classes, server_and_internal_secret_class: druid .spec @@ -105,7 +105,7 @@ impl DruidTlsSecurity { .tls .as_ref() .and_then(|tls| tls.server_and_internal_secret_class.clone()), - }) + } } /// Check if TLS encryption is enabled. This could be due to: diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 5d392de7..14886117 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -274,9 +274,7 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< .context(FailedToInitializeSecurityContextSnafu)?; let druid_tls_security = - DruidTlsSecurity::new_from_druid_cluster(&druid, resolved_authentication_classes.clone()) - .await - .context(FailedToInitializeSecurityContextSnafu)?; + DruidTlsSecurity::new_from_druid_cluster(&druid, resolved_authentication_classes.clone()); let druid_ldap_settings = DruidLdapSettings::new_from(resolved_authentication_classes); From a24863ff464d068e22183bc81da40af0c7efa51b Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 16:41:04 +0100 Subject: [PATCH 39/59] style: simplify the use blocks --- rust/operator-binary/src/druid_controller.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 14886117..612a1cec 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -33,13 +33,14 @@ use stackable_operator::{ cluster_resources::ClusterResources, commons::{ opa::OpaApiVersion, + product_image_selection::ResolvedProductImage, s3::{S3AccessStyle, S3ConnectionSpec}, tls::{CaCert, TlsVerification}, }, k8s_openapi::{ api::{ apps::v1::{StatefulSet, StatefulSetSpec}, - core::v1::{ConfigMap, EnvVar, Service, ServiceSpec}, + core::v1::{ConfigMap, EnvVar, Service, ServiceSpec, Volume}, }, apimachinery::pkg::apis::meta::v1::LabelSelector, }, @@ -54,9 +55,6 @@ use stackable_operator::{ product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config}, role_utils::RoleGroupRef, }; -use stackable_operator::{ - commons::product_image_selection::ResolvedProductImage, k8s_openapi::api::core::v1::Volume, -}; use std::{ collections::{BTreeMap, HashMap}, ops::Deref, From 235911d6aa918bb591f8f44a4870c758202b01b6 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 16:54:19 +0100 Subject: [PATCH 40/59] fix: one more borrow, one less clone needed --- rust/crd/src/ldap.rs | 2 +- rust/operator-binary/src/druid_controller.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 35308611..50f345ca 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -12,7 +12,7 @@ pub struct DruidLdapSettings { impl DruidLdapSettings { pub fn new_from( - resolved_authentication_config: ResolvedAuthenticationClasses, + resolved_authentication_config: &ResolvedAuthenticationClasses, ) -> Option { if let Some(authentication_class) = resolved_authentication_config.get_ldap_authentication_class() diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 612a1cec..c13374c8 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -271,10 +271,10 @@ pub async fn reconcile_druid(druid: Arc, ctx: Arc) -> Result< .await .context(FailedToInitializeSecurityContextSnafu)?; - let druid_tls_security = - DruidTlsSecurity::new_from_druid_cluster(&druid, resolved_authentication_classes.clone()); + let druid_ldap_settings = DruidLdapSettings::new_from(&resolved_authentication_classes); - let druid_ldap_settings = DruidLdapSettings::new_from(resolved_authentication_classes); + let druid_tls_security = + DruidTlsSecurity::new_from_druid_cluster(&druid, resolved_authentication_classes); // False positive, auto-deref breaks type inference #[allow(clippy::explicit_auto_deref)] From ceb8110e5497768b799e7699977c3394f5764cb3 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 23 Jan 2023 17:08:35 +0100 Subject: [PATCH 41/59] test: reduce auth check timeout --- tests/templates/kuttl/ldap-authentication/07-assert.yaml | 2 +- tests/templates/kuttl/ldap-authentication/07-authcheck.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/templates/kuttl/ldap-authentication/07-assert.yaml b/tests/templates/kuttl/ldap-authentication/07-assert.yaml index a3cc6c3f..48cc7f14 100644 --- a/tests/templates/kuttl/ldap-authentication/07-assert.yaml +++ b/tests/templates/kuttl/ldap-authentication/07-assert.yaml @@ -3,4 +3,4 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert commands: - script: kubectl exec -n $NAMESPACE checks-0 -- python /tmp/authcheck.py -timeout: 600 +timeout: 60 diff --git a/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml b/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml index 719f96a9..58d7d1bc 100644 --- a/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml +++ b/tests/templates/kuttl/ldap-authentication/07-authcheck.yaml @@ -1,6 +1,6 @@ --- apiVersion: kuttl.dev/v1beta1 kind: TestStep -timeout: 600 +timeout: 60 commands: - script: kubectl cp -n $NAMESPACE ./authcheck.py checks-0:/tmp From 94844b71b77443b529a81e65e38fe41d7ec101ac Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 09:45:11 +0100 Subject: [PATCH 42/59] chore: bump dependency versions --- Cargo.lock | 366 +++++++++++++++----------------- rust/crd/Cargo.toml | 2 +- rust/operator-binary/Cargo.toml | 4 +- 3 files changed, 174 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29520695..c079f3f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464b3811b747f8f7ebc8849c9c728c39f6ac98a055edad93baf9eb330e3f8f9d" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" dependencies = [ "cfg-if", "getrandom", @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -34,15 +34,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" dependencies = [ "proc-macro2", "quote", @@ -72,6 +72,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "bit-set" version = "0.5.3" @@ -95,9 +101,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "built" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f346b6890a0dfa7266974910e7df2d5088120dd54721b9b0e5aae1ae5e05715" +checksum = "5b9c056b9ed43aee5e064b683aa1ec783e19c6acec7559e3ae931b7490472fbe" dependencies = [ "cargo-lock", "chrono", @@ -106,9 +112,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -118,15 +124,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cargo-lock" -version = "7.1.0" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c408da54db4c50d4693f7e649c299bc9de9c23ead86249e5368830bb32a734b" +checksum = "031718ddb8f78aa5def78a09e90defe30151d1f6c672f937af4dd916429ed996" dependencies = [ "semver", "serde", @@ -136,9 +142,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.74" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] @@ -156,20 +162,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", - "js-sys", "num-integer", "num-traits", "serde", - "time", - "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "4.0.29" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" +checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920" dependencies = [ "bitflags", "clap_derive", @@ -182,9 +185,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -195,9 +198,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -250,18 +253,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] [[package]] name = "cxx" -version = "1.0.80" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" +checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" dependencies = [ "cc", "cxxbridge-flags", @@ -271,9 +274,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.80" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" dependencies = [ "cc", "codespan-reporting", @@ -286,15 +289,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.80" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" +checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" [[package]] name = "cxxbridge-macro" -version = "1.0.80" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" +checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" dependencies = [ "proc-macro2", "quote", @@ -389,9 +392,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "either" @@ -633,17 +636,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", - "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", + "wasi", ] [[package]] name = "git2" -version = "0.13.25" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" dependencies = [ "bitflags", "libc", @@ -664,15 +665,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -724,9 +716,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.22" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -817,9 +809,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -848,9 +840,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", "windows-sys", @@ -858,11 +850,11 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "io-lifetimes", "rustix", "windows-sys", @@ -870,9 +862,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "java-properties" @@ -905,12 +897,13 @@ dependencies = [ [[package]] name = "json-patch" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f995a3c8f2bc3dd52a18a583e90f9ec109c047fa1603a853e46bcda14d2e279d" +checksum = "e712e62827c382a77b87f590532febb1f8b2fdbc3eefa1ee37fe7281687075ef" dependencies = [ "serde", "serde_json", + "thiserror", "treediff", ] @@ -927,11 +920,11 @@ dependencies = [ [[package]] name = "k8s-openapi" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9455388f4977de4d0934efa9f7d36296295537d774574113a20f6082de03da" +checksum = "3d1985030683a2bac402cbda61222195de80d3f66b4c87ab56e5fea379bd98c3" dependencies = [ - "base64", + "base64 0.20.0", "bytes", "chrono", "schemars", @@ -942,9 +935,9 @@ dependencies = [ [[package]] name = "kube" -version = "0.76.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf241a3a42bca4a2d1c21f2f34a659655032a7858270c7791ad4433aa8d79cb" +checksum = "53ee2ba94546e32a5aef943e5831c6ac25592ff8dcfa8b2a06e0aaea90c69188" dependencies = [ "k8s-openapi", "kube-client", @@ -955,11 +948,11 @@ dependencies = [ [[package]] name = "kube-client" -version = "0.76.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e442b4e6d55c4b3d0c0c70d79a8865bf17e2c33725f9404bfcb8a29ee002ffe" +checksum = "7c9ca1f597bd48ed26f45f601bf2fa3aaa0933b8d1652d883b8444519b72af4a" dependencies = [ - "base64", + "base64 0.20.0", "bytes", "chrono", "dirs-next", @@ -990,9 +983,9 @@ dependencies = [ [[package]] name = "kube-core" -version = "0.76.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca2e1b1528287ba61602bbd17d0aa717fbb4d0fb257f4fa3a5fa884116ef778" +checksum = "61f2c6d1a2d1584859499eb05a41c5a44713818041621fa7515cfdbdf4769ea7" dependencies = [ "chrono", "form_urlencoded", @@ -1008,9 +1001,9 @@ dependencies = [ [[package]] name = "kube-derive" -version = "0.76.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1af50996adb7e1251960d278859772fa30df99879dc154d792e36832209637cb" +checksum = "3e1dfe288fd87029f87c5713ddf585a4221e1b5be8f8c7c02ba28f5211f2a6d7" dependencies = [ "darling", "proc-macro2", @@ -1021,9 +1014,9 @@ dependencies = [ [[package]] name = "kube-runtime" -version = "0.76.0" +version = "0.78.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9b312c38884a3f41d67e2f7580824b6f45d360b98497325b5630664b3a359d" +checksum = "7995aaf15656bf3694cd455578d59119acc3ca55f71e9a1b579a34a47d17ecce" dependencies = [ "ahash", "backoff", @@ -1051,15 +1044,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libgit2-sys" -version = "0.12.26+1.3.0" +version = "0.14.2+1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" dependencies = [ "cc", "libc", @@ -1081,9 +1074,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] @@ -1151,7 +1144,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys", ] @@ -1186,25 +1179,25 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ "bitflags", "cfg-if", @@ -1228,9 +1221,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -1333,9 +1326,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.1" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "overload" @@ -1355,9 +1348,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", @@ -1368,11 +1361,11 @@ dependencies = [ [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64", + "base64 0.13.1", ] [[package]] @@ -1421,9 +1414,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-error" @@ -1451,9 +1444,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -1476,9 +1469,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -1535,9 +1528,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -1555,9 +1548,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rstest" @@ -1596,9 +1589,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" dependencies = [ "bitflags", "errno", @@ -1610,15 +1603,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "schemars" @@ -1652,9 +1645,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "secrecy" @@ -1668,18 +1661,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -1696,9 +1689,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -1742,9 +1735,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.14" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" +checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567" dependencies = [ "indexmap", "itoa", @@ -1788,9 +1781,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snafu" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ba99b054b22972ee794cf04e5ef572da1229e33b65f3c57abbff0525a454" +checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" dependencies = [ "doc-comment", "snafu-derive", @@ -1798,9 +1791,9 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e79cdebbabaebb06a9bdbaedc7f159b410461f63611d4d0e3fb0fab8fed850" +checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" dependencies = [ "heck", "proc-macro2", @@ -1851,7 +1844,7 @@ dependencies = [ "semver", "serde", "serde_json", - "serde_yaml 0.9.14", + "serde_yaml 0.9.17", "snafu", "stackable-druid-crd", "stackable-operator", @@ -1862,8 +1855,8 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.30.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.30.1#34cc76200b6432f7b143ca1350e665ddb169df88" +version = "0.31.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.31.0#db959dbc83628be48c8e0706182e55f027f7b678" dependencies = [ "chrono", "clap", @@ -1883,7 +1876,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_yaml 0.9.14", + "serde_yaml 0.9.17", "snafu", "stackable-operator-derive", "strum", @@ -1896,8 +1889,8 @@ dependencies = [ [[package]] name = "stackable-operator-derive" -version = "0.30.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.30.1#34cc76200b6432f7b143ca1350e665ddb169df88" +version = "0.31.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.31.0#db959dbc83628be48c8e0706182e55f027f7b678" dependencies = [ "darling", "proc-macro2", @@ -1946,27 +1939,27 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -2004,17 +1997,6 @@ dependencies = [ "threadpool", ] -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2032,9 +2014,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.24.1" +version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg", "bytes", @@ -2062,9 +2044,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -2111,9 +2093,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] @@ -2137,11 +2119,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "base64", + "base64 0.13.1", "bitflags", "bytes", "futures-core", @@ -2246,30 +2228,30 @@ dependencies = [ [[package]] name = "treediff" -version = "3.0.2" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" +checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" dependencies = [ "serde_json", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" @@ -2294,9 +2276,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" [[package]] name = "url" @@ -2337,12 +2319,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2451,45 +2427,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "xml-rs" diff --git a/rust/crd/Cargo.toml b/rust/crd/Cargo.toml index ecce9756..3fa24a49 100644 --- a/rust/crd/Cargo.toml +++ b/rust/crd/Cargo.toml @@ -9,7 +9,7 @@ version = "0.9.0-nightly" publish = false [dependencies] -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.30.1" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.31.0" } semver = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index cad4f076..6eeed2d1 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -9,7 +9,7 @@ version = "0.9.0-nightly" publish = false [dependencies] -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.30.1" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.31.0" } stackable-druid-crd = { path = "../crd" } anyhow = "1.0" clap = "4.0" @@ -29,7 +29,7 @@ tracing = "0.1" [build-dependencies] built = { version = "0.5", features = ["chrono", "git2"] } -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.30.1" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.31.0" } stackable-druid-crd = { path = "../crd" } [dev-dependencies] From cde2691685bd323c6f660c150a74b2c9848a47c4 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 10:41:55 +0100 Subject: [PATCH 43/59] refactor: use operrator-rs helper function for ldap volumes and mounts --- rust/operator-binary/src/druid_controller.rs | 39 ++++++-------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index c13374c8..13d2eb59 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -40,7 +40,7 @@ use stackable_operator::{ k8s_openapi::{ api::{ apps::v1::{StatefulSet, StatefulSetSpec}, - core::v1::{ConfigMap, EnvVar, Service, ServiceSpec, Volume}, + core::v1::{ConfigMap, EnvVar, Service, ServiceSpec}, }, apimachinery::pkg::apis::meta::v1::LabelSelector, }, @@ -647,15 +647,18 @@ fn build_rolegroup_statefulset( let mut pb = PodBuilder::new(); pb.node_selector_opt(druid.node_selector(rolegroup_ref)); - let (ldap_auth_mounts, ldap_auth_cmd) = - get_ldap_secret_volume_and_volume_mounts_and_commands(ldap_settings); + if let Some(ldap_settings) = ldap_settings { + ldap_settings + .ldap + .add_volumes_and_mounts(&mut pb, vec![&mut cb_druid]); + } + let ldap_auth_cmd = get_ldap_secret_placeholder_replacement_commands(ldap_settings); // volume and volume mounts druid_tls_security .add_tls_volume_and_volume_mounts(&mut cb_prepare, &mut cb_druid, &mut pb) .context(FailedToInitializeSecurityContextSnafu)?; add_s3_volume_and_volume_mounts(s3_conn, &mut cb_druid, &mut pb)?; - add_ldap_secret_volume_mounts(ldap_auth_mounts, &mut cb_druid, &mut pb); add_config_volume_and_volume_mounts(rolegroup_ref, &mut cb_druid, &mut pb); add_hdfs_cm_volume_and_volume_mounts( &druid.spec.cluster_config.deep_storage, @@ -775,23 +778,14 @@ fn add_hdfs_cm_volume_and_volume_mounts( } } -fn get_ldap_secret_volume_and_volume_mounts_and_commands( +fn get_ldap_secret_placeholder_replacement_commands( ldap_settings: &Option, -) -> (BTreeMap, Vec) { - let mut volumes = BTreeMap::new(); +) -> Vec { let mut commands = Vec::new(); if let Some(ldap_settings) = ldap_settings { if let Some(credentials) = &ldap_settings.ldap.bind_credentials { - let volume_name = credentials.secret_class.clone(); - let secret_volume = VolumeBuilder::new(&volume_name) - .ephemeral(SecretOperatorVolumeSourceBuilder::new(volume_name.clone()).build()) - .build(); - - volumes.insert( - volume_name.clone(), - (format!("/stackable/secrets/{volume_name}"), secret_volume), - ); + let volume_name = credentials.secret_class.clone(); // TODO: get another way? let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/user)"); let ldap_bind_password = format!("$(cat /stackable/secrets/{volume_name}/password)"); @@ -812,18 +806,7 @@ fn get_ldap_secret_volume_and_volume_mounts_and_commands( } } - (volumes, commands) -} - -fn add_ldap_secret_volume_mounts( - ldap_auth_volumes: BTreeMap, - cb_druid: &mut ContainerBuilder, - pb: &mut PodBuilder, -) { - for (name, (path, volume)) in ldap_auth_volumes { - cb_druid.add_volume_mount(name, path); - pb.add_volume(volume.clone()); - } + commands } fn add_config_volume_and_volume_mounts( From 2d41b8c05dd2467f3915414f28304f657f67e0ae Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 11:51:36 +0100 Subject: [PATCH 44/59] docs: remove unneeded comment and README --- rust/crd/src/ldap.rs | 2 +- tests/templates/kuttl/ldap-authentication/README.md | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 tests/templates/kuttl/ldap-authentication/README.md diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 50f345ca..9d3d42bb 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -123,7 +123,7 @@ impl DruidLdapSettings { ); config.insert( "druid.auth.authorizer.LdapAuthorizer.type".to_string(), - Some(r#"allowAll"#.to_string()), // TODO: Respect opa setting + Some(r#"allowAll"#.to_string()), ); } diff --git a/tests/templates/kuttl/ldap-authentication/README.md b/tests/templates/kuttl/ldap-authentication/README.md deleted file mode 100644 index 2df2377d..00000000 --- a/tests/templates/kuttl/ldap-authentication/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# LDAP Authenticator Test - -This test sets an LDAP user called 'alice'. - -See `authcheck.py` for examples of authorized access. From aceab12704df369c8afe6420659e734beb9d60bf Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 11:52:03 +0100 Subject: [PATCH 45/59] refactor: replace placeholder strings with constants --- rust/crd/src/ldap.rs | 13 +++++++++---- rust/operator-binary/src/druid_controller.rs | 11 +++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 9d3d42bb..df701d62 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -10,6 +10,11 @@ pub struct DruidLdapSettings { pub ldap: LdapAuthenticationProvider, } +pub const PLACEHOLDER_INTERNAL_CLIENT_PASSWORD: &str = + "xxx_druid_system_internal_client_password_xxx"; +pub const PLACEHOLDER_LDAP_BIND_PASSWORD: &str = "xxx_ldap_bind_password_xxx"; +pub const PLACEHOLDER_LDAP_BIND_USER: &str = "xxx_ldap_bind_user_xxx"; + impl DruidLdapSettings { pub fn new_from( resolved_authentication_config: &ResolvedAuthenticationClasses, @@ -42,7 +47,7 @@ impl DruidLdapSettings { config.insert( format!("{PREFIX}.initialInternalClientPassword"), - Some("xxx_druid_system_internal_client_password_xxx".to_string()), + Some(PLACEHOLDER_INTERNAL_CLIENT_PASSWORD.to_string()), ); config.insert( format!("{PREFIX}.authorizerName"), @@ -69,11 +74,11 @@ impl DruidLdapSettings { ); config.insert( format!("{PREFIX}.credentialsValidator.bindUser"), - Some("xxx_ldap_bind_user_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup + Some(PLACEHOLDER_LDAP_BIND_USER.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); config.insert( format!("{PREFIX}.credentialsValidator.bindPassword"), - Some("xxx_ldap_bind_password_xxx".to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup + Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); config.insert( format!("{PREFIX}.credentialsValidator.baseDn"), @@ -104,7 +109,7 @@ impl DruidLdapSettings { ); config.insert( "druid.escalator.internalClientPassword".to_string(), - Some("xxx_druid_system_internal_client_password_xxx".to_string()), + Some(PLACEHOLDER_INTERNAL_CLIENT_PASSWORD.to_string()), ); config.insert( "druid.escalator.authorizerName".to_string(), diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 13d2eb59..a9334ea1 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -14,7 +14,10 @@ use snafu::{OptionExt, ResultExt, Snafu}; use stackable_druid_crd::{ authorization::DruidAuthorization, build_string_list, - ldap::DruidLdapSettings, + ldap::{ + DruidLdapSettings, PLACEHOLDER_INTERNAL_CLIENT_PASSWORD, PLACEHOLDER_LDAP_BIND_PASSWORD, + PLACEHOLDER_LDAP_BIND_USER, + }, security::{resolve_authentication_classes, DruidTlsSecurity}, DeepStorageSpec, DruidCluster, DruidRole, APP_NAME, AUTH_AUTHORIZER_OPA_URI, CERTS_DIR, CREDENTIALS_SECRET_PROPERTY, DRUID_CONFIG_DIRECTORY, DS_BUCKET, EXTENSIONS_LOADLIST, @@ -795,13 +798,13 @@ fn get_ldap_secret_placeholder_replacement_commands( commands .push(r#"echo "Replacing LDAP placeholders with their proper values in {RUNTIME_PROPERTIES_FILE}""#.to_string()); commands.push(format!( - r#"sed "s/xxx_ldap_bind_user_xxx/{ldap_bind_user}/g" -i {runtime_properties_file}"# + r#"sed "s/{PLACEHOLDER_LDAP_BIND_USER}/{ldap_bind_user}/g" -i {runtime_properties_file}"# )); commands.push(format!( - r#"sed "s/xxx_ldap_bind_password_xxx/{ldap_bind_password}/g" -i {runtime_properties_file}"# + r#"sed "s/{PLACEHOLDER_LDAP_BIND_PASSWORD}/{ldap_bind_password}/g" -i {runtime_properties_file}"# )); commands.push(format!( - r#"sed "s|xxx_druid_system_internal_client_password_xxx|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string + r#"sed "s|{PLACEHOLDER_INTERNAL_CLIENT_PASSWORD}|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string )); } } From 8ec425822973645bf619488eac930254bac41aaa Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 13:05:35 +0100 Subject: [PATCH 46/59] test: fix wrong hdfs operator version --- tests/test-definition.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index fd5c8390..ddf37acf 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -19,10 +19,10 @@ dimensions: - 0.45.0-stackable0.3.0 - name: hadoop values: - - 3.3.4-stackable0.2.0 + - 3.3.4-stackable0.3.0 - name: hadoop-latest values: - - 3.3.4-stackable0.2.0 + - 3.3.4-stackable0.3.0 - name: s3-use-tls values: - "true" From e4edbce92fbe811f9709241a94412a7a3c5f230c Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 13:07:58 +0100 Subject: [PATCH 47/59] docs: add dependency bump line to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f34fe4a..72433d43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. - Do not run init container as root anymore and avoid chmod and chown ([#353]) - Fixed role group node selector ([#362]) - Bitnami Helm chart 12.1.5 for kuttl tests. ([#363]) +- Upgrade to `operator-rs` `0.31.0` ([#374]) ### Removed From 464f43735ad299162317fd31c3d5afdbd3f57395 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 13:19:36 +0100 Subject: [PATCH 48/59] chore: add regenerated helm yaml --- deploy/helm/druid-operator/crds/crds.yaml | 1056 +-------------------- 1 file changed, 32 insertions(+), 1024 deletions(-) diff --git a/deploy/helm/druid-operator/crds/crds.yaml b/deploy/helm/druid-operator/crds/crds.yaml index fcb6fa07..7ee8df84 100644 --- a/deploy/helm/druid-operator/crds/crds.yaml +++ b/deploy/helm/druid-operator/crds/crds.yaml @@ -44,111 +44,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -190,111 +97,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -717,111 +531,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -863,111 +584,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -1050,111 +678,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -1174,38 +709,7 @@ spec: capacity: null properties: capacity: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string medium: @@ -1253,111 +757,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -1377,38 +788,7 @@ spec: capacity: null properties: capacity: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string medium: @@ -1540,111 +920,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -1686,111 +973,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -1873,111 +1067,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: @@ -2019,111 +1120,18 @@ spec: max: null properties: max: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string min: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string type: object memory: properties: limit: - description: |- - Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors. - - The serialization format is: - - ::= - (Note that may be empty, from the "" case in .) - ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= "+" | "-" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei - (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) - ::= m | "" | k | M | G | T | P | E - (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) - ::= "e" | "E" - - No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities. - - When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized. - - Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that: - a. No precision is lost - b. No fractional digits will be emitted - c. The exponent (or suffix) is as large as possible. - The sign will be omitted unless the number is negative. - - Examples: - 1.5 will be serialized as "1500m" - 1.5Gi will be serialized as "1536Mi" - - Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise. - - Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.) - - This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation. + description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." nullable: true type: string runtimeLimits: From d6e15bc0866a2fac4fa0624d1d07071538c604dc Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 13:21:38 +0100 Subject: [PATCH 49/59] test: expand ldap auth check to all roles --- tests/templates/kuttl/ldap-authentication/authcheck.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/templates/kuttl/ldap-authentication/authcheck.py b/tests/templates/kuttl/ldap-authentication/authcheck.py index 592ce51f..2bf1ecce 100755 --- a/tests/templates/kuttl/ldap-authentication/authcheck.py +++ b/tests/templates/kuttl/ldap-authentication/authcheck.py @@ -11,10 +11,10 @@ def main(): druid_ports = { "coordinator": 8081, - # "broker": 8082, - # "middlemanager": 8091, - # "historical": 8083, - # "router": 8888 + "broker": 8082, + "middlemanager": 8091, + "historical": 8083, + "router": 8888 } log_level = 'INFO' logging.basicConfig(level=log_level, format='%(asctime)s %(levelname)s: %(message)s', stream=sys.stdout) From f17cb84319e6502aa7638b8d076cca9daf08c732 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 14:00:13 +0100 Subject: [PATCH 50/59] feat: use helper function for bind secret paths, error on fail --- rust/operator-binary/src/druid_controller.rs | 42 ++++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index a9334ea1..7ce104d2 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -197,6 +197,10 @@ pub enum Error { FailedInternalSecretCreation { source: crate::internal_secret::Error, }, + #[snafu(display( + "failed to access bind credentials although they are required for LDAP to work" + ))] + LdapBindCredentialsAreRequired, } type Result = std::result::Result; @@ -655,7 +659,7 @@ fn build_rolegroup_statefulset( .ldap .add_volumes_and_mounts(&mut pb, vec![&mut cb_druid]); } - let ldap_auth_cmd = get_ldap_secret_placeholder_replacement_commands(ldap_settings); + let ldap_auth_cmd = get_ldap_secret_placeholder_replacement_commands(ldap_settings)?; // volume and volume mounts druid_tls_security @@ -783,33 +787,37 @@ fn add_hdfs_cm_volume_and_volume_mounts( fn get_ldap_secret_placeholder_replacement_commands( ldap_settings: &Option, -) -> Vec { +) -> Result, Error> { let mut commands = Vec::new(); if let Some(ldap_settings) = ldap_settings { - if let Some(credentials) = &ldap_settings.ldap.bind_credentials { - let volume_name = credentials.secret_class.clone(); // TODO: get another way? + // having ldap settings, but no bind credentials does not seem to sit well with how druid approaches LDAP auth + // see https://www.bookstack.cn/read/apache-druid-0.21.0-en/50dcbe5ee010849f.md#properties-for-ldap-user-authentication + // search for "credentialsValidator.bindUser" - it is required + // thus the error if we don't manage to construct mount paths below + let (ldap_bind_user_path, ldap_bind_password_path) = ldap_settings + .ldap + .bind_credentials_mount_paths() + .context(LdapBindCredentialsAreRequiredSnafu)?; - let ldap_bind_user = format!("$(cat /stackable/secrets/{volume_name}/user)"); - let ldap_bind_password = format!("$(cat /stackable/secrets/{volume_name}/password)"); - let internal_client_password = format!("$(echo ${ENV_INTERNAL_SECRET})"); + let ldap_bind_user = format!("$(cat {ldap_bind_user_path}"); + let ldap_bind_password = format!("$(cat {ldap_bind_password_path}"); + let internal_client_password = format!("$(echo ${ENV_INTERNAL_SECRET})"); - let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); - commands + let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); + commands .push(r#"echo "Replacing LDAP placeholders with their proper values in {RUNTIME_PROPERTIES_FILE}""#.to_string()); - commands.push(format!( + commands.push(format!( r#"sed "s/{PLACEHOLDER_LDAP_BIND_USER}/{ldap_bind_user}/g" -i {runtime_properties_file}"# )); - commands.push(format!( + commands.push(format!( r#"sed "s/{PLACEHOLDER_LDAP_BIND_PASSWORD}/{ldap_bind_password}/g" -i {runtime_properties_file}"# )); - commands.push(format!( - r#"sed "s|{PLACEHOLDER_INTERNAL_CLIENT_PASSWORD}|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string - )); - } + commands.push(format!( + r#"sed "s|{PLACEHOLDER_INTERNAL_CLIENT_PASSWORD}|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string + )); } - - commands + Ok(commands) } fn add_config_volume_and_volume_mounts( From d2524d8202969ef315860309c7b4e75055a1119f Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 16:48:27 +0100 Subject: [PATCH 51/59] fix: add missing braces --- rust/operator-binary/src/druid_controller.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 7ce104d2..fb514a3b 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -800,8 +800,8 @@ fn get_ldap_secret_placeholder_replacement_commands( .bind_credentials_mount_paths() .context(LdapBindCredentialsAreRequiredSnafu)?; - let ldap_bind_user = format!("$(cat {ldap_bind_user_path}"); - let ldap_bind_password = format!("$(cat {ldap_bind_password_path}"); + let ldap_bind_user = format!("$(cat {ldap_bind_user_path})"); + let ldap_bind_password = format!("$(cat {ldap_bind_password_path})"); let internal_client_password = format!("$(echo ${ENV_INTERNAL_SECRET})"); let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); From 6ba398dfe35a19f566aaf4449a03ab3495febe8a Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 16:49:08 +0100 Subject: [PATCH 52/59] test: add non-bind option to LDAP test case --- ...{03-install-openldap.yaml => 03-install-openldap.yaml.j2} | 4 ++++ .../kuttl/ldap-authentication/05-install-druid.yaml.j2 | 2 ++ tests/test-definition.yaml | 5 +++++ 3 files changed, 11 insertions(+) rename tests/templates/kuttl/ldap-authentication/{03-install-openldap.yaml => 03-install-openldap.yaml.j2} (91%) diff --git a/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml b/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml.j2 similarity index 91% rename from tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml rename to tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml.j2 index b5898764..151f6aeb 100644 --- a/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml +++ b/tests/templates/kuttl/ldap-authentication/03-install-openldap.yaml.j2 @@ -32,6 +32,10 @@ spec: value: /tls/tls.key - name: LDAP_TLS_CA_FILE value: /tls/ca.crt +{% if test_scenario['values']['ldap-no-bind-credentials'] == 'true' %} + - name: LDAP_ALLOW_ANON_BINDING + value: "yes" +{% endif %} ports: - name: ldap containerPort: 1389 diff --git a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 index b0745fd4..8d4ce06c 100644 --- a/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 +++ b/tests/templates/kuttl/ldap-authentication/05-install-druid.yaml.j2 @@ -25,6 +25,7 @@ spec: caCert: secretClass: openldap-tls {% endif %} +{% if test_scenario['values']['ldap-no-bind-credentials'] == 'false' %} bindCredentials: secretClass: druid-ldap-secret --- @@ -47,6 +48,7 @@ metadata: stringData: user: cn=admin,dc=example,dc=org password: admin +{% endif %} --- apiVersion: druid.stackable.tech/v1alpha1 kind: DruidCluster diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index ddf37acf..29685ae2 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -36,6 +36,10 @@ dimensions: values: # - "true" # disabled, until ldaps is actually supported - "false" + - name: ldap-no-bind-credentials + values: + # - "true" + - "false" tests: - name: smoke dimensions: @@ -89,3 +93,4 @@ tests: - opa - hadoop-latest - ldap-use-tls + - ldap-no-bind-credentials From 8559317e35b61d81fe8f081b91ea538d44dd8251 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Tue, 24 Jan 2023 17:53:23 +0100 Subject: [PATCH 53/59] wip: towards ldap without bind credentials spoiler: it does not seem to be allowed by druid --- rust/crd/src/ldap.rs | 30 +++++++++++++----- rust/operator-binary/src/druid_controller.rs | 32 +++++++++----------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index df701d62..4263e118 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -72,14 +72,28 @@ impl DruidLdapSettings { format!("{PREFIX}.credentialsValidator.url"), Some(self.credentials_validator_url()), ); - config.insert( - format!("{PREFIX}.credentialsValidator.bindUser"), - Some(PLACEHOLDER_LDAP_BIND_USER.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup - ); - config.insert( - format!("{PREFIX}.credentialsValidator.bindPassword"), - Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup - ); + + // we only add these lines if bind credentials are configured + if self.ldap.bind_credentials.is_some() { + config.insert( + format!("{PREFIX}.credentialsValidator.bindUser"), + Some(PLACEHOLDER_LDAP_BIND_USER.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup + ); + config.insert( + format!("{PREFIX}.credentialsValidator.bindPassword"), + Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup + ); + } /* else { + // set empty pw and user, as leaving them blank leads to null pointer exceptions + config.insert( + format!("{PREFIX}.credentialsValidator.bindUser"), + Some("".to_string()), + ); + config.insert( + format!("{PREFIX}.credentialsValidator.bindPassword"), + Some("".to_string()), + ); + }*/ config.insert( format!("{PREFIX}.credentialsValidator.baseDn"), Some(self.ldap.search_base.to_string()), diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index fb514a3b..9cae38fa 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -791,31 +791,29 @@ fn get_ldap_secret_placeholder_replacement_commands( let mut commands = Vec::new(); if let Some(ldap_settings) = ldap_settings { - // having ldap settings, but no bind credentials does not seem to sit well with how druid approaches LDAP auth - // see https://www.bookstack.cn/read/apache-druid-0.21.0-en/50dcbe5ee010849f.md#properties-for-ldap-user-authentication - // search for "credentialsValidator.bindUser" - it is required - // thus the error if we don't manage to construct mount paths below - let (ldap_bind_user_path, ldap_bind_password_path) = ldap_settings - .ldap - .bind_credentials_mount_paths() - .context(LdapBindCredentialsAreRequiredSnafu)?; + let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); - let ldap_bind_user = format!("$(cat {ldap_bind_user_path})"); - let ldap_bind_password = format!("$(cat {ldap_bind_password_path})"); let internal_client_password = format!("$(echo ${ENV_INTERNAL_SECRET})"); - let runtime_properties_file: String = format!("{RW_CONFIG_DIRECTORY}/{RUNTIME_PROPS}"); commands .push(r#"echo "Replacing LDAP placeholders with their proper values in {RUNTIME_PROPERTIES_FILE}""#.to_string()); - commands.push(format!( - r#"sed "s/{PLACEHOLDER_LDAP_BIND_USER}/{ldap_bind_user}/g" -i {runtime_properties_file}"# - )); - commands.push(format!( - r#"sed "s/{PLACEHOLDER_LDAP_BIND_PASSWORD}/{ldap_bind_password}/g" -i {runtime_properties_file}"# - )); commands.push(format!( r#"sed "s|{PLACEHOLDER_INTERNAL_CLIENT_PASSWORD}|{internal_client_password}|g" -i {runtime_properties_file}"# // using another delimeter (|) here because of base64 string )); + + if let Some((ldap_bind_user_path, ldap_bind_password_path)) = + ldap_settings.ldap.bind_credentials_mount_paths() + { + let ldap_bind_user = format!("$(cat {ldap_bind_user_path})"); + let ldap_bind_password = format!("$(cat {ldap_bind_password_path})"); + + commands.push(format!( + r#"sed "s/{PLACEHOLDER_LDAP_BIND_USER}/{ldap_bind_user}/g" -i {runtime_properties_file}"# + )); + commands.push(format!( + r#"sed "s/{PLACEHOLDER_LDAP_BIND_PASSWORD}/{ldap_bind_password}/g" -i {runtime_properties_file}"# + )); + } } Ok(commands) } From cf59deb69405140d76d3084e9cc6f89afa0228a2 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 26 Jan 2023 10:28:41 +0100 Subject: [PATCH 54/59] wip: disable failing test case --- rust/crd/src/ldap.rs | 22 +++++++++++----------- tests/test-definition.yaml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 4263e118..1d9f3cf7 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -83,17 +83,17 @@ impl DruidLdapSettings { format!("{PREFIX}.credentialsValidator.bindPassword"), Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); - } /* else { - // set empty pw and user, as leaving them blank leads to null pointer exceptions - config.insert( - format!("{PREFIX}.credentialsValidator.bindUser"), - Some("".to_string()), - ); - config.insert( - format!("{PREFIX}.credentialsValidator.bindPassword"), - Some("".to_string()), - ); - }*/ + } else { + // uncomment to set an empty password and user. Not-specifying these fields at all leads to null pointer exceptions + config.insert( + format!("{PREFIX}.credentialsValidator.bindUser"), + Some("".to_string()), + ); + config.insert( + format!("{PREFIX}.credentialsValidator.bindPassword"), + Some("".to_string()), + ); + } config.insert( format!("{PREFIX}.credentialsValidator.baseDn"), Some(self.ldap.search_base.to_string()), diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index 29685ae2..3032d9b8 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -38,7 +38,7 @@ dimensions: - "false" - name: ldap-no-bind-credentials values: - # - "true" + # - "true" # disabled, as anonymous ldap usage does not seem to be supported in druid - "false" tests: - name: smoke From 339faad3a509a2bbcc61ab7615c9dbad83150d6d Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Thu, 26 Jan 2023 10:30:32 +0100 Subject: [PATCH 55/59] docs: rework comment --- rust/crd/src/ldap.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 1d9f3cf7..7ed3da84 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -84,7 +84,9 @@ impl DruidLdapSettings { Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); } else { - // uncomment to set an empty password and user. Not-specifying these fields at all leads to null pointer exceptions + // WIP: attempt to use LDAP without bind credentials. + // Setting an empty password and user does not work. + // Not-specifying these fields at all leads to null pointer exceptions. config.insert( format!("{PREFIX}.credentialsValidator.bindUser"), Some("".to_string()), From b4df75c087978f7c1f29771c5ad365fbe660b928 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 30 Jan 2023 13:19:55 +0100 Subject: [PATCH 56/59] docs: migrate and fix changelog --- CHANGELOG.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72433d43..251bd421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,20 @@ All notable changes to this project will be documented in this file. ### Added +- Add support for non-TLS LDAP authentication. ([#374]) + +### Changed + +- Upgrade to `operator-rs` `0.31.0` ([#374]) + +[#374]: https://github.com/stackabletech/druid-operator/pull/374 + +## [23.1.0] - 2023-01-23 + +### Added + - BREAKING: Support for TLS encryption (activated per default -> port changes) and TLS authentication ([#333]) - Use emptyDir for segment cache on historicals ([#342]) -- Add support for non-TLS LDAP authentication. ([#374]) ### Changed @@ -20,7 +31,6 @@ All notable changes to this project will be documented in this file. - Do not run init container as root anymore and avoid chmod and chown ([#353]) - Fixed role group node selector ([#362]) - Bitnami Helm chart 12.1.5 for kuttl tests. ([#363]) -- Upgrade to `operator-rs` `0.31.0` ([#374]) ### Removed @@ -37,7 +47,6 @@ All notable changes to this project will be documented in this file. [#362]: https://github.com/stackabletech/druid-operator/pull/362 [#363]: https://github.com/stackabletech/druid-operator/pull/363 [#366]: https://github.com/stackabletech/druid-operator/pull/366 -[#374]: https://github.com/stackabletech/druid-operator/pull/374 ## [0.8.0] - 2022-11-07 From 2d332f1fc9679dfccb288db9ab1b18ff915f4138 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 30 Jan 2023 13:46:22 +0100 Subject: [PATCH 57/59] docs: add note on non-bind LDAP authentication --- docs/modules/ROOT/pages/usage.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 3df52ade..af72e557 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -333,6 +333,8 @@ At the moment you can either use TLS authentication or LDAP authentication. Both Communication with a TLS-secured LDAP server is currently not implemented, but will be tackled in the future. +Using an LDAP server **without** bind credentials is not supported. This limitation is due to Druid not supporting this scenario. + Authorization is done using the `allowAll` authorizer. Supporting for `memberOf` and OPA authorization are planned. === Authorization with Open Policy Agent (OPA) From 02bd94cf5fc30e133c72aa5c06dd0f034845cb92 Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 30 Jan 2023 13:58:25 +0100 Subject: [PATCH 58/59] docs: add comment to missing non-bind LDAP functionality story --- rust/crd/src/ldap.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 7ed3da84..8f66f304 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -83,19 +83,10 @@ impl DruidLdapSettings { format!("{PREFIX}.credentialsValidator.bindPassword"), Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); - } else { - // WIP: attempt to use LDAP without bind credentials. - // Setting an empty password and user does not work. - // Not-specifying these fields at all leads to null pointer exceptions. - config.insert( - format!("{PREFIX}.credentialsValidator.bindUser"), - Some("".to_string()), - ); - config.insert( - format!("{PREFIX}.credentialsValidator.bindPassword"), - Some("".to_string()), - ); } + // TODO: Connecting to an LDAP server without bind credentials does not seem to be configurable in Druid at the moment + // see https://github.com/stackabletech/druid-operator/issues/383 for future work + config.insert( format!("{PREFIX}.credentialsValidator.baseDn"), Some(self.ldap.search_base.to_string()), From eb9309b19cb00772307e81b1421f60c6f086d77f Mon Sep 17 00:00:00 2001 From: Vladislav Supalov Date: Mon, 30 Jan 2023 14:09:07 +0100 Subject: [PATCH 59/59] fix: return an error when no ldap bind credentials are provided --- rust/crd/src/ldap.rs | 2 -- rust/operator-binary/src/druid_controller.rs | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/rust/crd/src/ldap.rs b/rust/crd/src/ldap.rs index 8f66f304..0a80ddf6 100644 --- a/rust/crd/src/ldap.rs +++ b/rust/crd/src/ldap.rs @@ -84,8 +84,6 @@ impl DruidLdapSettings { Some(PLACEHOLDER_LDAP_BIND_PASSWORD.to_string()), // NOTE: this placeholder will be replaced from a mounted secret on container startup ); } - // TODO: Connecting to an LDAP server without bind credentials does not seem to be configurable in Druid at the moment - // see https://github.com/stackabletech/druid-operator/issues/383 for future work config.insert( format!("{PREFIX}.credentialsValidator.baseDn"), diff --git a/rust/operator-binary/src/druid_controller.rs b/rust/operator-binary/src/druid_controller.rs index 9cae38fa..bed732bc 100644 --- a/rust/operator-binary/src/druid_controller.rs +++ b/rust/operator-binary/src/druid_controller.rs @@ -655,6 +655,13 @@ fn build_rolegroup_statefulset( pb.node_selector_opt(druid.node_selector(rolegroup_ref)); if let Some(ldap_settings) = ldap_settings { + // TODO: Connecting to an LDAP server without bind credentials does not seem to be configurable in Druid at the moment + // see https://github.com/stackabletech/druid-operator/issues/383 for future work. + // Expect bind credentials to be provided for now, and throw return a useful error if there are none. + if ldap_settings.ldap.bind_credentials.is_none() { + return LdapBindCredentialsAreRequiredSnafu.fail(); + } + ldap_settings .ldap .add_volumes_and_mounts(&mut pb, vec![&mut cb_druid]);