import "./add-secret-dialog.scss" import React from "react"; import { observable } from "mobx"; import { observer } from "mobx-react"; import { t, Trans } from "@lingui/macro"; import { _i18n } from "../../i18n"; import { Dialog, DialogProps } from "../dialog"; import { Wizard, WizardStep } from "../wizard"; import { Input } from "../input"; import { systemName } from "../input/input.validators"; import { Secret, secretsApi, SecretType } from "../../api/endpoints"; import { SubTitle } from "../layout/sub-title"; import { NamespaceSelect } from "../+namespaces/namespace-select"; import { Select, SelectOption } from "../select"; import { Icon } from "../icon"; import { IKubeObjectMetadata } from "../../api/kube-object"; import { base64 } from "../../utils"; import { Notifications } from "../notifications"; import { showDetails } from "../../navigation"; import upperFirst from "lodash/upperFirst"; interface Props extends Partial { } interface ISecretTemplateField { key: string; value?: string; required?: boolean; } interface ISecretTemplate { [field: string]: ISecretTemplateField[]; annotations?: ISecretTemplateField[]; labels?: ISecretTemplateField[]; data?: ISecretTemplateField[]; } type ISecretField = keyof ISecretTemplate; @observer export class AddSecretDialog extends React.Component { @observable static isOpen = false; static open() { AddSecretDialog.isOpen = true; } static close() { AddSecretDialog.isOpen = false; } private secretTemplate: { [p: string]: ISecretTemplate } = { [SecretType.Opaque]: {}, [SecretType.ServiceAccountToken]: { annotations: [ { key: "kubernetes.io/service-account.name", required: true }, { key: "kubernetes.io/service-account.uid", required: true } ], }, } get types() { return Object.keys(this.secretTemplate) as SecretType[]; } @observable secret = this.secretTemplate; @observable name = ""; @observable namespace = "default"; @observable type = SecretType.Opaque; reset = () => { this.name = ""; this.secret = this.secretTemplate; } close = () => { AddSecretDialog.close(); } private getDataFromFields = (fields: ISecretTemplateField[] = [], processValue?: (val: string) => string) => { return fields.reduce((data, field) => { const { key, value } = field; if (key) { data[key] = processValue ? processValue(value) : value; } return data; }, {}) } createSecret = async () => { const { name, namespace, type } = this; const { data = [], labels = [], annotations = [] } = this.secret[type]; const secret: Partial = { type: type, data: this.getDataFromFields(data, val => val ? base64.encode(val) : ""), metadata: { name: name, namespace: namespace, annotations: this.getDataFromFields(annotations), labels: this.getDataFromFields(labels), } as IKubeObjectMetadata } try { const newSecret = await secretsApi.create({ namespace, name }, secret); showDetails(newSecret.selfLink); this.reset(); this.close(); } catch (err) { Notifications.error(err); } } addField = (field: ISecretField) => { const fields = this.secret[this.type][field] || []; fields.push({ key: "", value: "" }); this.secret[this.type][field] = fields; } removeField = (field: ISecretField, index: number) => { const fields = this.secret[this.type][field] || []; fields.splice(index, 1); } renderFields(field: ISecretField) { const fields = this.secret[this.type][field] || []; return ( <> this.addField(field)} />
{fields.map((item, index) => { const { key = "", value = "", required } = item; return (
item.key = v} /> item.value = v} /> Required field : Remove field} className="remove-icon" material="remove_circle_outline" onClick={() => this.removeField(field, index)} />
) })}
) } render() { const { ...dialogProps } = this.props; const { namespace, name, type } = this; const header =
Create Secret
; return ( Create} next={this.createSecret}>
Secret name}/> this.name = v} />
Namespace}/> this.namespace = value} />
Secret type}/>