mirror of https://github.com/rancher/dashboard.git
226 lines
5.1 KiB
Vue
226 lines
5.1 KiB
Vue
<script>
|
|
import { Card } from '@components/Card';
|
|
import AsyncButton from '@shell/components/AsyncButton';
|
|
import { Banner } from '@components/Banner';
|
|
|
|
import { RadioGroup } from '@components/Form/Radio';
|
|
import Select from '@shell/components/form/Select';
|
|
|
|
import { get, set } from '@shell/utils/object';
|
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
|
|
export default {
|
|
components: {
|
|
Select,
|
|
RadioGroup,
|
|
Card,
|
|
AsyncButton,
|
|
Banner
|
|
},
|
|
|
|
props: {
|
|
cluster: {
|
|
type: Object,
|
|
default: () => {
|
|
return {};
|
|
}
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
selectedService: '',
|
|
rotateAllServices: true,
|
|
errors: []
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
serviceOptions() {
|
|
if (this.cluster.isRke2) {
|
|
const options = [
|
|
'admin',
|
|
'api-server',
|
|
'controller-manager',
|
|
'scheduler',
|
|
'cloud-controller',
|
|
'etcd',
|
|
'auth-proxy',
|
|
'kubelet',
|
|
'kube-proxy'
|
|
];
|
|
|
|
if ( this.cluster.isK3s ) {
|
|
options.push('k3s-controller', 'k3s-server');
|
|
} else {
|
|
options.push('rke2-controller', 'rke2-server');
|
|
}
|
|
|
|
return options.sort();
|
|
}
|
|
// For RKE1 clusters, Norman provides the list of service options:
|
|
// type RotateCertificateInput struct {
|
|
// CACertificates bool `json:"caCertificates,omitempty"`
|
|
// Services []string `json:"services,omitempty" norman:"type=enum,options=etcd|kubelet|kube-apiserver|kube-proxy|kube-scheduler|kube-controller-manager"`
|
|
// }
|
|
const schema = this.$store.getters['rancher/schemaFor']('rotatecertificateinput');
|
|
|
|
return get(schema, 'resourceFields.services.options') || [];
|
|
},
|
|
|
|
actionParams() {
|
|
if (this.rotateAllServices) {
|
|
return {
|
|
// To rotate all services, RKE1 clusters need services
|
|
// to be null, while RKE2 requires an empty array.
|
|
services: this.cluster.isRke2 ? [] : null,
|
|
caCertificates: false,
|
|
};
|
|
} else {
|
|
return {
|
|
services: this.selectedService,
|
|
caCertificates: false,
|
|
};
|
|
}
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.selectedService = this.serviceOptions[0] || '';
|
|
},
|
|
|
|
methods: {
|
|
close() {
|
|
this.$emit('close');
|
|
},
|
|
|
|
async rotate(buttonDone) {
|
|
const params = this.actionParams;
|
|
|
|
try {
|
|
if (this.cluster.isRke2) {
|
|
// The Steve API doesn't support actions, so for RKE2 cluster cert rotation, we patch the cluster.
|
|
const currentGeneration = this.cluster.spec?.rkeConfig?.rotateCertificates?.generation || 0;
|
|
|
|
set(this.cluster, 'spec.rkeConfig.rotateCertificates', {
|
|
generation: currentGeneration + 1,
|
|
services: (this.selectedService && !this.rotateAllServices) ? [this.selectedService] : []
|
|
});
|
|
|
|
await this.cluster.save();
|
|
} else {
|
|
await this.cluster.mgmt.doAction('rotateCertificates', params);
|
|
}
|
|
buttonDone(true);
|
|
this.close();
|
|
} catch (err) {
|
|
this.errors = exceptionToErrorsArray(err);
|
|
buttonDone(false);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Card
|
|
class="prompt-rotate"
|
|
:show-highlight-border="false"
|
|
:style="{'height':'100%'}"
|
|
>
|
|
<template #title>
|
|
<h3>{{ t('cluster.rotateCertificates.modalTitle') }}</h3>
|
|
</template>
|
|
<template #body>
|
|
<Banner
|
|
v-for="(error, i) in errors"
|
|
:key="i"
|
|
class=""
|
|
color="error"
|
|
:label="error"
|
|
/>
|
|
<div class="options">
|
|
<RadioGroup
|
|
v-model="rotateAllServices"
|
|
name="service-mode"
|
|
:options="[
|
|
{
|
|
value: true,
|
|
label:t('cluster.rotateCertificates.allServices')
|
|
},
|
|
{
|
|
value: false,
|
|
label:t('cluster.rotateCertificates.selectService')
|
|
}
|
|
]"
|
|
/>
|
|
<Select
|
|
v-model="selectedService"
|
|
:options="serviceOptions"
|
|
class="service-select"
|
|
:class="{'invisible': rotateAllServices}"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<div
|
|
slot="actions"
|
|
class="buttons"
|
|
>
|
|
<button
|
|
class="btn role-secondary mr-20"
|
|
@click="close"
|
|
>
|
|
{{ t('generic.cancel') }}
|
|
</button>
|
|
<AsyncButton
|
|
mode="rotate"
|
|
:disabled="!rotateAllServices && !selectedService"
|
|
@click="rotate"
|
|
/>
|
|
</div>
|
|
</Card>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.prompt-rotate {
|
|
margin: 0;
|
|
}
|
|
.buttons {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
width: 100%;
|
|
}
|
|
.options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.service-select {
|
|
margin-left: 20px;
|
|
margin-top: 10px;
|
|
width: fit-content;
|
|
min-width: 260px;
|
|
}
|
|
}
|
|
|
|
.rotate-modal ::v-deep.v--modal-box{
|
|
border:none;
|
|
|
|
& .card-container{
|
|
margin: 0;
|
|
|
|
& .card-wrap {
|
|
display:flex;
|
|
flex-direction:column;
|
|
}
|
|
|
|
& .card-body {
|
|
flex: 1;
|
|
}
|
|
|
|
& .card-actions{
|
|
align-self: center
|
|
};
|
|
}
|
|
}
|
|
</style>
|