Skip to content

Commit 4ac2336

Browse files
lgrateaummoulyPYLochoujulie-garronecmosbach
authored
Review gke (#115)
* Okta (#108) * Start doc on okta * Add web app integration image * Update README.md * Add images for the group and claim * Update README.md * Update README.md * Update README.md * Update README.md * Add images * Update README.md * Add template script * Update README.md * prepare environment * ODM configuration * cosmetic * update * update * rename providers file * Update README.md * Update README.md * Add new images * Update README.md * change provider template * Update README.md * Update README.md * Update README.md * Add interaction diagram * Add credential flow * Update README.md * Update README.md * Add Toc * Update README.md * Update README.md * Update README.md * use last chart and remove post install use last chart 21.3.0 and remove post install not needed anymore * Add predefined password flow Add predefined password flow just to show that's a possibility * some comments on the mapping * use OPENID_PROVIDER linked to an existing provider use the predefined client_credentials provider name providerhttps://github.com/ODMDev/odm-docker-kubernetes/blob/Okta/authentication/Okta/templates/OdmOpenidProviders.json#L4 * Accept env vars as input for OKTA_* * missing OdmOidcProviders.json * Create OdmOidcProviders.json * Delete OdmOpenidProviders.json * Update README.md * Add new screenshot * Update README.md * Removed password auth between services * Add new sections to create an Okta account * Fixed Username format * no message * no message * no message * Added Sign-in redirect URIs * Link updated for ODM 8.11 * Added explanation about OKTA_SERVER_NAME (instead of _URL) and other needed info * OKTA_SERVER_URL (as input) has been renamed to OKTA_SERVER_NAME for coherence with README.md * no message * no message * OKTA_API_SCOPE is now a variable * no message * Moved images outside * no message * no message * Updated with right scope * Mention only "Okta server" * Removed mention of Helm release version * Remove line about 1000 requests limits and added a link to rate limits * no message * New Interaction diagram * Add blabla a bout claims * Add license section * Update README.md * no message * Added "job done" * Add "add person" screenshot * Added comment about logout issue * Clarify ID and Access tokens * Added Mathias suggestion about using a private window * Update README.md * Refactor the okta template section * Update README.md * Update README.md * Update README.md * Add application informations screenshot * use kubectl instead of oc * Fix syntaxe for linux for the sed command * Update README.md * Change Year + Add link to Okta documentation * Add rule designer registration URL + Rule designer configuration * Added endpoints doc URL * Update README.md * Doc review (#102) * Review Okta documentation * Fix Okta images * Fix formatting in Okta doc * Fix typos * Release name have change. Needs to redo the screenshot * Remove localhost:8080 url from the screenshot * Update README.md * Update README.md (#104) proofreading * Update README.md (#106) proofreading * Update README.md (#107) proofreading Co-authored-by: mathias-mouly <[email protected]> Co-authored-by: Pierre-Yves Lochou <[email protected]> Co-authored-by: Pierre-Yves Lochou <[email protected]> Co-authored-by: Julie Garrone <[email protected]> Co-authored-by: cmosbach <[email protected]> * Add Gcloud link * Update links * Update links * Last GKE Review * Update README.md * Update README.md Co-authored-by: mathias-mouly <[email protected]> Co-authored-by: Pierre-Yves Lochou <[email protected]> Co-authored-by: Pierre-Yves Lochou <[email protected]> Co-authored-by: Julie Garrone <[email protected]> Co-authored-by: cmosbach <[email protected]>
1 parent dda3d05 commit 4ac2336

File tree

6 files changed

+78
-60
lines changed

6 files changed

+78
-60
lines changed

platform/azure/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The commands and tools have been tested on macOS and Linux.
2424
First, install the following software on your machine:
2525

2626
- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest)
27-
- [Helm v3](https://github.com/helm/helm/releases)
27+
- [Helm v3](https://helm.sh/docs/intro/install/)
2828

2929
Then [create an Azure account and pay as you go](https://azure.microsoft.com/en-us/pricing/purchase-options/pay-as-you-go/).
3030

platform/eks/README.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,27 @@
33
This project demonstrates how to deploy an IBM® Operational Decision Manager (ODM) clustered topology on the Amazon Elastic Kubernetes Service (EKS) cloud service. This deployment implements Kubernetes and Docker technologies.
44

55
<img src="./images/eks-schema.jpg" alt="Flow" width="2050" height="600" />
6-
The ODM Docker material is available in Passport Advantage. It includes Docker container images and Helm chart descriptors.
6+
7+
The ODM on Kubernetes material is available in [IBM Entitled Registry](https://www.ibm.com/cloud/container-registry) for the Docker images, and the [IBM Helm charts repository](https://github.com/IBM/charts) for the ODM Helm chart.
78

89
## Included components
910
The project uses the following components:
10-
- [IBM Operational Decision Manager](https://www.ibm.com/support/knowledgecenter/en/SSQP76_8.10.x/com.ibm.odm.kube/kc_welcome_odm_kube.html)
11+
- [IBM Operational Decision Manager](https://www.ibm.com/docs/en/odm/8.11.0)
1112
- [Amazon Elastic Kubernetes Service (Amazon EKS)](https://aws.amazon.com/eks/)
12-
- [Amazon Elastic Container Registry (Amazon ECR) ](https://aws.amazon.com/ecr/)
13-
- [Amazon Relational Database Service (Amazon RDS) ](https://aws.amazon.com/rds/)
14-
- [Amazon Application Load Balancer(ALB)](https://aws.amazon.com/elasticloadbalancing/?nc=sn&loc=0)
13+
- [Amazon Elastic Container Registry (Amazon ECR)](https://aws.amazon.com/ecr/)
14+
- [Amazon Relational Database Service (Amazon RDS)](https://aws.amazon.com/rds/)
15+
- [AWS Application Load Balancer (ALB)](https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html)
1516

1617
## Tested environment
17-
The commands and tools have been tested on MacOS and linux.
18+
The commands and tools have been tested on Linux and macOS.
1819

1920
## Prerequisites
2021
First, install the following software on your machine:
2122
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)
22-
* [Helm](https://github.com/helm/helm/releases)
23-
* [Kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
23+
* [Helm v3](https://helm.sh/docs/intro/install/)
24+
* [Kubectl](https://kubernetes.io/docs/tasks/tools/)
2425

25-
Then, create an [AWS Account](https://aws.amazon.com/getting-started/?sc_icontent=awssm-evergreen-getting_started&sc_iplace=2up&trk=ha_awssm-evergreen-getting_started&sc_ichannel=ha&sc_icampaign=evergreen-getting_started)
26+
Then, create an [AWS Account](https://aws.amazon.com/getting-started/).
2627

2728
## Steps to deploy ODM on Kubernetes from Amazon EKS
2829

@@ -36,8 +37,8 @@ Then, create an [AWS Account](https://aws.amazon.com/getting-started/?sc_iconten
3637

3738
For more information, see [Getting started with Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html)
3839

39-
4040
### 1. Prepare your environment (40 min)
41+
4142
#### a. Create an EKS cluster (30 min)
4243

4344
Create an EKS cluster following [this documentation](https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html)

platform/gcloud/README.md

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,35 @@ Without the relevant billing level, some Google Cloud resources will not be crea
4646
## Steps to deploy ODM on Kubernetes from Google GKE
4747

4848
<!-- TOC titleSize:2 tabSpaces:2 depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 skip:0 title:0 charForUnorderedList:* -->
49-
* [Deploying IBM Operational Decision Manager on Google GKE](#deploying-ibm-operational-decision-manager-on-google-gke)
50-
* [Included components](#included-components)
51-
* [Tested environment](#tested-environment)
52-
* [Prerequisites](#prerequisites)
53-
* [Steps to deploy ODM on Kubernetes from Google GKE](#steps-to-deploy-odm-on-kubernetes-from-google-gke)
54-
* [Prepare your GKE instance (30 min)](#prepare-your-gke-instance-30-min)
55-
* [Log into Google Cloud](#log-into-google-cloud)
56-
* [Create a GKE cluster](#create-a-gke-cluster)
57-
* [Set up your environment to this cluster](#set-up-your-environment-to-this-cluster)
58-
* [Create the Google Cloud SQL PostgreSQL instance (10 min)](#create-the-google-cloud-sql-postgresql-instance-10-min)
59-
* [Prepare your environment for the ODM installation](#prepare-your-environment-for-the-odm-installation)
60-
* [Using the IBM Entitled registry with your IBMid (10 min)](#using-the-ibm-entitled-registry-with-your-ibmid-10-min)
61-
* [Create the datasource secrets for Google Cloud SQL PostgreSQL](#create-the-datasource-secrets-for-google-cloud-sql-postgresql)
62-
* [Manage a digital certificate (2 min)](#manage-a-digital-certificate-2-min)
63-
* [Install an ODM Helm release using the GKE loadbalancer (10 min)](#install-an-odm-helm-release-using-the-gke-loadbalancer-10-min)
64-
* [Manage a PV containing the JDBC driver](#manage-a-pv-containing-the-jdbc-driver)
65-
* [Install the ODM release](#install-the-odm-release)
66-
* [Check the topology](#check-the-topology)
67-
* [Check the Ingress and GKE LoadBalancer](#check-the-ingress-and-gke-loadbalancer)
68-
* [Create a Backend Configuration for the Decision Center Service](#create-a-backend-configuration-for-the-decision-center-service)
69-
* [Access ODM services](#access-odm-services)
70-
* [Install the IBM License Service and retrieve license usage](#install-the-ibm-license-service-and-retrieve-license-usage)
71-
* [Create a NGINX Ingress controller](#create-a-nginx-ingress-controller)
72-
* [Install the IBM License Service](#install-the-ibm-license-service)
73-
* [Create the Licensing instance](#create-the-licensing-instance)
74-
* [Retrieving license usage](#retrieving-license-usage)
75-
* [Optional steps](#optional-steps)
76-
* [Troubleshooting](#troubleshooting)
77-
* [License](#license)
49+
- [Deploying IBM Operational Decision Manager on Google GKE](#deploying-ibm-operational-decision-manager-on-google-gke)
50+
- [Included components](#included-components)
51+
- [Tested environment](#tested-environment)
52+
- [Prerequisites](#prerequisites)
53+
- [Steps to deploy ODM on Kubernetes from Google GKE](#steps-to-deploy-odm-on-kubernetes-from-google-gke)
54+
- [Prepare your GKE instance (30 min)](#prepare-your-gke-instance-30-min)
55+
- [Log into Google Cloud](#log-into-google-cloud)
56+
- [Create a GKE cluster](#create-a-gke-cluster)
57+
- [Set up your environment to this cluster](#set-up-your-environment-to-this-cluster)
58+
- [Create the Google Cloud SQL PostgreSQL instance (10 min)](#create-the-google-cloud-sql-postgresql-instance-10-min)
59+
- [Prepare your environment for the ODM installation](#prepare-your-environment-for-the-odm-installation)
60+
- [Using the IBM Entitled registry with your IBMid (10 min)](#using-the-ibm-entitled-registry-with-your-ibmid-10-min)
61+
- [Create the datasource secrets for Google Cloud SQL PostgreSQL](#create-the-datasource-secrets-for-google-cloud-sql-postgresql)
62+
- [Manage a digital certificate (2 min)](#manage-a-digital-certificate-2-min)
63+
- [Install an ODM Helm release using the GKE loadbalancer (10 min)](#install-an-odm-helm-release-using-the-gke-loadbalancer-10-min)
64+
- [Manage a PV containing the JDBC driver](#manage-a-pv-containing-the-jdbc-driver)
65+
- [Install the ODM release](#install-the-odm-release)
66+
- [Check the topology](#check-the-topology)
67+
- [Check the Ingress and GKE LoadBalancer](#check-the-ingress-and-gke-loadbalancer)
68+
- [Create a Backend Configuration for the Decision Center Service](#create-a-backend-configuration-for-the-decision-center-service)
69+
- [Access ODM services](#access-odm-services)
70+
- [Install the IBM License Service and retrieve license usage](#install-the-ibm-license-service-and-retrieve-license-usage)
71+
- [Create a NGINX Ingress controller](#create-a-nginx-ingress-controller)
72+
- [Install the IBM License Service](#install-the-ibm-license-service)
73+
- [Create the Licensing instance](#create-the-licensing-instance)
74+
- [Retrieving license usage](#retrieving-license-usage)
75+
- [Optional steps](#optional-steps)
76+
- [Troubleshooting](#troubleshooting)
77+
- [License](#license)
7878
<!-- /TOC -->
7979

8080
## Prepare your GKE instance (30 min)
@@ -98,7 +98,7 @@ In this article we chose to create a [regional cluster](https://cloud.google.com
9898
Set the project (associated to a billing account):
9999

100100
```
101-
gcloud config set project [PROJECT_NAME]
101+
gcloud config set project [PROJECT_ID]
102102
```
103103

104104
Set the zone:
@@ -110,13 +110,13 @@ gcloud config set compute/zone [ZONE (ex: europe-west1-b)]
110110
Set the region:
111111

112112
```
113-
gcloud config set compute/region [REGION (ex: europe-west1-b)]
113+
gcloud config set compute/region [REGION (ex: europe-west1)]
114114
```
115115

116116
Create a cluster and enable autoscaling. Here, we start with 4 nodes (with 16 max):
117117

118118
```
119-
gcloud container clusters create [CLUSTER_NAME] --num-nodes 4 --enable-autoscaling --min-nodes 1 --max-nodes 16
119+
gcloud container clusters create [CLUSTER_NAME] --num-nodes 6 --enable-autoscaling --min-nodes 1 --max-nodes 16
120120
```
121121

122122
You can also create your cluster from the Google Cloud Platform using the Kubernetes Engine Clusters panel, by clicking on the Create button
@@ -146,11 +146,13 @@ We will use the Google Cloud Console to create this instance:
146146

147147
- Go on the [SQL context](https://console.cloud.google.com/sql) and click on the "CREATE INSTANCE" button
148148
- Choose PostgreSQL
149+
- Instance ID : Chosse a name for your instance.
150+
- Password : <PASSWORD> (Take a note of this password. You need it in the "Create the datasource secrets for Google Cloud SQL PostgreSQL" section)
149151
- Take "PostgreSQL 13" as database version
150152
- Choose a region similar to the cluster. So, the communication is optimal between the database and the ODM instance
151153
- Keep "Multiple zones" for Zonal availability to the highest availability
152154
- Expand "Customize your instance" and Expand "Connections"
153-
- As Public IP is selected by default, click on the "ADD NETWORK" button, put a name and add "0.0.0.0/0" for Network, then click on "DONE"
155+
- As Public IP is selected by default, click on the "ADD NETWORK" button, put a name and add "0.0.0.0/0" for Network, then click on "DONE". It's not recommended to use plublic IP. In production environment, you should use private IP.
154156

155157
When created, you can drill on the SQL instance overview to retrieve needed information to connect to this instance like the IP adress and the connection name:
156158

@@ -204,17 +206,15 @@ NAME CHART VERSION APP VERSION DESCRIPTION
204206
ibmcharts/ibm-odm-prod 21.3.0 8.11.0.0 IBM Operational Decision Manager
205207
```
206208

207-
You can now proceed to the [datasource secret's creation](#create-the-datasource-secrets-for-azure-postgresql).
208-
209209
### Create the datasource secrets for Google Cloud SQL PostgreSQL
210210

211211
The Google Cloud SQL PostgreSQL connection will be done using [Cloud SQL Connector for Java](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#cloud-sql-connector-for-java).
212212

213-
If you don't want to build the driver, you can get the last [driver](https://storage.googleapis.com/cloud-sql-java-connector/) named postgres-socket-factory-X.X.X-jar-with-driver-and-dependencies.jar.
213+
If you don't want to build the driver, you can get the last [driver](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory/releases) named postgres-socket-factory-X.X.X-jar-with-driver-and-dependencies.jar.
214214

215215
We realised the test with the driver version [postgres-socket-factory-1.4.2-jar-with-driver-and-dependencies.jar](https://storage.googleapis.com/cloud-sql-java-connector/v1.4.2/postgres-socket-factory-1.4.2-jar-with-driver-and-dependencies.jar).
216216

217-
Copy the files [datasource-dc.xml.template](datasource-dc.xml.template) and [datasource-ds.xml.template](datasource-ds.xml.template) to your local machine and rename them `datasource-dc.xml` and `datasource-ds.xml`.
217+
Copy the files [datasource-dc.xml](datasource-dc.xml) and [datasource-ds.xml](datasource-ds.xml) to your local machine.
218218

219219
Replace the following placeholders:
220220

@@ -223,7 +223,7 @@ Replace the following placeholders:
223223
- CONNECTION_NAME: The database connection name
224224
- DBNAME: The database name (default is postgres)
225225
- USERNAME: The database username (default is postgres)
226-
- PASSWORD: The database password
226+
- PASSWORD: The database password (PASSWORD enter in the step [Create the Google Cloud SQL PostgreSQL instance](#create-the-datasource-secrets-for-google-cloud-sql-postgresql))
227227

228228
It should be something like in the following extract:
229229

@@ -250,6 +250,7 @@ kubectl create secret generic <customdatasourcesecret> \
250250

251251
1. Generate a self-signed certificate
252252

253+
In this step, you will generate a certificate used by the GKE loadbalancer.
253254
If you do not have a trusted certificate, you can use OpenSSL and other cryptography and certificate management libraries to generate a certificate file and a private key, to define the domain name, and to set the expiration date. The following command creates a self-signed certificate (.crt file) and a private key (.key file) that accepts the domain name *mycompany.com*. The expiration is set to 1000 days:
254255

255256
```
@@ -281,6 +282,7 @@ To workaround this issue, we will use a ReadWriteOnce PV used by an NGINX pod th
281282
Then, we will change the PV permission to ReadOnlyMany before to launch the ODM release in order to be able to install ODM on many nodes.
282283

283284
1. [Enable the SCI FileStore Driver](https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/filestore-csi-driver#console_1).
285+
The cluster will restart. This take several minutes until the cluster is ready.
284286

285287
2. Create the [filestore-example](filestore-example.yaml) storageClass:
286288

@@ -293,18 +295,24 @@ kubectl apply -f filestore-example.yaml
293295
```
294296
kubectl apply -f customdatasource-pvc.yaml
295297
```
298+
This take some time until the PVC is ready. Wait until the PVC is ready before moving to the next step. You can verify the status by using this command line.
299+
300+
```
301+
kubectl get pvc
302+
```
296303

297304
4. Create a [nginx](nginx.yaml) pod using this PVC that will be used only to copy the driver because this container is accessible as root:
298305

299306
```
300307
kubectl apply -f nginx.yaml
301308
```
302309

310+
303311
5. Copy the Google Cloud PostgresSQL driver on the nginx pod:
304312

305313
```
306314
export NGINX_COPY_POD=$(kubectl get pod | grep nginx-driver-copy)
307-
kubectl cp postgres-socket-factory-<X.X.X>-jar-with-driver-and-dependencies.jar $(NGINX_COPY_POD):/usr/share/nginx/html
315+
kubectl cp postgres-socket-factory-<X.X.X>-jar-with-driver-and-dependencies.jar ${NGINX_COPY_POD}:/usr/share/nginx/html
308316
```
309317

310318
6. Change the PV accessmode to ReadOnlyMany; this way, all ODM containers will be able to access the PV as readonly and scheduled on several node:
@@ -314,6 +322,13 @@ export PV_NAME=$(kubectl get pvc customdatasource-pvc -o jsonpath={.spec.volumeN
314322
kubectl patch pv $PV_NAME -p '{"spec":{"accessModes":["ReadOnlyMany"]}}'
315323
```
316324

325+
7. Clean up
326+
327+
You can now delete the nginx pods used to copy the PostgreSQL driver.
328+
```
329+
kubectl delete -f nginx.yaml
330+
```
331+
317332
### Install the ODM release
318333

319334
You can now install the product.
@@ -397,7 +412,7 @@ We just have to manage a configuration to simulate the mycompany.com access.
397412
You can get the EXTERNAL-IP using the command line:
398413

399414
```
400-
kubectl get ingress <release>-odm-ingress -o json | jq -r '.status.loadBalancer.ingress[].ip'
415+
kubectl get ingress <release>-odm-ingress -o jsonpath='{.status.loadBalancer.ingress[].ip}'
401416
```
402417

403418
So, to access ODM services, you have to edit your /etc/hosts file and add the following entry:
@@ -430,6 +445,7 @@ This section explains how to track ODM usage with the IBM License Service.
430445

431446
```
432447
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
448+
helm repo update
433449
```
434450
435451
2. Use Helm to deploy an NGINX Ingress controller:

platform/gcloud/README_NGINX.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ For reference, see the Google Cloud documentation https://cloud.google.com/commu
55

66
<!-- TOC titleSize:2 tabSpaces:2 depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 skip:0 title:1 charForUnorderedList:* -->
77
## Table of Contents
8-
* [Install an ODM Helm release and expose it with a NGINX Ingress controller (15 min)](#install-an-odm-helm-release-and-expose-it-with-a-nginx-ingress-controller-15-min)
9-
* [Create a Kubernetes secret for the TLS certificate](#create-a-kubernetes-secret-for-the-tls-certificate)
10-
* [Install the ODM release](#install-the-odm-release)
11-
* [Edit your /etc/hosts](#edit-your-etchosts)
12-
* [Access the ODM services](#access-the-odm-services)
13-
* [Troubleshooting](#troubleshooting)
14-
* [License](#license)
8+
- [Install an ODM Helm release and expose it with a NGINX Ingress controller (15 min)](#install-an-odm-helm-release-and-expose-it-with-a-nginx-ingress-controller-15-min)
9+
- [Table of Contents](#table-of-contents)
10+
- [Create a Kubernetes secret for the TLS certificate](#create-a-kubernetes-secret-for-the-tls-certificate)
11+
- [Install the ODM release](#install-the-odm-release)
12+
- [Edit your /etc/hosts](#edit-your-etchosts)
13+
- [Access the ODM services](#access-the-odm-services)
14+
- [Troubleshooting](#troubleshooting)
15+
- [License](#license)
1516
<!-- /TOC -->
1617

1718
NGINX has been installed while deploying IBM License Manager, see [README.md](README.md#create-a-nginx-ingress-controller).
@@ -28,7 +29,7 @@ openssl req -x509 -nodes -days 1000 -newkey rsa:2048 -keyout mycompany.key \
2829
-addext "subjectAltName=DNS:mycompany.com"
2930
```
3031

31-
>Pay attention to use a real OpenSSL version and not LibreSSL.
32+
>By default on mac osx, the default Openssl (LibreSSL version) does not have the -addtext option. You need to install the "Official" OpenSSL implementation.
3233
3334
2. Create the according Kubernetes secret that contains the certificate
3435

platform/gcloud/datasource-dc.xml.template renamed to platform/gcloud/datasource-dc.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
javax.sql.ConnectionPoolDataSource="org.postgresql.ds.PGConnectionPoolDataSource"/>
1919
<properties URL="jdbc:postgresql://<IP>/<DBNAME>?cloudSqlInstance=<CONNECTION_NAME>;socketFactory=com.google.cloud.sql.postgres.SocketFactory"
2020
user="postgres"
21-
password="postgres"/>
21+
password="PASSWORD"/>
2222
  </dataSource>
2323
</server>
File renamed without changes.

0 commit comments

Comments
 (0)