Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YAML Editor enhancements #462

Merged
merged 52 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d205913
Fixed vm.save is not a function
grolu Sep 19, 2019
24c0237
Fixed Leave Create Cluster Popup did not show after switching back fr…
grolu Sep 19, 2019
431540b
Fixed does not leave create cluster page when switching to all projects
grolu Sep 19, 2019
7ec31dc
First implementation of YAML Editor Autocompletion
grolu Sep 20, 2019
223f470
Improved leave create cluster warning popup logic
grolu Sep 20, 2019
0b1492d
do not auto complete single results
grolu Sep 20, 2019
5e21991
First implementation of editor tooltips
grolu Sep 24, 2019
40653fc
Fixed: Only one machine image version per vendor visible in dashboard
grolu Sep 24, 2019
e03046d
Make create cluster toolbar fixed, so that create button is always vi…
grolu Sep 25, 2019
8bed82c
Merge branch 'master' into create_cluster_enhancements
grolu Sep 27, 2019
1d1cee5
Merge branch 'master' into create_cluster_enhancements
grolu Nov 8, 2019
c7d79be
switched to new api group
grolu Nov 8, 2019
cea7fcd
Merge branch 'master' into create_cluster_enhancements
grolu Nov 25, 2019
ae3b259
Merge branch 'master' into create_cluster_enhancements
grolu Jan 22, 2020
5fd549c
refactored code completion to improve testability
grolu Jan 22, 2020
152f2f0
Added tests for code completion
grolu Jan 23, 2020
78fc240
Merge branch 'master' into create_cluster_enhancements
grolu Jan 30, 2020
e1403ac
Refactored ShootCompletion class for better readability
grolu Feb 4, 2020
dd63fcf
Added tests for editorEnter function
grolu Feb 5, 2020
94c2133
Refactored backend to make call to Open API Endpoint via kubernetes-c…
grolu Feb 5, 2020
bf01279
rm unnecessary error handling
grolu Feb 5, 2020
035ad8e
Merge branch 'master' into create_cluster_enhancements
grolu Feb 27, 2020
fc03534
improved code
grolu Feb 27, 2020
3c73418
Update frontend/src/utils/api.js
grolu Mar 3, 2020
6ddb2c5
Update frontend/src/components/ShootEditor.vue
grolu Mar 3, 2020
7932222
changes as discussed
grolu Mar 3, 2020
7624b34
Merge branch 'create_cluster_enhancements' of github.com:gardener/das…
grolu Mar 3, 2020
c01d9ae
changes as discussed
grolu Mar 3, 2020
392a515
Merge branch 'master' into create_cluster_enhancements
grolu Mar 3, 2020
7e788a9
use admin client to fetch Open API spec
grolu Mar 4, 2020
aa3770b
Added access review for accessOpenAPI
grolu Mar 5, 2020
2beda8b
some renaming
grolu Mar 5, 2020
7dc8c8d
changes as requested
grolu Mar 19, 2020
a53e76b
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
0195ea1
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
e3d46bd
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
ba31db3
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
dd28e00
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
e92fb2a
Fixed test errors
grolu Mar 19, 2020
7da85e5
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
4709048
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 19, 2020
a09dc2f
changes as discussed
grolu Mar 20, 2020
22feb08
Merge branch 'master' into create_cluster_enhancements
grolu Mar 20, 2020
9e7d2a7
decreased branches threshold
grolu Mar 20, 2020
8b7df2c
Update backend/lib/openapi/index.js
grolu Mar 20, 2020
634e05b
Update frontend/src/utils/api.js
grolu Mar 20, 2020
d46bb04
Update frontend/src/utils/shootEditorCompletions.js
grolu Mar 20, 2020
9030b5c
fixed changes
grolu Mar 20, 2020
537d743
Update frontend/src/components/ShootEditor.vue
grolu Mar 20, 2020
8bde6c6
Update frontend/src/components/ShootEditor.vue
grolu Mar 20, 2020
154d9cc
changes as discussed
grolu Mar 20, 2020
e05135d
fix for duplicate completion issue
grolu Mar 20, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ Copyright (c) Isaac Z. Schlueter and Contributors\
Copyright (c) 2014-2017 Automattic <dev@cloudup.com>\
[MIT License](https://github.com/socketio/socket.io/blob/master/LICENSE)

- [swagger-parser](https://github.com/APIDevTools/swagger-parser)\
Copyright (c) 2015 James Messinger\
[MIT License](https://github.com/APIDevTools/swagger-parser/blob/master/LICENSE)

- [uuid](https://github.com/kelektiv/node-uuid)\
Copyright (c) 2010-2016 Robert Kieffer and other contributors\
[MIT License](https://github.com/kelektiv/node-uuid/blob/master/LICENSE.md)
Expand Down
2 changes: 1 addition & 1 deletion backend/.nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"lines": 87,
"statements": 87,
"functions": 91,
"branches": 66,
"branches": 65,
"include": [
"lib/**/*.js"
],
Expand Down
32 changes: 32 additions & 0 deletions backend/lib/kubernetes-client/nonResourceEndpoints/OpenAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright (c) 2019 by SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

'use strict'

const HttpClient = require('../HttpClient')
const { http } = require('../symbols')

class OpenAPI extends HttpClient {
constructor (options = {}) {
super({ ...options, responseType: 'json' })
}

get () {
return this[http.request]('openapi/v2', { method: 'get' })
}
}

module.exports = OpenAPI
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
'use strict'

const Healthz = require('./Healthz')
const OpenAPI = require('./OpenAPI')

function load (options) {
return {
healthz: new Healthz(options)
healthz: new Healthz(options),
openapi: new OpenAPI(options)
}
}

Expand Down
63 changes: 63 additions & 0 deletions backend/lib/openapi/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Copyright (c) 2019 by SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

'use strict'

const { canGetOpenAPI } = require('../services/authorization')
const { Forbidden } = require('../errors')
const SwaggerParser = require('swagger-parser')
const express = require('express')
const { dashboardClient } = require('../kubernetes-client')
const _ = require('lodash')

const router = module.exports = express.Router()

router.route('/')
.get(async (req, res, next) => {
try {
const user = req.user
const schemaDefinitions = await getSchemaDefinitions(user)
res.send(schemaDefinitions)
} catch (err) {
next(err)
}
})

const schemaDefinitions = {} // Cache, TODO: Need to update cache when apiserver gets updated

async function getSchemaDefinitions (user) {
const hasAuthorization = await canGetOpenAPI(user)
if (!hasAuthorization) {
throw new Forbidden('User is not allowed to read OpenAPI schemas definitions')
}

if (_.isEmpty(schemaDefinitions)) {
// Do not use client of user as the result gets cached and returned to other users
const swaggerApi = await dashboardClient.openapi.get()
const dereferencedSwaggerApi = await SwaggerParser.dereference(swaggerApi)

const selectedSchemaDefinitions = _
.chain(dereferencedSwaggerApi)
.get('definitions')
.pick([
'com.github.gardener.gardener.pkg.apis.core.v1beta1.Shoot'
])
.value()
_.assign(schemaDefinitions, selectedSchemaDefinitions)
}

return schemaDefinitions
}
1 change: 1 addition & 0 deletions backend/lib/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const config = require('../config')

module.exports = {
'/info': require('./info'),
'/openapi': require('../openapi'),
'/user': require('./user'),
'/cloudprofiles': require('./cloudprofiles'),
'/shoots': require('./shoots'),
Expand Down
23 changes: 17 additions & 6 deletions backend/lib/services/authorization.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

const { Resources } = require('../kubernetes-client')

async function hasAuthorization (user, resourceAttributes) {
async function hasAuthorization (user, { resourceAttributes, nonResourceAttributes }) {
if (!user) {
return false
}
Expand All @@ -28,7 +28,8 @@ async function hasAuthorization (user, resourceAttributes) {
kind,
apiVersion,
spec: {
resourceAttributes
resourceAttributes,
nonResourceAttributes
}
}
const {
Expand All @@ -38,14 +39,24 @@ async function hasAuthorization (user, resourceAttributes) {
} = await client['authorization.k8s.io'].selfsubjectaccessreviews.create(body)
return allowed
}
exports.hasAuthorization = hasAuthorization

exports.isAdmin = function (user) {
// if someone is allowed to get secrets in all namespaces he is considered to be an administrator
return hasAuthorization(user, {
verb: 'get',
group: '',
resource: 'secrets'
resourceAttributes: {
verb: 'get',
group: '',
resource: 'secrets'
}
})
}

exports.canGetOpenAPI = function (user) {
return hasAuthorization(user, {
nonResourceAttributes: {
verb: 'get',
path: '/openapi/v2'
}
})
}

Expand Down
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"p-retry": "^4.2.0",
"p-timeout": "^3.2.0",
"reconnect-core": "^1.3.0",
"swagger-parser": "^8.0.1",
"semver": "^7.1.2",
"socket.io": "^2.3.0",
"uuid": "^3.3.2",
Expand Down
4 changes: 4 additions & 0 deletions backend/test/acceptance.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ describe('acceptance', function () {
require('./acceptance/healthz.spec.js')(context)
})

describe('openapi', function () {
require('./acceptance/openapi.spec.js')(context)
})

describe('security', function () {
require('./acceptance/security.spec.js')(context)
})
Expand Down
4 changes: 2 additions & 2 deletions backend/test/acceptance/api.info.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = function info ({ agent, k8s, auth }) {
/* eslint no-unused-expressions: 0 */
const username = 'john.doe@example.org'
const id = username
const aud = [ 'gardener' ]
const aud = ['gardener']

it('should reject requests csrf protection error', async function () {
const res = await agent
Expand Down Expand Up @@ -58,7 +58,7 @@ module.exports = function info ({ agent, k8s, auth }) {
})

it('should reject requests with invalid audience', async function () {
const user = auth.createUser({ id, aud: [ 'invalid-audience' ] })
const user = auth.createUser({ id, aud: ['invalid-audience'] })
const res = await agent
.get('/api/info')
.set('cookie', await user.cookie)
Expand Down
45 changes: 45 additions & 0 deletions backend/test/acceptance/openapi.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright (c) 2019 by SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

'use strict'
const SwaggerParser = require('swagger-parser')

module.exports = function ({ agent, k8s, auth }) {
/* eslint no-unused-expressions: 0 */
const username = 'john.doe@example.org'
const id = username
const sandbox = sinon.createSandbox()

afterEach(function () {
sandbox.restore()
})

it('should fetch shoot openapi schema', async function () {
const user = auth.createUser({ id })
const bearer = await user.bearer
k8s.stub.getShootDefinition(bearer)
sandbox.stub(SwaggerParser, 'dereference').callsFake((obj) => {
return obj
})
const res = await agent
.get('/api/openapi')
.set('cookie', await user.cookie)

expect(res).to.have.status(200)
expect(res).to.be.json
expect(res.body).to.have.property('com.github.gardener.gardener.pkg.apis.core.v1beta1.Shoot').that.is.eql({ type: 'object' })
})
}
59 changes: 47 additions & 12 deletions backend/test/support/nocks/k8s.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,24 @@ function canGetSecretsInAllNamespaces (scope) {
})
}

function canGetOpenAPI (scope) {
return scope
.post('/apis/authorization.k8s.io/v1/selfsubjectaccessreviews', body => {
const { verb, path } = body.spec.nonResourceAttributes
return path === '/openapi/v2' && verb === 'get'
})
.reply(200, function (body) {
const [, token] = _.split(this.req.headers.authorization, ' ', 2)
const payload = jwt.decode(token)
const allowed = !_.isEmpty(payload.id)
return _.assign({
status: {
allowed
}
}, body)
})
}

function getKubeconfigSecret (scope, { namespace, name, server }) {
const url = new URL(server)
const secret = {
Expand Down Expand Up @@ -316,7 +334,7 @@ function getUser (name) {
}
}

function getProject ({ name, namespace, createdBy, owner, members = [], description, purpose, phase = 'Ready', costObject = "" }) {
function getProject ({ name, namespace, createdBy, owner, members = [], description, purpose, phase = 'Ready', costObject = '' }) {
owner = owner || createdBy
namespace = namespace || `garden-${name}`
members = _
Expand Down Expand Up @@ -1387,16 +1405,16 @@ const stub = {
const incomplete = false
if (_.endsWith(payload.id, 'example.org')) {
resourceRules = resourceRules.concat([{
verbs: ['get'],
apiGroups: ['core.gardener.cloud'],
resources: ['projects'],
resourceName: ['foo']
},
{
verbs: ['create'],
apiGroups: ['core.gardener.cloud'],
resources: ['projects']
}
verbs: ['get'],
apiGroups: ['core.gardener.cloud'],
resources: ['projects'],
resourceName: ['foo']
},
{
verbs: ['create'],
apiGroups: ['core.gardener.cloud'],
resources: ['projects']
}
])
} else {
resourceRules = resourceRules.concat([{
Expand All @@ -1405,7 +1423,7 @@ const stub = {
resources: ['projects'],
resourceName: ['foo']
}
])
])
}
return {
status: { resourceRules, nonResourceRules, incomplete }
Expand All @@ -1416,6 +1434,23 @@ const stub = {
const adminScope = nockWithAuthorization(auth.bearer)
reviewToken(adminScope)
return adminScope
},
getShootDefinition (bearer) {
const scope = nockWithAuthorization(bearer)
canGetOpenAPI(scope)
const body = {
definitions: {
'com.github.gardener.gardener.pkg.apis.core.v1beta1.Shoot': {
type: 'object'
}
}
}
return [
scope,
nockWithAuthorization(auth.bearer)
.get('/openapi/v2')
.reply(200, body)
]
}
}

Expand Down
Loading