From db5e9b5166364cf3899679a1f535c287b2dc94ac Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 10:46:48 +0200 Subject: [PATCH 01/13] feat: cherry pick from task/169/sub-setting-endpoi --- .vscode/launch.json | 33 +++++------ README.md | 108 +++++++++++++++++++++++++++++++++- bin/generate-client.sh | 20 ++++--- src/api/settings/{setting}.ts | 22 +++++++ 4 files changed, 156 insertions(+), 27 deletions(-) create mode 100644 src/api/settings/{setting}.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 93e865ebd..dd58e22e4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,30 +2,26 @@ "version": "0.2.0", "configurations": [ { - "type": "node", - "request": "attach", - "name": "Attach to current script", - "protocol": "inspector", - "port": 4321, - "restart": true, - "cwd": "${workspaceRoot}" - }, - { - "type": "node", + "name": "Debug src/app.ts", + "type": "pwa-node", "request": "launch", - "name": "Debug npm run dev", - "runtimeExecutable": "npm", - "runtimeArgs": ["run-script", "dev"], + "runtimeExecutable": "node", + "runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"], + "args": ["src/app.ts"], "cwd": "${workspaceRoot}", - - "console": "integratedTerminal" + "internalConsoleOptions": "openOnSessionStart", + "skipFiles": ["/**", "node_modules/**"], + "env": { + "NODE_ENV": "test" + }, + "resolveSourceMapLocations": ["!${workspaceFolder}/**"] }, { "type": "node", "request": "launch", - "name": "Debug npm run dev:no-authz", + "name": "Debug npm run dev", "runtimeExecutable": "npm", - "runtimeArgs": ["run-script", "dev:no-authz"], + "runtimeArgs": ["run-script", "dev"], "cwd": "${workspaceRoot}", "console": "integratedTerminal" @@ -41,8 +37,7 @@ }, "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/ts-mocha", "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "protocol": "inspector" + "internalConsoleOptions": "neverOpen" }, { "type": "node", diff --git a/README.md b/README.md index 209959ddd..378083225 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ method. For the api client there is an `operationId` property defined. It can be used to client with expected method names (see relevant usage in otomi-web repo) -### 1.2 Authentication +### 2.2 Authentication The authentication ensures that a user is identified, so the request contains required headers. @@ -193,6 +193,112 @@ The `TeamSelfService` schema is composed by: - property that corresponds to a schema name from `api.yaml` file. - `enum` property that indicates JSON paths for attributes that shall be controlled. +**Note:** + +- `delete` permission cannot be set for ABAC + +For example: + +``` + Service: + x-acl: + admin: [delete-any, read-any, create-any, update-any] + team: [delete, read, create, update] + type: object + properties: + name: + type: string + ingress: + type: object + x-acl: + admin: [read, create] + team: [read] +``` + +From above: + +A user with admin role can: + +- perform all CRUD operations regardless resource ownership (RBAC) +- all attributes can be edited except ingress that can be only set on resource creation event (ABAC) + +A user with team role can: + +- perform all CRUD operations only withing its own team (RBAC) +- all attributes can be edited except ingress that isn be only read (ABAC) + +#### 2.3.3 Limitations + +##### 2.3.3.1 OpenAPI-generator limitations + + + +Known issues: + +- https://github.com/redkubes/otomi-api/issues/155 + +###### Problem + +It doesn't matter if you've entered a valid OpenAPI specification, it isn't useful as long as it isn't generated as a client library. + +###### Cause + +There are too many variations of this problem to be listed here and still make sense, but they follow the following cycle in general: + +1. `src/openapi/\*.yaml` cannot be dereferenced/bundled by parsing JSON `$refs`. +2. Dereferenced/bundled OpenAPI spec cannot be generated +3. Client libary in `vendors/client/otomi-api/axios/...` cannot be compiled with `tsc` +4. Code cannot be committed in version control (Git) +5. Consume API methods and/or models + +###### Solutions + +In this paragraph the causes are addressed by the corresponding number under "Cause": + +1. In the `npm run ...` scripts, `vendors/openapi/otomi-api.json` may be deleted to see if the spec can be successfully dereferenced/bundled and used as input for `openapi-generator`. +2. The `openapi-generator` can throw useful/meaningful errors. But there are errors under known issues (see above) that need a work-around. +3. These errors happen the most arbitrarily. See if you can go back in your small increments in `src/openapi/...` until you can successfully build the client library again. +4. These errors are often due to our own code. E.g.: a generated model is used, and by changing the OpenAPI spec you change the schema. Models used to rely on the schema and now they are missing. +5. If you change the name of a schema, add a title, etc., the respective reference might change. Then the consumption in the API library might break. + +###### Note + +- Also check if you can successfully generate the client library again after committing, just as a pre-caution. +- To determine a successful generation of the client library, please check out the generated models in `vendors/client/otomi-api/axios/models` if they make sense or not. +- As general advice, make sure to increment the specification VERY slowly and always see if a spec can be generated or not. + +##### 2.3.3.2 Specific limitations + +- nested ABAC is NOT supported E.g.: + +``` + Service: + type: object + properties: + name: + type: object + properties: + name: + type: string + x-acl: [read] # nested x-acl not supported + ingress: + type: object + x-acl: + team: [read] +``` + +- ABAC is not applied for resource collections, e.g.: + +``` + Services: + x-acl: + admin: [read-any] + team: [read-any] + type: array + items: + $ref: '#/components/schemas/Service' # even if the components/schemas/Service defines ABAC it will NOT be applied +``` + ## 2. Viewing/consuming openapi spec In order to inspect the api file it is recommended to either: diff --git a/bin/generate-client.sh b/bin/generate-client.sh index 6a21603a5..e64de6274 100755 --- a/bin/generate-client.sh +++ b/bin/generate-client.sh @@ -26,11 +26,18 @@ validate() { fi } +clean_up() { + if [ -f vendors/openapi/otomi-api.json ]; then + rm vendors/openapi/otomi-api.json + fi + rm -rf vendors/client/otomi-api >/dev/null || exit 1 +} + generate_client() { - echo "Generating client code from openapi specification $openapi_doc.." - rm -rf $target_dir >/dev/null + echo "Generating client code from openapi specification $openapi_doc..." # npx openapi bundle --output src/openapi --ext yaml src/openapi/api.yaml + npm run build:spec docker run --rm -v $PWD:/local -w /local -u "$(id -u $USER)" \ openapitools/openapi-generator-cli:v5.1.0 generate \ @@ -42,7 +49,7 @@ generate_client() { } set_package_json() { - echo "Updating $target_package_json file.." + echo "Updating $target_package_json file..." jq \ --arg type 'git' \ @@ -68,17 +75,16 @@ set_bluebird() { } build_npm_package() { - echo "Building $target_npm_name npm package" - cd $target_dir + cd $target_dir && echo "Building $target_npm_name npm package..." npm install && npm run build cd - } -rm -rf $target_dir >/dev/null validate +clean_up generate_client set_package_json set_bluebird build_npm_package -echo "The client code has been generated at $target_dir/ directory" +echo "The client code has been generated at $target_dir/ directory." diff --git a/src/api/settings/{setting}.ts b/src/api/settings/{setting}.ts new file mode 100644 index 000000000..e35619df2 --- /dev/null +++ b/src/api/settings/{setting}.ts @@ -0,0 +1,22 @@ +import { Operation, OperationHandlerArray } from 'express-openapi' +import { OpenApiRequest } from '../../otomi-models' + +export default function (): OperationHandlerArray { + const GET: Operation = [ + ({ params: { setting } }: OpenApiRequest, res): void => { + console.debug(`Get settings: ${JSON.stringify({ setting })}`) + res.json({}) + }, + ] + const PUT: Operation = [ + ({ params: { setting }, body }: OpenApiRequest, res): void => { + console.debug(`Modify settings: ${JSON.stringify({ setting })}`) + res.json({ body }) + }, + ] + const api = { + GET, + PUT, + } + return api +} From b3f6f955f955cfbafbc018772fc9ae8ffa5757ad Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 13:10:11 +0200 Subject: [PATCH 02/13] fix: review jeho package scripts --- README.md | 32 -------------------------------- bin/generate-client.sh | 2 +- package.json | 7 ++++--- 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 378083225..1276b0672 100644 --- a/README.md +++ b/README.md @@ -267,38 +267,6 @@ In this paragraph the causes are addressed by the corresponding number under "Ca - To determine a successful generation of the client library, please check out the generated models in `vendors/client/otomi-api/axios/models` if they make sense or not. - As general advice, make sure to increment the specification VERY slowly and always see if a spec can be generated or not. -##### 2.3.3.2 Specific limitations - -- nested ABAC is NOT supported E.g.: - -``` - Service: - type: object - properties: - name: - type: object - properties: - name: - type: string - x-acl: [read] # nested x-acl not supported - ingress: - type: object - x-acl: - team: [read] -``` - -- ABAC is not applied for resource collections, e.g.: - -``` - Services: - x-acl: - admin: [read-any] - team: [read-any] - type: array - items: - $ref: '#/components/schemas/Service' # even if the components/schemas/Service defines ABAC it will NOT be applied -``` - ## 2. Viewing/consuming openapi spec In order to inspect the api file it is recommended to either: diff --git a/bin/generate-client.sh b/bin/generate-client.sh index e64de6274..cd126e835 100755 --- a/bin/generate-client.sh +++ b/bin/generate-client.sh @@ -30,7 +30,7 @@ clean_up() { if [ -f vendors/openapi/otomi-api.json ]; then rm vendors/openapi/otomi-api.json fi - rm -rf vendors/client/otomi-api >/dev/null || exit 1 + rm -rf vendors/client/otomi-api >/dev/null } generate_client() { diff --git a/package.json b/package.json index 7b89ac5f2..2fdb6856c 100644 --- a/package.json +++ b/package.json @@ -133,14 +133,15 @@ "build": "tsc && copyup --error src/openapi/*.yaml src/*.json dist/src", "build:models": "npm run build:spec && openapi-typescript src/generated-schema.json -o src/generated-schema.ts", "build:spec": "ts-node-dev src/build-spec.ts", - "build:client": "npm run build:spec && rm -rf vendors/client/otomi-api >/dev/null && bin/generate-client.sh", + "build:client": "bin/generate-client.sh", + "build:pre-commit": "npm run build:models && bin/generate-client.sh", "cz": "git-cz", "cz:retry": "git-cz --retry", "dev:watch": "run-p dev watch:openapi watch:ts", "dev": "ts-node-dev --watch 'src/openapi/*.yaml' --inspect=4321 --respawn --transpile-only src/app.ts", "dev:docker": "npm install && npm run dev", "husky:lint-staged": "lint-staged", - "husky:pre-commit": "run-p lint husky:lint-staged build:models", + "husky:pre-commit": "npm run build:pre-commit && npm run lint && npm run husky:lint-staged", "lint": "run-p types lint:es", "lint:es": "eslint --ext ts .", "lint:fix": "eslint --ext ts --fix .", @@ -159,4 +160,4 @@ } }, "version": "0.4.53" -} +} \ No newline at end of file From 28ad5a4e9009aabf1e2cc13d55e58236be7221af Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 13:28:06 +0200 Subject: [PATCH 03/13] fix: simplify schema --- src/openapi/api.yaml | 74 ++++++++++++++++++----- src/openapi/definitions.yaml | 16 ++++- src/openapi/settings.yaml | 114 +++-------------------------------- 3 files changed, 79 insertions(+), 125 deletions(-) diff --git a/src/openapi/api.yaml b/src/openapi/api.yaml index 3193fcd76..5c99560c0 100644 --- a/src/openapi/api.yaml +++ b/src/openapi/api.yaml @@ -408,7 +408,14 @@ paths: default: description: The requested apiDoc. - /settings: + /settings/{setting}: + parameters: + - in: path + name: setting + required: true + schema: + type: string + example: oidc get: operationId: getSettings description: Get settings from the `settings.yaml` and `secret.settings.yaml` file. @@ -420,6 +427,8 @@ paths: 'application/json': schema: $ref: '#/components/schemas/Settings' + + # TODO: https://github.com/redkubes/otomi-api/issues/155 put: operationId: editSettings description: Edits the settings from the `settings.yaml` file @@ -476,40 +485,63 @@ components: name: Authorization in: header + # -------------------------------------------- Schemas + # Indicate root schemas with UpperCamelCase (e.g. Cluster) + # Indicate definition references with camelCase (e.g. azureCreds) + # This is the way to properly generate polymorphic schemas (e.g. oneOf, allOf). + # + # Please stick to: alphabetical sorting, + # and after that: capital letters before lower case letters. + # -------------------------------------------- schemas: + alerts: + $ref: 'definitions.yaml#/alerts' + + awsCreds: + $ref: 'definitions.yaml#/awsCreds' + + azureCreds: + $ref: 'definitions.yaml#/azureCreds' + + azureMonitor: + $ref: 'definitions.yaml#/azureMonitor' + Cluster: $ref: 'cluster.yaml#/Cluster' Deployment: $ref: 'deployment.yaml#/Deployment' + DockerRegistry: + $ref: 'secret.yaml#/DockerRegistry' + + email: + $ref: 'definitions.yaml#/email' + + Generic: + $ref: 'secret.yaml#/Generic' + + googleCreds: + $ref: 'definitions.yaml#/googleCreds' + # Job: # $ref: 'job.yaml#/Job' Kubecfg: $ref: 'kubecfg.yaml#/Kubecfg' + kms: + $ref: 'definitions.yaml#/kms' + OpenApiValidationError: $ref: 'error.yaml#/OpenApiValidationError' OtomiStackError: $ref: 'error.yaml#/OtomiStackError' - TeamSelfService: - $ref: 'team.yaml#/TeamSelfService' - Secret: $ref: 'secret.yaml#/Secret' - Generic: - $ref: 'secret.yaml#/Generic' - - DockerRegistry: - $ref: 'secret.yaml#/DockerRegistry' - - TLS: - $ref: 'secret.yaml#/TLS' - # -------------------------------------------- Misc. Service: $ref: 'service.yaml#/Service' @@ -522,8 +554,20 @@ components: Team: $ref: 'team.yaml#/Team' + TeamAuthz: + $ref: 'user.yaml#/TeamAuthz' + + TeamSelfService: + $ref: 'team.yaml#/TeamSelfService' + + TLS: + $ref: 'secret.yaml#/TLS' + + url: + $ref: 'definitions.yaml#/url' + User: $ref: 'user.yaml#/User' - TeamAuthz: - $ref: 'user.yaml#/TeamAuthz' + vaultCreds: + $ref: 'definitions.yaml#/vaultCreds' diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index 83fd3560d..456897f73 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -235,6 +235,19 @@ k8sVersion: - '1.18' - '1.19' type: string +kms: + additionalProperties: false + description: Holds KMS settings like credentials for retrieving enc/decyption keys. + title: KMS settings + properties: + sops: + description: Encryption credentials for SOPS to encrypt the platform secrets. + title: SOPS credentials + oneOf: + - $ref: definitions.yaml#/awsCreds + - $ref: definitions.yaml#/azureCreds + - $ref: definitions.yaml#/googleCreds + - $ref: definitions.yaml#/vaultCreds labels: $ref: '#/annotations' description: A set of labels. @@ -432,7 +445,6 @@ volumes: type: string type: object type: array - ingressPublic: type: object title: Public @@ -490,7 +502,6 @@ ingressPublic: - subdomain required: - public - ingressPrivate: type: object title: Private @@ -504,7 +515,6 @@ ingressPrivate: required: - private additionalProperties: false - ingressCluster: title: Cluster type: object diff --git a/src/openapi/settings.yaml b/src/openapi/settings.yaml index 4434db203..a836fb262 100644 --- a/src/openapi/settings.yaml +++ b/src/openapi/settings.yaml @@ -1,92 +1,3 @@ -url: &url - pattern: ^(https:\/\/)([\w\-])+\.{1}([a-zA-Z]{2,63})([\/\w-]*)*\/?\??([^#\n\r]*)?#?([^\n\r]*)$ - type: string - -alerts: &alerts - type: object - properties: - drone: - default: slack - enum: - - slack - - msteams - type: string - email: - additionalProperties: false - properties: - critical: - $ref: 'definitions.yaml#/email' - description: One or more email addresses (comma separated) for critical events. - nonCritical: - $ref: 'definitions.yaml#/email' - description: One or more email addresses (comma separated) for non-critical events. - type: object - groupInterval: - default: 5m - description: How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.) - type: string -kms: - additionalProperties: false - properties: - sops: - oneOf: - - aws: - $ref: definitions.yaml#/awsCreds - required: - - aws - - azure: - $ref: definitions.yaml#/azureCreds - required: - - azure - - google: - $ref: definitions.yaml#/googleCreds - required: - - google - - vault: - $ref: definitions.yaml#/vaultCreds - required: - - vault - type: object - type: object -msteams: - additionalProperties: false - properties: - highPrio: - description: The low prio web hook. - type: string - lowPrio: - description: The high prio web hook. - type: string - type: object -receivers: - description: Notification receivers. - items: - enum: - - slack - - msteams - - email - type: string - type: array -repeatInterval: - default: 3h - description: How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). - type: string -slack: - additionalProperties: false - properties: - channel: - default: mon-otomi - description: The Slack channel for non-critical notifications. - type: string - channelCrit: - default: mon-otomi - description: The Slack channel for critical notifications. - type: string - url: - <<: *url - description: A Slack webhook URL. - type: object - Settings: x-acl: admin: [read-any, update-any] @@ -94,7 +5,7 @@ Settings: additionalProperties: false properties: alerts: - <<: *alerts + $ref: definitions.yaml#/alerts azure: description: Azure specific configuration. properties: @@ -216,20 +127,9 @@ Settings: - domain type: object kms: - additionalProperties: false - description: Holds KMS settings like credentials for retrieving enc/decyption keys. - title: KMS settings - properties: - sops: - description: Encryption credentials for SOPS to encrypt the platform secrets. - title: SOPS credentials - oneOf: - - $ref: definitions.yaml#/awsCreds - - $ref: definitions.yaml#/azureCreds - - $ref: definitions.yaml#/googleCreds - - $ref: definitions.yaml#/vaultCreds + $ref: 'definitions.yaml#/kms' home: - <<: *alerts + $ref: definitions.yaml#/alerts oidc: additionalProperties: false description: 'Holds many parts used in different locations. Please see keycloak, istio and oauth-proxy all consuming parts.' @@ -237,15 +137,15 @@ Settings: adminGroupID: type: string apiUrl: - <<: *url + $ref: 'definitions.yaml#/url' authUrl: - <<: *url + $ref: 'definitions.yaml#/url' clientID: type: string clientSecret: type: string issuer: - <<: *url + $ref: 'definitions.yaml#/url' scope: type: string teamAdminGroupID: @@ -253,7 +153,7 @@ Settings: tenantID: type: string tokenUrl: - <<: *url + $ref: 'definitions.yaml#/url' usernameClaimMapper: type: string description: Claim name used by Keycloak to identify incoming users from identity provider From d224350d871c133f7de674f94d3c033299b1fa55 Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 13:34:53 +0200 Subject: [PATCH 04/13] feat: safely remove old EP (this commit works) --- src/api.authz.test.ts | 43 ----------------------------------------- src/api/settings.ts | 19 ------------------ src/otomi-stack.test.ts | 12 ------------ src/otomi-stack.ts | 22 +++++++++++++++++---- 4 files changed, 18 insertions(+), 78 deletions(-) delete mode 100644 src/api/settings.ts diff --git a/src/api.authz.test.ts b/src/api.authz.test.ts index 8e220b40c..e4f079e04 100644 --- a/src/api.authz.test.ts +++ b/src/api.authz.test.ts @@ -16,14 +16,6 @@ describe('Admin API tests', () => { sinon.stub(otomiStack) app = await initApp(otomiStack) }) - it('admin can get all settings', (done) => { - request(app) - .get('/v1/settings') - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200) - .end(done) - }) it('admin can update team self-service-flags', (done) => { request(app) .put('/v1/teams/team1') @@ -39,41 +31,6 @@ describe('Admin API tests', () => { .expect(200) .end(done) }) - - it('admin can put with payload that matches the schema', (done) => { - request(app) - .put('/v1/settings') - .send({ - alerts: { - drone: 'msteams', - }, - }) - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200) - .expect('Content-Type', /json/) - .end(done) - }) - it('admin can put with empty body (empty object is valid JSON Schema 7)', (done) => { - request(app) - .put('/v1/settings') - .send({}) - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200) - .expect('Content-Type', /json/) - .end(done) - }) - it(`admin can't put with keys that don't match settings object`, (done) => { - request(app) - .put('/v1/settings') - .send({ foo: 'bar' }) - .set('Accept', 'application/json') - .set('Authorization', `Bearer ${adminToken}`) - .expect(400) - .expect('Content-Type', /json/) - .end(done) - }) it('admin can get all teams', (done) => { request(app) .get('/v1/teams') diff --git a/src/api/settings.ts b/src/api/settings.ts deleted file mode 100644 index 4ab1c8a96..000000000 --- a/src/api/settings.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Operation, OperationHandlerArray } from 'express-openapi' -import OtomiStack from '../otomi-stack' -import { OpenApiRequest } from '../otomi-models' - -export default function (otomi: OtomiStack): OperationHandlerArray { - const GET: Operation = [(req: OpenApiRequest, res): any => res.json(otomi.getSettings())] - const PUT: Operation = [ - (req: OpenApiRequest, res): void => { - otomi.editSettings(req.body) - res.json({}) - }, - ] - - const api = { - GET, - PUT, - } - return api -} diff --git a/src/otomi-stack.test.ts b/src/otomi-stack.test.ts index f1b237a73..62c8b63ca 100644 --- a/src/otomi-stack.test.ts +++ b/src/otomi-stack.test.ts @@ -7,18 +7,6 @@ import { Repo } from './repo' describe('Settings', () => { const otomi = new OtomiStack() otomi.repo = new Repo('./test', undefined, undefined, undefined, undefined, undefined) - - it('should assign a dummy payload', () => { - const settings = otomi.getSettings() - const payload: any = { - ...settings, - alerts: { - drone: 'someTeamChat', - }, - } - - expect(otomi.editSettings(payload)).to.deep.equal(payload) - }) }) describe('Data validation', () => { diff --git a/src/otomi-stack.ts b/src/otomi-stack.ts index c516cb32e..ca42f2581 100644 --- a/src/otomi-stack.ts +++ b/src/otomi-stack.ts @@ -109,12 +109,26 @@ export default class OtomiStack { this.loadValues() } - getSettings(): Settings { - return this.db.db.get('settings').value() as Settings + getSubSetting(type, key) { + return { settings: this.db.db.get([type, key]).value() } + } + + setSubSetting(type, data, key) { + if (!isEmpty(data)) { + return ( + this.db.db + .get([type, key]) + // @ts-ignore + .assign(data[type]) + .write() + ) + } + // If it returns the same object, unchanged, then you'll know that there is no successful PUT, + // at least it will not write empty values. + return this.db.db.get([type, key]).value() } - editSettings(data: Settings): Settings { - this.db.db.set('settings', data).write() + getSettings(): Settings { return this.db.db.get('settings').value() as Settings } From ac847e9f289f20ac4292a27ff8380d4d019033a6 Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 14:58:12 +0200 Subject: [PATCH 05/13] fix: debug endpoint --- src/api/settings/{setting}.ts | 7 ++++--- src/app.ts | 1 - src/otomi-stack.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/settings/{setting}.ts b/src/api/settings/{setting}.ts index e35619df2..e082fe02e 100644 --- a/src/api/settings/{setting}.ts +++ b/src/api/settings/{setting}.ts @@ -1,17 +1,18 @@ import { Operation, OperationHandlerArray } from 'express-openapi' +import OtomiStack from '../../otomi-stack' import { OpenApiRequest } from '../../otomi-models' -export default function (): OperationHandlerArray { +export default function (otomi: OtomiStack): OperationHandlerArray { const GET: Operation = [ ({ params: { setting } }: OpenApiRequest, res): void => { console.debug(`Get settings: ${JSON.stringify({ setting })}`) - res.json({}) + res.json(otomi.getSubSetting('settings', setting)) }, ] const PUT: Operation = [ ({ params: { setting }, body }: OpenApiRequest, res): void => { console.debug(`Modify settings: ${JSON.stringify({ setting })}`) - res.json({ body }) + res.json(otomi.setSubSetting('settings', body, setting)) }, ] const api = { diff --git a/src/app.ts b/src/app.ts index 1e31985b3..f549e5ab8 100644 --- a/src/app.ts +++ b/src/app.ts @@ -28,6 +28,5 @@ otomiStack }) .catch((e) => { console.error(e) - server.close() process.exit(1) }) diff --git a/src/otomi-stack.ts b/src/otomi-stack.ts index ca42f2581..ae72505ab 100644 --- a/src/otomi-stack.ts +++ b/src/otomi-stack.ts @@ -110,7 +110,7 @@ export default class OtomiStack { } getSubSetting(type, key) { - return { settings: this.db.db.get([type, key]).value() } + return { [key]: this.db.db.get([type, key]).value() } } setSubSetting(type, data, key) { @@ -119,7 +119,7 @@ export default class OtomiStack { this.db.db .get([type, key]) // @ts-ignore - .assign(data[type]) + .assign(data[key]) .write() ) } From 2c1a9f51051f864d749f67dcd7bec2426fea064a Mon Sep 17 00:00:00 2001 From: svatwork Date: Tue, 8 Jun 2021 16:37:39 +0200 Subject: [PATCH 06/13] feat: endpoint tests --- src/api.authz.test.ts | 70 +++++++++++++++++++++++++++++++++++++++ src/openapi/settings.yaml | 5 +++ src/otomi-stack.test.ts | 4 +++ 3 files changed, 79 insertions(+) diff --git a/src/api.authz.test.ts b/src/api.authz.test.ts index e4f079e04..7136481bd 100644 --- a/src/api.authz.test.ts +++ b/src/api.authz.test.ts @@ -16,6 +16,76 @@ describe('Admin API tests', () => { sinon.stub(otomiStack) app = await initApp(otomiStack) }) + + describe('Admin /settings/{setting} endpoint tests', () => { + const endpoints = ['alerts', 'azure', 'customer', 'dns', 'kms', 'home', 'oidc', 'otomi', 'smtp'] + endpoints.forEach((ep) => { + it(`admin can get /settings/${ep}`, (done) => { + request(app) + .get(`/v1/settings/${ep}`) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200) + .expect('Content-Type', /json/) + .end(done) + }) + }) + + it('admin can put /settings/alerts with correct payload', (done) => { + request(app) + .put('/v1/settings/alerts') + .send({ + alerts: { + drone: 'msteams', + groupInterval: '5m', + msteams: { + highPrio: 'bla', + lowPrio: 'bla', + }, + receivers: ['slack'], + repeatInterval: '3h', + }, + }) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200) + .end(done) + }) + it('admin cannot put /settings/alerts with extra properties', (done) => { + request(app) + .put('/v1/settings/alerts') + .send({ + alerts: { + drone: 'msteams', + groupInterval: '5m', + msteams: { + highPrio: 'bla', + lowPrio: 'bla', + }, + receivers: ['slack'], + repeatInterval: '3h', + randomProp: 'randomValue', + }, + }) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${adminToken}`) + .expect(400) + .end(done) + }) + + it('admin can put empty payload, but it wont change anything', (done) => { + request(app) + .put('/v1/settings/dns') + .send({ + zones: ['someZoneOne', 'someZoneTwo'], + }) + .set('Accept', 'application/json') + .set('Authorization', `Bearer ${adminToken}`) + .expect(400) + .end(done) + }) + }) + it('admin can update team self-service-flags', (done) => { request(app) .put('/v1/teams/team1') diff --git a/src/openapi/settings.yaml b/src/openapi/settings.yaml index a836fb262..31f929e2f 100644 --- a/src/openapi/settings.yaml +++ b/src/openapi/settings.yaml @@ -5,8 +5,10 @@ Settings: additionalProperties: false properties: alerts: + additionalProperties: false $ref: definitions.yaml#/alerts azure: + additionalProperties: false description: Azure specific configuration. properties: appgw: @@ -39,6 +41,7 @@ Settings: type: string type: object dns: + additionalProperties: false properties: zones: description: Extra dns zones that the cluster can administer (see dns). Team services can use this to publish their URLs on. @@ -127,8 +130,10 @@ Settings: - domain type: object kms: + additionalProperties: false $ref: 'definitions.yaml#/kms' home: + additionalProperties: false $ref: definitions.yaml#/alerts oidc: additionalProperties: false diff --git a/src/otomi-stack.test.ts b/src/otomi-stack.test.ts index 62c8b63ca..aa807823e 100644 --- a/src/otomi-stack.test.ts +++ b/src/otomi-stack.test.ts @@ -7,6 +7,10 @@ import { Repo } from './repo' describe('Settings', () => { const otomi = new OtomiStack() otomi.repo = new Repo('./test', undefined, undefined, undefined, undefined, undefined) + it('admin can put empty payload, but it wont change anything', (done) => { + expect(() => otomi.setSubSetting('settings', {}, 'alerts').to.not.be.empty) + done() + }) }) describe('Data validation', () => { From fe98dea5c0d398afd197d28f2d177888244837ac Mon Sep 17 00:00:00 2001 From: svatwork Date: Wed, 9 Jun 2021 11:54:54 +0200 Subject: [PATCH 07/13] feat: update schema with title and description --- src/openapi/api.yaml | 10 +++ src/openapi/cluster.yaml | 11 +++- src/openapi/definitions.yaml | 65 +++++++++++++++----- src/openapi/settings.yaml | 116 ++++++++++++++++++++++------------- 4 files changed, 146 insertions(+), 56 deletions(-) diff --git a/src/openapi/api.yaml b/src/openapi/api.yaml index 5c99560c0..93c092ef3 100644 --- a/src/openapi/api.yaml +++ b/src/openapi/api.yaml @@ -415,6 +415,16 @@ paths: required: true schema: type: string + enum: + - alerts + - azure + - customer + - dns + - home + - kms + - oidc + - otomi + - smtp example: oidc get: operationId: getSettings diff --git a/src/openapi/cluster.yaml b/src/openapi/cluster.yaml index 6fc06f37f..9b8a8bcc5 100644 --- a/src/openapi/cluster.yaml +++ b/src/openapi/cluster.yaml @@ -1,31 +1,39 @@ Cluster: properties: apiName: + title: API Name description: Only used for API/UI to show in app. type: string readOnly: true apiServer: + title: API Server description: Used by kubectl for local deployment to target cluster. type: string readOnly: true domainSuffix: + title: Domain Suffix $ref: 'definitions.yaml#/domain' description: Domain suffix for the cluster. Also added to list of dns zones in the Otomi Console. entrypoint: + title: Entrypoint description: A Kubernetes API public IP address (onprem only). type: string readOnly: true k8sVersion: + title: Kubernetes Version $ref: 'definitions.yaml#/k8sVersion' readOnly: true name: + title: Name type: string readOnly: true otomiVersion: + title: Otomi Version default: latest description: 'Please pin this a valid release version found in the repo. Suggestion: try the most recent stable version.' type: string provider: + title: Provider type: string enum: - aws @@ -34,10 +42,12 @@ Cluster: - onprem readOnly: true region: + title: Region description: Dependent on provider. type: string readOnly: true vpcID: + title: VPC ID description: AWS only. If provided will override autodiscovery from metadata. type: string readOnly: true @@ -49,4 +59,3 @@ Cluster: - otomiVersion - provider - region - type: object diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index 456897f73..ab07ae6fc 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -1,48 +1,57 @@ alerts: - type: object + description: Define Alerts for communication channels. properties: drone: default: slack + description: Select default notification channel for Drone. enum: - slack - msteams + title: Drone Notifications type: string email: additionalProperties: false properties: critical: + title: Critical Events $ref: '#/email' description: One or more email addresses (comma separated) for critical events. nonCritical: + title: Non-critical Events $ref: '#/email' description: One or more email addresses (comma separated) for non-critical events. type: object groupInterval: default: 5m description: How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.) + title: Group Interval type: string msteams: additionalProperties: false + description: Select Microsoft Teams notification settings. properties: highPrio: - description: The low prio web hook. + title: High Priority Web Hook type: string lowPrio: - description: The high prio web hook. + title: Low Priority Web Hook type: string + title: Microsoft Teams type: object receivers: - description: Notification receivers. + description: Select default notification channel for receiving notifications. items: enum: - slack - msteams - email type: string + title: Notification Receivers type: array repeatInterval: default: 3h - description: How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). + description: Indicates waiting time before sending a notification again after it was sent successfully for an alert. (Usually ~3h or more). + title: Repeat Interval type: string slack: additionalProperties: false @@ -76,10 +85,13 @@ annotations: awsCreds: properties: accessKey: + title: Access Key type: string secretKey: + title: Secret Key type: string region: + title: Region type: string type: object required: @@ -89,12 +101,20 @@ awsCreds: azureCreds: properties: clientId: + description: Enter client ID. + title: Client ID type: string clientSecret: + description: Enter client secret. + title: Client Secret type: string environment: + description: Enter Azure environment. + title: Environment type: string tenantId: + description: Enter tenant ID. + title: Tenant ID type: string type: object required: @@ -185,12 +205,21 @@ env: - value type: object googleCreds: + title: Google Credentials + description: Enter Google credentials. properties: - accountJson: - type: string - project: - type: string - type: object + google: + description: Enter GCP details. + properties: + accountJson: + description: Enter GCP account JSON for authentication. + title: Account JSON + type: string + project: + description: Enter GCP project. + title: GCP Project + type: string + title: Google hostPort: pattern: ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]):()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$ type: string @@ -425,12 +454,20 @@ url: pattern: ^(https:\/\/)([\w\-])+\.{1}([a-zA-Z]{2,63})([\/\w-]*)*\/?\??([^#\n\r]*)?#?([^\n\r]*)$ type: string vaultCreds: + description: Hashicorp's Vault credentials. properties: - token: - type: string - type: object + vault: + title: Vault Credentials + description: Hashicorp's Vault credentials. + properties: + token: + title: Token + type: string + required: + - token required: - - token + - vault + title: Vault Credentials volumes: items: additionalProperties: false diff --git a/src/openapi/settings.yaml b/src/openapi/settings.yaml index 31f929e2f..5c5613cbf 100644 --- a/src/openapi/settings.yaml +++ b/src/openapi/settings.yaml @@ -5,9 +5,11 @@ Settings: additionalProperties: false properties: alerts: + title: Alerts additionalProperties: false $ref: definitions.yaml#/alerts azure: + title: Azure additionalProperties: false description: Azure specific configuration. properties: @@ -15,31 +17,31 @@ Settings: properties: isManaged: default: true - description: Is this appgw installed as AKS addon? + description: Determines whether this AppGW Ingress Controller is installed as an AKS addon. type: boolean type: object monitor: $ref: definitions.yaml#/azureMonitor resourceGroup: - description: An Azure resource group. + title: Azure Resource Group type: string subscriptionId: - description: An Azure subscription ID. + title: Subscription ID type: string tenantId: - description: An Azure tenant ID. + title: Tenant ID type: string required: - resourceGroup - subscriptionId - tenantId - type: object customer: + title: Customer additionalProperties: false properties: name: type: string - type: object + title: Name dns: additionalProperties: false properties: @@ -47,17 +49,22 @@ Settings: description: Extra dns zones that the cluster can administer (see dns). Team services can use this to publish their URLs on. items: type: string + title: Zone + title: Zones type: array domain: + description: Define a FQDN for this Kubernetes cluster. + title: Fully Qualified Domain Name (FQDN) type: string - description: A fqdn for the cluster oneOf: - - title: aws + - title: AWS properties: aws: + title: AWS properties: region: type: string + title: Region required: - region type: object @@ -106,71 +113,84 @@ Settings: - subscriptionId - aadClientId - aadClientSecret - type: object - type: object required: - azure - - title: google + - title: Google properties: google: + title: Google properties: serviceAccountKey: description: A service account key for managing a DNS zone. + title: Service Account Key type: string project: + title: Project type: string required: - serviceAccountKey - project - type: object - type: object required: - google required: - domain - type: object kms: additionalProperties: false $ref: 'definitions.yaml#/kms' home: + title: Home Alerts additionalProperties: false $ref: definitions.yaml#/alerts oidc: + title: OIDC additionalProperties: false - description: 'Holds many parts used in different locations. Please see keycloak, istio and oauth-proxy all consuming parts.' + description: Holds many parts used in different locations. Please see keycloak, istio and oauth-proxy all consuming parts. properties: adminGroupID: + title: Admin Group ID type: string apiUrl: - $ref: 'definitions.yaml#/url' + title: API URL + type: string authUrl: - $ref: 'definitions.yaml#/url' + title: Authentication URL + type: string clientID: + title: Client ID type: string clientSecret: + title: Client Secret type: string issuer: - $ref: 'definitions.yaml#/url' + title: Issuer + type: string scope: + title: Scope + type: string + subClaimMapper: + default: sub + description: Select OIDC claim to be used as a unique user identifier. + title: Sub Claim Mapper type: string teamAdminGroupID: + title: Team Admin Group ID type: string tenantID: + title: Tenant ID type: string tokenUrl: - $ref: 'definitions.yaml#/url' - usernameClaimMapper: + title: Token URL type: string - description: Claim name used by Keycloak to identify incoming users from identity provider - subClaimMapper: + usernameClaimMapper: + description: Claim name used by Keycloak to identify incoming users from identity provider. + title: Username Claim Mapper type: string - description: Select OIDC claim to be used as a unique user identifier - default: sub - type: object otomi: additionalProperties: false properties: additionalClusters: + title: Additional Clusters + description: Enter other k8s clusters, if there are any. type: array items: $ref: cluster.yaml#/Cluster @@ -178,21 +198,39 @@ Settings: - domainSuffix - name - provider + addons: + additionalProperties: false + description: Manage addon configuration. + properties: + conftest: + description: Manage Conftest configuration. + properties: + enabled: + default: true + description: Use this flag to enable conftest for policy validation + title: Enabled + type: boolean + title: Conftest + title: Addons hasCloudLB: default: false description: Set this to true when an external LB exists or needs to be started (AWS ALB, Azure AppGW, Google Apigee). This will then be configured through ingress controllers. Expects existing LBs to terminate https. Currently this is only working correctly for Azure, and not for AWS and Google. AWS is close to completion. + title: Cloud Load Balancer type: boolean isHomeMonitored: default: false description: Whether this cluster is home monitored (like when under a Premium SLA). Sends criticals home. + title: Home Monitored type: boolean isManaged: default: true description: Whether masters are managed and not under control. Set this to false when onprem. + title: Managed Masters type: boolean isMultitenant: default: true description: Whether to separate team metrics and logs. Disabling this lets everybody be admin and see everything. + title: Multi Tenancy type: boolean mode: default: ee @@ -200,49 +238,45 @@ Settings: enum: - ce - ee + title: Otomi Core Edition type: string pullSecret: default: '' description: The pullsecret to deploy the Otomi API and Console. Requires an Otomi license. + title: Pull Secret type: string teamPrefix: default: team- description: The prefix to use in URLs for team domains. pattern: ^[a-z]+[-]{1}$ + title: Team Prefix type: string - addons: - description: Manage addon configuration - additionalProperties: false - properties: - conftest: - properties: - enabled: - type: boolean - default: true - description: Use this flag to enable conftest for policy validation - type: object - type: object - type: object smtp: additionalProperties: false + description: Define SMTP settings. properties: auth_identity: + title: Authentication Identity type: string auth_password: + title: Authentication Password type: string auth_secret: + title: Authentication Secret type: string auth_username: + title: Authentication Username type: string from: $ref: definitions.yaml#/email description: The "from" address. Defaults to alerts@$clusterDomain. + title: From Address hello: + title: Hello type: string smarthost: - $ref: definitions.yaml#/hostPort description: The smtp host:port combination. + pattern: ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]):()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$ + title: Smart Host required: - smarthost - type: object - type: object From 6397f0ba9e3621654ae07cad52a42c9f6703b871 Mon Sep 17 00:00:00 2001 From: svatwork Date: Wed, 9 Jun 2021 13:36:02 +0200 Subject: [PATCH 08/13] fix: review morriz remove title uppercase --- src/openapi/settings.yaml | 58 ++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/openapi/settings.yaml b/src/openapi/settings.yaml index 5c5613cbf..0c6e50c8c 100644 --- a/src/openapi/settings.yaml +++ b/src/openapi/settings.yaml @@ -23,7 +23,7 @@ Settings: monitor: $ref: definitions.yaml#/azureMonitor resourceGroup: - title: Azure Resource Group + title: Azure resource group type: string subscriptionId: title: Subscription ID @@ -71,7 +71,7 @@ Settings: type: object required: - aws - - title: azure + - title: Azure properties: azure: properties: @@ -89,23 +89,23 @@ Settings: tenantId: title: Tenant ID type: string - description: Azure tenant ID + description: Azure tenant ID. subscriptionId: title: Subscription ID type: string - description: Azure subscription ID + description: Azure subscription ID. aadClientId: title: Client ID type: string - description: Azure Application Client ID + description: Azure Application Client ID. aadClientSecret: title: Client secret type: string - description: Azure Application Client Secret + description: Azure Application Client Secret. useManagedIdentityExtension: - title: Using managed identities? + title: Managed identities type: boolean - description: If you use Azure MSI, this should be set to true + description: If you use Azure MSI, this should be set to true. default: false required: - resourceGroup @@ -122,7 +122,7 @@ Settings: properties: serviceAccountKey: description: A service account key for managing a DNS zone. - title: Service Account Key + title: Service account key type: string project: title: Project @@ -138,7 +138,7 @@ Settings: additionalProperties: false $ref: 'definitions.yaml#/kms' home: - title: Home Alerts + title: Home alerts additionalProperties: false $ref: definitions.yaml#/alerts oidc: @@ -147,7 +147,7 @@ Settings: description: Holds many parts used in different locations. Please see keycloak, istio and oauth-proxy all consuming parts. properties: adminGroupID: - title: Admin Group ID + title: Admin group ID type: string apiUrl: title: API URL @@ -170,10 +170,10 @@ Settings: subClaimMapper: default: sub description: Select OIDC claim to be used as a unique user identifier. - title: Sub Claim Mapper + title: Sub claim mapper type: string teamAdminGroupID: - title: Team Admin Group ID + title: Team admin group ID type: string tenantID: title: Tenant ID @@ -182,14 +182,14 @@ Settings: title: Token URL type: string usernameClaimMapper: - description: Claim name used by Keycloak to identify incoming users from identity provider. - title: Username Claim Mapper + description: Claim name used by Keycloak to identify incoming users from the identity provider. + title: Username claim mapper type: string otomi: additionalProperties: false properties: additionalClusters: - title: Additional Clusters + title: Additional clusters description: Enter other k8s clusters, if there are any. type: array items: @@ -215,22 +215,22 @@ Settings: hasCloudLB: default: false description: Set this to true when an external LB exists or needs to be started (AWS ALB, Azure AppGW, Google Apigee). This will then be configured through ingress controllers. Expects existing LBs to terminate https. Currently this is only working correctly for Azure, and not for AWS and Google. AWS is close to completion. - title: Cloud Load Balancer + title: Cloud load balancer type: boolean isHomeMonitored: default: false description: Whether this cluster is home monitored (like when under a Premium SLA). Sends criticals home. - title: Home Monitored + title: Home monitored type: boolean isManaged: default: true description: Whether masters are managed and not under control. Set this to false when onprem. - title: Managed Masters + title: Managed masters type: boolean isMultitenant: default: true description: Whether to separate team metrics and logs. Disabling this lets everybody be admin and see everything. - title: Multi Tenancy + title: Multi-tenancy type: boolean mode: default: ee @@ -238,40 +238,42 @@ Settings: enum: - ce - ee - title: Otomi Core Edition + title: Otomi core edition type: string pullSecret: default: '' description: The pullsecret to deploy the Otomi API and Console. Requires an Otomi license. - title: Pull Secret + title: Pull secret type: string teamPrefix: default: team- description: The prefix to use in URLs for team domains. pattern: ^[a-z]+[-]{1}$ - title: Team Prefix + title: Team prefix type: string smtp: + # https://prometheus.io/docs/alerting/latest/configuration/#tmpl_string additionalProperties: false description: Define SMTP settings. properties: auth_identity: - title: Authentication Identity + title: Authentication identity type: string auth_password: - title: Authentication Password + title: Authentication password type: string auth_secret: - title: Authentication Secret + title: Authentication secret type: string auth_username: - title: Authentication Username + title: Authentication username type: string from: $ref: definitions.yaml#/email description: The "from" address. Defaults to alerts@$clusterDomain. - title: From Address + title: From address hello: + description: The hostname to identify to the SMTP server. title: Hello type: string smarthost: From 2152b9766b748ff52b123e04f6bcb73fb974c783 Mon Sep 17 00:00:00 2001 From: Maurice Faber Date: Thu, 10 Jun 2021 14:14:51 +0200 Subject: [PATCH 09/13] Update src/openapi/definitions.yaml Co-authored-by: Sebastiaan Verbeek --- src/openapi/definitions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index ab07ae6fc..5a19f535e 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -266,7 +266,7 @@ k8sVersion: type: string kms: additionalProperties: false - description: Holds KMS settings like credentials for retrieving enc/decyption keys. + description: Holds KMS settings like credentials for retrieving encryption/decryption keys. title: KMS settings properties: sops: From edc145c07e1a1a62eba0bafd9f54ce33ff730f95 Mon Sep 17 00:00:00 2001 From: svatwork Date: Thu, 10 Jun 2021 15:34:27 +0200 Subject: [PATCH 10/13] fix: review Morriz --- src/openapi/api.yaml | 12 ++++++------ src/openapi/definitions.yaml | 38 +++++++++++++----------------------- src/otomi-stack.test.ts | 10 ---------- 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/openapi/api.yaml b/src/openapi/api.yaml index 93c092ef3..9d63f92ad 100644 --- a/src/openapi/api.yaml +++ b/src/openapi/api.yaml @@ -427,8 +427,8 @@ paths: - smtp example: oidc get: - operationId: getSettings - description: Get settings from the `settings.yaml` and `secret.settings.yaml` file. + operationId: getSubSetting + description: Get a setting from the `settings.yaml` or `secret.settings.yaml` file. x-aclSchema: Settings responses: '200': @@ -440,20 +440,20 @@ paths: # TODO: https://github.com/redkubes/otomi-api/issues/155 put: - operationId: editSettings - description: Edits the settings from the `settings.yaml` file + operationId: editSubSetting + description: Edit a setting from the `settings.yaml` or `secret.settings.yaml` file. x-aclSchema: Settings requestBody: content: application/json: schema: $ref: '#/components/schemas/Settings' - description: Settings object that contains updated values + description: Given a setting parameter, put new setting for a given rootprop. required: true responses: <<: *DefaultGetResponses '200': - description: Successfully edited `settings.yaml` + description: Successfully edited this setting. # -------------------------------------------- Servers diff --git a/src/openapi/definitions.yaml b/src/openapi/definitions.yaml index 5a19f535e..d7bfc97e7 100644 --- a/src/openapi/definitions.yaml +++ b/src/openapi/definitions.yaml @@ -31,10 +31,10 @@ alerts: description: Select Microsoft Teams notification settings. properties: highPrio: - title: High Priority Web Hook + title: High prio web hook type: string lowPrio: - title: Low Priority Web Hook + title: Low prio web hook type: string title: Microsoft Teams type: object @@ -208,18 +208,14 @@ googleCreds: title: Google Credentials description: Enter Google credentials. properties: - google: - description: Enter GCP details. - properties: - accountJson: - description: Enter GCP account JSON for authentication. - title: Account JSON - type: string - project: - description: Enter GCP project. - title: GCP Project - type: string - title: Google + accountJson: + description: Enter GCP account JSON for authentication. + title: Account JSON + type: string + project: + description: Enter GCP project. + title: GCP Project + type: string hostPort: pattern: ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]):()([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$ type: string @@ -456,17 +452,11 @@ url: vaultCreds: description: Hashicorp's Vault credentials. properties: - vault: - title: Vault Credentials - description: Hashicorp's Vault credentials. - properties: - token: - title: Token - type: string - required: - - token + token: + title: Token + type: string required: - - vault + - token title: Vault Credentials volumes: items: diff --git a/src/otomi-stack.test.ts b/src/otomi-stack.test.ts index aa807823e..3260fc24e 100644 --- a/src/otomi-stack.test.ts +++ b/src/otomi-stack.test.ts @@ -2,16 +2,6 @@ import './test-init' import { expect } from 'chai' import OtomiStack from './otomi-stack' -import { Repo } from './repo' - -describe('Settings', () => { - const otomi = new OtomiStack() - otomi.repo = new Repo('./test', undefined, undefined, undefined, undefined, undefined) - it('admin can put empty payload, but it wont change anything', (done) => { - expect(() => otomi.setSubSetting('settings', {}, 'alerts').to.not.be.empty) - done() - }) -}) describe('Data validation', () => { let otomiStack: OtomiStack From b6ff5da778fde312ef9222e2effedf9970e596a3 Mon Sep 17 00:00:00 2001 From: svatwork Date: Thu, 10 Jun 2021 16:12:44 +0200 Subject: [PATCH 11/13] fix: merge --- src/openapi/api.yaml | 9 --------- src/otomi-stack.ts | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/openapi/api.yaml b/src/openapi/api.yaml index 80f61fafd..3073cfdd1 100644 --- a/src/openapi/api.yaml +++ b/src/openapi/api.yaml @@ -677,15 +677,9 @@ components: Deployment: $ref: 'deployment.yaml#/Deployment' - DockerRegistry: - $ref: 'secret.yaml#/DockerRegistry' - email: $ref: 'definitions.yaml#/email' - Generic: - $ref: 'secret.yaml#/Generic' - googleCreds: $ref: 'definitions.yaml#/googleCreds' @@ -734,9 +728,6 @@ components: TeamSelfService: $ref: 'team.yaml#/TeamSelfService' - TLS: - $ref: 'secret.yaml#/TLS' - url: $ref: 'definitions.yaml#/url' diff --git a/src/otomi-stack.ts b/src/otomi-stack.ts index b158b571a..54ce781cf 100644 --- a/src/otomi-stack.ts +++ b/src/otomi-stack.ts @@ -1,7 +1,7 @@ import * as k8s from '@kubernetes/client-node' import fs from 'fs' import yaml from 'js-yaml' -import { cloneDeep, merge, filter, get, omit, set, unset, isEqual, union } from 'lodash' +import { cloneDeep, merge, filter, get, omit, set, unset, isEqual, union, isEmpty } from 'lodash' import generatePassword from 'password-generator' import { V1ObjectReference } from '@kubernetes/client-node' import Db from './db' From b229c2eba94710ded865fcbadcbb579ed0c5a032 Mon Sep 17 00:00:00 2001 From: svatwork Date: Thu, 10 Jun 2021 16:16:14 +0200 Subject: [PATCH 12/13] revert: dns.domain --- src/openapi/settings.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/openapi/settings.yaml b/src/openapi/settings.yaml index d02a3fbe4..747de2b4d 100644 --- a/src/openapi/settings.yaml +++ b/src/openapi/settings.yaml @@ -52,11 +52,6 @@ Settings: title: Zone title: Zones type: array - domain: - description: Define a FQDN for this Kubernetes cluster. - title: Fully Qualified Domain Name (FQDN) - type: string - nullable: true provider: description: The DNS provider managing the domains. oneOf: From 99a35b3bc1c926a26e9746ff2022bd6513576b11 Mon Sep 17 00:00:00 2001 From: svatwork Date: Thu, 10 Jun 2021 16:19:38 +0200 Subject: [PATCH 13/13] fix: sort schema --- src/openapi/api.yaml | 151 +++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 100 deletions(-) diff --git a/src/openapi/api.yaml b/src/openapi/api.yaml index 3073cfdd1..0d908512a 100644 --- a/src/openapi/api.yaml +++ b/src/openapi/api.yaml @@ -587,152 +587,103 @@ components: # -------------------------------------------- Schemas # Indicate root schemas with UpperCamelCase (e.g. Cluster) # Indicate definition references with camelCase (e.g. azureCreds) - # This is the way to properly generate polymorphic schemas (e.g. oneOf, allOf). # - # Please stick to: alphabetical sorting, - # and after that: capital letters before lower case letters. + # Please stick to: alphabetical sorting, all Uppercase first and then lowercase. # -------------------------------------------- schemas: + Cluster: + $ref: cluster.yaml#/Cluster + Deployment: + $ref: deployment.yaml#/Deployment + Job: + $ref: job.yaml#/Job + Kubecfg: + $ref: kubecfg.yaml#/Kubecfg + OpenApiValidationError: + $ref: error.yaml#/OpenApiValidationError + OtomiStackError: + $ref: error.yaml#/OtomiStackError + Secret: + $ref: secret.yaml#/Secret + SecretDockerRegistry: + $ref: secret.yaml#/SecretDockerRegistry + SecretGeneric: + $ref: secret.yaml#/SecretGeneric + SecretTLS: + $ref: secret.yaml#/SecretTLS + Service: + $ref: service.yaml#/Service + Session: + $ref: session.yaml#/Session + Settings: + $ref: settings.yaml#/Settings + Team: + $ref: team.yaml#/Team + TeamAuthz: + $ref: user.yaml#/TeamAuthz + TeamSelfService: + $ref: team.yaml#/TeamSelfService + User: + $ref: user.yaml#/User alerts: - $ref: 'definitions.yaml#/alerts' - + $ref: definitions.yaml#/alerts awsCreds: - $ref: 'definitions.yaml#/awsCreds' - + $ref: definitions.yaml#/awsCreds azureCreds: - $ref: 'definitions.yaml#/azureCreds' - + $ref: definitions.yaml#/azureCreds azureMonitor: - $ref: 'definitions.yaml#/azureMonitor' - + $ref: definitions.yaml#/azureMonitor containerSpec: $ref: definitions.yaml#/containerSpec - - Cluster: - $ref: 'cluster.yaml#/Cluster' - domain: $ref: definitions.yaml#/domain - + email: + $ref: definitions.yaml#/email env: $ref: definitions.yaml#/env - + googleCreds: + $ref: definitions.yaml#/googleCreds idName: $ref: definitions.yaml#/idName - image: $ref: definitions.yaml#/image - - jobSpec: - $ref: definitions.yaml#/jobSpec - ingress: $ref: definitions.yaml#/ingress - ingressCluster: $ref: definitions.yaml#/ingressCluster - ingressPrivate: $ref: definitions.yaml#/ingressPrivate - ingressPublic: $ref: definitions.yaml#/ingressPublic - ingressTls: $ref: definitions.yaml#/ingressTls - + jobSpec: + $ref: definitions.yaml#/jobSpec + kms: + $ref: definitions.yaml#/kms ksvcNew: $ref: definitions.yaml#/ksvcNew - ksvcPredeployed: $ref: definitions.yaml#/ksvcPredeployed - offChoice: $ref: definitions.yaml#/offChoice - path: $ref: definitions.yaml#/path - podSpec: $ref: definitions.yaml#/podSpec - resource: $ref: definitions.yaml#/resource - resources: $ref: definitions.yaml#/resources - - secrets: - $ref: definitions.yaml#/secrets - script: $ref: definitions.yaml#/script - + secrets: + $ref: definitions.yaml#/secrets securityContext: $ref: definitions.yaml#/securityContext - svcPredeployed: $ref: definitions.yaml#/svcPredeployed - - Deployment: - $ref: 'deployment.yaml#/Deployment' - - email: - $ref: 'definitions.yaml#/email' - - googleCreds: - $ref: 'definitions.yaml#/googleCreds' - - Job: - $ref: 'job.yaml#/Job' - - Kubecfg: - $ref: 'kubecfg.yaml#/Kubecfg' - - kms: - $ref: 'definitions.yaml#/kms' - - OpenApiValidationError: - $ref: 'error.yaml#/OpenApiValidationError' - - OtomiStackError: - $ref: 'error.yaml#/OtomiStackError' - - Secret: - $ref: 'secret.yaml#/Secret' - - SecretGeneric: - $ref: 'secret.yaml#/SecretGeneric' - - SecretDockerRegistry: - $ref: 'secret.yaml#/SecretDockerRegistry' - - SecretTLS: - $ref: 'secret.yaml#/SecretTLS' - - Service: - $ref: 'service.yaml#/Service' - - Session: - $ref: 'session.yaml#/Session' - - Settings: - $ref: 'settings.yaml#/Settings' - - Team: - $ref: 'team.yaml#/Team' - - TeamAuthz: - $ref: 'user.yaml#/TeamAuthz' - - TeamSelfService: - $ref: 'team.yaml#/TeamSelfService' - url: - $ref: 'definitions.yaml#/url' - - User: - $ref: 'user.yaml#/User' - + $ref: definitions.yaml#/url vaultCreds: - $ref: 'definitions.yaml#/vaultCreds' + $ref: definitions.yaml#/vaultCreds