├── .cra
├── .cveignore
└── .fileignore
├── .gitignore
├── .pipeline-config.yaml
├── .secrets.baseline
├── LICENSE
├── README.md
├── cd-scripts
├── acceptance-test.sh
├── deploy.sh
├── deploy_setup.sh
├── doi-publish-deploy.sh
└── setup.sh
├── configuration
├── global.json
└── tenants
│ ├── tenant-a.json
│ └── tenant-b.json
├── documentation
├── Options-Simple.drawio
├── Options-Simple.png
├── SaaS-Options.png
├── announcement-blog-template.md
├── announcement-blog.md
├── diagrams
│ ├── multi-tenancy-app-architecture.drawio
│ ├── multi-tenant-app-architecture.jpg
│ └── multi-tenant-app-architecture.png
├── example-app.png
└── images
│ ├── IKS-Infrastructure-by-Terraform-1.png
│ ├── Multi-tenancy-serverless.png
│ ├── VPC-Infrastructure-by-Terraform-1.png
│ ├── VPC-Infrastructure-by-Terraform-2.png
│ ├── VPC-Infrastructure-by-Terraform-Subnets.png
│ └── button-documentation.png
├── installapp
├── appid-configs
│ ├── add-application-template.json
│ ├── add-redirecturis-template.json
│ ├── add-roles-template.json
│ ├── add-scope.json
│ ├── add-ui-color.json
│ ├── add-ui-text-template.json
│ ├── idps-clouddirectory.json
│ ├── idps-custom.json
│ ├── idps-facebook.json
│ ├── idps-google.json
│ ├── idps-saml.json
│ ├── user-export.json
│ └── user-import.json
├── appid-images
│ └── logo.png
├── backup-bash-scripts
│ ├── ce-build-images-quay-podman.sh
│ ├── ce-install-application-ibmcr.sh
│ ├── ce-install-application-quay.sh
│ └── run-two-applications.sh
├── ce-build-images-ibm-buildah.sh
├── ce-check-prerequisites.sh
├── ce-clean-up-two-tenancies.sh
├── ce-clean-up.sh
├── ce-create-two-tenancies.sh
├── ce-install-application.sh
├── ops-create-two-appids.sh
├── ops-create-two-postgres.sh
├── ops-install-single-appid.sh
├── ops-install-single-postgres.sh
├── postgres-config
│ ├── create-populate-tenant-a.sql
│ ├── create-populate-tenant-b.sql
│ └── insert-template.sh
├── tools-image
│ └── Dockerfile
├── tz-build-images-code-engine.sh
├── tz-check-prerequisites.sh
├── tz-create-two-tenancies.sh
├── tz-install-application.sh
└── tz-update-config.sh
├── local.env.template
├── operator
├── .dockerignore
├── .gitignore
├── Dockerfile
├── Makefile
├── PROJECT
├── api
│ └── v1alpha1
│ │ ├── ecommerceapplication_types.go
│ │ ├── groupversion_info.go
│ │ └── zz_generated.deepcopy.go
├── appIdHelper
│ └── appId.go
├── config
│ ├── crd
│ │ ├── bases
│ │ │ └── cache.saas.ecommerce.sample.com_ecommerceapplications.yaml
│ │ ├── kustomization.yaml
│ │ ├── kustomizeconfig.yaml
│ │ └── patches
│ │ │ ├── cainjection_in_ecommerceapplications.yaml
│ │ │ └── webhook_in_ecommerceapplications.yaml
│ ├── default
│ │ ├── kustomization.yaml
│ │ ├── manager_auth_proxy_patch.yaml
│ │ └── manager_config_patch.yaml
│ ├── manager
│ │ ├── controller_manager_config.yaml
│ │ ├── kustomization.yaml
│ │ └── manager.yaml
│ ├── manifests
│ │ └── kustomization.yaml
│ ├── prometheus
│ │ ├── kustomization.yaml
│ │ └── monitor.yaml
│ ├── rbac
│ │ ├── auth_proxy_client_clusterrole.yaml
│ │ ├── auth_proxy_role.yaml
│ │ ├── auth_proxy_role_binding.yaml
│ │ ├── auth_proxy_service.yaml
│ │ ├── ecommerceapplication_editor_role.yaml
│ │ ├── ecommerceapplication_viewer_role.yaml
│ │ ├── kustomization.yaml
│ │ ├── leader_election_role.yaml
│ │ ├── leader_election_role_binding.yaml
│ │ ├── role.yaml
│ │ ├── role_binding.yaml
│ │ └── service_account.yaml
│ ├── samples
│ │ ├── cache_v1alpha1_ecommerceapplication.yaml
│ │ ├── job.yml
│ │ └── kustomization.yaml
│ └── scorecard
│ │ ├── bases
│ │ └── config.yaml
│ │ ├── kustomization.yaml
│ │ └── patches
│ │ ├── basic.config.yaml
│ │ └── olm.config.yaml
├── controllers
│ ├── ecommerceapplication_controller.go
│ └── suite_test.go
├── ecommerceapplication
│ └── tmp
│ │ └── copy-secret.sh
├── go.mod
├── go.sum
├── hack
│ └── boilerplate.go.txt
├── main.go
└── postgres.yaml
└── scripts
├── env-config-template.js
├── run-locally-backend.sh
├── run-locally-container-backend.sh
├── run-locally-container-frontend.sh
└── run-locally-frontend.sh
/.cra/.cveignore:
--------------------------------------------------------------------------------
1 | *
2 | */
--------------------------------------------------------------------------------
/.cra/.fileignore:
--------------------------------------------------------------------------------
1 | *
2 | */
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | javacore*
2 | jitdump*
3 | .mvn
4 | certificates
5 | code/service-catalog/run-locally.sh
6 | local.env
7 | test.env
8 | test.sh
9 | cert.temp
10 | postgres-key-temp.json
11 | appid-key-temp.json
12 | target/
13 | pom.xml.tag
14 | pom.xml.releaseBackup
15 | pom.xml.versionsBackup
16 | pom.xml.next
17 | release.properties
18 | dependency-reduced-pom.xml
19 | buildNumber.properties
20 | .mvn/timing.properties
21 | code/frontend/package-lock.json
22 | #various files
23 | temp_information.txt
24 |
25 | #fontend configs
26 | env-config.js
27 | env-config
28 | -env-config
29 |
30 | #env configs
31 | local.env_tmp
32 | local.env-tmp
33 |
34 | #appid configs
35 | add-redirecturis.json
36 | add-roles.json
37 | add-application.json
38 |
39 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
40 | !/.mvn/wrapper/maven-wrapper.jar
41 |
42 | .gradle/
43 | build/
44 | .gradletasknamecache
45 |
46 | *.log
47 | graph-output.dot
48 |
49 | .settings
50 | .vscode
51 | .project
52 | .classpath
53 | *.iml
54 | .idea
55 | .factorypath
56 |
57 | settings.xml
58 | quarkus.log
59 |
60 | bin/
61 | .DS_Store
62 | ObjectStore
63 |
64 | lsp/
65 |
66 | *.swp
67 | *.swo
68 |
--------------------------------------------------------------------------------
/.pipeline-config.yaml:
--------------------------------------------------------------------------------
1 | # Documentation on available configuration
2 | # https://pages.github.ibm.com/one-pipeline/docs/custom-scripts.html
3 |
4 | version: '1'
5 |
6 | setup:
7 | image: icr.io/continuous-delivery/pipeline/pipeline-base-image:2.12@sha256:ff4053b0bca784d6d105fee1d008cfb20db206011453071e86b69ca3fde706a4
8 | script: |
9 | #!/usr/bin/env bash
10 |
11 | source cd-scripts/setup.sh
12 |
13 | deploy:
14 | image: icr.io/continuous-delivery/pipeline/pipeline-base-image:2.12@sha256:ff4053b0bca784d6d105fee1d008cfb20db206011453071e86b69ca3fde706a4
15 | script: |
16 | #!/usr/bin/env bash
17 |
18 | if [[ "$PIPELINE_DEBUG" == 1 ]]; then
19 | trap env EXIT
20 | env
21 | set -x
22 | fi
23 |
24 | source cd-scripts/deploy_setup.sh
25 | source cd-scripts/deploy.sh
26 | export DEPLOY_EXIT=$?
27 | source cd-scripts/doi-publish-deploy.sh
28 |
29 | acceptance-test:
30 | image: icr.io/continuous-delivery/pipeline/pipeline-base-image:2.12@sha256:ff4053b0bca784d6d105fee1d008cfb20db206011453071e86b69ca3fde706a4
31 | script: |
32 | #!/usr/bin/env bash
33 |
34 | #source scripts/acceptance-test.sh
35 | exit 0
36 |
--------------------------------------------------------------------------------
/.secrets.baseline:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": {
3 | "files": "^.secrets.baseline$|documentation/getting-started.md|documentation/local-development.md|ci-scripts/sign_image.sh|README.md|installapp/appid-configs/user-export.json|installapp/appid-configs/user-import.json|installapp/backup-bash-scripts/ce-install-application-ibmcr.sh|installapp/ce-install-application.sh|local.env.template",
4 | "lines": null
5 | },
6 | "generated_at": "2021-06-16T07:00:42Z",
7 | "plugins_used": [
8 | {
9 | "name": "AWSKeyDetector"
10 | },
11 | {
12 | "name": "ArtifactoryDetector"
13 | },
14 | {
15 | "base64_limit": 4.5,
16 | "name": "Base64HighEntropyString"
17 | },
18 | {
19 | "name": "BasicAuthDetector"
20 | },
21 | {
22 | "name": "BoxDetector"
23 | },
24 | {
25 | "name": "CloudantDetector"
26 | },
27 | {
28 | "ghe_instance": "github.ibm.com",
29 | "name": "GheDetector"
30 | },
31 | {
32 | "hex_limit": 3,
33 | "name": "HexHighEntropyString"
34 | },
35 | {
36 | "name": "IbmCloudIamDetector"
37 | },
38 | {
39 | "name": "IbmCosHmacDetector"
40 | },
41 | {
42 | "name": "JwtTokenDetector"
43 | },
44 | {
45 | "keyword_exclude": null,
46 | "name": "KeywordDetector"
47 | },
48 | {
49 | "name": "MailchimpDetector"
50 | },
51 | {
52 | "name": "PrivateKeyDetector"
53 | },
54 | {
55 | "name": "SlackDetector"
56 | },
57 | {
58 | "name": "SoftlayerDetector"
59 | },
60 | {
61 | "name": "StripeDetector"
62 | },
63 | {
64 | "name": "TwilioKeyDetector"
65 | }
66 | ],
67 | "results": {},
68 | "version": "0.13.1+ibm.38.dss",
69 | "word_list": {
70 | "file": null,
71 | "hash": null
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Multi-tenancy Assets for IBM Clients to build SaaS
2 |
3 | This repo contains multi-tenancy assets for IBM clients and partners to build SaaS (Software as a Service).
4 |
5 | ### Project Structure
6 |
7 | * [Introduction](#introduction)
8 | * [Documentation](#documentation)
9 | * [Getting Started](#getting-started)
10 | * [Repositories](#repositories)
11 |
12 | ## Introduction
13 |
14 | A key benefit of the cloud is the ability to deploy software for multiple consumers without having to install it redundantly on-premises. When software is provided as a managed service (SaaS), costs can be reduced for the deployments and the operations of applications. Additionally SaaS can be scaled and new consumers can be added easily.
15 |
16 | In order to leverage these advantages, applications need to be designed, so that they can support multiple tenants. Often tenants are not single users, but clients of SaaS providers with their own corporate authentication mechanisms. When running SaaS for multiple tenants, it's often required to keep the workloads isolated from each other for security reasons. For example, typically separate databases are used for tenants.
17 |
18 | At the same time common deployment and operation models are required, so that new SaaS versions can be deployed to different tenants in an unique and efficient way.
19 |
20 | This project aims to support IBM partners to build SaaS for different platforms including Kubernetes, OpenShift, Serverless, Satellite, AWS and Azure. The used sample application, which contains two containers, is the same one for all platforms. The CI/CD mechanisms slightly differentiate between the platforms.
21 |
22 | ### Platform Options
23 |
24 | The following diagram shows the different platform options. At this point the repo contains the IBM Cloud platforms.
25 |
26 | More options are planned to be added. For example with Satellite the SaaS application can be deployed on-premises to client data centers, but managed centrally. Additionally the same SaaS application can be deployed on other managed OpenShift services like AWS ROSA and Azure ARO.
27 |
28 | **Serverless on IBM Cloud**
29 |
30 | The easiest way to get started is to use serverless. The repo describes how to use IBM Code Engine to run the application logic, IBM App ID for authentication, IBM Postgres for persistence and IBM Toolchain for CI/CD. Scripts are provided to make the setup as easy as possible.
31 |
32 | **Managed Kubernetes and OpenShift on IBM Cloud**
33 |
34 | For more advanced cloud-native applications Kubernetes and OpenShift can be used. Compute isolation can be done either by sharing clusters and using Kubernetes namespaces/OpenShift projects or by having separate clusters for tenants. For authentication the managed services App ID and Postgres can be used, but they can also be replaced by other managed services or services running within the clusters.
35 |
36 | For CI/CD the IBM DevSecOps reference architecture based on IBM Toolchain is used which is also the internal IBM standard and which guarantees compliance for regulated industries.
37 |
38 |
39 |
40 | ### Sample Application
41 |
42 | The project comes with a simple e-commerce example application. A SaaS provider might have one client selling books, another one selling shoes.
43 |
44 |
45 |
46 |
47 |
48 | ## Documentation
49 |
50 | You can access the easier navigable and consumable version of the documentation by pressing this button [](https://ibm.github.io/multi-tenancy-documentation/)
51 |
52 | * [Introduction](#introduction)
53 | * Development of Microservices
54 | * [Quarkus Backend Service Code](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/backend-service-impl.md)
55 | * [Quarkus Backend Service Container](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/backend-service-container.md)
56 | * [Vue.js Frontend Service Code](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/frontend-service-code.md)
57 | * [Vue.js Frontend Service Container](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/frontend-service-container.md)
58 | * [Externalization of Variables in Backend Microservices](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/externalization-of-variables-in-backend-microservices.md)
59 | * [Externalization of Variables in Frontend Microservices](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/externalization-of-variables-in-frontend-microservices.md)
60 | * [Local Development of Services](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/local-development.md)
61 | * [Authentication Flow (AppID, backend, frontend)](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/development_of_microservices/authentication-flow-appip-backend-frontend.md)
62 | * Creation of managed IBM Cloud Services
63 | * Database
64 | * [Programmatic Creation of Postgres](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/creation-of-managed-ibm-cloud-services/create-postgres.md)
65 | * [Programmatic Configuration of Postgres including Schema](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/creation-of-managed-ibm-cloud-services/create-postgres-schema.md)
66 | * Authentication
67 | * [Programmatic Creation of AppID](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/creation-of-managed-ibm-cloud-services/create-appid.md)
68 | * [Programmatic Configuration of AppID](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/creation-of-managed-ibm-cloud-services/configure-appid.md)
69 | * Serverless via IBM Code Engine
70 | * [Architecture](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/ce-arcitecture.md)
71 | * Initial Setup via Scripts
72 | 1. [Create the instances](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/ce-setup-create-the-instances.md)
73 | 2. [Verify the created instances](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/ce-verify-the-created-instances.md)
74 | * [CI/CD](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/serverless-cicd.md)
75 | * [Onboarding](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/code-engine-onboarding.md)
76 | * [Observability (logging, monitoring, vulnerabilities)](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/observability.md)
77 | * [Billing](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/code-engine-billing.md)
78 | * [Clean Up](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/ce_clean_up.md)
79 | * Kubernetes via IBM Kubernetes Service and IBM OpenShift
80 | * Architecture
81 | * [Initial Setup via Scripts](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/automation/terraform/3-Provisionning-A-Kubernetes-Based-Infrastructure.md)
82 | * CI/CD DevSecOps
83 | * [Overview](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/devsecops-overview.md)
84 | * CI
85 | * [CI pull request](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/ci-pull-request.md)
86 | * [CI pipeline](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/ci-pipeline.md)
87 | * CD
88 | * [CD pull request](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/cd-pull-request.md)
89 | * [CD pipeline](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/cd-pipeline.md)
90 | * [Security and Compliance](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/security-and-compliance.md)
91 | * Setup of the Toolchains
92 | * [CI Toolchains](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/k8s/3-ci-cd/README_ci.md)
93 | * [CD Toolchains](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/k8s/3-ci-cd/README_cd.md)
94 | * [Onboarding](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/k8s-onboarding.md)
95 | * [Observability (logging, monitoring, vulnerabilities)](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/observability.md)
96 | * [Billing](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/kubernetes-via-ibm-kubernetes-service-and-ibm-openshift/k8s-billing.md)
97 |
98 | ## Repositories
99 |
100 | This repo is the 'parent repo' including documentation and global configuration. The other four repos contain the implementation of the microservices and the serverless pipelines.
101 |
102 | * [multi-tenancy](https://github.com/IBM/multi-tenancy) - this repo (parent repo)
103 | * Overview documentation
104 | * Global and tenant specific application configuration
105 | * CD pipeline
106 | * Scripts to deploy cloud services/infrastructure
107 |
108 | * [multi-tenancy-backend](https://github.com/IBM/multi-tenancy-backend) - backend microservice
109 | * Code
110 | * CI pipeline
111 |
112 | * [multi-tenancy-frontend](https://github.com/IBM/multi-tenancy-frontend) - frontend microservice
113 | * Code
114 | * CI pipeline
115 |
116 | * [multi-tenancy-serverless-ci-cd](https://github.com/IBM/multi-tenancy-serverless-ci-cd) - CI and CD pipelines for serverless
117 |
118 |
119 | ## Getting Started
120 |
121 | The easiest way to get started is to set up the sample application for two tenants on the IBM Cloud using serverless technology. The following diagram describes the serverless architecture of the simple e-commerce application which has two images (backend and frontend).
122 |
123 | Isolated Compute:
124 | * One frontend container per tenant
125 | * One backend container per tenant
126 | * One App ID instance per tenant
127 | * One Postgres instance (with one database) per tenant
128 |
129 | Shared CI/CD:
130 | * One code base for frontend and backend services
131 | * One image for frontend service
132 | * One image for backend service
133 | * One toolchain for all tenants (with four pipelines)
134 |
135 |
136 |
137 | Used IBM Services:
138 | * IBM Code Engine
139 | * IBM Container Registry
140 | * IBM App ID
141 | * IBM Postgres
142 | * IBM Toolchain
143 |
144 | Used Technologies:
145 | * Quarkus
146 | * Vue.js and nginx
147 | * Bash scripts
148 |
149 | **Initial Deployment Scripts**
150 |
151 | Scripts and provided to set up all services and the application automatically. Follow this [step by step guide](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/ce-setup-create-the-instances.md) to set up everything using local bash scripts.
152 |
153 | **Deployments of Updates via CI/CD**
154 |
155 | Additionally pipelines are provided to re-deploy the backend and frontend services when their implementations have changed. Follow this [step by step guide](https://github.com/IBM/multi-tenancy-documentation/blob/main/documentation/serverless-via-ibm-code-engine/serverless-cicd.md) to set up the pipelines.
156 |
157 |
--------------------------------------------------------------------------------
/cd-scripts/acceptance-test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | export TARGET_ENVIRONMENT
4 | export HOME
5 | export DEPLOYMENT_DELTA
6 |
7 | TARGET_ENVIRONMENT="$(cat /config/environment)"
8 | INVENTORY_PATH="$(cat /config/inventory-path)"
9 | DEPLOYMENT_DELTA_PATH="$(cat /config/deployment-delta-path)"
10 | DEPLOYMENT_DELTA=$(cat "${DEPLOYMENT_DELTA_PATH}")
11 |
12 | echo "Target environment: ${TARGET_ENVIRONMENT}"
13 | echo "Deployment Delta (inventory entries with updated artifacts)"
14 | echo ""
15 |
16 | echo "$DEPLOYMENT_DELTA" | jq '.'
17 |
18 | echo ""
19 | echo "Inventory content"
20 | echo ""
21 |
22 | ls -la ${INVENTORY_PATH}
23 |
24 | test_count=0
25 |
26 | #
27 | # prepare acceptance tests
28 | #
29 | source /root/.nvm/nvm.sh
30 | npm ci
31 |
32 | #
33 | # iterate over inventory deployment delta
34 | # and run acceptance tests
35 | #
36 | exit_code=0
37 | for INVENTORY_ENTRY in $(echo "${DEPLOYMENT_DELTA}" | jq -r '.[] '); do
38 | APP_URL_PATH="$(echo ${INVENTORY_ENTRY} | sed 's/\//_/g')_app-url.json"
39 | if [[ -f "../$APP_URL_PATH" ]]; then
40 | export APP_URL=$(cat "../${APP_URL_PATH}")
41 |
42 | echo "Running acceptance test for: '$APP_URL_PATH'"
43 | if ! npm run acceptance-test; then
44 | exit_code=1
45 | fi
46 | ((test_count+=1))
47 | fi
48 | done
49 |
50 | echo "Run $test_count tests for $(echo "${DEPLOYMENT_DELTA}" | jq '. | length') entries"
51 | exit $exit_code
52 |
--------------------------------------------------------------------------------
/cd-scripts/deploy_setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export IBMCLOUD_API_KEY
4 | export IBMCLOUD_TOOLCHAIN_ID
5 | export IBMCLOUD_IKS_REGION
6 | export IBMCLOUD_IKS_CLUSTER_NAME
7 | export IBMCLOUD_IKS_CLUSTER_NAMESPACE
8 | export IMAGE_PULL_SECRET_NAME
9 | export TARGET_ENVIRONMENT
10 | export HOME
11 | export BREAK_GLASS
12 | export DEPLOYMENT_DELTA
13 |
14 | if [ -f /config/api-key ]; then
15 | IBMCLOUD_API_KEY="$(cat /config/api-key)" # pragma: allowlist secret
16 | else
17 | IBMCLOUD_API_KEY="$(cat /config/ibmcloud-api-key)" # pragma: allowlist secret
18 | fi
19 |
20 | HOME=/root
21 |
22 | TARGET_ENVIRONMENT="$(cat /config/environment)"
23 | INVENTORY_PATH="$(cat /config/inventory-path)"
24 | DEPLOYMENT_DELTA_PATH="$(cat /config/deployment-delta-path)"
25 | DEPLOYMENT_DELTA=$(cat "${DEPLOYMENT_DELTA_PATH}")
26 |
27 | echo "Target environment: ${TARGET_ENVIRONMENT}"
28 | echo "Deployment Delta (inventory entries with updated artifacts)"
29 | echo ""
30 |
31 | echo "$DEPLOYMENT_DELTA" | jq '.'
32 |
33 | echo ""
34 | echo "Inventory content"
35 | echo ""
36 |
37 | ls -la ${INVENTORY_PATH}
38 |
39 | BREAK_GLASS=$(cat /config/break_glass || echo "")
40 | IBMCLOUD_TOOLCHAIN_ID="$(jq -r .toolchain_guid /toolchain/toolchain.json)"
41 | IBMCLOUD_IKS_REGION="$(cat /config/dev-region | awk -F ":" '{print $NF}')"
42 | #IBMCLOUD_IKS_CLUSTER_NAMESPACE="$(cat /config/dev-cluster-namespace)"
43 | #IBMCLOUD_IKS_CLUSTER_NAME="$(cat /config/cluster-name)"
44 |
45 | #example: https://github.com/IBM/multi-tenancy/blob/main/configuration/global.json
46 | CONFIG_FILE="configuration/global.json"
47 | IBM_CLOUD_RESOURCE_GROUP=$(cat ./$CONFIG_FILE | jq '.IBM_CLOUD.RESOURCE_GROUP' | sed 's/"//g')
48 | IBM_CLOUD_REGION=$(cat ./$CONFIG_FILE | jq '.IBM_CLOUD.REGION' | sed 's/"//g')
49 | REGISTRY_NAMESPACE=$(cat ./$CONFIG_FILE | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
50 | REGISTRY_TAG=$(cat ./$CONFIG_FILE | jq '.REGISTRY.TAG' | sed 's/"//g')
51 | REGISTRY_URL=$(cat ./$CONFIG_FILE | jq '.REGISTRY.URL' | sed 's/"//g')
52 | REGISTRY_SECRET_NAME=$(cat ./$CONFIG_FILE | jq '.REGISTRY.SECRET_NAME' | sed 's/"//g')
53 | IMAGES_NAME_BACKEND=$(cat ./$CONFIG_FILE | jq '.IMAGES.NAME_BACKEND' | sed 's/"//g')
54 | IMAGES_NAME_FRONTEND=$(cat ./$CONFIG_FILE | jq '.IMAGES.NAME_FRONTEND' | sed 's/"//g')
55 |
56 | #example: https://github.com/IBM/multi-tenancy/blob/main/configuration/tenants/tenant-a.json
57 | TENANT=$(get_env tenant '')
58 | echo "niklas tenant"
59 | echo ${TENANT}
60 | CONFIG_FILE="configuration/tenants/${TENANT}.json"
61 | APPID_SERVICE_INSTANCE_NAME=$(cat ./$CONFIG_FILE | jq '.APP_ID.SERVICE_INSTANCE' | sed 's/"//g')
62 | APPID_SERVICE_KEY_NAME=$(cat ./$CONFIG_FILE | jq '.APP_ID.SERVICE_KEY_NAME' | sed 's/"//g')
63 | POSTGRES_SERVICE_INSTANCE=$(cat ./$CONFIG_FILE | jq '.POSTGRES.SERVICE_INSTANCE' | sed 's/"//g')
64 | POSTGRES_SERVICE_KEY_NAME=$(cat ./$CONFIG_FILE | jq '.POSTGRES.SERVICE_KEY_NAME' | sed 's/"//g')
65 | POSTGRES_SQL_FILE=$(cat ./$CONFIG_FILE | jq '.POSTGRES.SQL_FILE' | sed 's/"//g')
66 | APPLICATION_CONTAINER_NAME_BACKEND=$(cat ./$CONFIG_FILE | jq '.APPLICATION.CONTAINER_NAME_BACKEND' | sed 's/"//g')
67 | APPLICATION_CONTAINER_NAME_FRONTEND=$(cat ./$CONFIG_FILE | jq '.APPLICATION.CONTAINER_NAME_FRONTEND' | sed 's/"//g')
68 | APPLICATION_CATEGORY=$(cat ./$CONFIG_FILE | jq '.APPLICATION.CATEGORY' | sed 's/"//g')
69 | CODE_ENGINE_PROJECT_NAME=$(cat ./$CONFIG_FILE | jq '.CODE_ENGINE.PROJECT_NAME' | sed 's/"//g')
70 | IBM_KUBERNETES_SERVICE_NAME=$(cat ./$CONFIG_FILE | jq '.IBM_KUBERNETES_SERVICE.NAME' | sed 's/"//g')
71 | IBM_KUBERNETES_SERVICE_NAMESPACE=$(cat ./$CONFIG_FILE | jq '.IBM_KUBERNETES_SERVICE.NAMESPACE' | sed 's/"//g')
72 | IBM_OPENSHIFT_SERVICE_NAME=$(cat ./$CONFIG_FILE | jq '.IBM_OPENSHIFT_SERVICE.NAME' | sed 's/"//g')
73 | IBM_OPENSHIFT_SERVICE_NAMESPACE=$(cat ./$CONFIG_FILE | jq '.IBM_OPENSHIFT_SERVICE.NAMESPACE' | sed 's/"//g')
74 | PLATFORM_NAME=$(cat ./$CONFIG_FILE | jq '.PLATFORM.NAME' | sed 's/"//g')
75 |
76 | IBM_KUBERNETES_SERVICE_NAMESPACE=${IBM_KUBERNETES_SERVICE_NAMESPACE}
77 |
78 | if [ "$PLATFORM_NAME" = "IBM_KUBERNETES_SERVICE" ]; then
79 | IBMCLOUD_IKS_CLUSTER_NAMESPACE=${IBM_KUBERNETES_SERVICE_NAMESPACE}
80 | IBMCLOUD_IKS_CLUSTER_NAME=${IBM_KUBERNETES_SERVICE_NAME}
81 | else
82 | IBMCLOUD_IKS_CLUSTER_NAMESPACE=${IBM_OPENSHIFT_SERVICE_NAMESPACE}
83 | IBMCLOUD_IKS_CLUSTER_NAME=${IBM_OPENSHIFT_SERVICE_NAME}
84 | fi
85 |
86 | IBMCLOUD_IKS_REGION=${IBM_CLOUD_REGION}
87 | #IBMCLOUD_IKS_CLUSTER_NAMESPACE=${IBM_KUBERNETES_SERVICE_NAMESPACE}
88 | #IBMCLOUD_IKS_CLUSTER_NAME=${IBM_KUBERNETES_SERVICE_NAME}
89 |
90 | if [[ -n "$BREAK_GLASS" ]]; then
91 | export KUBECONFIG
92 | KUBECONFIG=/config/cluster-cert
93 | else
94 | IBMCLOUD_IKS_REGION=$(echo "${IBMCLOUD_IKS_REGION}" | awk -F ":" '{print $NF}')
95 | ibmcloud login -r "$IBMCLOUD_IKS_REGION"
96 | ibmcloud ks cluster config --cluster "$IBMCLOUD_IKS_CLUSTER_NAME"
97 |
98 | ibmcloud ks cluster get --cluster "${IBMCLOUD_IKS_CLUSTER_NAME}" --json > "${IBMCLOUD_IKS_CLUSTER_NAME}.json"
99 | # If the target cluster is openshift then make the appropriate additional login with oc tool
100 | if which oc > /dev/null && jq -e '.type=="openshift"' "${IBMCLOUD_IKS_CLUSTER_NAME}.json" > /dev/null; then
101 | echo "${IBMCLOUD_IKS_CLUSTER_NAME} is an openshift cluster. Doing the appropriate oc login to target it"
102 | oc login -u apikey -p "${IBMCLOUD_API_KEY}"
103 | fi
104 | #
105 | # check pull traffic & storage quota in container registry
106 | #
107 | if ibmcloud cr quota | grep 'Your account has exceeded its pull traffic quota'; then
108 | echo "Your account has exceeded its pull traffic quota for the current month. Review your pull traffic quota in the preceding table."
109 | exit 1
110 | fi
111 |
112 | if ibmcloud cr quota | grep 'Your account has exceeded its storage quota'; then
113 | echo "Your account has exceeded its storage quota. You can check your images at https://cloud.ibm.com/kubernetes/registry/main/images"
114 | exit 1
115 | fi
116 | fi
117 |
118 | IMAGE="$(cat /config/image)"
119 | IMAGE_PULL_SECRET_NAME="ibmcloud-toolchain-${IBMCLOUD_TOOLCHAIN_ID}-${REGISTRY_URL}"
120 |
121 | IBMCLOUD_IKS_REGION=$(echo "${IBMCLOUD_IKS_REGION}" | awk -F ":" '{print $NF}')
122 | ibmcloud login -r "${IBMCLOUD_IKS_REGION}"
123 | ibmcloud target -g ${IBM_CLOUD_RESOURCE_GROUP}
124 | ibmcloud ks cluster config --cluster "${IBMCLOUD_IKS_CLUSTER_NAME}"
125 |
126 | ibmcloud ks cluster get --cluster "${IBMCLOUD_IKS_CLUSTER_NAME}" --json > "${IBMCLOUD_IKS_CLUSTER_NAME}.json"
127 | # If the target cluster is openshift then make the appropriate additional login with oc tool
128 | if which oc > /dev/null && jq -e '.type=="openshift"' "${IBMCLOUD_IKS_CLUSTER_NAME}.json" > /dev/null; then
129 | echo "${IBMCLOUD_IKS_CLUSTER_NAME} is an openshift cluster. Doing the appropriate oc login to target it"
130 | oc login -u apikey -p "${IBMCLOUD_API_KEY}"
131 | fi
132 |
133 | set_env IBM_CLOUD_REGION "${IBM_CLOUD_REGION}"
134 | set_env IBM_CLOUD_RESOURCE_GROUP "${IBM_CLOUD_RESOURCE_GROUP}"
135 | set_env REGISTRY_NAMESPACE "${REGISTRY_NAMESPACE}"
136 | set_env REGISTRY_TAG "${REGISTRY_TAG}"
137 | set_env REGISTRY_URL "${REGISTRY_URL}"
138 | set_env REGISTRY_SECRET_NAME "${REGISTRY_SECRET_NAME}"
139 | set_env IMAGES_NAME_BACKEND "${IMAGES_NAME_BACKEND}"
140 | set_env IMAGES_NAME_FRONTEND "${IMAGES_NAME_FRONTEND}"
141 | set_env APPID_SERVICE_INSTANCE_NAME "${APPID_SERVICE_INSTANCE_NAME}"
142 | set_env APPID_SERVICE_KEY_NAME "${APPID_SERVICE_KEY_NAME}"
143 | set_env POSTGRES_SERVICE_INSTANCE "${POSTGRES_SERVICE_INSTANCE}"
144 | set_env POSTGRES_SERVICE_KEY_NAME "${POSTGRES_SERVICE_KEY_NAME}"
145 | set_env POSTGRES_SQL_FILE "${POSTGRES_SQL_FILE}"
146 | set_env APPLICATION_CONTAINER_NAME_BACKEND "${APPLICATION_CONTAINER_NAME_BACKEND}"
147 | set_env APPLICATION_CONTAINER_NAME_FRONTEND "${APPLICATION_CONTAINER_NAME_FRONTEND}"
148 | set_env APPLICATION_CATEGORY "${APPLICATION_CATEGORY}"
149 | set_env CODE_ENGINE_PROJECT_NAME "${CODE_ENGINE_PROJECT_NAME}"
150 | set_env IBM_KUBERNETES_SERVICE_NAME "${IBM_KUBERNETES_SERVICE_NAME}"
151 | set_env IBM_KUBERNETES_SERVICE_NAMESPACE "${IBM_KUBERNETES_SERVICE_NAMESPACE}"
152 | set_env IBM_OPENSHIFT_SERVICE_NAME "${IBM_OPENSHIFT_SERVICE_NAME}"
153 | set_env IBM_OPENSHIFT_SERVICE_NAMESPACE "${IBM_OPENSHIFT_SERVICE_NAMESPACE}"
154 | set_env PLATFORM_NAME "${PLATFORM_NAME}"
--------------------------------------------------------------------------------
/cd-scripts/doi-publish-deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | export IBM_CLOUD_API
4 | export IBMCLOUD_API_KEY
5 | export BREAK_GLASS
6 | export DEPLOYMENT_DELTA
7 |
8 | if [ -f /config/api-key ]; then
9 | IBMCLOUD_API_KEY="$(cat /config/api-key)" # pragma: allowlist secret
10 | else
11 | IBMCLOUD_API_KEY="$(cat /config/ibmcloud-api-key)" # pragma: allowlist secret
12 | fi
13 |
14 | BREAK_GLASS=$(cat /config/break_glass || echo false)
15 |
16 | if [[ "$BREAK_GLASS" != "false" ]]; then
17 | echo "Break-Glass mode is on, skipping the rest of the task..."
18 | exit 0
19 | fi
20 |
21 | IBM_CLOUD_API="$(cat /config/ibmcloud-api || echo "https://cloud.ibm.com")"
22 | ##########################################################################
23 | # Setting HOME explicitly to have ibmcloud plugins available
24 | # doing the export rather than env definition is a workaround
25 | # until https://github.com/tektoncd/pipeline/issues/1836 is fixed
26 | export HOME="/root"
27 | ##########################################################################
28 | if [[ "$IBM_CLOUD_API" == *test* ]]; then
29 | export IBM_CLOUD_DEVOPS_ENV=dev
30 | fi
31 |
32 | TOOLCHAIN_ID=$(cat /config/doi-toolchain-id)
33 | CURRENT_TOOLCHAIN_ID=$(jq -r '.toolchain_guid' /toolchain/toolchain.json)
34 | DOI_IN_TOOLCHAIN=$(jq -e '[.services[] | select(.service_id=="draservicebroker")] | length' /toolchain/toolchain.json)
35 | DOI_ENVIRONMENT=$(cat /config/doi-environment 2> /dev/null || echo "")
36 | ENVIRONMENT=$(cat /config/environment 2> /dev/null || echo "")
37 | DEPLOYMENT_DELTA_PATH="$(cat /config/deployment-delta-path)"
38 | DEPLOYMENT_DELTA=$(cat "${DEPLOYMENT_DELTA_PATH}")
39 | JOB_URL=$(cat /config/job-url)
40 | INVENTORY_PATH="$(cat /config/inventory-path)"
41 |
42 | if [ "$DOI_IN_TOOLCHAIN" == 0 ]; then
43 | if [ -z "$TOOLCHAIN_ID" ] || [ "$CURRENT_TOOLCHAIN_ID" == "$TOOLCHAIN_ID" ]; then
44 | echo "No Devops Insights integration found in toolchain. Skipping ..."
45 | exit 0
46 | fi
47 | fi
48 |
49 | if [ "$DEPLOY_EXIT" -eq 0 ]; then
50 | DEPLOY_STATUS="pass"
51 | else
52 | DEPLOY_STATUS="fail"
53 | fi
54 |
55 | # Default Toolchain ID if needed
56 | if [ -z "$TOOLCHAIN_ID" ]; then
57 | TOOLCHAIN_ID="$CURRENT_TOOLCHAIN_ID"
58 | fi
59 |
60 | # Default Job URL if needed
61 | if [ -z "$JOB_URL" ]; then
62 | JOB_URL="$PIPELINE_RUN_URL"
63 | fi
64 |
65 | export TOOLCHAIN_ID=${TOOLCHAIN_ID} # for doi plugin
66 |
67 | if [ "$DOI_ENVIRONMENT" ]; then
68 | ENVIRONMENT="$DOI_ENVIRONMENT"
69 | fi
70 |
71 | ibmcloud login --apikey "${IBMCLOUD_API_KEY}" -a "${IBM_CLOUD_API}" --no-region
72 |
73 |
74 | declare -A app_hash_map
75 | for INVENTORY_ENTRY in $(echo "${DEPLOYMENT_DELTA}" | jq -r '.[] '); do
76 | APP=$(cat "${INVENTORY_PATH}/${INVENTORY_ENTRY}")
77 | BUILD_NUMBER=$(echo "${APP}" | jq -r '.build_number')
78 |
79 | APP_REPO=$(echo "${APP}" | jq -r '.repository_url')
80 | APP_REPO=$(echo -n "${APP_REPO}" | sed 's:/*$::')
81 | APP_NAME=$(echo "${APP_REPO}" | cut -f5 -d/)
82 |
83 | if [ ! "${app_hash_map[$APP_NAME]}" ] ; then
84 | ibmcloud doi publishdeployrecord \
85 | --env "${ENVIRONMENT}" \
86 | --status="${DEPLOY_STATUS}" \
87 | --joburl="${JOB_URL}" \
88 | --buildnumber="${BUILD_NUMBER}" \
89 | --logicalappname="${APP_NAME}"
90 | fi
91 | app_hash_map[$APP_NAME]=1
92 | done
93 |
--------------------------------------------------------------------------------
/cd-scripts/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -x
4 |
5 | echo "This is a setup stage. You can add your custom settings in this stage."
6 |
7 | echo "scripts/setup.sh"
8 | cd ..
9 |
10 | export BACKEND=$(get_env "multi-tenancy-backend")
11 | echo $BACKEND
12 | git clone $BACKEND
13 | save_repo multi-tenancy-backend "url=${BACKEND}"
14 |
15 | export FRONTEND=$(get_env "multi-tenancy-frontend")
16 | echo $FRONTEND
17 | git clone $FRONTEND
18 | save_repo multi-tenancy-frontend "url=${FRONTEND}"
19 |
20 | #export PARENT=$(get_env "multi-tenancy")
21 | #echo $PARENT
22 | #git clone $PARENT
23 | #save_repo multi-tenancy "url=${PARENT}"
24 |
--------------------------------------------------------------------------------
/configuration/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "IBM_CLOUD": {
3 | "RESOURCE_GROUP": "default",
4 | "REGION": "eu-de"
5 | },
6 | "REGISTRY": {
7 | "URL": "de.icr.io",
8 | "NAMESPACE": "multi-tenancy-example",
9 | "TAG": "v2",
10 | "SECRET_NAME": "multi.tenancy.cr.sec"
11 | },
12 | "IMAGES": {
13 | "NAME_BACKEND": "multi-tenancy-service-backend",
14 | "NAME_FRONTEND": "multi-tenancy-service-frontend"
15 | }
16 | }
--------------------------------------------------------------------------------
/configuration/tenants/tenant-a.json:
--------------------------------------------------------------------------------
1 | {
2 | "APP_ID": {
3 | "SERVICE_INSTANCE": "multi-tenancy-serverless-appid-a",
4 | "SERVICE_KEY_NAME": "multi-tenancy-serverless-appid-key-a"
5 | },
6 | "POSTGRES": {
7 | "SERVICE_INSTANCE": "multi-tenancy-serverless-pg-ten-a",
8 | "SERVICE_KEY_NAME": "multi-tenancy-serverless-pg-ten-a-key",
9 | "SQL_FILE": "create-populate-tenant-a.sql"
10 | },
11 | "APPLICATION": {
12 | "CONTAINER_NAME_BACKEND": "multi-tenancy-service-backend-movies",
13 | "CONTAINER_NAME_FRONTEND": "multi-tenancy-service-frontend-movies",
14 | "CATEGORY": "Movies"
15 | },
16 | "CODE_ENGINE": {
17 | "PROJECT_NAME": "multi-tenancy-serverless-a"
18 | },
19 | "IBM_KUBERNETES_SERVICE": {
20 | "NAME": "niklas-heidloff3-fra04-b3c.4x16",
21 | "NAMESPACE": "tenant-a"
22 | },
23 | "IBM_OPENSHIFT_SERVICE": {
24 | "NAME": "roks-gen2-suedbro",
25 | "NAMESPACE": "tenant-a"
26 | },
27 | "PLATFORM": {
28 | "NAME": "IBM_OPENSHIFT_SERVICE"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/configuration/tenants/tenant-b.json:
--------------------------------------------------------------------------------
1 | {
2 | "APP_ID": {
3 | "SERVICE_INSTANCE": "multi-tenancy-serverless-appid-b",
4 | "SERVICE_KEY_NAME": "multi-tenancy-serverless-appid-key-b"
5 | },
6 | "POSTGRES": {
7 | "SERVICE_INSTANCE": "multi-tenancy-serverless-pg-ten-b",
8 | "SERVICE_KEY_NAME": "multi-tenancy-serverless-pg-ten-b-key",
9 | "SQL_FILE": "create-populate-tenant-b.sql"
10 | },
11 | "APPLICATION": {
12 | "CONTAINER_NAME_BACKEND": "multi-tenancy-service-backend-fantasy",
13 | "CONTAINER_NAME_FRONTEND": "multi-tenancy-service-frontend-fantasy",
14 | "CATEGORY": "Fantasy"
15 | },
16 | "CODE_ENGINE": {
17 | "PROJECT_NAME": "multi-tenancy-serverless-b"
18 | },
19 | "IBM_KUBERNETES_SERVICE": {
20 | "NAME": "niklas-heidloff3-fra04-b3c.4x16",
21 | "NAMESPACE": "tenant-b"
22 | },
23 | "IBM_OPENSHIFT_SERVICE": {
24 | "NAME": "roks-gen2-suedbro",
25 | "NAMESPACE": "tenant-b"
26 | },
27 | "PLATFORM": {
28 | "NAME": "IBM_KUBERNETES_SERVICE"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/documentation/Options-Simple.drawio:
--------------------------------------------------------------------------------
1 | 7Z1rc9o4F8c/TWZ2X6Tj++Ull2zSTbPJhvbp9tWOggW4NRZri5Dk0z8ytsCWBCEEyyYo00mxbBxZ56cj6S/56MzsTZ8uEzCb3KAARmeGFjydmf0zwzA00yX/ZSnPeYquu36eMk7CIE/T1gmD8AUWF9LUeRjAtEjLkzBCEQ5n1cQhimM4xJU0kCRoUb1shKKgkjADY8glDIYg4lO/hwGe5KmW45VOXMFwPCn+tGlqRc6ngF5dJKQTEKBFKcm8ODN7CUI4/zR96sEoKz5aMN+u/x3Nbi60Jzu4fepfe3B41T3Pb/bHW76yymoCY7z3rdGvtNsfXd2ayTR+ueh0R1fG87mT3/oRRPOiwL7C4SRGERqHxGz5c+NnWprpIpxGICZH3RGK8aA4o5Hj4SSMgi/gGc2zLKYYDH/Ro+4EJeELuR5E5JROEsjpBBewGE7likH2zeKeCUzJNXf0uXUm6QY8VS78AlJMc4OiCMzS8GGZv+yLU5CMw7iLMEbT4qLlI+SZ0LNMjMIo6pFHT0hCjJZPuWPJFxZ6hAmGTyXwCktcQjSFOHkmlxRnHa+AqqhXjmPnx4s1pIZdXDMp8WloXlE5iooxXt17bXvyoTD/G1BwORTuYUASrgAmv29nMB5MwhHmmCBPjJcmTdAvyBSfoERBFI5jchjBUfa1rMhCUl07RTJGs+xmMzAM4/GX5TV9a51yX5REloTId0fRskZOwiCAcQYCwgCDhxWVMxTGeFlSdpf8IwXa0z7ZZzbJeI8c6+tj8i+7PME9FJNnAeHS4JAgtYAZVhwtPBpba9jraBQoGM6OJDjvB+Hu7vqqp9v+X1cXd1eDf56//1zgFcxrED53b0hCHz4O4PCWOO6ecdbRvhI3PpyQclJE1EyEbcgjQphjfwMRdyjF4yRrJ7Qzt1dJUEjUioTrNYwE7d9xTHRmM/L7c3/FxDV8HkYI/FJM1MyErllNQ7Gp6biH4zDN/sKy5ci5KPUotN69oqNuOkytaToMwWAjScAIJVNl/rrNb0vsVorNb3Lmv0EJ+dTJnMFDCpNH8BBGIc5yDeJg6SJUT6J2MDyJvUvh4JnnYgCn4XmYogjg5RC0h6azOYYcCwmaxwHM7p2N8BeTEMMBMVx2dpGAzLITPKWaw7bR/plhBjb0Aosji5zxjAfTEZvgMGKAbhW9Odrf9/iGXDcEJnG0mkxi8SYhpohI5azXCCMNmBoQGaHbdzWNijaldG35U6dxNN2qWMcydd461IKHts6X86j/cPnXy+Xdz4Xx7cfl6PzCFFSYzvcB06P67f520Pn95Ixl+N7rxqLjaSnG4qtS3iXuRWge1Gqfbqfn9U2RfZzlD2+f/KdG+1hO1TyGJ6pLvkTz2ILGJyEPFMGU9D2cKGuKHxLyaZx9GoTTWVSvC3Q04OuuyGpG33WaqVW+/slmqpXP282pqYUS2o2fuBhMyINnvYXNwuQH6S+s1J8m+gtCa/BzBzfzCIfnJ+nkdIvpMQi9nNTa4nH2uY3P7xLSzU4FYq0c+6zclnT7sD06oX1sXaJ9BMLZyp31onmKYfJRnRlbWZp3ZgKdihqD7Q+sp8ROoNmxLdZSZtOW4kdCq9qizZa/v8IYxPyk9QcxCbuMoAUmEUgH1JOdVG3xdNY0jfs1fqzDFT6Mg0626upsJaBuKWxSYsnzP+Qo0zSLwx/FtcuDPl2fkx8906OnEJe+Ro5+lM6sv5Qd0O+8zUwpmidDuKUsiqLAIBlDvOW6wmYwqKwy441eMqpIO6VpCYwADh/L9xIbuvgLd5meXKrujl9hyrUYVvLnLr61xoW7katXb+T4zI3yguFutORu9djvQJEfvp0Cipr79J82xunc/dudp/9C3eheF6v2jg1F27Y4TYC5ya4wOloVRsuTDCM/OjoFGLfNxbwKo90qGE3Gndn7+kXHYG6kSUaRX2l1CihuU/iODEXjtZZ1VxQNn0FRslek5CkU1/JMmcVtbXlbWKRrPmh30diTRUFj7zO5qZtGfqR/ujQaR0mj5VVp3NsztoBGXuQ4XRp37TJSkb0tOPqMc2S7ervi6GkM17rkhlrJOpz6eGwwuvaBfGPjMKoBDLtM61UW3Vah6FP9mfpF6uDfiqKuGdU7OR5zp5pZpHpUs7NDIxC4YCiagniwHFuzapyC0A2LHUM2PQdh8qPK0vTQLELPU7iESMasd5OmMd3WmUYNsTjX8arvNlvlu3XNZ5y3ua/zNplmQHZHwlRDLM5pHhuNJtMBcLWjpZEfY51WV8J3TKa5anqhibl5BXDzq7Sa7FlofutMxS8Pfqcv38Mn7+v/D+3LBdNa217Baosv121m8R/tv77Zl+vswiiqpsry5Qef7j9iGgXroI6CRqdKo7M3jZrH3EmTTONpCmbbIHtdvG0VjK7PErTvojzmRjbbINeMosULACoYnIxgcOz73w71buVX8i2XJ/ggweDELGx8bRUFkPx3EZPy41+EVMEZ4m2Oa1XD3hUMTkjCAYIziLPMj3xzEFT4N2kQiOK/yYWAH2uzAeAUA/UyIAr4JpcBftjEBnxTDNTLgDDAm1QIvM3dAtJdIz2CTAldBXtTQNQNhCimm1wg+O7BbfyAQBIQE2TWHybhDGfCK8rQ+PZZQVE7FKJIb3VBIVx3yq+7UIHe2gCGKNKbVG9BO7JKYZCsMLBBFoQKQ23h5sUs8GrT9fwBJjHEajzxVmewqljHE2V+U/xoFWW+KSRkhpkXZ3mT1qTizDcGhcxA8+Isb9KelO4giwGpgeWFefb5jqPSHRoEQmYseXGe+SVFKpa8PPvLDCYvdgiCiJNKY2gBGTKjyYvzzM9TKI1ByioG9m2jNogM/OoqtandIRyDtzMdDegN4haDn7oqo9AJHkE8rAZc1W4+KTDqBqNx1cHhpyvy7RSU6tAYFI2rDg4/4Myh6MzxhDwtsRcOUbxCQ21yJwuN5sUIh5+5yNlQYkQjQDQuRvi8bq3ECHn2b16M4EcZSoxoAxmNixEOP+hQYoQMMYJ9X9lx+JdgJWsRDt9KKC3iAH5hVcdaqUUI1z5smudWax+aQqJ5FWLTNHdp403lI+QB0bwCwc9w8UAoRUoWDy2QHV5/AUeJUbKpaFx7cPixp1oZ0yAQzYsRvJtQYkQbyGhcjHD5Jdedl3nCdCy13zr3t6e3cbjtGZv2MyrHzaLhWuTsqnuakb3fFRumXcGUdddjNxRiwgvtHKiI3T7Bov5EUnQYGjG9/Kbn2muwIfeKjZ+1OC897jzpkcxJyS71r89xMCeeQKSHfvCN1V3DZVyOIAgw7VHI2Sj64EGAjzc4GiX+yHbm0zVmCyrLYu6x78Z8pstgV7PL8XlN7BTav62dt2NrAFfMvBdG7k5mfRuibdtNacP7qq81gKfVsFl0UV0TDZvQegZnvdMK4KzbTF9DalhgoUn4rsZpbgXh2G2zjFq10NArFGwllbtsQciCWrXwXgFxaxVr5aIFYY75zrh6f6J5LGQuXBDmmJ+mpqKyen+iKShkLl4Q5pifg6JQqPcnmkVD6joGYZb5CWvKhpqybgIImUsYxOECeWVFvT4hzfwyFyyIza/CRbYSDJnrFcRg8JodZ/UPKP9v08peV/8LA7VF/ve86tYWNg37+Gb53zOqb3RY9e2OIqbxNLdH3SamHx2Nll2d0OQ2e9p9Mordq0dncnM4Gn92/ndvnF8sOv95f/yJYfTDfMQC7fxmHuHwnE47DQBYvq2eppCX5hi9VjQxxMngqzOCtpQlexyBNC3Io3/rS7ZdTA73hpZoVz43q6da1dcYdDP6so7uaoKAx47zdvWUHCYI4bJZEzCb3KAAZlf8Hw==
--------------------------------------------------------------------------------
/documentation/Options-Simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/Options-Simple.png
--------------------------------------------------------------------------------
/documentation/SaaS-Options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/SaaS-Options.png
--------------------------------------------------------------------------------
/documentation/announcement-blog-template.md:
--------------------------------------------------------------------------------
1 | * One or two sentence(s) sub-title
2 | * Intro one or two paragraphs describing the 'why' with focus on multi-cloud
3 | * Status: IBM Cloud done, other platforms to be done, more documentation to be done
4 | * Team effort/authors
5 | * Introduction
6 | * [Input](https://github.com/IBM/multi-tenancy#introduction)
7 | * Plus personas: Dev, Ops, Sec
8 | * Key messages
9 | * Key values of this asset: 1. Starting point for SaaS, 2. best practices, 3. shared CI/CD
10 | * Code, images and CI/CD are shared
11 | * Isolated workloads at runtime for security reasons
12 | * Support for multiple target platforms/multi-cloud
13 | * Screenshot 1: [Diagram](https://github.com/IBM/multi-tenancy/raw/main/documentation/SaaS-Options.png)
14 | * IBM Cloud
15 | * Serverless
16 | * IKS
17 | * ROKS for FS-compliance
18 | * On-premises
19 | * Other public clouds: AWS, Azure
20 | * Sample e-commerce application
21 | * Screenshot 2: (remove the refresh symbol and use readable title)
22 | * Scenario: one tenant selling books, another one selling shoes
23 | * Full end-to-end application including authentication and persistence
24 | * Sample can be used as starting point
25 | * Value of managed services
26 | * ROKS, IKS and Code Engine for compute
27 | * Postgres, AppID, Container Registry, Toolchain
28 | * Replaceable
29 | * Automation first
30 | * First time experience via scripts
31 | * Terraform scripts to create clusters
32 | * Onboarding scripts and configuration
33 | * CI/CD pipeline for serverless
34 | * CI/CD pipelines for cloud-native
35 | * Regulated industries
36 | * Value proposition of IBM Cloud
37 | * Usage of approved services and OpenShift is required
38 | * Based on [IBM's DevSecOps reference implementation](https://www.ibm.com/cloud/blog/announcements/devsecops-reference-implementation-for-audit-ready-compliance-across-development-teams#:~:text=The%20reference%20implementation%20of%20DevSecOps,manual%20overrides%20for%20exceptional%20situations)
39 | * Examples: don't push to main; detect secrets; detect vulernabilities - shift left
40 | * Next
41 | * More documentation
42 | * Other clouds including cloud specific services
43 | * On-premises via Satellite
44 | * Call to action
45 | * Try serverless getting started
46 | * Provide feedback
47 |
48 |
49 |
50 |
51 | Other notes:
52 |
53 | * Pull Request (PR) Pipeline: This is typically triggered by a developer when they want to conribute source code from their own branch, by creating a merge or pull request (PR) in the application repository. The pipeline runs unit test and static scans on the source code and second developer must approve the PR.
54 | * Continuous Integration (CI) Pipeline: When a change is merged to the main branch of the application repository, the CI Pipeline runs tests on the source code and deployment manifests to detect secrets or security risks, as well as scanning the container images for vulnerabilities. Unit and code coverage tests can also be incorporated. The CI pipeline builds the binary artifacts (containers), uploads them to the IBM Container Registry and deploys them to the runtime environment as an integration test. The CI Pipeline also generates metadata about the build artifacts and stores this in another repository, for purposes of compliance and audit.
55 | * Promotion (CD) Pipeline: This is manually triggered to create a new merge / PR to push the latest code changes from the source (main) branch to the target branch of a particular tenant.
56 | * Continuous Deployment (CD) Pipeline: The CD pipeline is used to deploy the application to the production environments of specific tenant (i.e. a tenant specifc namespace in a Kubernetes cluster). For compliance reasons it needs to be triggered manually, after the merge is completed from the previous Promotion Pipeline.
57 |
--------------------------------------------------------------------------------
/documentation/announcement-blog.md:
--------------------------------------------------------------------------------
1 | ## New Open-Source Multi-Cloud Asset to build SaaS
2 | Development and automated deployment of SaaS for multiple tenants, using Red Hat OpenShift/Kubernetes and DevSecOps
3 |
4 | ### Introduction
5 |
6 | When software is provided as a managed service (SaaS), using a multi-tenant approach helps minimise costs for the deployments and operations of each tenant. In order to leverage these advantages, applications need to be designed so that they can be deployed to support multiple tenants, while maintaining isolation for security reasons. At the same time, common deployment and operation models are required so that new SaaS versions can be deployed to existing tenants, or to onboard new tenants, in a reliable and efficient way.
7 |
8 | A new [open-source project](https://github.com/IBM/multi-tenancy) from IBM developers aims to support a DevSecOps approach to building multi-tenant SaaS for different platforms including Kubernetes, Red Hat OpenShift, IBM Code Engine (serverless), IBM Satellite, and public clouds including IBM Cloud, AWS and Azure. The asset includes DevSecOps toolchains to automate the process for deploying a sample cloud native application to a common platform and create per-tenant cloud services for persistence and authentication.
9 |
10 | The initial release uses Continuous Integration and Continuous Delivery (CI/CD) toolchains on IBM Cloud to deploy SaaS to IBM Code Engine, Red Hat OpenShift on IBM Cloud, or IBM Kubernetes Service on IBM Cloud. We have plans to extend the deployment to support multi-cloud using Red Hat OpenShift on AWS (ROSA) and Azure Red Hat OpenShift (ARO).
11 |
12 | This asset has been created by IBM's Build Labs:
13 |
14 | * [Adam de Leeuw](https://www.linkedin.com/in/deleeuwa/)
15 | * [Alain Airom]()
16 | * [Niklas Heidloff](https://twitter.com/nheidloff)
17 | * [Thomas Suedbroecker]()
18 |
19 | ### Challenges
20 |
21 | Organisations offering SaaS will need to deliver rapidly and often, while maintaining a strong security posture and continuous state of audit-readiness. Achieving this goal involves several teams including developers, IT operations and security. DevSecOps integrates a set of security and compliance controls and makes application and infrastructure security a shared responsibility for all these teams, and automatically bakes in security at every phase of the software development lifecycle, bringing speed and security. This is of particular importance for SaaS where the challenges and benefits are a factor of the number of tenants!
22 |
23 | The project provides a starting point for learning how to create an application which is ready for SaaS, using best practices for DevSecOps such as detecting code or container vulnerabilities, and using CI/CD pipelines to automate deployments. The key value of this asset is that it shows how to reuse the same application code, containers, and CI/CD, with the flexibility to deploy to a dedicated or shared Kubernetes cluster, while maintaining isolation and security.
24 |
25 | ### Support for multiple platforms
26 |
27 | The following diagram shows the different deployment platform options, currently including several alternatives for IBM Cloud, as indicated by the green boxes. The orange boxes represent planned future developments, including the addition of IBM Cloud Satellite which will allow the SaaS application to be deployed on-premises at client data centers, while still taking advantage of an OpenShift cluster managed by IBM Cloud. Additionally, the same SaaS application could be deployed to other managed OpenShift services like AWS ROSA and Azure ARO.
28 |
29 | The easiest way to get started is with serverless, using the fully managed IBM Code Engine platform to run the application. For more advanced cloud-native applications, a dedicated Kubernetes or OpenShift cluster can be used. Compute isolation can be achieved with a shared cluster using Kubernetes namespaces/OpenShift projects, or by having dedicated clusters for each SaaS tenant.
30 |
31 |
32 |
33 | Core technologies used:
34 |
35 | * Kubernetes using either IBM Kubernetes Service or OpenShift on IBM Cloud
36 | * IBM Code Engine (serverless)
37 | * IBM Continuous Delivery CI/CD pipelines using Tekton
38 | * IBM Cloud Databases for PostgreSQL
39 | * IBM App ID
40 | * IBM Container Registry
41 | * Terraform
42 |
43 | ### Sample e-commerce application
44 |
45 | A sample e-commerce application is provided, which is deployed as two containers. A frontend web application displays a catalogue of products. The data for the catalogue is provided by a backend microservice. Configuration properties are used extensively to customise both the frontend and backend at deployment time, including titles, connection details for the PostgreSQL database and authentication service etc. This means the same e-commerce sample application can easily be used for multiple tenants, perhaps one selling books, the other shoes.
46 |
47 | ### Automation first
48 |
49 | Everything in this project embraces automation and a series of approaches to deploy SaaS are provided, each with an increasing degree of capability. You are able to start with any of the following approaches:
50 |
51 | * Simple bash scripts to create and deploy the sample application container images, and the PostgreSQL and AppID cloud services.
52 | * A simple DevOps toolchain with CI/CD pipelines which deploys to IBM Code Engine. The pipelines orchestrate build, test, and deployment jobs (optionally across multiple environments) as changes progress from the developer to production.
53 | * A more comprehensive DevSecOps toolchain which deploys to a Kubernetes cluster. This brings a more robust process where the CI/CD pipelines ensure that code is scanned for security vulnerabilities (e.g. secrets or credentials), and repository branch protection prevents a developer from directly updating the main branch without first issuing a pull/merge request to be approved by a second developer. In addition, the container images are scanned for vulnerabilities, a dynamic application security testing tool looks for vulnerabilities in the deployed application, and application acceptance tests all contribute to a secure and quality assured release.
54 |
55 | Any of these approaches are ready to deploy the multiple tenancies of a SaaS application. Simply change the externalised properties and re-run the script or trigger the pipelines.
56 |
57 | For deployments to IBM Kubernetes Service or OpenShift, terraform templates are also provided to automate the cluster creation on IBM Cloud.
58 |
59 | ### Ready for regulated industries
60 |
61 | Regulated industries such as financial institutions, insurance, healthcare and more, all want the advantages of a hybrid cloud, but need assurance they can protect their assets and maintain compliance with industry and regulatory requirements. The key to hosting regulated workloads in the cloud is to eliminate and mitigate the risks that might be standing in the way of progress. In regulated industries, critical risks fall into the general categories of compliance, cybersecurity, governance, business continuity and resilience.
62 |
63 | The DevSecOps approach of our CI/CD pipelines are based an [IBM DevSecOps reference architecture](https://www.ibm.com/cloud/blog/announcements/devsecops-reference-implementation-for-audit-ready-compliance-across-development-teams), helping to address some of the risks faced by regulated industries. The CI/CD pipelines include steps to collect and upload deployment log files, artifacts, and evidence to a secure evidence locker. In addition, a toolchain integration to [IBM Security and Compliance Center](https://cloud.ibm.com/docs/devsecops?topic=ContinuousDelivery-scc) verifies the security and compliance posture of the toolchain by identifying the location of the evidence locker, and the presence of the evidence information.
64 |
65 | ### What's next?
66 |
67 | Our project is constantly evolving. You can expect more supported platforms including IBM Cloud Satellite and other public clouds including support for their native database and authentication services. We still have some work to do on the documentation, e.g. explaining how to observe multi-tenant runtime logs, and understand how much cloud resource each tenant is consuming, to help calculate the bills.
68 |
69 | In the meantime, we invite you to explore the [repo](https://github.com/IBM/multi-tenancy) and give it a try. Why not start by using our most simple script-based approach with IBM Code Engine, and see for yourself how easy it is is to be a SaaS provider! We would also be happy to work together with you on using this asset to build your SaaS.
70 |
71 | If you have feedback or comments, please don't hesitate to get in touch via our social media links above.
72 |
--------------------------------------------------------------------------------
/documentation/diagrams/multi-tenant-app-architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/diagrams/multi-tenant-app-architecture.jpg
--------------------------------------------------------------------------------
/documentation/diagrams/multi-tenant-app-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/diagrams/multi-tenant-app-architecture.png
--------------------------------------------------------------------------------
/documentation/example-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/example-app.png
--------------------------------------------------------------------------------
/documentation/images/IKS-Infrastructure-by-Terraform-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/IKS-Infrastructure-by-Terraform-1.png
--------------------------------------------------------------------------------
/documentation/images/Multi-tenancy-serverless.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/Multi-tenancy-serverless.png
--------------------------------------------------------------------------------
/documentation/images/VPC-Infrastructure-by-Terraform-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/VPC-Infrastructure-by-Terraform-1.png
--------------------------------------------------------------------------------
/documentation/images/VPC-Infrastructure-by-Terraform-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/VPC-Infrastructure-by-Terraform-2.png
--------------------------------------------------------------------------------
/documentation/images/VPC-Infrastructure-by-Terraform-Subnets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/VPC-Infrastructure-by-Terraform-Subnets.png
--------------------------------------------------------------------------------
/documentation/images/button-documentation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/documentation/images/button-documentation.png
--------------------------------------------------------------------------------
/installapp/appid-configs/add-application-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "FRONTENDNAME",
3 | "type": "singlepageapp"
4 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/add-redirecturis-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "redirectUris": [
3 | "http://localhost:8080/*",
4 | "APPLICATION_REDIRECT_URL/*"
5 | ],
6 | "additionalProp1": {}
7 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/add-roles-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tenant_user_access",
3 | "description": "This is an example role.",
4 | "access": [
5 | {
6 | "application_id": "APPLICATIONID",
7 | "scopes": [
8 | "tenant_scope"
9 | ]
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/add-scope.json:
--------------------------------------------------------------------------------
1 | {
2 | "scopes": [
3 | "tenant_scope"
4 | ]
5 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/add-ui-color.json:
--------------------------------------------------------------------------------
1 | {
2 | "headerColor": "#008b8b"
3 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/add-ui-text-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabTitle": "Login to FRONTENDNAME",
3 | "footnote": "Powered by the EMEA - Hybrid Cloud Build Team"
4 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/idps-clouddirectory.json:
--------------------------------------------------------------------------------
1 | {
2 | "isActive": true,
3 | "config": {
4 | "selfServiceEnabled": true,
5 | "signupEnabled": true,
6 | "interactions": {
7 | "identityConfirmation": {
8 | "accessMode": "FULL",
9 | "methods": [
10 | "email"
11 | ]
12 | },
13 | "welcomeEnabled": false,
14 | "resetPasswordEnabled": false,
15 | "resetPasswordNotificationEnable": true
16 | },
17 | "identityField": "email"
18 | }
19 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/idps-custom.json:
--------------------------------------------------------------------------------
1 | {
2 | "isActive": false
3 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/idps-facebook.json:
--------------------------------------------------------------------------------
1 | {
2 | "isActive": false
3 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/idps-google.json:
--------------------------------------------------------------------------------
1 | {
2 | "isActive": false
3 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/idps-saml.json:
--------------------------------------------------------------------------------
1 | {
2 | "isActive": false
3 | }
--------------------------------------------------------------------------------
/installapp/appid-configs/user-export.json:
--------------------------------------------------------------------------------
1 | {"itemsPerPage":1,"totalResults":1,"users":[{"scimUser":{"originalId":"7cdf7ac3-371f-4b4c-8d0a-81e479ab449b","name":{"givenName":"Thomas","familyName":"Example","formatted":"Thomas Example"},"displayName":"Thomas Example","active":true,"emails":[{"value":"thomas@example.com","primary":true}],"passwordHistory":[{"passwordHash":"L6EEYnQANBPSBF0tDCPDZl4uVD07H3Ur8qIVynB1Ht4Bn4s/x0lA6kvyJxEPr/06m5hi5wdLM45JtYDlT8M0hjVIBI3YpXRR9J4oXZA/Yt/V13yjsUPsXKek6RWdOKWp+wuD5w3Bobh43QbRR3dXFoKUbcLVWQoKLWqvRATMQis=","hashAlgorithm":"PBKDF2WithHmacSHA512"}],"status":"CONFIRMED","passwordExpirationTimestamp":0,"passwordUpdatedTimestamp":0,"mfaContext":{}},"passwordHash":"L6EEYnQANBPSBF0tDCPDZl4uVD07H3Ur8qIVynB1Ht4Bn4s/x0lA6kvyJxEPr/06m5hi5wdLM45JtYDlT8M0hjVIBI3YpXRR9J4oXZA/Yt/V13yjsUPsXKek6RWdOKWp+wuD5w3Bobh43QbRR3dXFoKUbcLVWQoKLWqvRATMQis=","passwordHashAlg":"PBKDF2WithHmacSHA512","profile":{"attributes":{}},"roles":["ce_user_access"]}]}
2 |
--------------------------------------------------------------------------------
/installapp/appid-configs/user-import.json:
--------------------------------------------------------------------------------
1 | {"itemsPerPage":1,"totalResults":1,"users":[{"scimUser":{"originalId":"7cdf7ac3-371f-4b4c-8d0a-81e479ab449b","name":{"givenName":"Thomas","familyName":"Example","formatted":"Thomas Example"},"displayName":"Thomas Example","active":true,"emails":[{"value":"thomas@example.com","primary":true}],"passwordHistory":[{"passwordHash":"L6EEYnQANBPSBF0tDCPDZl4uVD07H3Ur8qIVynB1Ht4Bn4s/x0lA6kvyJxEPr/06m5hi5wdLM45JtYDlT8M0hjVIBI3YpXRR9J4oXZA/Yt/V13yjsUPsXKek6RWdOKWp+wuD5w3Bobh43QbRR3dXFoKUbcLVWQoKLWqvRATMQis=","hashAlgorithm":"PBKDF2WithHmacSHA512"}],"status":"CONFIRMED","passwordExpirationTimestamp":0,"passwordUpdatedTimestamp":0,"mfaContext":{}},"passwordHash":"L6EEYnQANBPSBF0tDCPDZl4uVD07H3Ur8qIVynB1Ht4Bn4s/x0lA6kvyJxEPr/06m5hi5wdLM45JtYDlT8M0hjVIBI3YpXRR9J4oXZA/Yt/V13yjsUPsXKek6RWdOKWp+wuD5w3Bobh43QbRR3dXFoKUbcLVWQoKLWqvRATMQis=","passwordHashAlg":"PBKDF2WithHmacSHA512","profile":{"attributes":{}},"roles":["tenant_user_access"]}]}
2 |
--------------------------------------------------------------------------------
/installapp/appid-images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/multi-tenancy/ef6dc9ebfaec0e8041964ec0407eb648ef35ac17/installapp/appid-images/logo.png
--------------------------------------------------------------------------------
/installapp/backup-bash-scripts/ce-build-images-quay-podman.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **************** Global variables
4 | export REPOSITORY=tsuedbroecker
5 | export SERVICE_CATALOG="multi-tenancy-service-catalog:v1"
6 | export FRONTEND="multi-tenancy-frontend:v1"
7 | #quay.io/tsuedbroecker/multi-tenancy-service-catalog:v1
8 | # **********************************************************************************
9 | # Execution
10 | # **********************************************************************************
11 |
12 | cd ..
13 | export ROOT_PATH=$(PWD)
14 | echo "Path: $ROOT_PATH"
15 |
16 | echo "************************************"
17 | echo " Clean up container if needed"
18 | echo "************************************"
19 | podman image list
20 | podman container list
21 | #podman container stop -f "TBD"
22 | #podman container rm -f "TBD"
23 | podman image prune -a -f
24 | podman version
25 | podman image rm -f "$SERVICE_CATALOG"
26 | podman image rm -f "$FRONTEND"
27 | # rm -rf ~/var/home/core/.local/share/containers/storage/overlay/* f
28 | #podman image rm -f "docker.io/adoptopenjdk/maven-openjdk11"
29 | #podman image rm -f "docker.io/adoptopenjdk/openjdk11-openj9:ubi-minimal"
30 | #podman image rm -f "registry.access.redhat.com/ubi8/ubi-minimal"
31 |
32 | echo "************************************"
33 | echo " Service catalog $SERVICE_CATALOG"
34 | echo "************************************"
35 | cd $ROOT_PATH/code/service-catalog-tmp
36 | pwd
37 | podman login quay.io
38 | podman build -t "quay.io/$REPOSITORY/$SERVICE_CATALOG" -f Dockerfile.simple-v1 .
39 | # podman push "quay.io/$REPOSITORY/$SERVICE_CATALOG"
40 |
41 | echo ""
42 |
43 | echo "************************************"
44 | echo " Frontend $FRONTEND"
45 | echo "************************************"
46 | cd $ROOT_PATH/code/frontend
47 | #pwd
48 | # podman login quay.io
49 | # podman build -t "quay.io/$REPOSITORY/$FRONTEND" -f Dockerfile.os4-webapp .
50 | # podman push "quay.io/$REPOSITORY/$FRONTEND"
51 |
--------------------------------------------------------------------------------
/installapp/backup-bash-scripts/run-two-applications.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #export REPOSITORY=$MY_REPOSITORY # Example to set the variable: export MY_REPOSITORY=tsuedbroecker
3 | export REPOSITORY=kdeif # Example to set the variable: export MY_REPOSITORY=tsuedbroecker
4 |
5 | echo "************************************"
6 | echo " frontend"
7 | echo "************************************"
8 | #docker login quay.io
9 |
10 | echo "************************************"
11 | echo " Build frontend"
12 | echo "************************************"
13 | #docker build -t "quay.io/$REPOSITORY/frontend:v0.0" -f Dockerfile.os4-webapp .
14 | docker build -t "frontend:v0.0" -f Dockerfile.os4-webapp .
15 |
16 | echo "************************************"
17 | echo " Test frontend container in interactive mode"
18 | echo " -d or -it"
19 | echo "************************************"
20 |
21 | docker run --name="frontend-a" \
22 | -d \
23 | -e VUE_APPID_CLIENT_ID='b3adeb3b-36fc-40cb-9bc3-dd6f15047195' \
24 | -e VUE_APPID_DISCOVERYENDPOINT='https://us-south.appid.cloud.ibm.com/oauth/v4/a7ec8ce4-3602-42c7-8e88-6f8a9db31935/.well-known/openid-configuration' \
25 | -e VUE_APP_API_URL_PRODUCTS='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/base/category/' \
26 | -e VUE_APP_API_URL_ORDERS='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/base/Customer/Orders' \
27 | -e VUE_APP_API_URL_CATEGORIES='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/base/category' \
28 | -e VUE_APP_CATEGORY_NAME='Movies' \
29 | -e VUE_APP_HEADLINE='Frontend A' \
30 | -p 8080:8081 \
31 | frontend:v0.0
32 | #"quay.io/$REPOSITORY/frontend-a:v0.0"
33 |
34 | docker run --name="frontend-b" \
35 | -d \
36 | -e VUE_APPID_CLIENT_ID='b3adeb3b-36fc-40cb-9bc3-dd6f15047195' \
37 | -e VUE_APPID_DISCOVERYENDPOINT='https://us-south.appid.cloud.ibm.com/oauth/v4/a7ec8ce4-3602-42c7-8e88-6f8a9db31935/.well-known/openid-configuration' \
38 | -e VUE_APP_API_URL_PRODUCTS='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/mycompany/category/' \
39 | -e VUE_APP_API_URL_ORDERS='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/mycompany/Customer/Orders' \
40 | -e VUE_APP_API_URL_CATEGORIES='https://service-catalog.ceqctuyxg6m.us-south.codeengine.appdomain.cloud/mycompany/category' \
41 | -e VUE_APP_CATEGORY_NAME='Fantasy' \
42 | -e VUE_APP_HEADLINE='Frontend B' \
43 | -p 8082:8081 \
44 | frontend:v0.0
45 | #"quay.io/$REPOSITORY/frontend-b:v0.0"
46 |
47 | echo "************************************"
48 | echo "Push frontend"
49 | echo "************************************"
50 | #docker push "quay.io/$REPOSITORY/frontend:v0.0"
51 |
--------------------------------------------------------------------------------
/installapp/ce-build-images-ibm-buildah.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "************************************"
4 | echo " Display parameter"
5 | echo "************************************"
6 | echo ""
7 | echo "Parameter count : $@"
8 | echo "Parameter zero 'name of the script': $0"
9 | echo "---------------------------------"
10 | echo "Service catalog image : $1"
11 | echo "Frontend image : $2"
12 | echo "Registry URL : $3"
13 | echo "---------------------------------"
14 | echo ""
15 |
16 | # **************** Global variables
17 | export SERVICE_CATALOG_IMAGE=$1
18 | export FRONTEND_IMAGE=$2
19 | export REGISTRY_URL=$3
20 |
21 | export ROOT_PROJECT=multi-tenancy
22 | export FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
23 | export BACKEND_SOURCEFOLDER=multi-tenancy-backend
24 |
25 |
26 | # **********************************************************************************
27 | # Functions
28 | # **********************************************************************************
29 |
30 | cleanUpLocalImages() {
31 |
32 | echo "************************************"
33 | echo " Clean up local images, if needed"
34 | echo "************************************"
35 |
36 | buildah rmi -f "$SERVICE_CATALOG_IMAGE"
37 | buildah rmi -f "$FRONTEND_IMAGE"
38 | }
39 |
40 | setROOT_PATH() {
41 | echo "************************************"
42 | echo " Set ROOT_PATH"
43 | echo "************************************"
44 | cd ../../
45 | export ROOT_PATH=$(pwd)
46 | echo "Path: $ROOT_PATH"
47 | }
48 |
49 | buildAndPushBackend() {
50 | echo "************************************"
51 | echo " Backend $SERVICE_CATALOG_IMAGE"
52 | echo "************************************"
53 | cd $ROOT_PATH/$BACKEND_SOURCEFOLDER
54 |
55 | ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
56 |
57 | buildah bud -t "$SERVICE_CATALOG_IMAGE" -f Dockerfile .
58 | buildah push "$SERVICE_CATALOG_IMAGE"
59 | echo ""
60 | }
61 |
62 | buildAndPushFrontend() {
63 | echo "************************************"
64 | echo " Frontend $FRONTEND_IMAGE"
65 | echo "************************************"
66 | cd $ROOT_PATH/$FRONTEND_SOURCEFOLDER
67 |
68 | ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
69 |
70 | buildah bud -t "$FRONTEND_IMAGE" -f Dockerfile .
71 | buildah push "$FRONTEND_IMAGE"
72 | echo ""
73 | }
74 |
75 | resetPath() {
76 | echo "************************************"
77 | echo " Reset path"
78 | echo "************************************"
79 | cd $ROOT_PATH/$ROOT_PROJECT
80 | echo ""
81 | }
82 |
83 |
84 | # **********************************************************************************
85 | # Execution
86 | # **********************************************************************************
87 |
88 | setROOT_PATH
89 | cleanUpLocalImages
90 | buildAndPushBackend
91 | buildAndPushFrontend
92 | resetPath
93 |
--------------------------------------------------------------------------------
/installapp/ce-check-prerequisites.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **************** Global variables
4 |
5 | export CHECK_IBMCLOUDCLI="ibmcloud"
6 | export CHECK_JQ="jq-"
7 | export CHECK_SED="sed"
8 | export CHECK_AWK="mawk:"
9 | export CHECK_CURL="curl"
10 | export CHECK_BUILDAH="buildah"
11 | export CHECK_KUBECTL="Client"
12 | export CHECK_PLUGIN_CLOUDDATABASES="cloud-databases"
13 | export CHECK_PLUGIN_CODEENGINE="code-engine"
14 | export CHECK_PLUGIN_CONTAINERREGISTERY="container-registry"
15 | export CHECK_GREP="grep"
16 | export CHECK_LIBPQ="psql"
17 |
18 | export VERICATION=""
19 |
20 |
21 | # **********************************************************************************
22 | # Functions definition
23 | # **********************************************************************************
24 |
25 | verifyLibpq() {
26 | VERICATION=$(psql --version)
27 |
28 | if [[ $VERICATION =~ $CHECK_LIBPQ ]]; then
29 | echo "- libpq (psql) is installed: $VERICATION !"
30 | else
31 | echo "*** libpq (psql) is NOT installed !"
32 | echo "*** The scripts ends here!"
33 | exit 1
34 | fi
35 | }
36 |
37 | verifyGrep() {
38 | VERICATION=$(grep --version)
39 |
40 | if [[ $VERICATION =~ $CHECK_GREP ]]; then
41 | echo "- Grep is installed: $VERICATION !"
42 | else
43 | echo "*** Grep is NOT installed !"
44 | echo "*** The scripts ends here!"
45 | exit 1
46 | fi
47 | }
48 |
49 | verifyCURL() {
50 | VERICATION=$(curl --version)
51 |
52 | if [[ $VERICATION =~ $CHECK_CURL ]]; then
53 | echo "- cURL is installed: $VERICATION !"
54 | else
55 | echo "*** cURL is NOT installed !"
56 | echo "*** The scripts ends here!"
57 | exit 1
58 | fi
59 | }
60 |
61 | verifyAWK() {
62 | VERICATION=$(mawk -W)
63 |
64 | if [[ $VERICATION =~ $CHECK_AWK ]]; then
65 | echo "- AWK is installed: $VERICATION !"
66 | else
67 | echo "*** AWK is NOT installed !"
68 | echo "*** The scripts ends here!"
69 | exit 1
70 | fi
71 | }
72 |
73 | verifySed() {
74 | VERICATION=$(sed --version)
75 |
76 | if [[ $VERICATION =~ $CHECK_SED ]]; then
77 | echo "- Sed is installed: $VERICATION !"
78 | else
79 | echo "*** Sed is NOT installed !"
80 | echo "*** The scripts ends here!"
81 | exit 1
82 | fi
83 | }
84 |
85 | verifyIBMCloudCLI() {
86 | VERICATION=$(ibmcloud --version)
87 |
88 | if [[ $VERICATION =~ $CHECK_IBMCLOUDCLI ]]; then
89 | echo "- IBM Cloud CLI is installed: $VERICATION !"
90 | else
91 | echo "*** IBM Cloud CLI is NOT installed !"
92 | echo "*** The scripts ends here!"
93 | exit 1
94 | fi
95 | }
96 |
97 | verifyJQ() {
98 | VERICATION=$(jq --version)
99 |
100 | if [[ $VERICATION =~ $CHECK_JQ ]]; then
101 | echo "- JQ is installed: $VERICATION !"
102 | else
103 | echo "*** JQ is NOT installed !"
104 | echo "*** The scripts ends here!"
105 | exit 1
106 | fi
107 | }
108 |
109 | verifyIBMCloudPluginCloudDatabases() {
110 | VERICATION=$(ibmcloud plugin show cloud-databases | grep 'Plugin Name' | awk '{print $3}' )
111 |
112 | if [[ $VERICATION =~ $CHECK_PLUGIN_CLOUDDATABASES ]]; then
113 | echo "- IBM Cloud Plugin 'cloud-databases' is installed: $VERICATION !"
114 | else
115 | echo "IBM Cloud Plugin 'cloud-databases' is NOT installed !"
116 | echo "*** The scripts ends here!"
117 | exit 1
118 | fi
119 | }
120 |
121 | verifyIBMCloudPluginCodeEngine() {
122 | VERICATION=$(ibmcloud plugin show code-engine | grep 'Plugin Name' | awk '{print $3}' )
123 |
124 | if [[ $VERICATION =~ $CHECK_PLUGIN_CODEENGINE ]]; then
125 | echo "- IBM Cloud Plugin 'code-engine' is installed: $VERICATION !"
126 | else
127 | echo "IBM Cloud Plugin 'code-engine' is NOT installed !"
128 | echo "*** The scripts ends here!"
129 | exit 1
130 | fi
131 | }
132 |
133 | verifyIBMCloudPluginContainerRegistry() {
134 | VERICATION=$(ibmcloud plugin show container-registry | grep 'Plugin Name' | awk '{print $3}' )
135 |
136 | if [[ $VERICATION =~ $CHECK_PLUGIN_CONTAINERREGISTERY ]]; then
137 | echo "- IBM Cloud Plugin 'container-registry' is installed: $VERICATION !"
138 | else
139 | echo "IBM Cloud Plugin 'container-registry' is NOT installed !"
140 | echo "*** The scripts ends here!"
141 | exit 1
142 | fi
143 | }
144 |
145 | verifyBuildah() {
146 | VERICATION=$(buildah --version)
147 |
148 | if [[ $VERICATION =~ $CHECK_BUILDAH ]]; then
149 | echo "- buildah is installed: $VERICATION !"
150 | else
151 | echo "*** buildah is NOT installed !"
152 | echo "*** The scripts ends here!"
153 | exit 1
154 | fi
155 | }
156 |
157 | verifyKubectl() {
158 | VERICATION=$(kubectl version)
159 |
160 | if [[ $VERICATION =~ $CHECK_KUBECTL ]]; then
161 | echo "- kubectl is installed: $VERICATION !"
162 | else
163 | echo "*** kubectl is NOT installed !"
164 | echo "*** The scripts ends here!"
165 | exit 1
166 | fi
167 | }
168 |
169 | # **********************************************************************************
170 | # Execution
171 | # **********************************************************************************
172 |
173 | echo "Check prereqisites"
174 | echo "1. Verify grep"
175 | verifyGrep
176 | echo "2. Verify awk"
177 | # verifyAWK
178 | echo "3. Verify cURL"
179 | verifyCURL
180 | echo "4. Verify Sed"
181 | verifySed
182 | echo "5. Verify jq"
183 | verifyJQ
184 | echo "6. Verify libpq (psql)"
185 | verifyLibpq
186 | echo "7. Verify Buildah"
187 | verifyBuildah
188 | echo "8. Verify ibmcloud cli"
189 | verifyIBMCloudCLI
190 | echo "9. Verify ibmcloud plugin cloud-databases"
191 | verifyIBMCloudPluginCloudDatabases
192 | echo "10. Verify ibmcloud plugin code-engine"
193 | verifyIBMCloudPluginCodeEngine
194 | echo "11. Verify ibmcloud plugin container-registry"
195 | verifyIBMCloudPluginContainerRegistry
196 | echo "12. Verify kubectl"
197 | verifyKubectl
198 |
199 | echo "Success! All prerequisites verified!"
200 |
--------------------------------------------------------------------------------
/installapp/ce-clean-up-two-tenancies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # CLI tools Documentation
4 | # ================
5 | # IBM Cloud CLI
6 | # Code Engine: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
7 | # Cloud databases
8 | # IBM Cloud Container Registry
9 |
10 | # Needed IBM Cloud CLI plugins
11 | # =============
12 | # - code engine
13 | # - cloud databases (ibmcloud plugin install cloud-databases)
14 | # - ibm cloud container registry
15 |
16 | # Needed tools
17 | # ============
18 | # brew install libpq
19 | # brew link --force libpq
20 |
21 | # Install jq to extract json in bash on mac
22 | # ===============
23 | # brew install jq
24 |
25 | # **************** Global variables
26 | # Configurations
27 | export GLOBAL="../configuration/global.json"
28 | export TENANT_A="../configuration/tenants/tenant-a.json"
29 | export TENANT_B="../configuration/tenants/tenant-b.json"
30 |
31 | # **********************************************************************************
32 | # Functions definition
33 | # **********************************************************************************
34 |
35 | # TBD
36 |
37 | # **********************************************************************************
38 | # Execution
39 | # **********************************************************************************
40 |
41 | echo "************************************"
42 | echo " Clean Tenant A"
43 | echo "************************************"
44 |
45 | bash ./ce-clean-up.sh $GLOBAL $TENANT_A
46 |
47 | bash ./ce-clean-up.sh $GLOBAL $TENANT_B
--------------------------------------------------------------------------------
/installapp/ce-clean-up.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # CLI tools Documentation
4 | # ================
5 | # Code Engine: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
6 | # Cloud databases
7 | # IBM Cloud Container Registry
8 |
9 | # Needed IBM Cloud CLI plugins
10 | # =============
11 | # - code engine
12 | # - cloud databases (ibmcloud plugin install cloud-databases)
13 | # - container registry
14 |
15 | # Needed tools
16 | # ============
17 | # For Postgres database
18 | # - brew install libpq
19 | # - brew link --force libpq
20 | # Install jq to extract json in bash on mac
21 | # - brew install jq
22 |
23 | echo "************************************"
24 | echo " Display parameter"
25 | echo "************************************"
26 | echo ""
27 | echo "Parameter count : $@"
28 | echo "Parameter zero 'name of the script': $0"
29 | echo "---------------------------------"
30 | echo "Global configuration : $1"
31 | echo "Tenant configuration : $2"
32 | echo "---------------------------------"
33 |
34 | # **************** Global variables set by parameters
35 |
36 | # Globale config
37 | # --------------
38 | # IBM Cloud Container Registry
39 | export IBM_CR_SERVER=$(cat ./$1 | jq '.REGISTRY.URL' | sed 's/"//g')
40 | # CE for IBM Cloud Container Registry access
41 | export SECRET_NAME=$(cat ./$1 | jq '.REGISTRY.SECRET_NAME' | sed 's/"//g')
42 | export IBMCLOUDCLI_KEY_NAME="cliapikey_for_multi_tenant_$PROJECT_NAME"
43 | export REGISTRY_URL=$(cat ./$1 | jq '.REGISTRY.URL' | sed 's/"//g')
44 | export IBM_CR_SERVER=$REGISTRY_URL
45 | export IMAGE_TAG=$(cat ./$1 | jq '.REGISTRY.TAG' | sed 's/"//g')
46 | export CR_NAMESPACE=$(cat ./$1 | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
47 | # IBM Cloud target
48 | export RESOURCE_GROUP=$(cat ./$1 | jq '.IBM_CLOUD.RESOURCE_GROUP' | sed 's/"//g')
49 | export REGION=$(cat ./$1 | jq '.IBM_CLOUD.REGION' | sed 's/"//g')
50 | # ecommerce application container registry
51 | export FRONTEND_IMAGE_NAME=$(cat ./$1 | jq '.IMAGES.NAME_FRONTEND' | sed 's/"//g')
52 | export BACKEND_IMAGE_NAME=$(cat ./$1 | jq '.IMAGES.NAME_BACKEND' | sed 's/"//g')
53 | export FRONTEND_IMAGE="$REGISTRY_URL/$CR_NAMESPACE/$FRONTEND_IMAGE_NAME:$IMAGE_TAG"
54 | export SERVICE_CATALOG_IMAGE="$REGISTRY_URL/$CR_NAMESPACE/$BACKEND_IMAGE_NAME:$IMAGE_TAG"
55 |
56 | # Tenant config
57 | # --------------
58 | # Code Engine
59 | export PROJECT_NAME=$(cat ./$2 | jq '.CODE_ENGINE.PROJECT_NAME' | sed 's/"//g')
60 | # postgres
61 | export POSTGRES_SERVICE_INSTANCE=$(cat ./$2 | jq '.POSTGRES.SERVICE_INSTANCE' | sed 's/"//g')
62 | export POSTGRES_SERVICE_KEY_NAME=$(cat ./$2 | jq '.POSTGRES.SERVICE_KEY_NAME' | sed 's/"//g')
63 | export POSTGRES_SQL_FILE=$(cat ./$2 | jq '.POSTGRES.SQL_FILE' | sed 's/"//g')
64 | # ecommerce application names
65 | export SERVICE_CATALOG_NAME=$(cat ./$2 | jq '.APPLICATION.CONTAINER_NAME_BACKEND' | sed 's/"//g')
66 | export FRONTEND_NAME=$(cat ./$2 | jq '.APPLICATION.CONTAINER_NAME_FRONTEND' | sed 's/"//g')
67 | export FRONTEND_CATEGORY=$(cat ./$2 | jq '.APPLICATION.CATEGORY' | sed 's/"//g')
68 | # App ID
69 | export APPID_SERVICE_INSTANCE_NAME=$(cat ./$2 | jq '.APP_ID.SERVICE_INSTANCE' | sed 's/"//g')
70 | export APPID_SERVICE_KEY_NAME=$(cat ./$2 | jq '.APP_ID.SERVICE_KEY_NAME' | sed 's/"//g')
71 | export IBMCLOUDCLI_KEY_NAME="cliapikey_for_multi_tenant_$PROJECT_NAME"
72 |
73 | echo "Code Engine project : $PROJECT_NAME"
74 | echo "---------------------------------"
75 | echo "App ID service instance name : $APPID_SERVICE_INSTANCE_NAME"
76 | echo "App ID service key name : $APPID_SERVICE_KEY_NAME"
77 | echo "---------------------------------"
78 | echo "Application Service Catalog name : $SERVICE_CATALOG_NAME"
79 | echo "Application Frontend name : $FRONTEND_NAME"
80 | echo "Application Frontend category : $FRONTEND_CATEGORY"
81 | echo "Application Service Catalog image: $SERVICE_CATALOG_IMAGE"
82 | echo "Application Frontend image : $FRONTEND_IMAGE"
83 | echo "---------------------------------"
84 | echo "Postgres instance name : $POSTGRES_SERVICE_INSTANCE"
85 | echo "Postgres service key name : $POSTGRES_SERVICE_KEY_NAME"
86 | echo "Postgres sample data sql : $POSTGRES_SQL_FILE"
87 | echo "---------------------------------"
88 | echo "IBM Cloud Container Registry URL : $IBM_CR_SERVER"
89 | echo "Registry Namespace : $CR_NAMESPACE"
90 | echo "---------------------------------"
91 | echo "IBM Cloud RESOURCE_GROUP : $RESOURCE_GROUP"
92 | echo "IBM Cloud REGION : $REGION"
93 | echo "---------------------------------"
94 | echo ""
95 | echo "Verify parameters and press return"
96 | read input
97 |
98 | # **********************************************************************************
99 | # Functions definition
100 | # **********************************************************************************
101 |
102 | setupCLIenvCE() {
103 | echo "**********************************"
104 | echo " Using following project: $PROJECT_NAME"
105 | echo "**********************************"
106 |
107 | ibmcloud target -g $RESOURCE_GROUP
108 | ibmcloud target -r $REGION
109 |
110 | ibmcloud ce project get --name $PROJECT_NAME
111 | ibmcloud ce project select -n $PROJECT_NAME
112 |
113 | #to use the kubectl commands
114 | ibmcloud ce project select -n $PROJECT_NAME --kubecfg
115 |
116 | NAMESPACE=$(ibmcloud ce project get --name $PROJECT_NAME --output json | grep "namespace" | awk '{print $2;}' | sed 's/"//g' | sed 's/,//g')
117 | echo "Namespace: $NAMESPACE"
118 | kubectl get pods -n $NAMESPACE
119 | }
120 |
121 | cleanIBMContainerImages() {
122 |
123 | echo "delete images"
124 | ibmcloud target -g $RESOURCE_GROUP
125 | ibmcloud target -r $REGION
126 | ibmcloud target
127 | # login with buildah
128 | ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
129 |
130 | ibmcloud cr image-rm $SERVICE_CATALOG_IMAGE
131 | ibmcloud cr image-rm $FRONTEND_IMAGE
132 |
133 | }
134 |
135 | cleanIBMContainerNamespace () {
136 |
137 | echo "delete namespace"
138 | ibmcloud target -g $RESOURCE_GROUP
139 | ibmcloud target -r $REGION
140 | ibmcloud target
141 | # login with buildah
142 | ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
143 |
144 | ibmcloud cr namespace-rm $CR_NAMESPACE
145 | }
146 |
147 | cleanCEsecrets () {
148 |
149 | echo "delete secrects postgres"
150 | ibmcloud ce secret delete --name postgres.certificate-data --force
151 | ibmcloud ce secret delete --name postgres.username --force
152 | ibmcloud ce secret delete --name postgres.password --force
153 | ibmcloud ce secret delete --name postgres.url --force
154 |
155 | echo "delete secrets appid"
156 | ibmcloud ce secret delete --name appid.discovery-endpoint --force
157 | ibmcloud ce secret delete --name appid.oauthserverurl --force
158 | ibmcloud ce secret delete --name appid.client-id-catalog-service --force
159 | ibmcloud ce secret delete --name appid.client-id-fronted --force
160 |
161 | }
162 |
163 | cleanCEapplications () {
164 |
165 | ibmcloud ce application delete --name $FRONTEND_NAME --force
166 | ibmcloud ce application delete --name $SERVICE_CATALOG_NAME --force
167 | }
168 |
169 | cleanCEregistry(){
170 |
171 | ibmcloud ce registry delete --name $SECRET_NAME
172 | }
173 |
174 | cleanKEYS () {
175 |
176 | echo "IBM Cloud Key: $IBMCLOUDCLI_KEY_NAME"
177 | #List api-keys
178 | ibmcloud iam api-keys | grep $IBMCLOUDCLI_KEY_NAME
179 | #Delete api-key
180 | ibmcloud iam api-key-delete $IBMCLOUDCLI_KEY_NAME -f
181 |
182 | #AppID
183 | ibmcloud resource service-keys | grep $APPID_SERVICE_KEY_NAME
184 | ibmcloud resource service-keys --instance-name $APPID_SERVICE_INSTANCE_NAME
185 | ibmcloud resource service-key-delete $APPID_SERVICE_KEY_NAME -f
186 |
187 | #Postgres
188 | ibmcloud resource service-keys | grep $POSTGRES_SERVICE_NAME
189 | ibmcloud resource service-keys --instance-name $POSTGRES_SERVICE_NAME
190 | ibmcloud resource service-key-delete $POSTGRES_SERVICE_KEY_NAME -f
191 | }
192 |
193 | cleanAppIDservice (){
194 | ibmcloud resource service-instance $APPID_SERVICE_INSTANCE_NAME
195 | ibmcloud resource service-instance-delete $APPID_SERVICE_INSTANCE_NAME -f
196 | }
197 |
198 | cleanPostgresService (){
199 | ibmcloud resource service-instance $POSTGRES_SERVICE_INSTANCE
200 | ibmcloud resource service-instance-delete $POSTGRES_SERVICE_INSTANCE -f
201 | }
202 |
203 | cleanCodeEngineProject (){
204 | ibmcloud ce project delete --name $PROJECT_NAME
205 | }
206 |
207 | # **********************************************************************************
208 | # Execution
209 | # **********************************************************************************
210 |
211 | echo "************************************"
212 | echo " CLI config"
213 | echo "************************************"
214 |
215 | setupCLIenvCE
216 |
217 | echo "************************************"
218 | echo " Clean secrets"
219 | echo "************************************"
220 |
221 | cleanCEsecrets
222 |
223 | echo "************************************"
224 | echo " Clean CE apps"
225 | echo "************************************"
226 |
227 | cleanCEapplications
228 |
229 | echo "************************************"
230 | echo " Clean CE registry"
231 | echo "************************************"
232 |
233 | cleanCEregistry
234 |
235 | echo "************************************"
236 | echo " Clean IBM ContainerImages registry"
237 | echo "************************************"
238 |
239 | cleanIBMContainerImages
240 | # To avoid deletion of the Container Registry Namespace
241 | # please comment out the `cleanIBMContainerNamespace`
242 | cleanIBMContainerNamespace
243 |
244 | echo "************************************"
245 | echo " Clean keys "
246 | echo " - $IBMCLOUDCLI_KEY_NAME"
247 | echo " - $APPID_SERVICE_KEY_NAME"
248 | echo " - $POSTGRES_SERVICE_KEY_NAME"
249 | echo "************************************"
250 |
251 | cleanKEYS
252 |
253 | echo "************************************"
254 | echo " Clean AppID service $APPID_INSTANCE_NAME"
255 | echo "************************************"
256 |
257 | cleanAppIDservice
258 |
259 | echo "************************************"
260 | echo " Clean Postgres service $POSTGRES_SERVICE_INSTANCE"
261 | echo "************************************"
262 |
263 | cleanPostgresService
264 |
265 | echo "************************************"
266 | echo " Clean Code Engine Project $PROJECT_NAME"
267 | echo "************************************"
268 |
269 | # To avoid the deletion of the Code Engine project
270 | # please comment out the `cleanCodeEngineProject`
271 | cleanCodeEngineProject
--------------------------------------------------------------------------------
/installapp/ce-create-two-tenancies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # CLI Documentation
3 | # ================
4 | # command documentation: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
5 |
6 | # Install jq to extract json in bash on mac
7 | # ===============
8 | # brew install jq
9 |
10 | # **************** Global variables
11 |
12 | # Configurations
13 | export GLOBAL="../configuration/global.json"
14 | export TENANT_A="../configuration/tenants/tenant-a.json"
15 | export TENANT_B="../configuration/tenants/tenant-b.json"
16 |
17 | # ecommerce application container image names
18 | export REGISTRY_URL=$(cat ./$GLOBAL | jq '.REGISTRY.URL' | sed 's/"//g')
19 | export IMAGE_TAG=$(cat ./$GLOBAL | jq '.REGISTRY.TAG' | sed 's/"//g')
20 | export NAMESPACE=$(cat ./$GLOBAL | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
21 | export FRONTEND_IMAGE_NAME=$(cat ./$GLOBAL | jq '.IMAGES.NAME_FRONTEND' | sed 's/"//g')
22 | export BACKEND_IMAGE_NAME=$(cat ./$GLOBAL | jq '.IMAGES.NAME_BACKEND' | sed 's/"//g')
23 | export FRONTEND_IMAGE="$REGISTRY_URL/$NAMESPACE/$FRONTEND_IMAGE_NAME:$IMAGE_TAG"
24 | export SERVICE_CATALOG_IMAGE="$REGISTRY_URL/$NAMESPACE/$BACKEND_IMAGE_NAME:$IMAGE_TAG"
25 |
26 | # Registry settings
27 | # ibm cloud container registry settings
28 | export IBMCLOUD_CR_NAMESPACE=$(cat ./$GLOBAL | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
29 | export IBMCLOUD_CR_REGION_URL=$(cat ./$GLOBAL | jq '.REGISTRY.URL' | sed 's/"//g')
30 | export IBMCLOUD_CR_TAG=$(cat ./$GLOBAL | jq '.REGISTRY.TAG' | sed 's/"//g')
31 |
32 | # IBM Cloud target
33 | export RESOURCE_GROUP=$(cat ./$GLOBAL | jq '.IBM_CLOUD.RESOURCE_GROUP' | sed 's/"//g')
34 | export REGION=$(cat ./$GLOBAL | jq '.IBM_CLOUD.REGION' | sed 's/"//g')
35 |
36 | # **********************************************************************************
37 | # Functions definition
38 | # **********************************************************************************
39 | # FYI: https://stackoverflow.com/questions/12468889/bash-script-error-function-not-found-why-would-this-appear
40 |
41 | createNamespaceBuildah() {
42 |
43 | echo "IBMCLOUD_CR_NAMESPACE: $IBMCLOUD_CR_NAMESPACE"
44 | echo "RESOURCE_GROUP : $RESOURCE_GROUP"
45 | echo "REGION : $REGION"
46 | echo "------------------------------"
47 | echo "Verify the given entries and press return"
48 |
49 | read input
50 |
51 | ibmcloud target -g $RESOURCE_GROUP
52 | ibmcloud target -r $REGION
53 | ibmcloud target
54 | # login with buildah
55 | ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $IBMCLOUD_CR_REGION_URL
56 | RESULT=$(ibmcloud cr namespace-add $IBMCLOUD_CR_NAMESPACE | grep "FAILED")
57 |
58 | if [[ $RESULT =~ "FAILED" ]]; then
59 | echo "*** Namespace $IBMCLOUD_CR_NAMESPACE in IBM Cloud Container Registry NOT created !"
60 | echo "*** The scripts ends here!"
61 | echo "*** Please define a different namespace name in the 'configuration/global.json' file."
62 | exit 1
63 | else
64 | echo "- Namespace $IBMCLOUD_CR_NAMESPACE in IBM Cloud Container Registry created !"
65 | fi
66 |
67 | }
68 |
69 | createAndPushIBMContainer () {
70 |
71 | echo "FRONTEND_IMAGE: $FRONTEND_IMAGE"
72 | echo "SERVICE_CATALOG_IMAGE: $SERVICE_CATALOG_IMAGE"
73 | echo "IBMCLOUD_CR_REGION_URL: $IBMCLOUD_CR_REGION_URL"
74 |
75 | createNamespaceBuildah
76 |
77 | bash ./ce-build-images-ibm-buildah.sh $SERVICE_CATALOG_IMAGE $FRONTEND_IMAGE
78 |
79 | if [ $? == "1" ]; then
80 | echo "*** Creation of the container images failed !"
81 | echo "*** The script 'ce-create-two-tenancies.sh' ends here!"
82 | exit 1
83 | fi
84 | }
85 |
86 | # **********************************************************************************
87 | # Execution
88 | # **********************************************************************************
89 |
90 | echo "************************************"
91 | echo " Create and push container image to IBM Container Registry"
92 | echo "************************************"
93 |
94 | createAndPushIBMContainer
95 |
96 | echo "************************************"
97 | echo " Tenant A"
98 | echo "************************************"
99 |
100 | bash ./ce-install-application.sh $GLOBAL $TENANT_A
101 |
102 | if [ $? == "1" ]; then
103 | echo "*** The installation for '$GLOBAL' '$TENANT_A' configuation failed !"
104 | echo "*** The script 'ce-create-two-tenancies.sh' ends here!"
105 | exit 1
106 | fi
107 |
108 | echo "************************************"
109 | echo " Tenant B"
110 | echo "************************************"
111 |
112 | bash ./ce-install-application.sh $GLOBAL $TENANT_B
113 |
114 | if [ $? == "1" ]; then
115 | echo "*** The installation for '$GLOBAL' '$TENANT_B' configuation failed !"
116 | echo "*** The script 'ce-create-two-tenancies.sh' ends here!"
117 | exit 1
118 | fi
--------------------------------------------------------------------------------
/installapp/ops-create-two-appids.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # CLI Documentation
4 | # ================
5 | # command documentation: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
6 |
7 | # Install jq to extract json in bash on mac
8 | # ===============
9 | # brew install jq
10 |
11 | # **************** Global variables
12 |
13 | # Configurations
14 | export GLOBAL="../configuration/global.json"
15 | export TENANT_A="../configuration/tenants/tenant-a.json"
16 | export TENANT_B="../configuration/tenants/tenant-b.json"
17 |
18 | # **********************************************************************************
19 | # Functions definition
20 | # **********************************************************************************
21 |
22 | # TBD
23 |
24 | # **********************************************************************************
25 | # Execution
26 | # **********************************************************************************
27 |
28 | echo "************************************"
29 | echo " App ID for Tenant A"
30 | echo "************************************"
31 |
32 | bash ./ops-install-single-appid.sh $GLOBAL $TENANT_A
33 |
34 | if [ $? == "1" ]; then
35 | echo "*** The installation for '$GLOBAL' '$TENANT_A' configuation failed !"
36 | echo "*** The script 'ops-create-two-appids.sh' ends here!"
37 | exit 1
38 | fi
39 |
40 | echo "************************************"
41 | echo " App ID for Tenant B"
42 | echo "************************************"
43 |
44 | bash ./ops-install-single-appid.sh $GLOBAL $TENANT_B
45 |
46 | if [ $? == "1" ]; then
47 | echo "*** The installation for '$GLOBAL' '$TENANT_B' configuation failed !"
48 | echo "*** The script 'ops-create-two-appids.sh' ends here!"
49 | exit 1
50 | fi
--------------------------------------------------------------------------------
/installapp/ops-create-two-postgres.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # CLI Documentation
4 | # ================
5 | # command documentation: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
6 |
7 | # Install jq to extract json in bash on mac
8 | # ===============
9 | # brew install jq
10 |
11 | # **************** Global variables
12 |
13 | # Configurations
14 | export GLOBAL="../configuration/global.json"
15 | export TENANT_A="../configuration/tenants/tenant-a.json"
16 | export TENANT_B="../configuration/tenants/tenant-b.json"
17 |
18 | # **********************************************************************************
19 | # Functions definition
20 | # **********************************************************************************
21 |
22 | # TBD
23 |
24 | # **********************************************************************************
25 | # Execution
26 | # **********************************************************************************
27 |
28 | echo "************************************"
29 | echo " Postgres for Tenant A"
30 | echo "************************************"
31 |
32 | bash ./ops-install-single-postgres.sh $GLOBAL $TENANT_A
33 |
34 | if [ $? == "1" ]; then
35 | echo "*** The installation for '$GLOBAL' '$TENANT_A' configuation failed !"
36 | echo "*** The script 'ops-create-two-postgres.sh' ends here!"
37 | exit 1
38 | fi
39 |
40 | echo "************************************"
41 | echo " App ID for Tenant B"
42 | echo "************************************"
43 |
44 | bash ./ops-install-single-postgres.sh $GLOBAL $TENANT_B
45 |
46 | if [ $? == "1" ]; then
47 | echo "*** The installation for '$GLOBAL' '$TENANT_B' configuation failed !"
48 | echo "*** The script 'ops-create-two-postgres.sh' ends here!"
49 | exit 1
50 | fi
--------------------------------------------------------------------------------
/installapp/ops-install-single-appid.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Needed tools
4 | # ============
5 | # Install jq to extract json in bash on mac
6 | # - brew install jq
7 |
8 | # **********************************************************************************
9 | # Set global variables using parameters
10 | # **********************************************************************************
11 |
12 | echo "************************************"
13 | echo " Display parameter"
14 | echo "************************************"
15 | echo ""
16 | echo "Parameter count : $@"
17 | echo "Parameter zero 'name of the script': $0"
18 | echo "---------------------------------"
19 | echo "Global configuration : $1"
20 | echo "Tenant configuration : $2"
21 | echo "---------------------------------"
22 |
23 | # **************** Global variables set by parameters
24 |
25 | # Globale config
26 | # --------------
27 | # IBM Cloud target
28 | export RESOURCE_GROUP=$(cat ./$1 | jq '.IBM_CLOUD.RESOURCE_GROUP' | sed 's/"//g')
29 | export REGION=$(cat ./$1 | jq '.IBM_CLOUD.REGION' | sed 's/"//g')
30 |
31 | # Tenant config
32 | # --------------
33 | # App ID
34 | export APPID_SERVICE_INSTANCE_NAME=$(cat ./$2 | jq '.APP_ID.SERVICE_INSTANCE' | sed 's/"//g')
35 | export APPID_SERVICE_KEY_NAME=$(cat ./$2 | jq '.APP_ID.SERVICE_KEY_NAME' | sed 's/"//g')
36 | # ecommerce application names
37 | export FRONTEND_NAME=$(cat ./$2 | jq '.APPLICATION.CONTAINER_NAME_FRONTEND' | sed 's/"//g')
38 |
39 | echo "Application Frontend name : $FRONTEND_NAME"
40 | echo "---------------------------------"
41 | echo "App ID service instance name : $APPID_SERVICE_INSTANCE_NAME"
42 | echo "App ID service key name : $APPID_SERVICE_KEY_NAME"
43 | echo "---------------------------------"
44 | echo "IBM Cloud RESOURCE_GROUP : $RESOURCE_GROUP"
45 | echo "IBM Cloud REGION : $REGION"
46 | echo "---------------------------------"
47 | echo ""
48 | echo "------------------------------"
49 | echo "Verify parameters and press return"
50 | read input
51 |
52 | # **************** Global variables set as default values
53 |
54 | # AppID Service
55 | export SERVICE_PLAN="graduated-tier"
56 | export APPID_SERVICE_NAME="appid"
57 | export APPID_SERVICE_KEY_ROLE="Manager"
58 | export TENANTID=""
59 | export MANAGEMENTURL=""
60 | export APPLICATION_DISCOVERYENDPOINT=""
61 |
62 | # AppID User
63 | export USER_IMPORT_FILE="appid-configs/user-import.json"
64 | export USER_EXPORT_FILE="appid-configs/user-export.json"
65 | export ENCRYPTION_SECRET="12345678"
66 |
67 | # AppID Application configuration
68 | export ADD_APPLICATION="appid-configs/add-application.json"
69 | export ADD_SCOPE="appid-configs/add-scope.json"
70 | export ADD_ROLE="appid-configs/add-roles.json"
71 | export ADD_REDIRECT_URIS="appid-configs/add-redirecturis.json"
72 | export ADD_UI_TEXT="appid-configs/add-ui-text.json"
73 | export ADD_IMAGE="appid-images/logo.png"
74 | export ADD_COLOR="appid-configs/add-ui-color.json"
75 | export APPLICATION_CLIENTID=""
76 | export APPLICATION_TENANTID=""
77 | export APPLICATION_OAUTHSERVERURL=""
78 |
79 | # **********************************************************************************
80 | # Functions definition
81 | # **********************************************************************************
82 |
83 | setupCLIenv() {
84 | echo "**********************************"
85 | echo " Using following: "
86 | echo " - Resource group: $RESOURCE_GROUP "
87 | echo " - Region: $REGION "
88 | echo "**********************************"
89 |
90 | ibmcloud target -g $RESOURCE_GROUP
91 | ibmcloud target -r $REGION
92 | }
93 |
94 | # **** AppID ****
95 |
96 | createAppIDService() {
97 | ibmcloud target -g $RESOURCE_GROUP
98 | ibmcloud target -r $REGION
99 |
100 | # Create AppID service
101 | RESULT=$(ibmcloud resource service-instance $APPID_SERVICE_INSTANCE_NAME --location $REGION -g $RESOURCE_GROUP | grep "OK")
102 | if [[ $RESULT == "OK" ]]; then
103 | echo "*** The AppID service '$APPID_SERVICE_INSTANCE_NAME' already exists!"
104 | echo "*** The script 'ops-install-single-appid.sh' ends here!"
105 | exit 1
106 | fi
107 | ibmcloud resource service-instance-create $APPID_SERVICE_INSTANCE_NAME $APPID_SERVICE_NAME $SERVICE_PLAN $REGION
108 | # Create a service key for the service
109 | ibmcloud resource service-key-create $APPID_SERVICE_KEY_NAME $APPID_SERVICE_KEY_ROLE --instance-name $APPID_SERVICE_INSTANCE_NAME
110 | # Get the tenantId of the AppID service key
111 | TENANTID=$(ibmcloud resource service-keys --instance-name $APPID_SERVICE_INSTANCE_NAME --output json | grep "tenantId" | awk '{print $2;}' | sed 's/"//g')
112 | echo "Tenant ID: $TENANTID"
113 | # Get the managementUrl of the AppID from service key
114 | MANAGEMENTURL=$(ibmcloud resource service-keys --instance-name $APPID_SERVICE_INSTANCE_NAME --output json | grep "managementUrl" | awk '{print $2;}' | sed 's/"//g' | sed 's/,//g')
115 | echo "Management URL: $MANAGEMENTURL"
116 | }
117 |
118 | configureAppIDInformation(){
119 |
120 | #****** Set identity providers
121 | echo ""
122 | echo "-------------------------"
123 | echo " Set identity providers"
124 | echo "-------------------------"
125 | echo ""
126 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
127 | result=$(curl -d @./appid-configs/idps-custom.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/idps/custom)
128 | echo ""
129 | echo "-------------------------"
130 | echo "Result custom: $result"
131 | echo "-------------------------"
132 | echo ""
133 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
134 | result=$(curl -d @./appid-configs/idps-facebook.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/idps/facebook)
135 | echo ""
136 | echo "-------------------------"
137 | echo "Result facebook: $result"
138 | echo "-------------------------"
139 | echo ""
140 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
141 | result=$(curl -d @./appid-configs/idps-google.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/idps/google)
142 | echo ""
143 | echo "-------------------------"
144 | echo "Result google: $result"
145 | echo "-------------------------"
146 | echo ""
147 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
148 | result=$(curl -d @./appid-configs/idps-clouddirectory.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/idps/cloud_directory)
149 | echo ""
150 | echo "-------------------------"
151 | echo "Result cloud directory: $result"
152 | echo "-------------------------"
153 | echo ""
154 |
155 | #****** Add application ******
156 | echo ""
157 | echo "-------------------------"
158 | echo " Create application"
159 | echo "-------------------------"
160 | echo ""
161 | sed "s+FRONTENDNAME+$FRONTEND_NAME+g" ./appid-configs/add-application-template.json > ./$ADD_APPLICATION
162 | result=$(curl -d @./$ADD_APPLICATION -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/applications)
163 | echo "-------------------------"
164 | echo "Result application: $result"
165 | echo "-------------------------"
166 | APPLICATION_CLIENTID=$(echo $result | sed -n 's|.*"clientId":"\([^"]*\)".*|\1|p')
167 | APPLICATION_TENANTID=$(echo $result | sed -n 's|.*"tenantId":"\([^"]*\)".*|\1|p')
168 | APPLICATION_OAUTHSERVERURL=$(echo $result | sed -n 's|.*"oAuthServerUrl":"\([^"]*\)".*|\1|p')
169 | APPLICATION_DISCOVERYENDPOINT=$(echo $result | sed -n 's|.*"discoveryEndpoint":"\([^"]*\)".*|\1|p')
170 | echo "ClientID: $APPLICATION_CLIENTID"
171 | echo "TenantID: $APPLICATION_TENANTID"
172 | echo "oAuthServerUrl: $APPLICATION_OAUTHSERVERURL"
173 | echo "discoveryEndpoint: $APPLICATION_DISCOVERYENDPOINT"
174 | echo ""
175 |
176 | #****** Add scope ******
177 | echo ""
178 | echo "-------------------------"
179 | echo " Add scope"
180 | echo "-------------------------"
181 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
182 | result=$(curl -d @./$ADD_SCOPE -H "Content-Type: application/json" -X PUT -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/applications/$APPLICATION_CLIENTID/scopes)
183 | echo "-------------------------"
184 | echo "Result scope: $result"
185 | echo "-------------------------"
186 | echo ""
187 |
188 | #****** Add role ******
189 | echo "-------------------------"
190 | echo " Add role"
191 | echo "-------------------------"
192 | #Create file from template
193 | sed "s+APPLICATIONID+$APPLICATION_CLIENTID+g" ./appid-configs/add-roles-template.json > ./$ADD_ROLE
194 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
195 | #echo $OAUTHTOKEN
196 | result=$(curl -d @./$ADD_ROLE -H "Content-Type: application/json" -X POST -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/roles)
197 | rm -f ./$ADD_ROLE
198 | echo "-------------------------"
199 | echo "Result role: $result"
200 | echo "-------------------------"
201 | echo ""
202 |
203 | #****** Import cloud directory users ******
204 | echo ""
205 | echo "-------------------------"
206 | echo " Cloud directory import users"
207 | echo "-------------------------"
208 | echo ""
209 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
210 | result=$(curl -d @./$USER_IMPORT_FILE -H "Content-Type: application/json" -X POST -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/cloud_directory/import?encryption_secret=$ENCRYPTION_SECRET)
211 | echo "-------------------------"
212 | echo "Result import: $result"
213 | echo "-------------------------"
214 | echo ""
215 |
216 | #******* Configure ui text ******
217 | echo ""
218 | echo "-------------------------"
219 | echo " Configure ui text"
220 | echo "-------------------------"
221 | echo ""
222 | sed "s+FRONTENDNAME+$FRONTEND_NAME+g" ./appid-configs/add-ui-text-template.json > ./$ADD_UI_TEXT
223 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
224 | echo "PUT url: $MANAGEMENTURL/config/ui/theme_txt"
225 | result=$(curl -d @./$ADD_UI_TEXT -H "Content-Type: application/json" -X PUT -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/ui/theme_text)
226 | rm -f $ADD_UI_TEXT
227 | echo "-------------------------"
228 | echo "Result import: $result"
229 | echo "-------------------------"
230 | echo ""
231 |
232 | #******* Configure ui color ******
233 | echo ""
234 | echo "-------------------------"
235 | echo " Configure ui color"
236 | echo "-------------------------"
237 | echo ""
238 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
239 | echo "PUT url: $MANAGEMENTURL/config/ui/theme_color"
240 | result=$(curl -d @./$ADD_COLOR -H "Content-Type: application/json" -X PUT -H "Authorization: Bearer $OAUTHTOKEN" $MANAGEMENTURL/config/ui/theme_color)
241 | echo "-------------------------"
242 | echo "Result import: $result"
243 | echo "-------------------------"
244 | echo ""
245 |
246 | #******* Configure ui image ******
247 | echo ""
248 | echo "-------------------------"
249 | echo " Configure ui image"
250 | echo "-------------------------"
251 | echo ""
252 | OAUTHTOKEN=$(ibmcloud iam oauth-tokens | awk '{print $4;}')
253 | echo "POST url: $MANAGEMENTURL/config/ui/media?mediaType=logo"
254 | result=$(curl -F "file=@./$ADD_IMAGE" -H "Content-Type: multipart/form-data" -X POST -v -H "Authorization: Bearer $OAUTHTOKEN" "$MANAGEMENTURL/config/ui/media?mediaType=logo")
255 | echo "-------------------------"
256 | echo "Result import: $result"
257 | echo "-------------------------"
258 | echo ""
259 | }
260 |
261 | # **********************************************************************************
262 | # Execution
263 | # **********************************************************************************
264 |
265 | echo "************************************"
266 | echo " CLI config"
267 | echo "************************************"
268 |
269 | setupCLIenv
270 |
271 | echo "************************************"
272 | echo " AppID creation"
273 | echo "************************************"
274 |
275 | createAppIDService
276 |
277 | echo "************************************"
278 | echo " AppID configuration"
279 | echo "************************************"
280 |
281 | configureAppIDInformation
282 |
283 | echo "************************************"
284 | echo " AppID URLs and IDs"
285 | echo "************************************"
286 | echo "- ClientID: $APPLICATION_CLIENTID"
287 | echo "- TenantID: $APPLICATION_TENANTID"
288 | echo "- oAuthServerUrl: $APPLICATION_OAUTHSERVERURL"
289 | echo "- discoveryEndpoint: $APPLICATION_DISCOVERYENDPOINT"
290 | echo "------------------------------"
291 |
--------------------------------------------------------------------------------
/installapp/postgres-config/create-populate-tenant-a.sql:
--------------------------------------------------------------------------------
1 | CREATE SEQUENCE product_id_seq START 1;
2 |
3 | CREATE TABLE product
4 | (
5 | id SERIAL PRIMARY KEY,
6 | price DECIMAL(14,2) NOT NULL,
7 | name TEXT NOT NULL,
8 | description TEXT NOT NULL,
9 | image TEXT NOT NULL
10 | );
11 |
12 | INSERT INTO product VALUES (nextval('product_id_seq'), 29.99, 'Return of the Jedi', 'Episode 6, Luke has the final confrontation with his father!', 'images/Return.jpg');
13 | INSERT INTO product VALUES (nextval('product_id_seq'), 39.99, 'Empire Strikes Back', 'Episode 5, Luke finds out a secret that will change his destiny', 'images/Empire.jpg');
14 | INSERT INTO product VALUES (nextval('product_id_seq'), 49.99, 'New Hope', 'Episode 4, after years of oppression, a band of rebels fight for freedom', 'images/NewHope.jpg');
15 | INSERT INTO product VALUES (nextval('product_id_seq'), 100.00, 'DVD Player', 'This Sony Player has crystal clear picture', 'images/Player.jpg');
16 | INSERT INTO product VALUES (nextval('product_id_seq'), 200.00, 'BlackBerry Curve', 'This BlackBerry offers rich PDA functions and works with Notes.', 'images/BlackBerry.jpg');
17 | INSERT INTO product VALUES (nextval('product_id_seq'), 150.00, 'Sony Ericsson', 'This Sony phone takes pictures and plays Music.', 'images/SonyPhone.jpg');
18 | INSERT INTO product VALUES (nextval('product_id_seq'), 1800.00, 'Sony Bravia', 'This is a 40 inch 1080p LCD HDTV', 'images/SonyTV.jpg');
19 | INSERT INTO product VALUES (nextval('product_id_seq'), 1150.00, 'Sharp Aquos', 'This is 32 inch 1080p LCD HDTV', 'images/SamTV.jpg');
20 | INSERT INTO product VALUES (nextval('product_id_seq'), 20.00, 'Go Fish: Superstar', 'Go Fish release their great new hit, Superstar', 'images/Superstar.jpg');
21 | INSERT INTO product VALUES (nextval('product_id_seq'), 20.00, 'Ludwig van Beethoven', 'This is a classic, the 9 Symphonies Box Set', 'images/Bet.jpg');
22 | INSERT INTO product VALUES (nextval('product_id_seq'), 399.99, 'PlayStation 3', 'Brace yourself for the marvels of the PlayStation 3 complete with 80GB hard disk storage', 'images/PS3.jpg');
23 | INSERT INTO product VALUES (nextval('product_id_seq'), 169.99, 'Nintendo Wii', 'Next-generation gaming with a motion-sensitive controller', 'images/wii.jpg');
24 | INSERT INTO product VALUES (nextval('product_id_seq'), 299.99, 'XBOX 360', 'Expand your horizons with the gaming and multimedia capabilities of this incredible system', 'images/xbox360.jpg');
25 |
26 |
27 | CREATE SEQUENCE category_id_seq START 1;
28 | CREATE TABLE category
29 | (
30 | id SERIAL PRIMARY KEY ,
31 | name TEXT NOT NULL,
32 | parent INT
33 | );
34 |
35 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Entertainment', null);
36 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Movies', 1);
37 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Music', 1);
38 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Games', 1);
39 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Electronics', null);
40 | INSERT INTO category VALUES (nextval('category_id_seq'), 'TV', 5);
41 | INSERT INTO category VALUES (nextval('category_id_seq'), 'CellPhones', 5);
42 | INSERT INTO category VALUES (nextval('category_id_seq'), 'DVD Players', 5);
43 |
44 | CREATE SEQUENCE productcategory_id_seq START 1;
45 | CREATE TABLE productcategory
46 | (
47 | id SERIAL PRIMARY KEY ,
48 | productid INT,
49 | categoryid INT
50 | );
51 |
52 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 1, 2);
53 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 2, 2);
54 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 3, 2);
55 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 4, 8);
56 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 5, 7);
57 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 6, 7);
58 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 7, 6);
59 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 8, 6);
60 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 9, 4);
61 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 10, 3);
62 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 11, 4);
63 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 12, 4);
64 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 13, 4);
65 |
66 |
--------------------------------------------------------------------------------
/installapp/postgres-config/create-populate-tenant-b.sql:
--------------------------------------------------------------------------------
1 | CREATE SEQUENCE product_id_seq START 1;
2 |
3 | CREATE TABLE product
4 | (
5 | id SERIAL PRIMARY KEY,
6 | price DECIMAL(14,2) NOT NULL,
7 | name TEXT NOT NULL,
8 | description TEXT NOT NULL,
9 | image TEXT NOT NULL
10 | );
11 |
12 | INSERT INTO product VALUES (nextval('product_id_seq'), 19.99, 'Beautiful Disaster', 'The new Abby Abernathy is a good girl.', 'images/beautifuldisaster.jpg');
13 | INSERT INTO product VALUES (nextval('product_id_seq'), 9.99, 'Dune', 'Dune is a 1965 science fiction novel by American author Frank Herbert', 'images/dune.jpg');
14 | INSERT INTO product VALUES (nextval('product_id_seq'), 15.99, 'Jade City', 'The Kaul family is one of two crime syndicates that control the island of Kekon.', 'images/greenbone.jpg');
15 | INSERT INTO product VALUES (nextval('product_id_seq'), 12.00, 'The Heart Principle', 'When violinist Anna Sun accidentally achieves career success with a viral YouTube video', 'images/heartprinciple.jpg');
16 | INSERT INTO product VALUES (nextval('product_id_seq'), 20.00, 'Handmaids Tale', 'It is set in a near-future New England, in a strongly patriarchal, totalitarian theonomic state, known as Republic of Gilead.', 'images/handmaidstale.jpg');
17 | INSERT INTO product VALUES (nextval('product_id_seq'), 30.00, 'Ancillary Justice', ' It is Leckies debut novel and the first in her Imperial Radch space opera trilogy', 'images/imperial.jpg');
18 | INSERT INTO product VALUES (nextval('product_id_seq'), 18.00, 'A Lesson in Vengence', 'Perched in the Catskill mountains, the centuries-old, ivy-covered campus was home until the tragic death of her girlfriend.', 'images/lessonvengence.jpg');
19 | INSERT INTO product VALUES (nextval('product_id_seq'), 11.00, 'The Lord of the Rings', 'The Lord of the Rings is a series of three epic fantasy adventure films directed by Peter Jackson, based on the novel written by J. R. R. Tolkien', 'images/lordofrings.jpg');
20 | INSERT INTO product VALUES (nextval('product_id_seq'), 22.00, 'A Slow Fire Burning', 'When a young man is found gruesomely murdered in a London houseboat, it triggers questions about three women who knew him.', 'images/slowfire.jpg');
21 | INSERT INTO product VALUES (nextval('product_id_seq'), 27.00, 'The Guide', 'Kingfisher Lodge, nestled in a canyon on a mile and a half of the most pristine river water on the planet', 'images/theguide.jpg');
22 |
23 |
24 | CREATE SEQUENCE category_id_seq START 1;
25 | CREATE TABLE category
26 | (
27 | id SERIAL PRIMARY KEY ,
28 | name TEXT NOT NULL,
29 | parent INT
30 | );
31 |
32 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Fantasy', null);
33 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Thriller', 1);
34 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Mystery', 1);
35 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Fiction', 1);
36 | INSERT INTO category VALUES (nextval('category_id_seq'), 'Non-Fiction', null);
37 |
38 | CREATE SEQUENCE productcategory_id_seq START 1;
39 | CREATE TABLE productcategory
40 | (
41 | id SERIAL PRIMARY KEY ,
42 | productid INT,
43 | categoryid INT
44 | );
45 |
46 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 1, 2);
47 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 2, 2);
48 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 3, 2);
49 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 4, 5);
50 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 5, 5);
51 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 6, 3);
52 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 7, 4);
53 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 8, 4);
54 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 9, 2);
55 | INSERT INTO productcategory VALUES (nextval('productcategory_id_seq'), 10, 1);
56 |
57 |
58 |
--------------------------------------------------------------------------------
/installapp/postgres-config/insert-template.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | COMMAND_INSERT
--------------------------------------------------------------------------------
/installapp/tools-image/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/ubuntu:22.04
2 |
3 | RUN apt-get update -qq \
4 | && apt-get --assume-yes install buildah \
5 | && apt-get --assume-yes install iptables
6 |
7 | # Java
8 | # *********** default-jdk -> openjdk-11-jdk ***************
9 | RUN apt update \
10 | && apt-get --assume-yes install default-jdk
11 |
12 | # *********** Basic tools ***************
13 | RUN apt-get update \
14 | && apt-get --assume-yes install curl \
15 | && apt-get --assume-yes install git-core \
16 | && apt-get --assume-yes install wget \
17 | && apt-get --assume-yes install gnupg2 \
18 | && apt-get --assume-yes install nano \
19 | && apt-get --assume-yes install apt-utils \
20 | && apt-get --assume-yes install unzip \
21 | && apt-get --assume-yes install zip \
22 | && apt-get --assume-yes install sed \
23 | && apt-get --assume-yes install jq
24 | ARG DEBIAN_FRONTEND="noninteractive"
25 | RUN apt-get --assume-yes install postgresql \
26 | && apt-get --assume-yes install postgresql-contrib \
27 | && apt-get --assume-yes install original-awk
28 |
29 | # Kubernetes
30 | # *********** Kubernetes ***************
31 | RUN apt-get update && apt-get install -y apt-transport-https \
32 | && curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \
33 | && echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list \
34 | && apt-get update \
35 | && apt-get --assume-yes install kubectl
36 |
37 | # *********** IBM Cloud CLI ***********
38 | RUN curl -fsSL https://clis.cloud.ibm.com/install/linux | sh \
39 | && ibmcloud plugin install container-service \
40 | && ibmcloud plugin install container-registry \
41 | && ibmcloud plugin install code-engine \
42 | && ibmcloud plugin install cloud-databases
43 |
--------------------------------------------------------------------------------
/installapp/tz-build-images-code-engine.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "************************************"
4 | echo " Display parameter"
5 | echo "************************************"
6 | echo ""
7 | echo "Parameter count : $@"
8 | echo "Parameter zero 'name of the script': $0"
9 | echo "---------------------------------"
10 | echo "Service catalog image : $1"
11 | echo "Frontend image : $2"
12 | echo "Registry URL : $3"
13 | echo "Build CE Project : $4"
14 | echo "Registry Secret Name : $5"
15 | echo "---------------------------------"
16 | echo ""
17 |
18 | # **************** Global variables
19 | export SERVICE_CATALOG_IMAGE=$1
20 | export FRONTEND_IMAGE=$2
21 | export REGISTRY_URL=$3
22 | export PROJECT_NAME=$4
23 | export SECRET_NAME=$5
24 |
25 | export ROOT_PROJECT=multi-tenancy
26 | export FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
27 | export BACKEND_SOURCEFOLDER=multi-tenancy-backend
28 |
29 |
30 | # **********************************************************************************
31 | # Functions
32 | # **********************************************************************************
33 |
34 | setROOT_PATH() {
35 | echo "************************************"
36 | echo " Set ROOT_PATH"
37 | echo "************************************"
38 | cd ../../
39 | export ROOT_PATH=$(pwd)
40 | echo "Path: $ROOT_PATH"
41 | }
42 |
43 | setupCRenvCE() {
44 |
45 | IBMCLOUDCLI_KEY_NAME="cliapikey_for_multi_tenant_$PROJECT_NAME"
46 | IBMCLOUDCLI_KEY_DESCRIPTION="CLI APIkey $IBMCLOUDCLI_KEY_NAME"
47 | CLIKEY_FILE="cli_key.json"
48 | USERNAME="iamapikey"
49 |
50 | RESULT=$(ibmcloud iam api-keys | grep '$IBMCLOUDCLI_KEY_NAME' | awk '{print $1;}' | head -n 1)
51 | echo "API key: $RESULT"
52 | if [[ $RESULT == $IBMCLOUDCLI_KEY_NAME ]]; then
53 | echo "*** The Cloud API key '$IBMCLOUDCLI_KEY_NAME' already exists !"
54 | echo "*** The script 'ce-install-application.sh' ends here!"
55 | echo "*** Review your existing API keys 'https://cloud.ibm.com/iam/apikeys'."
56 | exit 1
57 | fi
58 |
59 | ibmcloud iam api-key-create $IBMCLOUDCLI_KEY_NAME -d "My IBM CLoud CLI API key for project $PROJECT_NAME" --file $CLIKEY_FILE
60 | CLIAPIKEY=$(cat $CLIKEY_FILE | grep '"apikey":' | awk '{print $2;}' | sed 's/"//g' | sed 's/,//g' )
61 | #echo $CLIKEY
62 | rm -f $CLIKEY_FILE
63 |
64 | ibmcloud ce project select --name $PROJECT_NAME
65 |
66 | ibmcloud ce registry create --name $SECRET_NAME \
67 | --server $REGISTRY_URL \
68 | --username $USERNAME \
69 | --password $CLIAPIKEY
70 | }
71 |
72 | buildBackend() {
73 | echo "************************************"
74 | echo " Backend $SERVICE_CATALOG_IMAGE"
75 | echo "************************************"
76 | cd $ROOT_PATH/$BACKEND_SOURCEFOLDER
77 |
78 | # ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
79 |
80 | # buildah bud -t "$SERVICE_CATALOG_IMAGE" -f Dockerfile .
81 | # buildah push "$SERVICE_CATALOG_IMAGE"
82 |
83 | ibmcloud ce buildrun submit --name service-catalog --image $SERVICE_CATALOG_IMAGE \
84 | --registry-secret $SECRET_NAME --source .
85 |
86 | ibmcloud ce buildrun logs -f -n service-catalog
87 | echo ""
88 | }
89 |
90 | buildFrontend() {
91 | echo "************************************"
92 | echo " Frontend $FRONTEND_IMAGE"
93 | echo "************************************"
94 | cd $ROOT_PATH/$FRONTEND_SOURCEFOLDER
95 |
96 | # ibmcloud iam oauth-tokens | sed -ne '/IAM token/s/.* //p' | buildah login -u iambearer --password-stdin $REGISTRY_URL
97 |
98 | # buildah bud -t "$FRONTEND_IMAGE" -f Dockerfile .
99 | # buildah push "$FRONTEND_IMAGE"
100 |
101 | ibmcloud ce buildrun submit --name frontend-image --image $FRONTEND_IMAGE \
102 | --registry-secret $SECRET_NAME --source .
103 |
104 | ibmcloud ce buildrun logs -f -n frontend-image
105 | echo ""
106 | }
107 |
108 | resetPath() {
109 | echo "************************************"
110 | echo " Reset path"
111 | echo "************************************"
112 | cd $ROOT_PATH/$ROOT_PROJECT
113 | echo ""
114 | }
115 |
116 |
117 | # **********************************************************************************
118 | # Execution
119 | # **********************************************************************************
120 |
121 | setROOT_PATH
122 | setupCRenvCE
123 | buildBackend
124 | buildFrontend
125 | resetPath
126 |
--------------------------------------------------------------------------------
/installapp/tz-check-prerequisites.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **************** Global variables
4 |
5 | export CHECK_IBMCLOUDCLI="ibmcloud"
6 | export CHECK_JQ="jq-"
7 | export CHECK_SED="sed"
8 | export CHECK_AWK="mawk:"
9 | export CHECK_CURL="curl"
10 | export CHECK_KUBECTL="Client"
11 | export CHECK_PLUGIN_CLOUDDATABASES="cloud-databases"
12 | export CHECK_PLUGIN_CODEENGINE="code-engine"
13 | export CHECK_PLUGIN_CONTAINERREGISTERY="container-registry"
14 | export CHECK_GREP="grep"
15 | export CHECK_LIBPQ="psql"
16 |
17 | export VERICATION=""
18 |
19 |
20 | # **********************************************************************************
21 | # Functions definition
22 | # **********************************************************************************
23 |
24 | verifyLibpq() {
25 | VERICATION=$(psql --version)
26 |
27 | if [[ $VERICATION =~ $CHECK_LIBPQ ]]; then
28 | echo "- libpq (psql) is installed: $VERICATION !"
29 | else
30 | echo "*** libpq (psql) is NOT installed !"
31 | echo "*** The scripts ends here!"
32 | exit 1
33 | fi
34 | }
35 |
36 | verifyGrep() {
37 | VERICATION=$(grep --version)
38 |
39 | if [[ $VERICATION =~ $CHECK_GREP ]]; then
40 | echo "- Grep is installed: $VERICATION !"
41 | else
42 | echo "*** Grep is NOT installed !"
43 | echo "*** The scripts ends here!"
44 | exit 1
45 | fi
46 | }
47 |
48 | verifyCURL() {
49 | VERICATION=$(curl --version)
50 |
51 | if [[ $VERICATION =~ $CHECK_CURL ]]; then
52 | echo "- cURL is installed: $VERICATION !"
53 | else
54 | echo "*** cURL is NOT installed !"
55 | echo "*** The scripts ends here!"
56 | exit 1
57 | fi
58 | }
59 |
60 | verifyAWK() {
61 | VERICATION=$(mawk -W)
62 |
63 | if [[ $VERICATION =~ $CHECK_AWK ]]; then
64 | echo "- AWK is installed: $VERICATION !"
65 | else
66 | echo "*** AWK is NOT installed !"
67 | echo "*** The scripts ends here!"
68 | exit 1
69 | fi
70 | }
71 |
72 | verifySed() {
73 | VERICATION=$(sed --version)
74 |
75 | if [[ $VERICATION =~ $CHECK_SED ]]; then
76 | echo "- Sed is installed: $VERICATION !"
77 | else
78 | echo "*** Sed is NOT installed !"
79 | echo "*** The scripts ends here!"
80 | exit 1
81 | fi
82 | }
83 |
84 | verifyIBMCloudCLI() {
85 | VERICATION=$(ibmcloud --version)
86 |
87 | if [[ $VERICATION =~ $CHECK_IBMCLOUDCLI ]]; then
88 | echo "- IBM Cloud CLI is installed: $VERICATION !"
89 | else
90 | echo "*** IBM Cloud CLI is NOT installed !"
91 | echo "*** The scripts ends here!"
92 | exit 1
93 | fi
94 | }
95 |
96 | verifyJQ() {
97 | VERICATION=$(jq --version)
98 |
99 | if [[ $VERICATION =~ $CHECK_JQ ]]; then
100 | echo "- JQ is installed: $VERICATION !"
101 | else
102 | echo "*** JQ is NOT installed !"
103 | echo "*** The scripts ends here!"
104 | exit 1
105 | fi
106 | }
107 |
108 | verifyIBMCloudPluginCloudDatabases() {
109 | VERICATION=$(ibmcloud plugin show cloud-databases | grep 'Plugin Name' | awk '{print $3}' )
110 |
111 | if [[ $VERICATION =~ $CHECK_PLUGIN_CLOUDDATABASES ]]; then
112 | echo "- IBM Cloud Plugin 'cloud-databases' is installed: $VERICATION !"
113 | else
114 | echo "IBM Cloud Plugin 'cloud-databases' is NOT installed !"
115 | echo "*** The scripts ends here!"
116 | exit 1
117 | fi
118 | }
119 |
120 | verifyIBMCloudPluginCodeEngine() {
121 | VERICATION=$(ibmcloud plugin show code-engine | grep 'Plugin Name' | awk '{print $3}' )
122 |
123 | if [[ $VERICATION =~ $CHECK_PLUGIN_CODEENGINE ]]; then
124 | echo "- IBM Cloud Plugin 'code-engine' is installed: $VERICATION !"
125 | else
126 | echo "IBM Cloud Plugin 'code-engine' is NOT installed !"
127 | echo "*** The scripts ends here!"
128 | exit 1
129 | fi
130 | }
131 |
132 | verifyIBMCloudPluginContainerRegistry() {
133 | VERICATION=$(ibmcloud plugin show container-registry | grep 'Plugin Name' | awk '{print $3}' )
134 |
135 | if [[ $VERICATION =~ $CHECK_PLUGIN_CONTAINERREGISTERY ]]; then
136 | echo "- IBM Cloud Plugin 'container-registry' is installed: $VERICATION !"
137 | else
138 | echo "IBM Cloud Plugin 'container-registry' is NOT installed !"
139 | echo "*** The scripts ends here!"
140 | exit 1
141 | fi
142 | }
143 |
144 | verifyKubectl() {
145 | VERICATION=$(kubectl version)
146 |
147 | if [[ $VERICATION =~ $CHECK_KUBECTL ]]; then
148 | echo "- kubectl is installed: $VERICATION !"
149 | else
150 | echo "*** kubectl is NOT installed !"
151 | echo "*** The scripts ends here!"
152 | exit 1
153 | fi
154 | }
155 |
156 | # **********************************************************************************
157 | # Execution
158 | # **********************************************************************************
159 |
160 | echo "Check prereqisites"
161 | echo "1. Verify grep"
162 | verifyGrep
163 | echo "2. Verify awk"
164 | # verifyAWK
165 | echo "3. Verify cURL"
166 | verifyCURL
167 | echo "4. Verify Sed"
168 | verifySed
169 | echo "5. Verify jq"
170 | verifyJQ
171 | echo "6. Verify libpq (psql)"
172 | verifyLibpq
173 | echo "7. Verify ibmcloud cli"
174 | verifyIBMCloudCLI
175 | echo "8. Verify ibmcloud plugin cloud-databases"
176 | verifyIBMCloudPluginCloudDatabases
177 | echo "9. Verify ibmcloud plugin code-engine"
178 | verifyIBMCloudPluginCodeEngine
179 | echo "10. Verify ibmcloud plugin container-registry"
180 | verifyIBMCloudPluginContainerRegistry
181 | echo "11. Verify kubectl"
182 | verifyKubectl
183 |
184 | echo "Success! All prerequisites verified!"
185 |
--------------------------------------------------------------------------------
/installapp/tz-create-two-tenancies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # CLI Documentation
3 | # ================
4 | # command documentation: https://cloud.ibm.com/docs/codeengine?topic=codeengine-cli#cli-application-create
5 |
6 | # Install jq to extract json in bash on mac
7 | # ===============
8 | # brew install jq
9 |
10 | # **************** Global variables
11 |
12 | # Configurations
13 | export GLOBAL="../configuration/global.json"
14 | export TENANT_A="../configuration/tenants/tenant-a.json"
15 | export TENANT_B="../configuration/tenants/tenant-b.json"
16 |
17 | # ecommerce application container image names
18 | export REGISTRY_URL=$(cat ./$GLOBAL | jq '.REGISTRY.URL' | sed 's/"//g')
19 | export IMAGE_TAG=$(cat ./$GLOBAL | jq '.REGISTRY.TAG' | sed 's/"//g')
20 | export NAMESPACE=$(cat ./$GLOBAL | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
21 | export FRONTEND_IMAGE_NAME=$(cat ./$GLOBAL | jq '.IMAGES.NAME_FRONTEND' | sed 's/"//g')
22 | export BACKEND_IMAGE_NAME=$(cat ./$GLOBAL | jq '.IMAGES.NAME_BACKEND' | sed 's/"//g')
23 | export FRONTEND_IMAGE="$REGISTRY_URL/$NAMESPACE/$FRONTEND_IMAGE_NAME:$IMAGE_TAG"
24 | export SERVICE_CATALOG_IMAGE="$REGISTRY_URL/$NAMESPACE/$BACKEND_IMAGE_NAME:$IMAGE_TAG"
25 |
26 | # Registry settings
27 | # ibm cloud container registry settings
28 | export IBMCLOUD_CR_NAMESPACE=$(cat ./$GLOBAL | jq '.REGISTRY.NAMESPACE' | sed 's/"//g')
29 | export IBMCLOUD_CR_REGION_URL=$(cat ./$GLOBAL | jq '.REGISTRY.URL' | sed 's/"//g')
30 | export IBMCLOUD_CR_TAG=$(cat ./$GLOBAL | jq '.REGISTRY.TAG' | sed 's/"//g')
31 | export IBMCLOUD_CR_SECRET_NAME=$(cat ./$GLOBAL | jq -r '.REGISTRY.SECRET_NAME' | sed 's/"//g')
32 |
33 | # IBM Cloud target
34 | export RESOURCE_GROUP=$(cat ./$GLOBAL | jq -r '.IBM_CLOUD.RESOURCE_GROUP')
35 | export REGION=$(cat ./$GLOBAL | jq -r '.IBM_CLOUD.REGION')
36 | # Code Engine
37 | export IBMCLOUD_CE_BUILD_PROJECT=$(cat ./$TENANT_A | jq -r '.CODE_ENGINE.PROJECT_NAME')
38 |
39 | # **********************************************************************************
40 | # Functions definition
41 | # **********************************************************************************
42 | # FYI: https://stackoverflow.com/questions/12468889/bash-script-error-function-not-found-why-would-this-appear
43 |
44 | configLogin() {
45 |
46 | echo "IBMCLOUD_CR_NAMESPACE: $IBMCLOUD_CR_NAMESPACE"
47 | echo "RESOURCE_GROUP : $RESOURCE_GROUP"
48 | echo "REGION : $REGION"
49 | echo "------------------------------"
50 | echo "Verify the given entries and press return"
51 |
52 | read input
53 |
54 | ibmcloud target -g $RESOURCE_GROUP
55 | ibmcloud target -r $REGION
56 | ibmcloud target
57 |
58 | }
59 |
60 | createIBMContainer () {
61 |
62 | echo "FRONTEND_IMAGE: $FRONTEND_IMAGE"
63 | echo "SERVICE_CATALOG_IMAGE: $SERVICE_CATALOG_IMAGE"
64 | echo "IBMCLOUD_CR_REGION_URL: $IBMCLOUD_CR_REGION_URL"
65 | echo "IBMCLOUD_CE_BUILD_PROJECT: $IBMCLOUD_CE_BUILD_PROJECT"
66 |
67 | # createNamespaceBuildah
68 |
69 | bash ./tz-build-images-code-engine.sh $SERVICE_CATALOG_IMAGE $FRONTEND_IMAGE $IBMCLOUD_CR_REGION_URL \
70 | $IBMCLOUD_CE_BUILD_PROJECT $IBMCLOUD_CR_SECRET_NAME
71 |
72 | if [ $? == "1" ]; then
73 | echo "*** Creation of the container images failed !"
74 | echo "*** The script 'tz-create-two-tenancies.sh' ends here!"
75 | exit 1
76 | fi
77 | }
78 |
79 | # **********************************************************************************
80 | # Execution
81 | # **********************************************************************************
82 |
83 | echo "************************************"
84 | echo " Create container image in IBM Container Registry"
85 | echo "************************************"
86 |
87 | configLogin
88 | createIBMContainer
89 |
90 | echo "************************************"
91 | echo " Tenant A"
92 | echo "************************************"
93 |
94 | bash ./tz-install-application.sh $GLOBAL $TENANT_A skip-cr-secret
95 |
96 | if [ $? == "1" ]; then
97 | echo "*** The installation for '$GLOBAL' '$TENANT_A' configuation failed !"
98 | echo "*** The script 'tz-create-two-tenancies.sh' ends here!"
99 | exit 1
100 | fi
101 |
102 | echo "************************************"
103 | echo " Tenant B"
104 | echo "************************************"
105 |
106 | bash ./tz-install-application.sh $GLOBAL $TENANT_B
107 |
108 | if [ $? == "1" ]; then
109 | echo "*** The installation for '$GLOBAL' '$TENANT_B' configuation failed !"
110 | echo "*** The script 'tz-create-two-tenancies.sh' ends here!"
111 | exit 1
112 | fi
--------------------------------------------------------------------------------
/installapp/tz-update-config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **********************************************************************************
4 | # Set global variables using parameters
5 | # **********************************************************************************
6 |
7 | echo "************************************"
8 | echo " Display parameter"
9 | echo "************************************"
10 | echo ""
11 | echo "Parameter count : $@"
12 | echo "Parameter zero 'name of the script': $0"
13 | echo "---------------------------------"
14 | echo "Code Engine Region : $1"
15 | echo "Container Registry Namespace : $2"
16 | echo "Resource Group Name : $3"
17 | echo "---------------------------------"
18 |
19 | export REGION=${REGION:-${1}}
20 | export NAMESPACE=${NAMESPACE:-${2}}
21 | export RESOURCE_GROUP=${RESOURCE_GROUP:-${3}}
22 | export RANDOM8=$(echo ${NAMESPACE} | cut -f3 -d"-")
23 |
24 | # Configurations
25 | export GLOBAL="../configuration/global.json"
26 | export TENANT_A="../configuration/tenants/tenant-a.json"
27 | export TENANT_B="../configuration/tenants/tenant-b.json"
28 |
29 | # **********************************************************************************
30 | # Determine host for Container Registry based on region
31 | # **********************************************************************************
32 |
33 | case $REGION in
34 | us-south)
35 | export REGISTRY_URL=us.icr.io
36 | ;;
37 | ca-tor)
38 | export REGISTRY_URL=ca.icr.io
39 | ;;
40 | eu-gb)
41 | export REGISTRY_URL=uk.icr.io
42 | ;;
43 | eu-de)
44 | export REGISTRY_URL=de.icr.io
45 | ;;
46 | jp-tok)
47 | export REGISTRY_URL=jp.icr.io
48 | ;;
49 | au-syd)
50 | export REGISTRY_URL=au.icr.io
51 | ;;
52 | esac
53 |
54 | # **********************************************************************************
55 | # Update global configuration properties
56 | # **********************************************************************************
57 |
58 | sed -i.bak "s/default/${RESOURCE_GROUP}/" ${GLOBAL} && rm ${GLOBAL}.bak
59 | sed -i.bak "s/eu-de/${REGION}/" ${GLOBAL} && rm ${GLOBAL}.bak
60 | sed -i.bak "s/de\.icr\.io/${REGISTRY_URL}/" ${GLOBAL} && rm ${GLOBAL}.bak
61 | sed -i.bak "s/multi-tenancy-example/${NAMESPACE}/" ${GLOBAL} && rm ${GLOBAL}.bak
62 |
63 | # **********************************************************************************
64 | # Update Tenant A configuration properties
65 | # **********************************************************************************
66 |
67 | sed -i.bak "s/multi-tenancy-serverless-appid-a/${NAMESPACE}-00/" ${TENANT_A}&& rm ${TENANT_A}.bak
68 | sed -i.bak "s/itzce/appid/" ${TENANT_A}&& rm ${TENANT_A}.bak
69 | sed -i.bak "s/multi-tenancy-serverless-appid-key-a/appid-service-key-${RANDOM8}-00/" ${TENANT_A}&& rm ${TENANT_A}.bak
70 | sed -i.bak "s/multi-tenancy-serverless-pg-ten-a-key/pgsql-service-key-${RANDOM8}-00/" ${TENANT_A}&& rm ${TENANT_A}.bak
71 | sed -i.bak "s/multi-tenancy-serverless-pg-ten-a/${NAMESPACE}-00/" ${TENANT_A}&& rm ${TENANT_A}.bak
72 | sed -i.bak "s/itzce/pgsql/" ${TENANT_A}&& rm ${TENANT_A}.bak
73 | sed -i.bak "s/multi-tenancy-serverless-a/${NAMESPACE}-00/" ${TENANT_A}&& rm ${TENANT_A}.bak
74 |
75 | # **********************************************************************************
76 | # Update Tenant B configuration properties
77 | # **********************************************************************************
78 |
79 | sed -i.bak "s/multi-tenancy-serverless-appid-b/${NAMESPACE}-01/" ${TENANT_B}&& rm ${TENANT_B}.bak
80 | sed -i.bak "s/itzce/appid/" ${TENANT_B}&& rm ${TENANT_B}.bak
81 | sed -i.bak "s/multi-tenancy-serverless-appid-key-b/appid-service-key-${RANDOM8}-01/" ${TENANT_B}&& rm ${TENANT_B}.bak
82 | sed -i.bak "s/multi-tenancy-serverless-pg-ten-b-key/pgsql-service-key-${RANDOM8}-01/" ${TENANT_B}&& rm ${TENANT_B}.bak
83 | sed -i.bak "s/multi-tenancy-serverless-pg-ten-b/${NAMESPACE}-01/" ${TENANT_B}&& rm ${TENANT_B}.bak
84 | sed -i.bak "s/itzce/pgsql/" ${TENANT_B}&& rm ${TENANT_B}.bak
85 | sed -i.bak "s/multi-tenancy-serverless-b/${NAMESPACE}-01/" ${TENANT_B}&& rm ${TENANT_B}.bak
--------------------------------------------------------------------------------
/local.env.template:
--------------------------------------------------------------------------------
1 | POSTGRES_USERNAME="aaa"
2 | POSTGRES_PASSWORD="bbb"
3 | POSTGRES_URL="jdbc:postgresql://NO_USERNAME_NO_PASSWORDccc.ddd.databases.appdomain.cloud:30143/ibmclouddbNOTHING_ELSE"
4 | POSTGRES_CERTIFICATE_FILE_NAME="5df929c2-b76a-11e9-b3dd-4acf6c229d45"
5 | APPID_CLIENT_ID='XXXX-XXXX-XXX-XXXX-XXXXX'
6 | APPID_DISCOVERYENDPOINT=https://us-south.appid.cloud.ibm.com/oauth/v4/YOUR_TENDANT/.well-known/openid-configuration
7 | APPID_AUTH_SERVER_URL=https://us-south.appid.cloud.ibm.com/oauth/v4/YOUR_TENDANT
--------------------------------------------------------------------------------
/operator/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore build and test binaries.
3 | bin/
4 | testbin/
5 |
--------------------------------------------------------------------------------
/operator/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | bin
9 | testbin/*
10 |
11 | # Test binary, build with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Kubernetes Generated files - skip generated files, except for vendored files
18 |
19 | !vendor/**/zz_generated.*
20 |
21 | # editor and IDE paraphernalia
22 | .idea
23 | *.swp
24 | *.swo
25 | *~
26 |
--------------------------------------------------------------------------------
/operator/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.16 as builder
3 |
4 | WORKDIR /workspace
5 | # Copy the Go Modules manifests
6 | COPY go.mod go.mod
7 | COPY go.sum go.sum
8 | # cache deps before building and copying source so that we don't need to re-download as much
9 | # and so that source changes don't invalidate our downloaded layer
10 | RUN go mod download
11 |
12 | # Copy the go source
13 | COPY main.go main.go
14 | COPY api/ api/
15 | COPY controllers/ controllers/
16 |
17 | # Build
18 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
19 |
20 | # Use distroless as minimal base image to package the manager binary
21 | # Refer to https://github.com/GoogleContainerTools/distroless for more details
22 | FROM gcr.io/distroless/static:nonroot
23 | WORKDIR /
24 | COPY --from=builder /workspace/manager .
25 | USER 65532:65532
26 |
27 | ENTRYPOINT ["/manager"]
28 |
--------------------------------------------------------------------------------
/operator/Makefile:
--------------------------------------------------------------------------------
1 | # VERSION defines the project version for the bundle.
2 | # Update this value when you upgrade the version of your project.
3 | # To re-generate a bundle for another specific version without changing the standard setup, you can:
4 | # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
5 | # - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
6 | VERSION ?= 0.0.1
7 |
8 | # CHANNELS define the bundle channels used in the bundle.
9 | # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
10 | # To re-generate a bundle for other specific channels without changing the standard setup, you can:
11 | # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
12 | # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
13 | ifneq ($(origin CHANNELS), undefined)
14 | BUNDLE_CHANNELS := --channels=$(CHANNELS)
15 | endif
16 |
17 | # DEFAULT_CHANNEL defines the default channel used in the bundle.
18 | # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
19 | # To re-generate a bundle for any other default channel without changing the default setup, you can:
20 | # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
21 | # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
22 | ifneq ($(origin DEFAULT_CHANNEL), undefined)
23 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
24 | endif
25 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
26 |
27 | # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
28 | # This variable is used to construct full image tags for bundle and catalog images.
29 | #
30 | # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
31 | # saas.ecommerce.sample.com/temp-bundle:$VERSION and saas.ecommerce.sample.com/temp-catalog:$VERSION.
32 | IMAGE_TAG_BASE ?= saas.ecommerce.sample.com/temp
33 |
34 | # BUNDLE_IMG defines the image:tag used for the bundle.
35 | # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:)
36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
37 |
38 | # Image URL to use all building/pushing image targets
39 | IMG ?= docker.io/nheidloff/controller:latest
40 | # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
41 | ENVTEST_K8S_VERSION = 1.22
42 |
43 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
44 | ifeq (,$(shell go env GOBIN))
45 | GOBIN=$(shell go env GOPATH)/bin
46 | else
47 | GOBIN=$(shell go env GOBIN)
48 | endif
49 |
50 | # Setting SHELL to bash allows bash commands to be executed by recipes.
51 | # This is a requirement for 'setup-envtest.sh' in the test target.
52 | # Options are set to exit when a recipe line exits non-zero or a piped command fails.
53 | SHELL = /usr/bin/env bash -o pipefail
54 | .SHELLFLAGS = -ec
55 |
56 | .PHONY: all
57 | all: build
58 |
59 | ##@ General
60 |
61 | # The help target prints out all targets with their descriptions organized
62 | # beneath their categories. The categories are represented by '##@' and the
63 | # target descriptions by '##'. The awk commands is responsible for reading the
64 | # entire set of makefiles included in this invocation, looking for lines of the
65 | # file as xyz: ## something, and then pretty-format the target and help. Then,
66 | # if there's a line with ##@ something, that gets pretty-printed as a category.
67 | # More info on the usage of ANSI control characters for terminal formatting:
68 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
69 | # More info on the awk command:
70 | # http://linuxcommand.org/lc3_adv_awk.php
71 |
72 | .PHONY: help
73 | help: ## Display this help.
74 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
75 |
76 | ##@ Development
77 |
78 | .PHONY: manifests
79 | manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
80 | $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
81 |
82 | .PHONY: generate
83 | generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
84 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
85 |
86 | .PHONY: fmt
87 | fmt: ## Run go fmt against code.
88 | go fmt ./...
89 |
90 | .PHONY: vet
91 | vet: ## Run go vet against code.
92 | go vet ./...
93 |
94 | .PHONY: test
95 | test: manifests generate fmt vet envtest ## Run tests.
96 | KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
97 |
98 | ##@ Build
99 |
100 | .PHONY: build
101 | build: generate fmt vet ## Build manager binary.
102 | go build -o bin/manager main.go
103 |
104 | .PHONY: run
105 | run: manifests generate fmt vet ## Run a controller from your host.
106 | go run ./main.go
107 |
108 | .PHONY: docker-build
109 | docker-build: test ## Build docker image with the manager.
110 | podman build -t ${IMG} .
111 |
112 | .PHONY: docker-push
113 | docker-push: ## Push docker image with the manager.
114 | podman push ${IMG}
115 |
116 | ##@ Deployment
117 |
118 | ifndef ignore-not-found
119 | ignore-not-found = false
120 | endif
121 |
122 | .PHONY: install
123 | install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
124 | $(KUSTOMIZE) build config/crd | kubectl apply -f -
125 |
126 | .PHONY: uninstall
127 | uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
128 | $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
129 |
130 | .PHONY: deploy
131 | deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
132 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
133 | $(KUSTOMIZE) build config/default | kubectl apply -f -
134 |
135 | .PHONY: undeploy
136 | undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
137 | $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -
138 |
139 | CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
140 | .PHONY: controller-gen
141 | controller-gen: ## Download controller-gen locally if necessary.
142 | $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0)
143 |
144 | KUSTOMIZE = $(shell pwd)/bin/kustomize
145 | .PHONY: kustomize
146 | kustomize: ## Download kustomize locally if necessary.
147 | $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
148 |
149 | ENVTEST = $(shell pwd)/bin/setup-envtest
150 | .PHONY: envtest
151 | envtest: ## Download envtest-setup locally if necessary.
152 | $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
153 |
154 | # go-get-tool will 'go get' any package $2 and install it to $1.
155 | PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
156 | define go-get-tool
157 | @[ -f $(1) ] || { \
158 | set -e ;\
159 | TMP_DIR=$$(mktemp -d) ;\
160 | cd $$TMP_DIR ;\
161 | go mod init tmp ;\
162 | echo "Downloading $(2)" ;\
163 | GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
164 | rm -rf $$TMP_DIR ;\
165 | }
166 | endef
167 |
168 | .PHONY: bundle
169 | bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
170 | operator-sdk generate kustomize manifests -q
171 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
172 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
173 | operator-sdk bundle validate ./bundle
174 |
175 | .PHONY: bundle-build
176 | bundle-build: ## Build the bundle image.
177 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
178 |
179 | .PHONY: bundle-push
180 | bundle-push: ## Push the bundle image.
181 | $(MAKE) docker-push IMG=$(BUNDLE_IMG)
182 |
183 | .PHONY: opm
184 | OPM = ./bin/opm
185 | opm: ## Download opm locally if necessary.
186 | ifeq (,$(wildcard $(OPM)))
187 | ifeq (,$(shell which opm 2>/dev/null))
188 | @{ \
189 | set -e ;\
190 | mkdir -p $(dir $(OPM)) ;\
191 | OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
192 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.19.1/$${OS}-$${ARCH}-opm ;\
193 | chmod +x $(OPM) ;\
194 | }
195 | else
196 | OPM = $(shell which opm)
197 | endif
198 | endif
199 |
200 | # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0).
201 | # These images MUST exist in a registry and be pull-able.
202 | BUNDLE_IMGS ?= $(BUNDLE_IMG)
203 |
204 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).
205 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
206 |
207 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.
208 | ifneq ($(origin CATALOG_BASE_IMG), undefined)
209 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
210 | endif
211 |
212 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.
213 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:
214 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator
215 | .PHONY: catalog-build
216 | catalog-build: opm ## Build a catalog image.
217 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
218 |
219 | # Push the catalog image.
220 | .PHONY: catalog-push
221 | catalog-push: ## Push a catalog image.
222 | $(MAKE) docker-push IMG=$(CATALOG_IMG)
223 |
--------------------------------------------------------------------------------
/operator/PROJECT:
--------------------------------------------------------------------------------
1 | domain: saas.ecommerce.sample.com
2 | layout:
3 | - go.kubebuilder.io/v3
4 | plugins:
5 | manifests.sdk.operatorframework.io/v2: {}
6 | scorecard.sdk.operatorframework.io/v2: {}
7 | projectName: temp
8 | repo: github.com/multi-tenancy/operator
9 | resources:
10 | - api:
11 | crdVersion: v1
12 | namespaced: true
13 | controller: true
14 | domain: saas.ecommerce.sample.com
15 | group: cache
16 | kind: ECommerceApplication
17 | path: github.com/multi-tenancy/operator/api/v1alpha1
18 | version: v1alpha1
19 | version: "3"
20 |
--------------------------------------------------------------------------------
/operator/api/v1alpha1/ecommerceapplication_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25 |
26 | // ECommerceApplicationSpec defines the desired state of ECommerceApplication
27 | type ECommerceApplicationSpec struct {
28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
29 | // Important: Run "make" to regenerate code after modifying this file
30 |
31 | // Foo is an example field of ECommerceApplication. Edit ecommerceapplication_types.go to remove/update
32 | Foo string `json:"foo,omitempty"`
33 |
34 | //+kubebuilder:validation:Minimum=0
35 | // Size is the size of the memcached deployment
36 | Size int32 `json:"size"`
37 |
38 | PostgresSecretName string `json:"postgresSecretName,omitempty"`
39 | AppIdSecretName string `json:"appIdSecretName,omitempty"`
40 |
41 | TenantName string `json:"tenantName,omitempty"`
42 |
43 | IbmCloudOperatorSecretName string `json:"ibmCloudOperatorSecretName,omitempty"`
44 | IbmCloudOperatorSecretNamespace string `json:"ibmCloudOperatorSecretNamespace,omitempty"`
45 | }
46 |
47 | // ECommerceApplicationStatus defines the observed state of ECommerceApplication
48 | type ECommerceApplicationStatus struct {
49 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
50 | // Important: Run "make" to regenerate code after modifying this file
51 | }
52 |
53 | //+kubebuilder:object:root=true
54 | //+kubebuilder:subresource:status
55 |
56 | // ECommerceApplication is the Schema for the ecommerceapplications API
57 | type ECommerceApplication struct {
58 | metav1.TypeMeta `json:",inline"`
59 | metav1.ObjectMeta `json:"metadata,omitempty"`
60 |
61 | Spec ECommerceApplicationSpec `json:"spec,omitempty"`
62 | Status ECommerceApplicationStatus `json:"status,omitempty"`
63 | }
64 |
65 | //+kubebuilder:object:root=true
66 |
67 | // ECommerceApplicationList contains a list of ECommerceApplication
68 | type ECommerceApplicationList struct {
69 | metav1.TypeMeta `json:",inline"`
70 | metav1.ListMeta `json:"metadata,omitempty"`
71 | Items []ECommerceApplication `json:"items"`
72 | }
73 |
74 | func init() {
75 | SchemeBuilder.Register(&ECommerceApplication{}, &ECommerceApplicationList{})
76 | }
77 |
--------------------------------------------------------------------------------
/operator/api/v1alpha1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1alpha1 contains API Schema definitions for the cache v1alpha1 API group
18 | //+kubebuilder:object:generate=true
19 | //+groupName=cache.saas.ecommerce.sample.com
20 | package v1alpha1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "cache.saas.ecommerce.sample.com", Version: "v1alpha1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 | )
37 |
--------------------------------------------------------------------------------
/operator/api/v1alpha1/zz_generated.deepcopy.go:
--------------------------------------------------------------------------------
1 | //go:build !ignore_autogenerated
2 | // +build !ignore_autogenerated
3 |
4 | /*
5 | Copyright 2022.
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License");
8 | you may not use this file except in compliance with the License.
9 | You may obtain a copy of the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software
14 | distributed under the License is distributed on an "AS IS" BASIS,
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | See the License for the specific language governing permissions and
17 | limitations under the License.
18 | */
19 |
20 | // Code generated by controller-gen. DO NOT EDIT.
21 |
22 | package v1alpha1
23 |
24 | import (
25 | runtime "k8s.io/apimachinery/pkg/runtime"
26 | )
27 |
28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
29 | func (in *ECommerceApplication) DeepCopyInto(out *ECommerceApplication) {
30 | *out = *in
31 | out.TypeMeta = in.TypeMeta
32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
33 | out.Spec = in.Spec
34 | out.Status = in.Status
35 | }
36 |
37 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ECommerceApplication.
38 | func (in *ECommerceApplication) DeepCopy() *ECommerceApplication {
39 | if in == nil {
40 | return nil
41 | }
42 | out := new(ECommerceApplication)
43 | in.DeepCopyInto(out)
44 | return out
45 | }
46 |
47 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
48 | func (in *ECommerceApplication) DeepCopyObject() runtime.Object {
49 | if c := in.DeepCopy(); c != nil {
50 | return c
51 | }
52 | return nil
53 | }
54 |
55 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
56 | func (in *ECommerceApplicationList) DeepCopyInto(out *ECommerceApplicationList) {
57 | *out = *in
58 | out.TypeMeta = in.TypeMeta
59 | in.ListMeta.DeepCopyInto(&out.ListMeta)
60 | if in.Items != nil {
61 | in, out := &in.Items, &out.Items
62 | *out = make([]ECommerceApplication, len(*in))
63 | for i := range *in {
64 | (*in)[i].DeepCopyInto(&(*out)[i])
65 | }
66 | }
67 | }
68 |
69 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ECommerceApplicationList.
70 | func (in *ECommerceApplicationList) DeepCopy() *ECommerceApplicationList {
71 | if in == nil {
72 | return nil
73 | }
74 | out := new(ECommerceApplicationList)
75 | in.DeepCopyInto(out)
76 | return out
77 | }
78 |
79 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
80 | func (in *ECommerceApplicationList) DeepCopyObject() runtime.Object {
81 | if c := in.DeepCopy(); c != nil {
82 | return c
83 | }
84 | return nil
85 | }
86 |
87 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
88 | func (in *ECommerceApplicationSpec) DeepCopyInto(out *ECommerceApplicationSpec) {
89 | *out = *in
90 | }
91 |
92 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ECommerceApplicationSpec.
93 | func (in *ECommerceApplicationSpec) DeepCopy() *ECommerceApplicationSpec {
94 | if in == nil {
95 | return nil
96 | }
97 | out := new(ECommerceApplicationSpec)
98 | in.DeepCopyInto(out)
99 | return out
100 | }
101 |
102 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
103 | func (in *ECommerceApplicationStatus) DeepCopyInto(out *ECommerceApplicationStatus) {
104 | *out = *in
105 | }
106 |
107 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ECommerceApplicationStatus.
108 | func (in *ECommerceApplicationStatus) DeepCopy() *ECommerceApplicationStatus {
109 | if in == nil {
110 | return nil
111 | }
112 | out := new(ECommerceApplicationStatus)
113 | in.DeepCopyInto(out)
114 | return out
115 | }
116 |
--------------------------------------------------------------------------------
/operator/appIdHelper/appId.go:
--------------------------------------------------------------------------------
1 | package appIdHelper
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "fmt"
8 | "io/ioutil"
9 | "net/http"
10 | "net/url"
11 | "strconv"
12 | "strings"
13 |
14 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
15 | )
16 |
17 | func GetClientId(managementUrl string, ibmCloudApiKey string, tenantId string, ctx context.Context) (string, error) {
18 |
19 | //$ curl -X POST "https://iam.cloud.ibm.com/identity/token" -H "content-type: application/x-www-form-urlencoded" -H "accept: application/json" -d 'grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&apikey=' > token.json
20 |
21 | /*APPID_MANAGEMENT_URL_ALL_APPLICATIONS=${APPID_MANAGEMENT_URL}/applications
22 | echo $APPID_MANAGEMENT_URL_ALL_APPLICATIONS
23 | result=$(curl -H "Content-Type: application/json" -H "Authorization: Bearer $OAUTHTOKEN" $APPID_MANAGEMENT_URL_ALL_APPLICATIONS)
24 | echo $result
25 | APPID_CLIENT_ID=$(echo $result | sed -n 's|.*"clientId":"\([^"]*\)".*|\1|p')
26 | echo $APPID_CLIENT_ID*/
27 |
28 | log := ctrllog.FromContext(ctx)
29 |
30 | //clientId := ""
31 | oauth, err := getIbmCloudOauthToken(ibmCloudApiKey, ctx)
32 | if err != nil {
33 | log.Error(err, "Error retrieving IBM Cloud Oauth token")
34 | return "", err
35 | }
36 |
37 | client := &http.Client{}
38 |
39 | //appIdUrl := fmt.Sprintf("%s%s", managementUrl, "applications")
40 | //log.Info(fmt.Sprintf("%s%s", "appIdUrl=", appIdUrl))
41 |
42 | jsonPayload := []byte(fmt.Sprintf("%s%s%s%s", "{\"tenantId\":", "\"", tenantId, "\"}"))
43 | //jsonPayload := []byte(`{"tenantId":"e38a8715-b8a4-4f1f-82b2-5e434e198768"}`)
44 |
45 | log.Info(fmt.Sprintf("%s%s", "managementUrl=", managementUrl))
46 | //log.Info(fmt.Sprintf("%s%s", "jsonPayload=", jsonPayload))
47 |
48 | bearer := fmt.Sprintf("%s%s", "Bearer ", oauth)
49 | log.Info(fmt.Sprintf("%s%s", "bearer=", bearer))
50 |
51 | //jsonStr = []byte(jsonPayload)
52 | req, err := http.NewRequest("GET", managementUrl, bytes.NewBuffer(jsonPayload))
53 | req.Header.Set("Content-Type", "application/json")
54 | req.Header.Set("Authorization", bearer)
55 |
56 | resp, err := client.Do(req)
57 | if err != nil {
58 | log.Error(err, "Create GET request failed")
59 | return "", err
60 | }
61 | defer resp.Body.Close()
62 |
63 | log.Info(fmt.Sprintf("%s%s", "response Status:", resp.Status))
64 | log.Info(fmt.Sprintf("%s%s", "response Headers:", resp.Header))
65 | //fmt.Println("response Status:", resp.Status)
66 | //fmt.Println("response Headers:", resp.Header)
67 | body, _ := ioutil.ReadAll(resp.Body)
68 | //fmt.Println("response Body:", string(body))
69 |
70 | log.Info(fmt.Sprintf("%s%s", "returning clientId=", string(body)))
71 | return string(body), nil
72 |
73 | }
74 |
75 | func getIbmCloudOauthToken(apiKey string, ctx context.Context) (string, error) {
76 |
77 | type oauthBindingJSON struct {
78 | Access_token string `json:"access_token"`
79 | Refresh_token string `json:"refresh_token"`
80 | Ims_user_id int `json:"ims_user_id"`
81 | Token_type string `json:"token_type"`
82 | Expires_in int `json:"expires_in"`
83 | Expiration int `json:"expiration"`
84 | Scope string `json:"scope"`
85 | }
86 |
87 | log := ctrllog.FromContext(ctx)
88 |
89 | endpoint := "https://iam.cloud.ibm.com/identity/token"
90 | data := url.Values{}
91 | data.Set("grant_type", "urn:ibm:params:oauth:grant-type:apikey")
92 |
93 | data.Set("apikey", apiKey)
94 |
95 | client := &http.Client{}
96 |
97 | r, err := http.NewRequest("POST", endpoint, strings.NewReader(data.Encode())) // URL-encoded payload
98 | if err != nil {
99 | log.Error(err, "Create request failed")
100 | return "", err
101 | }
102 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
103 | r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
104 |
105 | res, err := client.Do(r)
106 | if err != nil {
107 | log.Error(err, "Post failed")
108 | return "", err
109 | }
110 | log.Info(res.Status)
111 | defer res.Body.Close()
112 | body, err := ioutil.ReadAll(res.Body)
113 | if err != nil {
114 | log.Error(err, "Reading response failed")
115 | return "", err
116 | }
117 |
118 | log.Info(string(body))
119 |
120 | var jsonData oauthBindingJSON
121 |
122 | err = json.Unmarshal(body, &jsonData)
123 | if err != nil {
124 | log.Error(err, "unmarshall error")
125 | return "", err
126 | }
127 |
128 | return jsonData.Access_token, nil
129 | }
130 |
--------------------------------------------------------------------------------
/operator/config/crd/bases/cache.saas.ecommerce.sample.com_ecommerceapplications.yaml:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | apiVersion: apiextensions.k8s.io/v1
4 | kind: CustomResourceDefinition
5 | metadata:
6 | annotations:
7 | controller-gen.kubebuilder.io/version: v0.7.0
8 | creationTimestamp: null
9 | name: ecommerceapplications.cache.saas.ecommerce.sample.com
10 | spec:
11 | group: cache.saas.ecommerce.sample.com
12 | names:
13 | kind: ECommerceApplication
14 | listKind: ECommerceApplicationList
15 | plural: ecommerceapplications
16 | singular: ecommerceapplication
17 | scope: Namespaced
18 | versions:
19 | - name: v1alpha1
20 | schema:
21 | openAPIV3Schema:
22 | description: ECommerceApplication is the Schema for the ecommerceapplications
23 | API
24 | properties:
25 | apiVersion:
26 | description: 'APIVersion defines the versioned schema of this representation
27 | of an object. Servers should convert recognized schemas to the latest
28 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
29 | type: string
30 | kind:
31 | description: 'Kind is a string value representing the REST resource this
32 | object represents. Servers may infer this from the endpoint the client
33 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
34 | type: string
35 | metadata:
36 | type: object
37 | spec:
38 | description: ECommerceApplicationSpec defines the desired state of ECommerceApplication
39 | properties:
40 | appIdSecretName:
41 | type: string
42 | foo:
43 | description: Foo is an example field of ECommerceApplication. Edit
44 | ecommerceapplication_types.go to remove/update
45 | type: string
46 | ibmCloudOperatorSecretName:
47 | type: string
48 | ibmCloudOperatorSecretNamespace:
49 | type: string
50 | postgresSecretName:
51 | type: string
52 | size:
53 | description: Size is the size of the memcached deployment
54 | format: int32
55 | minimum: 0
56 | type: integer
57 | tenantName:
58 | type: string
59 | required:
60 | - size
61 | type: object
62 | status:
63 | description: ECommerceApplicationStatus defines the observed state of
64 | ECommerceApplication
65 | type: object
66 | type: object
67 | served: true
68 | storage: true
69 | subresources:
70 | status: {}
71 | status:
72 | acceptedNames:
73 | kind: ""
74 | plural: ""
75 | conditions: []
76 | storedVersions: []
77 |
--------------------------------------------------------------------------------
/operator/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This kustomization.yaml is not intended to be run by itself,
2 | # since it depends on service name and namespace that are out of this kustomize package.
3 | # It should be run by config/default
4 | resources:
5 | - bases/cache.saas.ecommerce.sample.com_ecommerceapplications.yaml
6 | #+kubebuilder:scaffold:crdkustomizeresource
7 |
8 | patchesStrategicMerge:
9 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
10 | # patches here are for enabling the conversion webhook for each CRD
11 | #- patches/webhook_in_ecommerceapplications.yaml
12 | #+kubebuilder:scaffold:crdkustomizewebhookpatch
13 |
14 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
15 | # patches here are for enabling the CA injection for each CRD
16 | #- patches/cainjection_in_ecommerceapplications.yaml
17 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch
18 |
19 | # the following config is for teaching kustomize how to do kustomization for CRDs.
20 | configurations:
21 | - kustomizeconfig.yaml
22 |
--------------------------------------------------------------------------------
/operator/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/operator/config/crd/patches/cainjection_in_ecommerceapplications.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: ecommerceapplications.cache.saas.ecommerce.sample.com
8 |
--------------------------------------------------------------------------------
/operator/config/crd/patches/webhook_in_ecommerceapplications.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: ecommerceapplications.cache.saas.ecommerce.sample.com
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1
17 |
--------------------------------------------------------------------------------
/operator/config/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # Adds namespace to all resources.
2 | namespace: temp-system
3 |
4 | # Value of this field is prepended to the
5 | # names of all resources, e.g. a deployment named
6 | # "wordpress" becomes "alices-wordpress".
7 | # Note that it should also match with the prefix (text before '-') of the namespace
8 | # field above.
9 | namePrefix: temp-
10 |
11 | # Labels to add to all resources and selectors.
12 | #commonLabels:
13 | # someName: someValue
14 |
15 | bases:
16 | - ../crd
17 | - ../rbac
18 | - ../manager
19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
20 | # crd/kustomization.yaml
21 | #- ../webhook
22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
23 | #- ../certmanager
24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
25 | #- ../prometheus
26 |
27 | patchesStrategicMerge:
28 | # Protect the /metrics endpoint by putting it behind auth.
29 | # If you want your controller-manager to expose the /metrics
30 | # endpoint w/o any authn/z, please comment the following line.
31 | - manager_auth_proxy_patch.yaml
32 |
33 | # Mount the controller config file for loading manager configurations
34 | # through a ComponentConfig type
35 | #- manager_config_patch.yaml
36 |
37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
38 | # crd/kustomization.yaml
39 | #- manager_webhook_patch.yaml
40 |
41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
43 | # 'CERTMANAGER' needs to be enabled to use ca injection
44 | #- webhookcainjection_patch.yaml
45 |
46 | # the following config is for teaching kustomize how to do var substitution
47 | vars:
48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
49 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
50 | # objref:
51 | # kind: Certificate
52 | # group: cert-manager.io
53 | # version: v1
54 | # name: serving-cert # this name should match the one in certificate.yaml
55 | # fieldref:
56 | # fieldpath: metadata.namespace
57 | #- name: CERTIFICATE_NAME
58 | # objref:
59 | # kind: Certificate
60 | # group: cert-manager.io
61 | # version: v1
62 | # name: serving-cert # this name should match the one in certificate.yaml
63 | #- name: SERVICE_NAMESPACE # namespace of the service
64 | # objref:
65 | # kind: Service
66 | # version: v1
67 | # name: webhook-service
68 | # fieldref:
69 | # fieldpath: metadata.namespace
70 | #- name: SERVICE_NAME
71 | # objref:
72 | # kind: Service
73 | # version: v1
74 | # name: webhook-service
75 |
--------------------------------------------------------------------------------
/operator/config/default/manager_auth_proxy_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch inject a sidecar container which is a HTTP proxy for the
2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
3 | apiVersion: apps/v1
4 | kind: Deployment
5 | metadata:
6 | name: controller-manager
7 | namespace: system
8 | spec:
9 | template:
10 | spec:
11 | containers:
12 | - name: kube-rbac-proxy
13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
14 | args:
15 | - "--secure-listen-address=0.0.0.0:8443"
16 | - "--upstream=http://127.0.0.1:8080/"
17 | - "--logtostderr=true"
18 | - "--v=10"
19 | ports:
20 | - containerPort: 8443
21 | protocol: TCP
22 | name: https
23 | - name: manager
24 | args:
25 | - "--health-probe-bind-address=:8081"
26 | - "--metrics-bind-address=127.0.0.1:8080"
27 | - "--leader-elect"
28 |
--------------------------------------------------------------------------------
/operator/config/default/manager_config_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - name: manager
11 | args:
12 | - "--config=controller_manager_config.yaml"
13 | volumeMounts:
14 | - name: manager-config
15 | mountPath: /controller_manager_config.yaml
16 | subPath: controller_manager_config.yaml
17 | volumes:
18 | - name: manager-config
19 | configMap:
20 | name: manager-config
21 |
--------------------------------------------------------------------------------
/operator/config/manager/controller_manager_config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
2 | kind: ControllerManagerConfig
3 | health:
4 | healthProbeBindAddress: :8081
5 | metrics:
6 | bindAddress: 127.0.0.1:8080
7 | webhook:
8 | port: 9443
9 | leaderElection:
10 | leaderElect: true
11 | resourceName: 27344913.saas.ecommerce.sample.com
12 |
--------------------------------------------------------------------------------
/operator/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manager.yaml
3 |
4 | generatorOptions:
5 | disableNameSuffixHash: true
6 |
7 | configMapGenerator:
8 | - files:
9 | - controller_manager_config.yaml
10 | name: manager-config
11 | apiVersion: kustomize.config.k8s.io/v1beta1
12 | kind: Kustomization
13 | images:
14 | - name: controller
15 | newName: docker.io/nheidloff/controller
16 | newTag: latest
17 |
--------------------------------------------------------------------------------
/operator/config/manager/manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | name: system
7 | ---
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | metadata:
11 | name: controller-manager
12 | namespace: system
13 | labels:
14 | control-plane: controller-manager
15 | spec:
16 | selector:
17 | matchLabels:
18 | control-plane: controller-manager
19 | replicas: 1
20 | template:
21 | metadata:
22 | annotations:
23 | kubectl.kubernetes.io/default-container: manager
24 | labels:
25 | control-plane: controller-manager
26 | spec:
27 | securityContext:
28 | runAsNonRoot: true
29 | containers:
30 | - command:
31 | - /manager
32 | args:
33 | - --leader-elect
34 | image: controller:latest
35 | name: manager
36 | securityContext:
37 | allowPrivilegeEscalation: false
38 | livenessProbe:
39 | httpGet:
40 | path: /healthz
41 | port: 8081
42 | initialDelaySeconds: 15
43 | periodSeconds: 20
44 | readinessProbe:
45 | httpGet:
46 | path: /readyz
47 | port: 8081
48 | initialDelaySeconds: 5
49 | periodSeconds: 10
50 | # TODO(user): Configure the resources accordingly based on the project requirements.
51 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
52 | resources:
53 | limits:
54 | cpu: 500m
55 | memory: 128Mi
56 | requests:
57 | cpu: 10m
58 | memory: 64Mi
59 | serviceAccountName: controller-manager
60 | terminationGracePeriodSeconds: 10
61 |
--------------------------------------------------------------------------------
/operator/config/manifests/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # These resources constitute the fully configured set of manifests
2 | # used to generate the 'manifests/' directory in a bundle.
3 | resources:
4 | - bases/temp.clusterserviceversion.yaml
5 | - ../default
6 | - ../samples
7 | - ../scorecard
8 |
9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.
10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.
11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount.
12 | #patchesJson6902:
13 | #- target:
14 | # group: apps
15 | # version: v1
16 | # kind: Deployment
17 | # name: controller-manager
18 | # namespace: system
19 | # patch: |-
20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
22 | # - op: remove
23 | # path: /spec/template/spec/containers/1/volumeMounts/0
24 | # # Remove the "cert" volume, since OLM will create and mount a set of certs.
25 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment.
26 | # - op: remove
27 | # path: /spec/template/spec/volumes/0
28 |
--------------------------------------------------------------------------------
/operator/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
--------------------------------------------------------------------------------
/operator/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 |
2 | # Prometheus Monitor Service (Metrics)
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | labels:
7 | control-plane: controller-manager
8 | name: controller-manager-metrics-monitor
9 | namespace: system
10 | spec:
11 | endpoints:
12 | - path: /metrics
13 | port: https
14 | scheme: https
15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
16 | tlsConfig:
17 | insecureSkipVerify: true
18 | selector:
19 | matchLabels:
20 | control-plane: controller-manager
21 |
--------------------------------------------------------------------------------
/operator/config/rbac/auth_proxy_client_clusterrole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-reader
5 | rules:
6 | - nonResourceURLs:
7 | - "/metrics"
8 | verbs:
9 | - get
10 |
--------------------------------------------------------------------------------
/operator/config/rbac/auth_proxy_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: proxy-role
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/operator/config/rbac/auth_proxy_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: proxy-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: proxy-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/operator/config/rbac/auth_proxy_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | name: controller-manager-metrics-service
7 | namespace: system
8 | spec:
9 | ports:
10 | - name: https
11 | port: 8443
12 | protocol: TCP
13 | targetPort: https
14 | selector:
15 | control-plane: controller-manager
16 |
--------------------------------------------------------------------------------
/operator/config/rbac/ecommerceapplication_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit ecommerceapplications.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: ecommerceapplication-editor-role
6 | rules:
7 | - apiGroups:
8 | - cache.saas.ecommerce.sample.com
9 | resources:
10 | - ecommerceapplications
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - cache.saas.ecommerce.sample.com
21 | resources:
22 | - ecommerceapplications/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/operator/config/rbac/ecommerceapplication_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view ecommerceapplications.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: ecommerceapplication-viewer-role
6 | rules:
7 | - apiGroups:
8 | - cache.saas.ecommerce.sample.com
9 | resources:
10 | - ecommerceapplications
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - cache.saas.ecommerce.sample.com
17 | resources:
18 | - ecommerceapplications/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/operator/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 | # Comment the following 4 lines if you want to disable
13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy)
14 | # which protects your /metrics endpoint.
15 | - auth_proxy_service.yaml
16 | - auth_proxy_role.yaml
17 | - auth_proxy_role_binding.yaml
18 | - auth_proxy_client_clusterrole.yaml
19 |
--------------------------------------------------------------------------------
/operator/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: leader-election-role
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - configmaps
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - create
16 | - update
17 | - patch
18 | - delete
19 | - apiGroups:
20 | - coordination.k8s.io
21 | resources:
22 | - leases
23 | verbs:
24 | - get
25 | - list
26 | - watch
27 | - create
28 | - update
29 | - patch
30 | - delete
31 | - apiGroups:
32 | - ""
33 | resources:
34 | - events
35 | verbs:
36 | - create
37 | - patch
38 |
--------------------------------------------------------------------------------
/operator/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: leader-election-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: Role
8 | name: leader-election-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/operator/config/rbac/role.yaml:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | apiVersion: rbac.authorization.k8s.io/v1
4 | kind: ClusterRole
5 | metadata:
6 | creationTimestamp: null
7 | name: manager-role
8 | rules:
9 | - apiGroups:
10 | - apps
11 | resources:
12 | - deployments
13 | verbs:
14 | - create
15 | - delete
16 | - get
17 | - list
18 | - patch
19 | - update
20 | - watch
21 | - apiGroups:
22 | - cache.saas.ecommerce.sample.com
23 | resources:
24 | - ecommerceapplications
25 | verbs:
26 | - create
27 | - delete
28 | - get
29 | - list
30 | - patch
31 | - update
32 | - watch
33 | - apiGroups:
34 | - cache.saas.ecommerce.sample.com
35 | resources:
36 | - ecommerceapplications/finalizers
37 | verbs:
38 | - update
39 | - apiGroups:
40 | - cache.saas.ecommerce.sample.com
41 | resources:
42 | - ecommerceapplications/status
43 | verbs:
44 | - get
45 | - patch
46 | - update
47 | - apiGroups:
48 | - ""
49 | resources:
50 | - pods
51 | verbs:
52 | - get
53 | - list
54 | - watch
55 |
--------------------------------------------------------------------------------
/operator/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: manager-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: manager-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/operator/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 |
--------------------------------------------------------------------------------
/operator/config/samples/cache_v1alpha1_ecommerceapplication.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cache.saas.ecommerce.sample.com/v1alpha1
2 | kind: ECommerceApplication
3 | metadata:
4 | name: ecommerceapplication-ten-c
5 | spec:
6 | size: 1
7 | appIdSecretName: multi-tenancy-appid-ten-c-secret
8 | postgresSecretName: multi-tenancy-pg-ten-c-secret
9 | tenantName: ten-c
10 | ibmCloudOperatorSecretName: ibmcloud-operator-secret
11 | ibmCloudOperatorSecretNamespace: default
12 | ---
13 | apiVersion: ibmcloud.ibm.com/v1
14 | kind: Service
15 | metadata:
16 | name: multi-tenancy-pg-ten-d
17 | spec:
18 | plan: standard
19 | serviceClass: databases-for-postgresql
20 | ---
21 | apiVersion: ibmcloud.ibm.com/v1
22 | kind: Service
23 | metadata:
24 | name: multi-tenancy-appid-ten-c
25 | spec:
26 | plan: graduated-tier
27 | serviceClass: appid
28 | ---
29 | apiVersion: ibmcloud.ibm.com/v1
30 | kind: Binding
31 | metadata:
32 | name: binding-multi-tenancy-pg-ten-c
33 | spec:
34 | serviceName: multi-tenancy-pg-ten-d
35 | secretName: multi-tenancy-pg-ten-c-secret
36 | ---
37 | apiVersion: ibmcloud.ibm.com/v1
38 | kind: Binding
39 | metadata:
40 | name: binding-multi-tenancy-appid-ten-c
41 | spec:
42 | serviceName: multi-tenancy-appid-ten-c
43 | secretName: multi-tenancy-appid-ten-c-secret
44 |
--------------------------------------------------------------------------------
/operator/config/samples/job.yml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: pg
5 | spec:
6 | template:
7 | spec:
8 | containers:
9 | - name: pg
10 | image: bash
11 | args:
12 | - /bin/sh
13 | - -c
14 | - date; echo Hello from the Kubernetes cluster
15 | restartPolicy: Never
16 | backoffLimit: 4
--------------------------------------------------------------------------------
/operator/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples you want in your CSV to this file as resources ##
2 | resources:
3 | - cache_v1alpha1_ecommerceapplication.yaml
4 | #+kubebuilder:scaffold:manifestskustomizesamples
5 |
--------------------------------------------------------------------------------
/operator/config/scorecard/bases/config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: scorecard.operatorframework.io/v1alpha3
2 | kind: Configuration
3 | metadata:
4 | name: config
5 | stages:
6 | - parallel: true
7 | tests: []
8 |
--------------------------------------------------------------------------------
/operator/config/scorecard/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - bases/config.yaml
3 | patchesJson6902:
4 | - path: patches/basic.config.yaml
5 | target:
6 | group: scorecard.operatorframework.io
7 | version: v1alpha3
8 | kind: Configuration
9 | name: config
10 | - path: patches/olm.config.yaml
11 | target:
12 | group: scorecard.operatorframework.io
13 | version: v1alpha3
14 | kind: Configuration
15 | name: config
16 | #+kubebuilder:scaffold:patchesJson6902
17 |
--------------------------------------------------------------------------------
/operator/config/scorecard/patches/basic.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - basic-check-spec
7 | image: quay.io/operator-framework/scorecard-test:v1.16.0
8 | labels:
9 | suite: basic
10 | test: basic-check-spec-test
11 |
--------------------------------------------------------------------------------
/operator/config/scorecard/patches/olm.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - olm-bundle-validation
7 | image: quay.io/operator-framework/scorecard-test:v1.16.0
8 | labels:
9 | suite: olm
10 | test: olm-bundle-validation-test
11 | - op: add
12 | path: /stages/0/tests/-
13 | value:
14 | entrypoint:
15 | - scorecard-test
16 | - olm-crds-have-validation
17 | image: quay.io/operator-framework/scorecard-test:v1.16.0
18 | labels:
19 | suite: olm
20 | test: olm-crds-have-validation-test
21 | - op: add
22 | path: /stages/0/tests/-
23 | value:
24 | entrypoint:
25 | - scorecard-test
26 | - olm-crds-have-resources
27 | image: quay.io/operator-framework/scorecard-test:v1.16.0
28 | labels:
29 | suite: olm
30 | test: olm-crds-have-resources-test
31 | - op: add
32 | path: /stages/0/tests/-
33 | value:
34 | entrypoint:
35 | - scorecard-test
36 | - olm-spec-descriptors
37 | image: quay.io/operator-framework/scorecard-test:v1.16.0
38 | labels:
39 | suite: olm
40 | test: olm-spec-descriptors-test
41 | - op: add
42 | path: /stages/0/tests/-
43 | value:
44 | entrypoint:
45 | - scorecard-test
46 | - olm-status-descriptors
47 | image: quay.io/operator-framework/scorecard-test:v1.16.0
48 | labels:
49 | suite: olm
50 | test: olm-status-descriptors-test
51 |
--------------------------------------------------------------------------------
/operator/controllers/suite_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controllers
18 |
19 | import (
20 | "path/filepath"
21 | "testing"
22 |
23 | . "github.com/onsi/ginkgo"
24 | . "github.com/onsi/gomega"
25 | "k8s.io/client-go/kubernetes/scheme"
26 | "k8s.io/client-go/rest"
27 | "sigs.k8s.io/controller-runtime/pkg/client"
28 | "sigs.k8s.io/controller-runtime/pkg/envtest"
29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
30 | logf "sigs.k8s.io/controller-runtime/pkg/log"
31 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
32 |
33 | cachev1alpha1 "github.com/multi-tenancy/operator/api/v1alpha1"
34 | //+kubebuilder:scaffold:imports
35 | )
36 |
37 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to
38 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
39 |
40 | var cfg *rest.Config
41 | var k8sClient client.Client
42 | var testEnv *envtest.Environment
43 |
44 | func TestAPIs(t *testing.T) {
45 | RegisterFailHandler(Fail)
46 |
47 | RunSpecsWithDefaultAndCustomReporters(t,
48 | "Controller Suite",
49 | []Reporter{printer.NewlineReporter{}})
50 | }
51 |
52 | var _ = BeforeSuite(func() {
53 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
54 |
55 | By("bootstrapping test environment")
56 | testEnv = &envtest.Environment{
57 | CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
58 | ErrorIfCRDPathMissing: true,
59 | }
60 |
61 | cfg, err := testEnv.Start()
62 | Expect(err).NotTo(HaveOccurred())
63 | Expect(cfg).NotTo(BeNil())
64 |
65 | err = cachev1alpha1.AddToScheme(scheme.Scheme)
66 | Expect(err).NotTo(HaveOccurred())
67 |
68 | //+kubebuilder:scaffold:scheme
69 |
70 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
71 | Expect(err).NotTo(HaveOccurred())
72 | Expect(k8sClient).NotTo(BeNil())
73 |
74 | }, 60)
75 |
76 | var _ = AfterSuite(func() {
77 | By("tearing down the test environment")
78 | err := testEnv.Stop()
79 | Expect(err).NotTo(HaveOccurred())
80 | })
81 |
--------------------------------------------------------------------------------
/operator/ecommerceapplication/tmp/copy-secret.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export DEVELOPER_1_PROJECT=deleeuw
4 | export DEVELOPER_2_PROJECT=saas-operator-development-thomas
5 |
6 | export APPID_SECRET_NAME=multi-tenancy-appid-ten-f-secret
7 | export POSTGRES_SECRET_NAME=multi-tenancy-pg-ten-f-secret
8 |
9 | echo "Set Project $DEVELOPER_1_PROJECT"
10 | oc project $DEVELOPER_1_PROJECT
11 | oc get secret $APPID_SECRET_NAME -o yaml
12 |
13 | echo "Extract secrect for AppID"
14 | mkdir appid_secrets
15 | cd appid_secrets
16 | oc extract secret/$APPID_SECRET_NAME --to=.
17 | cd ..
18 |
19 | echo "Extract secrect for Postgres"
20 | mkdir postgres_secrets
21 | cd postgres_secrets
22 | oc extract secret/$POSTGRES_SECRET_NAME --to=.
23 | cd ..
24 |
25 | echo "Set Project $DEVELOPER_2_PROJECT"
26 | oc project $DEVELOPER_2_PROJECT
27 |
28 | echo "Create postgres secret: $APPID_SECRET_NAME"
29 | cd appid_secrets
30 | kubectl create secret generic $APPID_SECRET_NAME \
31 | --from-file=apikey=../appid_secrets/apikey \
32 | --from-file=appidServiceEndpoint=../appid_secrets/appidServiceEndpoint \
33 | --from-file=clientId=../appid_secrets/clientId \
34 | --from-file=discoveryEndpoint=../appid_secrets/discoveryEndpoint \
35 | --from-file=iam_apikey_description=../appid_secrets/iam_apikey_description \
36 | --from-file=iam_apikey_name=../appid_secrets/iam_apikey_name \
37 | --from-file=iam_role_crn=../appid_secrets/iam_role_crn \
38 | --from-file=iam_serviceid_crn=../appid_secrets/iam_serviceid_crn \
39 | --from-file=managementUrl=../appid_secrets/managementUrl \
40 | --from-file=oauthServerUrl=../appid_secrets/oauthServerUrl \
41 | --from-file=secret=../appid_secrets/secret \
42 | --from-file=tenantId=../appid_secrets/tenantId
43 | cd ..
44 | rm -r appid_secrets
45 |
46 | echo "Create postgres secret: $POSTGRES_SECRET_NAME"
47 | cd postgres_secrets
48 | kubectl create secret generic $POSTGRES_SECRET_NAME \
49 | --from-file=connection=../postgres_secrets/connection \
50 | --from-file=instance_administration_api=../postgres_secrets/instance_administration_api
51 | cd ..
52 | rm -r postgres_secrets
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/operator/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/multi-tenancy/operator
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/jackc/pgx/v4 v4.15.0
7 | github.com/onsi/ginkgo v1.16.4
8 | github.com/onsi/gomega v1.15.0
9 | k8s.io/api v0.22.1
10 | k8s.io/apimachinery v0.22.1
11 | k8s.io/client-go v0.22.1
12 | sigs.k8s.io/controller-runtime v0.10.0
13 | )
14 |
--------------------------------------------------------------------------------
/operator/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
--------------------------------------------------------------------------------
/operator/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "flag"
21 | "os"
22 |
23 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
24 | // to ensure that exec-entrypoint and run can make use of them.
25 | _ "k8s.io/client-go/plugin/pkg/client/auth"
26 |
27 | "k8s.io/apimachinery/pkg/runtime"
28 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
29 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
30 | ctrl "sigs.k8s.io/controller-runtime"
31 | "sigs.k8s.io/controller-runtime/pkg/healthz"
32 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
33 |
34 | cachev1alpha1 "github.com/multi-tenancy/operator/api/v1alpha1"
35 | "github.com/multi-tenancy/operator/controllers"
36 | //+kubebuilder:scaffold:imports
37 | )
38 |
39 | var (
40 | scheme = runtime.NewScheme()
41 | setupLog = ctrl.Log.WithName("setup")
42 | )
43 |
44 | func init() {
45 | utilruntime.Must(clientgoscheme.AddToScheme(scheme))
46 |
47 | utilruntime.Must(cachev1alpha1.AddToScheme(scheme))
48 | //+kubebuilder:scaffold:scheme
49 | }
50 |
51 | func main() {
52 | var metricsAddr string
53 | var enableLeaderElection bool
54 | var probeAddr string
55 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
56 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
57 | flag.BoolVar(&enableLeaderElection, "leader-elect", false,
58 | "Enable leader election for controller manager. "+
59 | "Enabling this will ensure there is only one active controller manager.")
60 | opts := zap.Options{
61 | Development: true,
62 | }
63 | opts.BindFlags(flag.CommandLine)
64 | flag.Parse()
65 |
66 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
67 |
68 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
69 | Scheme: scheme,
70 | MetricsBindAddress: metricsAddr,
71 | Port: 9443,
72 | HealthProbeBindAddress: probeAddr,
73 | LeaderElection: enableLeaderElection,
74 | LeaderElectionID: "27344913.saas.ecommerce.sample.com",
75 | })
76 | if err != nil {
77 | setupLog.Error(err, "unable to start manager")
78 | os.Exit(1)
79 | }
80 |
81 | if err = (&controllers.ECommerceApplicationReconciler{
82 | Client: mgr.GetClient(),
83 | Scheme: mgr.GetScheme(),
84 | }).SetupWithManager(mgr); err != nil {
85 | setupLog.Error(err, "unable to create controller", "controller", "ECommerceApplication")
86 | os.Exit(1)
87 | }
88 | //+kubebuilder:scaffold:builder
89 |
90 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
91 | setupLog.Error(err, "unable to set up health check")
92 | os.Exit(1)
93 | }
94 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
95 | setupLog.Error(err, "unable to set up ready check")
96 | os.Exit(1)
97 | }
98 |
99 | setupLog.Info("starting manager")
100 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
101 | setupLog.Error(err, "problem running manager")
102 | os.Exit(1)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/operator/postgres.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: ibmcloud.ibm.com/v1
2 | kind: Service
3 | metadata:
4 | name: multi-tenancy-pg-ten-d
5 | spec:
6 | plan: standard
7 | serviceClass: databases-for-postgresql
8 | ---
9 | apiVersion: ibmcloud.ibm.com/v1
10 | kind: Binding
11 | metadata:
12 | name: binding-multi-tenancy-pg-ten-d
13 | spec:
14 | serviceName: multi-tenancy-pg-ten-d
15 | secretName: multi-tenancy-pg-ten-d-secret
--------------------------------------------------------------------------------
/scripts/env-config-template.js:
--------------------------------------------------------------------------------
1 | // used as 'environment' variables,
2 | // Single WebPage
3 | // App ID
4 | window.VUE_APPID_CLIENT_ID='APPID_CLIENT_ID_TEMPLATE'
5 | window.VUE_APPID_DISCOVERYENDPOINT='APPID_DISCOVERYENDPOINT_TEMPLATE'
6 | // Application URLs
7 | window.VUE_APP_API_URL_PRODUCTS="http://localhost:8081/category"
8 | window.VUE_APP_API_URL_CATEGORIES="http://localhost:8081/category"
9 | // window.VUE_APP_API_URL_ORDERS="http://localhost:8081/customer/orders"
10 | // Configurations
11 | window.VUE_APP_CATEGORY_NAME="Movies"
12 | window.VUE_APP_HEADLINE="Electronic and Movie Depot"
--------------------------------------------------------------------------------
/scripts/run-locally-backend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ROOT_PROJECT=multi-tenancy
4 | FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
5 | BACKEND_SOURCEFOLDER=multi-tenancy-backend
6 |
7 | # change the standard output
8 | exec 3>&1
9 |
10 | # **********************************************************************************
11 | # Functions definition
12 | # **********************************************************************************
13 |
14 | function setROOT_PATH() {
15 | echo "************************************"
16 | echo " Set ROOT_PATH"
17 | echo " REMEMBER: You must start the script from the project root folder as written in the documentation!"
18 | echo "************************************"
19 | cd ../
20 | export ROOT_PATH=$(PWD)
21 | echo "Path: $ROOT_PATH"
22 | }
23 |
24 | function resetPath() {
25 | echo "************************************"
26 | echo " Reset path"
27 | echo "************************************"
28 | cd $ROOT_PATH/$ROOT_PROJECT
29 | echo ""
30 | }
31 |
32 | function _out() {
33 | echo "$(date +'%F %H:%M:%S') $@"
34 | }
35 |
36 | function triggerScript() {
37 |
38 | echo "1. Have you created an App ID instance?"
39 | echo "Copy the credentials in local.env: APPID_CLIENT_ID, APPID_AUTH_SERVER_URL"
40 | echo ""
41 | echo "2. Have you created a Postgres instance?"
42 | echo "Copy the credentials in local.env: POSTGRES_USERNAME, POSTGRES_PASSWORD, POSTGRES_URL, POSTGRES_CERTIFICATE_FILE_NAME"
43 | echo "Copy the Postgres certificate in multi-tenancy-backend/src/main/resources/certificates"
44 | echo "Starting backend service locally ..."
45 | echo curl \"http://localhost:8081/category\"
46 | echo curl \"http://localhost:8081/category/2/products\"
47 | echo "/category will return a response code '401' not authorized!"
48 | echo "/category/2/products will return data from Postgres"
49 |
50 | cd $ROOT_PATH/$BACKEND_SOURCEFOLDER
51 | CFG_FILE=$ROOT_PATH/$ROOT_PROJECT/local.env
52 | if [ ! -f $CFG_FILE ]; then
53 | _out Config file local.env is missing!
54 | exit 1
55 | fi
56 |
57 | set -o allexport
58 | source $CFG_FILE
59 |
60 | APPID_AUTH_SERVER_URL=${APPID_AUTH_SERVER_URL}
61 | APPID_CLIENT_ID=${APPID_CLIENT_ID}
62 |
63 | POSTGRES_URL=$(echo $POSTGRES_URL| cut -d'?' -f 1)
64 | CERTIFICATE_PATH=$ROOT_PATH/$BACKEND_SOURCEFOLDER/src/main/resources/certificates/cloud-postgres-cert
65 | cp $ROOT_PATH/$BACKEND_SOURCEFOLDER/src/main/resources/certificates/$POSTGRES_CERTIFICATE_FILE_NAME $CERTIFICATE_PATH
66 | POSTGRES_URL="$POSTGRES_URL?sslmode=verify-full&sslrootcert=$CERTIFICATE_PATH"
67 |
68 | cd $ROOT_PATH/$BACKEND_SOURCEFOLDER
69 | mvn clean package
70 | mvn quarkus:dev
71 | }
72 |
73 | # **********************************************************************************
74 | # Execution
75 | # **********************************************************************************
76 |
77 | setROOT_PATH
78 | triggerScript
79 | resetPath
--------------------------------------------------------------------------------
/scripts/run-locally-container-backend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ROOT_PROJECT=multi-tenancy
4 | FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
5 | BACKEND_SOURCEFOLDER=multi-tenancy-backend
6 |
7 | # change the standard output
8 | exec 3>&1
9 |
10 | # **********************************************************************************
11 | # Functions definition
12 | # **********************************************************************************
13 |
14 | function setROOT_PATH() {
15 | echo "************************************"
16 | echo " Set ROOT_PATH"
17 | echo " REMEMBER: You must start the script from the project root folder as written in the documentation!"
18 | echo "************************************"
19 | cd ../
20 | export ROOT_PATH=$(PWD)
21 | echo "Path: $ROOT_PATH"
22 | }
23 |
24 | function resetPath() {
25 | echo "************************************"
26 | echo " Reset path"
27 | echo "************************************"
28 | cd $ROOT_PATH/$ROOT_PROJECT
29 | echo ""
30 | }
31 |
32 | function _out() {
33 | echo "$(date +'%F %H:%M:%S') $@"
34 | }
35 |
36 | function triggerScript() {
37 |
38 | echo "1. Have you created an App ID instance?"
39 | echo "Copy the credentials in local.env: APPID_CLIENT_ID, APPID_AUTH_SERVER_URL"
40 | echo ""
41 | echo "2. Have you created a Postgres instance?"
42 | echo "Copy the credentials in local.env: POSTGRES_USERNAME, POSTGRES_PASSWORD, POSTGRES_URL, POSTGRES_CERTIFICATE_FILE_NAME"
43 | echo "Copy the Postgres certificate in multi-tenancy-backend/src/main/resources/certificates"
44 | echo "Starting backend service locally in a container ..."
45 | echo curl \"http://localhost:8081/category\"
46 | echo curl \"http://localhost:8081/category/2/products\"
47 | echo "/category will return a response code '401' not authorized!"
48 | echo "/category/2/products will return data from Postgres"
49 |
50 | CFG_FILE=${ROOT_PATH}/$ROOT_PROJECT/local.env
51 | if [ ! -f $CFG_FILE ]; then
52 | _out Config file local.env is missing!
53 | exit 1
54 | fi
55 |
56 | cd ${ROOT_PATH}/$BACKEND_SOURCEFOLDER
57 | set -o allexport
58 | source $CFG_FILE
59 |
60 | POSTGRES_URL=$(echo $POSTGRES_URL| cut -d'?' -f 1)
61 | CERTIFICATE_PATH=/cloud-postgres-cert
62 | POSTGRES_URL="$POSTGRES_URL?sslmode=verify-full&sslrootcert=$CERTIFICATE_PATH"
63 | APPID_AUTH_SERVER_URL=${APPID_AUTH_SERVER_URL}
64 | APPID_CLIENT_ID=${APPID_CLIENT_ID}
65 |
66 | POSTGRES_CERTIFICATE_DATA=$(<$ROOT_PATH/$BACKEND_SOURCEFOLDER/src/main/resources/certificates/${POSTGRES_CERTIFICATE_FILE_NAME})
67 |
68 | cd ${ROOT_PATH}/$BACKEND_SOURCEFOLDER
69 | podman container stop service-catalog --ignore
70 | podman container rm -f service-catalog --ignore
71 | podman build --file Dockerfile --tag service-catalog
72 |
73 | podman run --name=service-catalog \
74 | -it \
75 | -e POSTGRES_CERTIFICATE_DATA="${POSTGRES_CERTIFICATE_DATA}" \
76 | -e POSTGRES_USERNAME="${POSTGRES_USERNAME}" \
77 | -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
78 | -e POSTGRES_URL="${POSTGRES_URL}" \
79 | -e APPID_AUTH_SERVER_URL="${APPID_AUTH_SERVER_URL}" \
80 | -e APPID_CLIENT_ID="${APPID_CLIENT_ID}" \
81 | -p 8081:8081/tcp \
82 | localhost/service-catalog:latest
83 | }
84 |
85 | # **********************************************************************************
86 | # Execution
87 | # **********************************************************************************
88 |
89 | setROOT_PATH
90 | triggerScript
91 | resetPath
--------------------------------------------------------------------------------
/scripts/run-locally-container-frontend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **************** Global variables set by parameters
4 | ROOT_PROJECT=multi-tenancy
5 | FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
6 | BACKEND_SOURCEFOLDER=multi-tenancy-backend
7 |
8 | vue_env_config=./public/env-config.js
9 | vue_env_config_template=./scripts/env-config-template.js
10 | service_catalog_categories_endpoint="http://localhost:8081/category"
11 | service_catalog_product_endpoint="http://localhost:8081/category"
12 |
13 | # change the standard output
14 | exec 3>&1
15 |
16 | # **********************************************************************************
17 | # Functions definition
18 | # **********************************************************************************
19 |
20 | function setROOT_PATH() {
21 | echo "************************************"
22 | echo " Set ROOT_PATH"
23 | echo " REMEMBER: You must start the script from the project root folder as written in the documentation!"
24 | echo "************************************"
25 | cd ../
26 | export ROOT_PATH=$(PWD)
27 | echo "Path: $ROOT_PATH"
28 | }
29 |
30 | function resetPath() {
31 | echo "************************************"
32 | echo " Reset path"
33 | echo "************************************"
34 | cd $ROOT_PATH/$ROOT_PROJECT
35 | echo ""
36 | }
37 |
38 | function _out() {
39 | echo "$(date +'%F %H:%M:%S') $@"
40 | }
41 |
42 | function triggerScript() {
43 |
44 | echo "********************"
45 | echo "Have you created an App ID instance?"
46 | echo "Copy the credentials in local.env: APPID_CLIENT_ID, APPID_DISCOVERYENDPOINT"
47 |
48 | CFG_FILE=${ROOT_PATH}/$ROOT_PROJECT/local.env
49 | if [ ! -f $CFG_FILE ]; then
50 | _out Config file local.env is missing!
51 | exit 1
52 | fi
53 | source $CFG_FILE
54 |
55 | cd ${ROOT_PATH}/$FRONTEND_SOURCEFOLDER
56 | echo "********************"
57 | echo "Clean-up container and image"
58 | podman container stop frontend-container --ignore
59 | podman container rm -f frontend-container --ignore
60 | #podman image rm -f 'frontend:v1'
61 |
62 | echo "********************"
63 | echo "Build container"
64 | podman build --file Dockerfile --tag 'frontend:v1'
65 |
66 | echo "********************"
67 | echo "Starting container with App ID configuration"
68 | echo " - $APPID_CLIENT_ID"
69 | echo " - $APPID_DISCOVERYENDPOINT"
70 |
71 | podman run --name=frontend-container \
72 | -it \
73 | -e VUE_APPID_CLIENT_ID=$APPID_CLIENT_ID \
74 | -e VUE_APPID_DISCOVERYENDPOINT=$APPID_DISCOVERYENDPOINT \
75 | -e VUE_APP_API_URL_PRODUCTS="${service_catalog_product_endpoint}" \
76 | -e VUE_APP_API_URL_CATEGORIES="${service_catalog_product_endpoint}" \
77 | -e VUE_APP_CATEGORY_NAME='Movies' \
78 | -e VUE_APP_HEADLINE='Frontend Docker' \
79 | -p 8080:8080/tcp \
80 | "localhost/frontend:v1"
81 | }
82 |
83 | # **********************************************************************************
84 | # Execution
85 | # **********************************************************************************
86 |
87 | setROOT_PATH
88 | triggerScript
89 | resetPath
--------------------------------------------------------------------------------
/scripts/run-locally-frontend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # **************** Global variables set by parameters
4 | ROOT_PROJECT=multi-tenancy
5 | FRONTEND_SOURCEFOLDER=multi-tenancy-frontend
6 | BACKEND_SOURCEFOLDER=multi-tenancy-backend
7 |
8 | # change the standard output
9 | exec 3>&1
10 |
11 | # **********************************************************************************
12 | # Functions definition
13 | # **********************************************************************************
14 |
15 | function setROOT_PATH() {
16 | echo "************************************"
17 | echo " Set ROOT_PATH"
18 | echo " REMEMBER: You must start the script from the project root folder as written in the documentation!"
19 | echo "************************************"
20 | cd ../
21 | export ROOT_PATH=$(PWD)
22 | echo "Path: $ROOT_PATH"
23 | }
24 |
25 | function resetPath() {
26 | echo "************************************"
27 | echo " Reset path"
28 | echo "************************************"
29 | cd $ROOT_PATH/$ROOT_PROJECT/
30 | echo ""
31 | }
32 |
33 | function _out() {
34 | echo "$(date +'%F %H:%M:%S') $@"
35 | }
36 |
37 | function triggerScript() {
38 |
39 | vue_env_config=$ROOT_PATH/$FRONTEND_SOURCEFOLDER/public/env-config.js
40 | vue_env_config_template=$ROOT_PATH/$ROOT_PROJECT/scripts/env-config-template.js
41 |
42 | echo "Have you created an App ID instance?"
43 | echo "Copy the credentials in local.env: APPID_CLIENT_ID, APPID_DISCOVERYENDPOINT"
44 |
45 | CFG_FILE=$ROOT_PATH/$ROOT_PROJECT/local.env
46 | if [ ! -f $CFG_FILE ]; then
47 | _out Config file local.env is missing!
48 | exit 1
49 | fi
50 |
51 | source $CFG_FILE
52 |
53 | echo "Creating App ID configuration $vue-env-config"
54 | echo " - $APPID_CLIENT_ID"
55 | echo " - $APPID_DISCOVERYENDPOINT"
56 |
57 | cd $ROOT_PATH/$FRONTEND_SOURCEFOLDER
58 | sed -e "s+APPID_CLIENT_ID_TEMPLATE+${APPID_CLIENT_ID}+g" \
59 | -e "s+APPID_DISCOVERYENDPOINT_TEMPLATE+${APPID_DISCOVERYENDPOINT}+g" \
60 | "${vue_env_config_template}" > "${vue_env_config}"
61 |
62 | echo "Starting frontend locally ..."
63 | cd $ROOT_PATH/$FRONTEND_SOURCEFOLDER
64 | npm install
65 | npm run serve
66 | }
67 |
68 | # **********************************************************************************
69 | # Execution
70 | # **********************************************************************************
71 |
72 | setROOT_PATH
73 | triggerScript
74 | resetPath
--------------------------------------------------------------------------------