Skip to content

Commit

Permalink
[Feat] Defaulting service and workload configuration + Switched to ne…
Browse files Browse the repository at this point in the history
…w Option feature (#6)
  • Loading branch information
anirudhprasad-sap authored Apr 5, 2024
1 parent 229a4eb commit bfac150
Show file tree
Hide file tree
Showing 16 changed files with 2,076 additions and 357 deletions.
81 changes: 51 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ CAP Operator Plugin provides an easy way to generate [CAP Operator](https://sap.

## Requirements

The CAP Operator plugin requires the following packages:

```sh
@sap/cds: ">=7"
@sap/cds-dk: ">=7"
```
The CAP Operator plugin requires `@sap/cds-dk: ">=7.8.1"`. If @sap/cds-dk is installed globally, please ensure that the installed version is greater than or equal to `7.8.1`.

## Setup

Expand All @@ -25,36 +20,62 @@ To integrate the CAP Operator Plugin into your project, follow these steps:

2. After installation, execute one of the following commands based on your requirements:

* To add a basic chart folder, use:
```sh
cds add cap-operator
```
> During `cds build`, the plugin will automatically inject the templates folder into the final chart.
* To add a basic chart folder, use:
```sh
cds add cap-operator
```
> During `cds build`, the plugin will automatically inject the templates folder into the final chart.

* To add a chart folder with templates included, use:
```sh
cds add cap-operator --add-with-templates
```
> During `cds build`, the plugin will copy the templates folder into the final chart.
* To add a chart folder with templates included, use:
```sh
cds add cap-operator --with-templates
```
> During `cds build`, the plugin will copy the templates folder into the final chart.

3. Once executed, the chart folder or chart folder with templates will be added to your project directory.
> ### ⚠️ Experimental
> To add a chart folder with the values.yaml prefilled with the design-time deployment details from the mta and mta extensions, use:
>```sh
> cds add cap-operator --with-mta <mta-yaml-file-path> --with-mta-extensions <mta-ext-yaml-file-path>
>```
> If you have multiple mta extensions, you can pass them as a comma-separated string in order to merge them.

4. Update the `values.yaml` file with your design-time deployment details according to `values.schema.json`. You can either use any YAML schema validation extension like [YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) or run the following command to ensure correctness:
```sh
helm lint <chart-path>
```
2. Once executed, the chart folder or chart folder with templates will be added to your project directory.

3. The `values.yaml` requires two type of details:

* Design-time deployment details
- serviceInstances
- serviceBindings
- workloads
- tenantOperations
- contentJobs
* Runtime deployment details
- app
- btp
- imagePullSecrets
- env information inside each workload like database instance ID

As a developer, you need to fill in the design-time deployment details in the `values.yaml` file, which can then be pushed to your repository. The plugin will auto-populate some of these details based on the project configuration, but it's essential to verify them and manually fill in any missing information. You can refer to `values.schema.json` file for the structure of the `values.yaml` file.
You can utilize a YAML schema validation extension such as [YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml), or alternatively, run the following command to validate the `values.yaml` file:
```sh
helm lint <chart-path>
```
4. After filling all the design-time information in `values.yaml`, run `cds build`. The final chart will be generated in the `gen` folder within your project directory.
5. Now to deploy the application, you can pass the runtime values in a separate `runtime-values.yaml` file and deploy the chart using the following command:
```sh
helm upgrade -i -n <namespace> <release-name> <chart-path> -f <runtime-values.yaml-path>
```
5. After filling all the design-time information in `values.yaml`, run `cds build`. The final chart will be generated in the `gen` folder within your project directory.
## ❗Things to Note
> Note: If you are adding the basic chart folder using the `cds add cap-operator` command, do not modify the `values.schema.json` file. The templates injected automatically during `cds build` are tightly coupled with the structure in `values.schema.json`. If schema changes are needed, use option `--add-with-templates` to add the templates folder and adjust them accordingly.
* If you are adding the basic chart folder using the `cds add cap-operator` command, do not modify the `values.schema.json` file. The templates injected automatically during `cds build` are tightly coupled with the structure in `values.schema.json`. If schema changes are needed, use option `--with-templates` to add the templates folder and adjust them accordingly.
> [!CAUTION]
> ### Experimental
> To add a chart folder with the values.yaml prefilled with the design-time deployment details from the mta and mta extensions, use:
>```sh
> cds add cap-operator --add-with-mta <mta-yaml-file-path> --add-with-mta-extensions <mta-ext-yaml-file-path>
>```
> If you have multiple mta extensions, you can pass them as a comma-separated string in the order that they should be merged.
* When defining environment variables for workloads in the `values.yaml` file, it's important to mirror these definitions in the `runtime-values.yaml` file. This ensures consistency and avoids potential conflicts, as Helm does not merge arrays. If you're introducing new environment variables in `runtime-values.yaml` for a workload, remember to include existing variables from `values.yaml` to maintain coherence.
## Contributing
Expand Down
24 changes: 24 additions & 0 deletions files/approuter.yaml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
workloads:
app-router:
name: app-router
labels:
sme.sap.com/app-type: {{appName}}
consumedBTPServices:
{{#hasXsuaa}}
- {{appName}}-uaa-bind
{{/hasXsuaa}}
{{#hasDestination}}
- {{appName}}-destination-bind
{{/hasDestination}}
{{#hasHTML5Repo}}
- {{appName}}-html5-repo-runtime-bind
{{/hasHTML5Repo}}
{{#hasMultitenancy}}
- {{appName}}-saas-registry-bind
{{/hasMultitenancy}}
deploymentDefinition:
type: Router
image:
ports:
- name: router-port
port: 5000
4 changes: 2 additions & 2 deletions files/destination.yaml.hbs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
serviceInstances:
destination-lite:
destination:
name: {{appName}}-destination
serviceOfferingName: destination
servicePlanName: lite
parameters:
HTML5Runtime_enabled: true

serviceBindings:
destination-lite:
destination:
name: {{appName}}-destination-bind
serviceInstanceName: {{appName}}-destination
secretName: {{appName}}-destination-bind-secret
Expand Down
24 changes: 24 additions & 0 deletions files/saas-registry.yaml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
serviceInstances:
saas-registry:
name: {{appName}}-saas-registry
serviceOfferingName: saas-registry
servicePlanName: application
parameters:
plan: general
xsappname: {{appName}}
appName: {{appName}}
displayName: ({{appName}})
description: ({{appDescription}})
appUrls:
callbackTimeoutMillis: 300000
onSubscriptionAsync: true
onUnSubscriptionAsync: true
category: "CAP"

serviceBindings:
saas-registry:
name: {{appName}}-saas-registry-bind
serviceInstanceName: {{appName}}-saas-registry
secretName: {{appName}}-saas-registry-bind-secret
secretKey: credentials
parameters: {}
16 changes: 16 additions & 0 deletions files/server.yaml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
workloads:
server:
name: server
labels:
sme.sap.com/app-type: {{appName}}
consumedBTPServices:
{{#hasXsuaa}}
- {{appName}}-uaa-bind
{{/hasXsuaa}}
{{#hasMultitenancy}}
- {{appName}}-saas-registry-bind
- {{appName}}-service-manager-bind
{{/hasMultitenancy}}
deploymentDefinition:
type: CAP
image:
14 changes: 14 additions & 0 deletions files/service-manager.yaml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
serviceInstances:
service-manager:
name: {{appName}}-service-manager
serviceOfferingName: service-manager
servicePlanName: container
parameters: {}

serviceBindings:
service-manager:
name: {{appName}}-service-manager-bind
serviceInstanceName: {{appName}}-service-manager
secretName: {{appName}}-service-manager-bind-secret
secretKey: credentials
parameters: {}
20 changes: 20 additions & 0 deletions files/xsuaa.yaml.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
serviceInstances:
xsuaa:
name: {{appName}}-uaa
serviceOfferingName: xsuaa
servicePlanName: broker
parameters:
xsappname: {{appName}}
tenant-mode: shared
oauth2-configuration:
credential-types:
- binding-secret
redirect-uris: []

serviceBindings:
xsuaa:
name: {{appName}}-uaa-bind
serviceInstanceName: {{appName}}-uaa
secretName: {{appName}}-uaa-bind-secret
secretKey: credentials
parameters: {}
59 changes: 49 additions & 10 deletions lib/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ const { isCAPOperatorChart } = require('./util')

module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {

options() {
return {
'with-templates': {
type: 'boolean',
help: 'To add the templates folder to the chart folder.'
},
'with-mta': {
type: 'string',
//help: 'Path to the mta.yaml file.'
},
'with-mta-extensions': {
type: 'string',
//help: 'Comma separated list of mta extensions to be applied to the mta.yaml file. Can be used only with --with-mta option.'
}
}
}

async canRun() {
const { hasMultitenancy, hasApprouter, hasXsuaa } = cds.add.readProject()
if (!hasXsuaa) {
Expand All @@ -23,7 +40,7 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {
console.log(`❌ approuter is not added to this project. Run 'cds add approuter'.`)
return false
} else if (!hasMultitenancy) {
console.log(`❌ Multitenancy is not added to this project. Run 'cds add multitenancy'.`)
console.log(`❌ multitenancy is not added to this project. Run 'cds add multitenancy'.`)
return false
}
return true
Expand All @@ -46,7 +63,7 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {
else if (exists('chart')) {
let isCAPOpChart = isCAPOperatorChart('chart')

if(isCAPOpChart && !exists('chart/templates') && cds.cli.options['add-with-templates']){
if(isCAPOpChart && !exists('chart/templates') && cds.cli.options['with-templates']){
await copy(join(__dirname, '../files/chart/templates')).to('chart/templates')
console.log("Added 'templates' folder to the 'chart' folder.")
}
Expand All @@ -64,19 +81,19 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {
await copy(join(__dirname, '../files/chart/values.yaml')).to('chart/values.yaml')
await copy(join(__dirname, '../files/chart/values.schema.json')).to('chart/values.schema.json')

if (cds.cli.options['add-with-templates'])
if (cds.cli.options['with-templates'])
await copy(join(__dirname, '../files/chart/templates')).to('chart/templates')

console.log("`chart` folder generated.")

if (!cds.cli.options['add-with-mta'] && cds.cli.options['add-with-mta-extensions'])
throw new Error(`mta YAML not provided. Please pass the mta YAML via option '--add-with-mta'.`)
if (!cds.cli.options['with-mta'] && cds.cli.options['with-mta-extensions'])
throw new Error(`mta YAML not provided. Please pass the mta YAML via option '--with-mta'.`)

if (cds.cli.options['add-with-mta']) {
if (cds.cli.options['with-mta']) {
const { hasMta } = project
if (!hasMta) throw new Error(`mta is not added to this project. Run 'cds add mta'.`)

const mtaTransformer = new MtaTransformer(cds.cli.options['add-with-mta'], cds.cli.options['add-with-mta-extensions'] ? cds.cli.options['add-with-mta-extensions'].split(',') : [])
const mtaTransformer = new MtaTransformer(cds.cli.options['with-mta'], cds.cli.options['with-mta-extensions'] ? cds.cli.options['with-mta-extensions'].split(',') : [])

console.log("⚠️ Deriving values.yaml from mta.yaml cannot be done one to one. It's a best guess, so some information might be missing and needs to be reviewed and corrected by the application developer.")

Expand All @@ -92,8 +109,12 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {
}

async combine() {

// In case of 'cds add cap-operator --with-mta', service instances, service bindings and workloads are derived from mta.yaml. No need to add them again.
if (cds.cli.options['with-mta']) return

const project = cds.add.readProject()
const { hasDestination, hasHtml5Repo } = project
const { hasDestination, hasHtml5Repo, hasXsuaa, hasApprouter, hasMultitenancy } = project
const Mustache = require('mustache')
const valuesYaml = yaml.parse(await read(join(cds.root, 'chart/values.yaml')))

Expand All @@ -107,9 +128,27 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin {
await cds.add.merge(html5RepoYaml).into(valuesYaml)
}

if (hasDestination || hasHtml5Repo) {
await write(yaml.stringify(valuesYaml)).to(join(cds.root, 'chart/values.yaml'))
if (hasXsuaa) {
const xsuaaaYaml = yaml.parse(Mustache.render( await read(join(__dirname, '../files/xsuaa.yaml.hbs')), project))
await cds.add.merge(xsuaaaYaml).into(valuesYaml)
}

if (hasApprouter || exists('approuter')) {
const approuterYaml = yaml.parse(Mustache.render( await read(join(__dirname, '../files/approuter.yaml.hbs')), project))
await cds.add.merge(approuterYaml).into(valuesYaml)
}

if (hasMultitenancy) {
const saasRegistryYaml = yaml.parse(Mustache.render( await read(join(__dirname, '../files/saas-registry.yaml.hbs')), project))
await cds.add.merge(saasRegistryYaml).into(valuesYaml)

const serviceManagerYaml = yaml.parse(Mustache.render( await read(join(__dirname, '../files/service-manager.yaml.hbs')), project))
await cds.add.merge(serviceManagerYaml).into(valuesYaml)
}

const serverYaml = yaml.parse(Mustache.render( await read(join(__dirname, '../files/server.yaml.hbs')), project))
await cds.add.merge(serverYaml).into(valuesYaml)

await write(yaml.stringify(valuesYaml)).to(join(cds.root, 'chart/values.yaml'))
}
}
8 changes: 6 additions & 2 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ function mergeObj(obj1, obj2) {
}

function isCAPOperatorChart(chartFolderPath) {
const chartYaml = cds.parse.yaml(fs.readFileSync(chartFolderPath + "/Chart.yaml").toString())
return chartYaml.annotations?.["app.kubernetes.io/managed-by"] === 'cap-operator-plugin' || false
try {
const chartYaml = cds.parse.yaml(fs.readFileSync(chartFolderPath + "/Chart.yaml").toString())
return chartYaml.annotations?.["app.kubernetes.io/managed-by"] === 'cap-operator-plugin' || false
} catch(err) {
return false
}
}

module.exports = { replacePlaceholders, mergeObj, isCAPOperatorChart }
Loading

0 comments on commit bfac150

Please sign in to comment.