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

Managing redactors updates #817

Merged
merged 1 commit into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion kotsadm/pkg/handlers/redact.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func SetRedactMetadataAndYaml(w http.ResponseWriter, r *http.Request) {
newRedactor, err := redact.SetRedactYaml(redactorSlug, updateRedactRequest.Description, updateRedactRequest.Enabled, updateRedactRequest.New, []byte(updateRedactRequest.Redactor))
if err != nil {
logger.Error(err)
metadataResponse.Error = "failed to update redactor"
metadataResponse.Error = err.Error()
JSON(w, 400, metadataResponse)
return
}
Expand Down
20 changes: 8 additions & 12 deletions kotsadm/pkg/redact/redact.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import (
"encoding/json"
"fmt"
"os"
"regexp"
"sort"
"strings"
"time"

"github.com/gosimple/slug"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/util"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
Expand Down Expand Up @@ -245,19 +244,19 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim

redactorEntry := RedactorMetadata{}
redactString, ok := data[slug]

if !ok || newRedact {
// if name is not set in yaml, autogenerate a name
// if name is not set in yaml throw error
// if name is set, create the slug from the name
if newRedactorSpec.Name == "" {
newRedactorSpec.Name = fmt.Sprintf("redactor-%d", len(data)+1)
slug = getSlug(newRedactorSpec.Name)
return nil, nil, fmt.Errorf("failed to create new redact spec: name can't be empty")
} else {
slug = getSlug(newRedactorSpec.Name)
}

if _, ok := data[slug]; ok {
// the target slug already exists - this is an error
return nil, nil, fmt.Errorf("refusing to create new redact spec with name %s - slug %s already exists", newRedactorSpec.Name, slug)
return nil, nil, fmt.Errorf("failed to create new redact spec: name %s - slug %s already exists", newRedactorSpec.Name, slug)
}

// create the new redactor
Expand All @@ -278,7 +277,7 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim

if _, ok := data[getSlug(newRedactorSpec.Name)]; ok {
// the target slug already exists - this is an error
return nil, nil, fmt.Errorf("refusing to change slug from %s to %s as that already exists", slug, getSlug(newRedactorSpec.Name))
return nil, nil, fmt.Errorf("failed to update redact spec: refusing to change slug from %s to %s as that already exists", slug, getSlug(newRedactorSpec.Name))
}

delete(data, slug)
Expand All @@ -288,8 +287,7 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim
}

if newRedactorSpec.Name == "" {
newRedactorSpec.Name = slug
redactorEntry.Metadata.Name = slug
return nil, nil, fmt.Errorf("failed to update redact spec: name can't be empty")
}
}

Expand Down Expand Up @@ -386,9 +384,7 @@ func writeConfigmap(configMap *v1.ConfigMap) (*v1.ConfigMap, error) {
}

func getSlug(name string) string {
name = strings.ReplaceAll(name, " ", "-")

name = regexp.MustCompile(`[^\w\d-_]`).ReplaceAllString(name, "")
name = slug.Make(name)

if name == "kotsadm-redact" {
name = "kotsadm-redact-metadata"
Expand Down
6 changes: 3 additions & 3 deletions kotsadm/pkg/redact/redact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func Test_getSlug(t *testing.T) {
{
name: "all alphanumeric",
input: "aBC123",
want: "aBC123",
want: "abc123",
},
{
name: "dashes",
Expand All @@ -34,9 +34,9 @@ func Test_getSlug(t *testing.T) {
want: "abc-123",
},
{
name: "aymbols",
name: "symbols",
input: "abc%^123!@#",
want: "abc123",
want: "abc-123-at",
},
{
name: "kotsadm-redact",
Expand Down
70 changes: 44 additions & 26 deletions kotsadm/web/src/components/redactors/EditRedactor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import "brace/theme/chrome";
import Loader from "../shared/Loader";
import { Utilities } from "../../utilities/utilities";

import "../../scss/components/redactors/EditRedactor.scss"

class EditRedactor extends Component {
state = {
redactorEnabled: false,
Expand Down Expand Up @@ -71,7 +73,6 @@ class EditRedactor extends Component {
this.setState({ editingRedactor: true, editingErrMsg: "" });

const payload = {
slug: slug,
enabled: enabled,
redactor: yaml
}
Expand Down Expand Up @@ -106,6 +107,7 @@ class EditRedactor extends Component {
setTimeout(() => {
this.setState({ editConfirm: false })
}, 3000);
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors`)
} else {
this.setState({
editingRedactor: false,
Expand All @@ -121,6 +123,22 @@ class EditRedactor extends Component {
});
}

getEmptyNameLine = (redactorYaml) => {
const splittedYaml = redactorYaml.split("\n");
let metadataFound = false;
let namePosition;
for(let i=0; i<splittedYaml.length; ++i) {
if (splittedYaml[i] === "metadata:") {
metadataFound = true;
}
if (metadataFound && splittedYaml[i].includes("name:")) {
namePosition = i + 1;
break;
}
}
return namePosition;
}

createRedactor = (enabled, newRedactor, yaml) => {
this.setState({ creatingRedactor: true, createErrMsg: "" });

Expand All @@ -144,8 +162,10 @@ class EditRedactor extends Component {
this.setState({
creatingRedactor: false,
createErrMsg: createResponse.error
})
return;
});
const editor = this.aceEditor.editor;
editor.scrollToLine(this.getEmptyNameLine(this.state.redactorYaml), true, true);
editor.gotoLine(this.getEmptyNameLine(this.state.redactorYaml), 1, true);
}

if (createResponse.success) {
Expand All @@ -160,7 +180,7 @@ class EditRedactor extends Component {
setTimeout(() => {
this.setState({ createConfirm: false })
}, 3000);
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors/${createResponse.redactorMetadata.slug}`)
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors`)
} else {
this.setState({
creatingRedactor: false,
Expand Down Expand Up @@ -189,7 +209,7 @@ class EditRedactor extends Component {
const defaultYaml = `kind: Redactor
apiVersion: troubleshoot.replicated.com/v1beta1
metadata:
name: kotsadm-redact
name:
spec:
redactors:
- name: myredactor
Expand All @@ -199,7 +219,7 @@ spec:
removals:
values:
- "removethis"`
this.setState({ redactorEnabled: false, redactorYaml: defaultYaml, redactorName: "New redactor" });
this.setState({ redactorEnabled: true, redactorYaml: defaultYaml, redactorName: "New redactor" });
}
}

Expand Down Expand Up @@ -233,39 +253,38 @@ spec:
<title>Redactors</title>
</Helmet>
<div className="Redactors--wrapper flex1 flex-column u-width--full">
{(createErrMsg || editingErrMsg) && <p className="ErrorToast flex justifyContent--center alignItems--center">{createErrMsg ? createErrMsg : editingErrMsg}</p>}
<div className="u-fontSize--small u-fontWeight--medium u-color--dustyGray u-marginBottom--20">
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors`} className="replicated-link u-marginRight--5">Redactors</Link> > <span className="u-marginLeft--5">{this.state.redactorName}</span>
</div>
<div className="flex flex-auto alignItems--flexStart justifyContent--spaceBetween">
<div className="flex flex1 alignItems--center">
<p className="u-fontWeight--bold u-color--tuna u-fontSize--jumbo u-lineHeight--normal u-marginRight--10">{this.state.redactorName}</p>
</div>
{!this.props.isNew &&
<div className="flex justifyContent--flexEnd">
<div className="toggle flex flex1">
<div className="flex flex1">
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
type="checkbox"
className="Checkbox-toggle"
name="isRedactorEnabled"
checked={this.state.redactorEnabled}
onChange={(e) => { this.handleEnableRedactor(e) }}
/>
</div>
</div>
<div className="flex flex1 u-marginLeft--5">
<p className="u-fontWeight--medium u-color--tundora u-fontSize--large alignSelf--center">{this.state.redactorEnabled ? "Enabled" : "Disabled"}</p>
<div className="flex justifyContent--flexEnd">
<div className="toggle flex flex1">
<div className="flex flex1">
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
type="checkbox"
className="Checkbox-toggle"
name="isRedactorEnabled"
checked={this.state.redactorEnabled}
onChange={(e) => { this.handleEnableRedactor(e) }}
/>
</div>
</div>
<div className="flex flex1 u-marginLeft--5">
<p className="u-fontWeight--medium u-color--tundora u-fontSize--large alignSelf--center">{this.state.redactorEnabled ? "Enabled" : "Disabled"}</p>
</div>
</div>
}
</div>
</div>
<p className="u-fontSize--normal u-color--dustyGray u-fontWeight--medium u-lineHeight--normal u-marginTop--small">For more information about creating redactors,
<a href="https://troubleshoot.sh/reference/redactors/overview/" target="_blank" rel="noopener noreferrer" className="replicated-link"> check out our docs</a>.</p>
<div className="flex1 u-marginTop--30 u-border--gray">
<AceEditor
ref={(input) => this.refAceEditor = input}
ref={el => (this.aceEditor = el)}
mode="yaml"
theme="chrome"
className="flex1 flex"
Expand All @@ -287,7 +306,7 @@ spec:
</div>
<div className="flex u-marginTop--20 justifyContent--spaceBetween">
<div className="flex">
<Link to="/redactors" className="btn secondary"> Cancel </Link>
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors`} className="btn secondary"> Cancel </Link>
</div>
<div className="flex alignItems--center">
{createConfirm || editConfirm &&
Expand All @@ -296,7 +315,6 @@ spec:
<span className="u-marginLeft--5 u-fontSize--small u-fontWeight--medium u-color--chateauGreen">{createConfirm ? "Redactor created" : "Redactor updated"}</span>
</div>
}
{(createErrMsg || editingErrMsg) && <p className="u-color--chestnut u-fontSize--normal u-fontWeight--medium u-lineHeight--normal u-marginRight--10">{createErrMsg ? createErrMsg : editingErrMsg}</p>}
<button type="button" className="btn primary blue" onClick={this.onSaveRedactor} disabled={creatingRedactor || editingRedactor}>{(creatingRedactor || editingRedactor) ? "Saving" : "Save redactor"} </button>
</div>
</div>
Expand Down
10 changes: 6 additions & 4 deletions kotsadm/web/src/components/redactors/RedactorRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import React from "react";
import dayjs from "dayjs";
import { Link } from "react-router-dom"

import { Utilities } from "../../utilities/utilities";

class RedactorRow extends React.Component {
state = {
redactorEnabled: false
redactorEnabled: false,
};

handleEnableRedactor = () => {
this.setState({
redactorEnabled: !this.state.redactorEnabled,
this.setState({ redactorEnabled: !this.state.redactorEnabled }, () => {
this.props.handleSetRedactEnabled(this.props.redactor, this.state.redactorEnabled);
});
}

Expand Down Expand Up @@ -37,7 +39,7 @@ class RedactorRow extends React.Component {
<p className="u-fontSize--small u-fontWeight--medium u-lineHeight--normal u-color--nevada u-marginLeft--10"> {redactor?.description} </p>
</div>
<div className="flex alignItems--center">
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors/${redactor?.slug}`} className="u-fontSize--normal u-fontWeight--medium u-color--royalBlue u-textDecoration--underlineOnHover u-marginRight--20">Edit redactor</Link>
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors/${redactor?.slug}`} className="u-fontSize--normal u-fontWeight--medium u-color--royalBlue u-textDecoration--underlineOnHover u-marginRight--20">Edit</Link>
<span className="u-fontSize--normal u-fontWeight--medium u-color--chestnut u-textDecoration--underlineOnHover u-marginRight--20" onClick={() => this.handleDeleteClick(redactor)}>Delete</span>
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
Expand Down
54 changes: 48 additions & 6 deletions kotsadm/web/src/components/redactors/Redactors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import Loader from "../shared/Loader";

import { Utilities } from "../../utilities/utilities";


class Redactors extends Component {
state = {
redactors: [],
sortedRedactors: [],
selectedOption: {
value: "createdAt",
label: "Sort by: Created At"
value: "enabled",
label: "Sort by: Status"
},
deleteRedactorModal: false,
redactorToDelete: {},
isLoadingRedactors: false,
redactorsErrMsg: "",
deletingRedactor: false,
deleteErrMsg: ""
deleteErrMsg: "",
enablingRedactorMsg: ""
};

getRedactors = () => {
Expand Down Expand Up @@ -81,8 +81,10 @@ class Redactors extends Component {
sortRedactors = value => {
if (value === "createdAt") {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => dayjs(b.createdAt) - dayjs(a.createdAt)) });
} else {
} else if (value === "updatedAt") {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => dayjs(b.updatedAt) - dayjs(a.updatedAt)) });
} else {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => (a.enabled === b.enabled) ? 0 : a.enabled ? -1 : 1 )});
}
}

Expand Down Expand Up @@ -121,9 +123,42 @@ class Redactors extends Component {
});
}

handleSetRedactEnabled = (redactor, redactorEnabled) => {
const payload = {
enabled: redactorEnabled
}
this.setState({ enablingRedactorMsg: "" });
fetch(`${window.env.API_ENDPOINT}/redact/enabled/${redactor.slug}`, {
method: "POST",
headers: {
"Authorization": Utilities.getToken(),
"Content-Type": "application/json",
},
body: JSON.stringify(payload)
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) {
this.setState({ enablingRedactorMsg: response.error });
return;
}
if (response.success) {
this.setState({ enablingRedactorMsg: "" });
} else {
this.setState({ enablingRedactorMsg: response.error });
}
})
.catch((err) => {
this.setState({
enablingRedactorMsg: err.message ? err.message : "Something went wrong, please try again!"
});
});
}



render() {
const { sortedRedactors, selectedOption, deleteRedactorModal, isLoadingRedactors } = this.state;
const { sortedRedactors, selectedOption, deleteRedactorModal, isLoadingRedactors, enablingRedactorMsg } = this.state;

if (isLoadingRedactors) {
return (
Expand All @@ -134,6 +169,10 @@ class Redactors extends Component {
}

const selectOptions = [
{
value: "enabled",
label: "Sort by: Status"
},
{
value: "createdAt",
label: "Sort by: Created At"
Expand Down Expand Up @@ -189,12 +228,15 @@ class Redactors extends Component {
</div>
<p className="u-fontSize--normal u-color--dustyGray u-fontWeight--medium u-lineHeight--normal u-marginTop--20 u-marginBottom--30">Define custom rules for sensitive values you need to be redacted when gathering a support bundle. This might include things like Secrets or IP addresses. For help with creating custom redactors,
<a href="https://troubleshoot.sh/reference/redactors/overview/" target="_blank" rel="noopener noreferrer" className="replicated-link"> check out our docs</a>.</p>
{enablingRedactorMsg && <p className="u-color--chestnut u-fontSize--normal u-fontWeight--medium u-lineHeight--normal u-marginBottom--10 flex justifyContent--center alignItems--center">{enablingRedactorMsg}</p>}
{sortedRedactors?.map((redactor) => (
<RedactorRow
key={`redactor-${redactor.slug}`}
redactor={redactor}
appSlug={this.props.appSlug}
toggleConfirmDeleteModal={this.toggleConfirmDeleteModal}
handleSetRedactEnabled={this.handleSetRedactEnabled}

/>
))}
</div>
Expand Down