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

Implemented Disable Gardener provided static admin kubeconfig #1249

Merged
merged 10 commits into from
Aug 16, 2022
13 changes: 13 additions & 0 deletions backend/lib/routes/shoots.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ router.route('/:name')
}
})

router.route('/:name/spec/kubernetes/enableStaticTokenKubeconfig')
.put(async (req, res, next) => {
try {
const user = req.user
const namespace = req.params.namespace
const name = req.params.name
const body = req.body
res.send(await shoots.replaceEnableStaticTokenKubeconfig({ user, namespace, name, body }))
} catch (err) {
next(err)
}
})

router.route('/:name/spec/kubernetes/version')
.put(async (req, res, next) => {
try {
Expand Down
11 changes: 11 additions & 0 deletions backend/lib/services/shoots.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ exports.replaceVersion = async function ({ user, namespace, name, body }) {
return client['core.gardener.cloud'].shoots.jsonPatch(namespace, name, patchOperations)
}

exports.replaceEnableStaticTokenKubeconfig = async function ({ user, namespace, name, body }) {
const client = user.client
const enableStaticTokenKubeconfig = body.enableStaticTokenKubeconfig === true
const patchOperations = [{
op: 'replace',
path: '/spec/kubernetes/enableStaticTokenKubeconfig',
value: enableStaticTokenKubeconfig
}]
return client['core.gardener.cloud'].shoots.jsonPatch(namespace, name, patchOperations)
}

exports.replaceHibernationEnabled = async function ({ user, namespace, name, body }) {
const client = user.client
const enabled = !!body.enabled
Expand Down
56 changes: 56 additions & 0 deletions backend/test/acceptance/__snapshots__/api.shoots.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,62 @@ Object {
}
`;

exports[`api shoots should replace shoot kubernetes enableStaticTokenKubeconfig 1`] = `
Array [
Array [
Object {
":authority": "kubernetes:6443",
":method": "patch",
":path": "/apis/core.gardener.cloud/v1beta1/namespaces/garden-foo/shoots/barShoot",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
"content-type": "application/json-patch+json",
},
Array [
Object {
"op": "replace",
"path": "/spec/kubernetes/enableStaticTokenKubeconfig",
"value": true,
},
],
],
]
`;

exports[`api shoots should replace shoot kubernetes enableStaticTokenKubeconfig 2`] = `
Object {
"metadata": Object {
"annotations": Object {
"gardener.cloud/created-by": "[email protected]",
},
"name": "barShoot",
"namespace": "garden-foo",
"resourceVersion": "43",
"uid": 2,
},
"spec": Object {
"cloudProfileName": "infra1-profileName",
"hibernation": Object {
"enabled": false,
},
"kubernetes": Object {
"enableStaticTokenKubeconfig": true,
"version": "1.16.0",
},
"provider": Object {
"type": "fooInfra",
},
"purpose": "barPurpose",
"region": "foo-west",
"secretBindingName": "foo-infra1",
"seedName": "infra1-seed",
},
"status": Object {
"technicalID": "shoot--foo--barShoot",
},
}
`;

exports[`api shoots should replace shoot kubernetes version 1`] = `
Array [
Array [
Expand Down
18 changes: 18 additions & 0 deletions backend/test/acceptance/api.shoots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,24 @@ describe('api', function () {
expect(res.body).toMatchSnapshot()
})

it('should replace shoot kubernetes enableStaticTokenKubeconfig', async function () {
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.patch())

const res = await agent
.put(`/api/namespaces/${namespace}/shoots/${name}/spec/kubernetes/enableStaticTokenKubeconfig`)
.set('cookie', await user.cookie)
.send({
enableStaticTokenKubeconfig: true
})
.expect('content-type', /json/)
.expect(200)

expect(mockRequest).toBeCalledTimes(1)
expect(mockRequest.mock.calls).toMatchSnapshot()

expect(res.body).toMatchSnapshot()
})

it('should replace shoot maintenance data', async function () {
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.patch())

Expand Down
18 changes: 14 additions & 4 deletions frontend/src/components/NewShoot/NewShootDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ SPDX-License-Identifier: Apache-2.0
></purpose>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<static-token-kubeconfig-switch v-model="enableStaticTokenKubeconfig"></static-token-kubeconfig-switch>
</v-col>
</v-row>
<v-row v-if="slaDescriptionHtml">
<v-col cols="12">
<label>{{slaTitle}}</label>
Expand All @@ -80,6 +85,7 @@ import asyncRef from '@/mixins/asyncRef'

import { getValidationErrors, transformHtml, setDelayedInputFocus } from '@/utils'
import { resourceName, noStartEndHyphen, noConsecutiveHyphen } from '@/utils/validators'
import StaticTokenKubeconfigSwitch from '@/components/StaticTokenKubeconfigSwitch'

const Purpose = () => import('@/components/Purpose')

Expand All @@ -101,7 +107,8 @@ export default {
name: 'new-shoot-details',
components: {
HintColorizer,
Purpose
Purpose,
StaticTokenKubeconfigSwitch
},
mixins: [
asyncRef('purpose')
Expand All @@ -122,7 +129,8 @@ export default {
purposeValid: false,
cloudProfileName: undefined,
secret: undefined,
updateK8sMaintenance: undefined
updateK8sMaintenance: undefined,
enableStaticTokenKubeconfig: undefined
}
},
validations () {
Expand Down Expand Up @@ -236,15 +244,17 @@ export default {
return {
name: this.name,
kubernetesVersion: this.kubernetesVersion,
purpose: this.purpose
purpose: this.purpose,
enableStaticTokenKubeconfig: this.enableStaticTokenKubeconfig
}
},
async setDetailsData ({ name, kubernetesVersion, purpose, cloudProfileName, secret, updateK8sMaintenance }) {
async setDetailsData ({ name, kubernetesVersion, purpose, cloudProfileName, secret, updateK8sMaintenance, enableStaticTokenKubeconfig }) {
this.name = name
this.cloudProfileName = cloudProfileName
this.secret = secret
this.kubernetesVersion = kubernetesVersion
this.updateK8sMaintenance = updateK8sMaintenance
this.enableStaticTokenKubeconfig = enableStaticTokenKubeconfig

await this.$purpose.dispatch('setPurpose', purpose)

Expand Down
23 changes: 17 additions & 6 deletions frontend/src/components/ShootDetails/ShootAccessCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ SPDX-License-Identifier: Apache-2.0
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Kubeconfig</v-list-item-title>
<v-list-item-subtitle v-if="!shootEnableStaticTokenKubeconfig">Static token kubeconfig is disabled for this cluster</v-list-item-subtitle>
<v-list-item-subtitle v-else-if="!isKubeconfigAvailable">Static token kubeconfig currently not available</v-list-item-subtitle>
petersutter marked this conversation as resolved.
Show resolved Hide resolved
</v-list-item-content>
<v-list-item-action class="mx-0">
<v-list-item-action class="mx-0" v-if="isKubeconfigAvailable">
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn v-on="on" icon @click.native.stop="onDownload" color="action-button">
Expand All @@ -111,10 +113,10 @@ SPDX-License-Identifier: Apache-2.0
<span>Download Kubeconfig</span>
</v-tooltip>
</v-list-item-action>
<v-list-item-action class="mx-0">
<v-list-item-action class="mx-0" v-if="isKubeconfigAvailable">
<copy-btn :clipboard-text="kubeconfig"></copy-btn>
</v-list-item-action>
<v-list-item-action class="mx-0">
<v-list-item-action class="mx-0" v-if="isKubeconfigAvailable">
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn v-on="on" icon @click.native.stop="expansionPanelKubeconfig = !expansionPanelKubeconfig" color="action-button">
Expand All @@ -124,6 +126,9 @@ SPDX-License-Identifier: Apache-2.0
<span>{{kubeconfigVisibilityTitle}}</span>
</v-tooltip>
</v-list-item-action>
<v-list-item-action class="mx-0">
<static-token-kubeconfig-configuration :shootItem="shootItem"></static-token-kubeconfig-configuration>
</v-list-item-action>
</v-list-item>
<v-expand-transition>
<v-card v-if="expansionPanelKubeconfig" flat>
Expand All @@ -145,6 +150,7 @@ import TerminalListTile from '@/components/TerminalListTile'
import TerminalShortcutsTile from '@/components/ShootDetails/TerminalShortcutsTile'
import GardenctlCommands from '@/components/ShootDetails/GardenctlCommands'
import LinkListTile from '@/components/LinkListTile'
import StaticTokenKubeconfigConfiguration from '@/components/StaticTokenKubeconfigConfiguration'
import get from 'lodash/get'
import isEmpty from 'lodash/isEmpty'
import download from 'downloadjs'
Expand All @@ -159,7 +165,8 @@ export default {
TerminalListTile,
LinkListTile,
GardenctlCommands,
TerminalShortcutsTile
TerminalShortcutsTile,
StaticTokenKubeconfigConfiguration
},
props: {
hideTerminalShortcuts: {
Expand All @@ -179,7 +186,8 @@ export default {
'hasShootTerminalAccess',
'isAdmin',
'hasControlPlaneTerminalAccess',
'isTerminalShortcutsFeatureEnabled'
'isTerminalShortcutsFeatureEnabled',
'canPatchShoots'
]),
...mapState([
'cfg'
Expand Down Expand Up @@ -250,9 +258,12 @@ export default {
isCredentialsTileVisible () {
return !!this.username && !!this.password
},
isKubeconfigTileVisible () {
isKubeconfigAvailable () {
return !!this.kubeconfig
},
isKubeconfigTileVisible () {
return this.isKubeconfigAvailable || this.canPatchShoots
},
isGardenctlTileVisible () {
return this.isAdmin
},
Expand Down
74 changes: 74 additions & 0 deletions frontend/src/components/StaticTokenKubeconfigConfiguration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!--
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors

SPDX-License-Identifier: Apache-2.0
-->

<template>
<action-button-dialog
:shoot-item="shootItem"
@dialog-opened="onConfigurationDialogOpened"
ref="actionDialog"
width="450"
confirm-required
caption="Configure Admin Kubeconfig">
<template v-slot:actionComponent>
<static-token-kubeconfig-switch
v-model="enableStaticTokenKubeconfig"
></static-token-kubeconfig-switch>
</template>
</action-button-dialog>
</template>

<script>
import ActionButtonDialog from '@/components/dialogs/ActionButtonDialog'
import StaticTokenKubeconfigSwitch from '@/components/StaticTokenKubeconfigSwitch'
import { updateShootEnableStaticTokenKubeconfig } from '@/utils/api'
import { errorDetailsFromError } from '@/utils/error'
import shootItem from '@/mixins/shootItem'

export default {
name: 'static-token-kubeconfig-configuration',
components: {
ActionButtonDialog,
StaticTokenKubeconfigSwitch
},
mixins: [
shootItem
],
data () {
return {
enableStaticTokenKubeconfig: this.shootEnableStaticTokenKubeconfig
}
},
methods: {
async onConfigurationDialogOpened () {
this.reset()
const confirmed = await this.$refs.actionDialog.waitForDialogClosed()
if (confirmed) {
await this.updateConfiguration()
}
},
async updateConfiguration () {
try {
await updateShootEnableStaticTokenKubeconfig({
namespace: this.shootNamespace,
grolu marked this conversation as resolved.
Show resolved Hide resolved
name: this.shootName,
data: {
enableStaticTokenKubeconfig: this.enableStaticTokenKubeconfig
}
})
} catch (err) {
const errorMessage = 'Could not update static kubeconfig flag'
const errorDetails = errorDetailsFromError(err)
const detailedErrorMessage = errorDetails.detailedMessage
this.$refs.actionDialog.setError({ errorMessage, detailedErrorMessage })
console.error(this.errorMessage, errorDetails.errorCode, errorDetails.detailedMessage, err)
}
},
reset () {
this.enableStaticTokenKubeconfig = this.shootEnableStaticTokenKubeconfig
}
}
}
</script>
45 changes: 45 additions & 0 deletions frontend/src/components/StaticTokenKubeconfigSwitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!--
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors

SPDX-License-Identifier: Apache-2.0
-->

<template>
<div>
<v-checkbox
v-model="enableStaticTokenKubeconfig"
label="Enable static token kubeconfig"
grolu marked this conversation as resolved.
Show resolved Hide resolved
color="primary"
hide-details
></v-checkbox>
<div class="text-caption mt-1">There are more <external-link class="text-caption" url="https://github.com/gardener/gardener/blob/master/docs/usage/shoot_access.md">secure alternatives</external-link> to access the Shoot cluster.</div>
</div>
</template>
<script>

import ExternalLink from '@/components/ExternalLink.vue'

export default {
name: 'static-token-kubeconfig-switch',
components: {
ExternalLink
},
props: {
value: {
type: Boolean,
default: false
}
},
computed: {
enableStaticTokenKubeconfig: {
get () {
return this.value
},
set (val) {
this.$emit('input', val)
}
}
}

}
</script>
3 changes: 3 additions & 0 deletions frontend/src/mixins/shootItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ export const shootItem = {
shootK8sVersion () {
return get(this.shootSpec, 'kubernetes.version')
},
shootEnableStaticTokenKubeconfig () {
return get(this.shootSpec, 'kubernetes.enableStaticTokenKubeconfig', true)
},
shootCloudProfileName () {
return this.shootSpec.cloudProfileName
},
Expand Down
Loading