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

Datanode migration telemetry #20225

Merged
merged 17 commits into from
Sep 6, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/unreleased/issue-20086.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type="a"
message="Datanode migration telemetry"

issues=["20086"]
pulls=["20225"]
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,53 @@ import { Tabs, Tab, Alert } from 'components/bootstrap';
import CACreateForm from 'components/datanode/DataNodeConfiguration/CACreateForm';
import CAUpload from 'components/datanode/DataNodeConfiguration/CAUpload';
import DocumentationLink from 'components/support/DocumentationLink';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

const StyledAlert = styled(Alert)`
margin-top: 10px;
margin-bottom: 10px;
`;

const TAB_KEYS = ['create', 'upload'];
const CAConfiguration = () => (
<>
<h2>Configure Certificate Authority</h2>
<p>
In this step you can either upload or create a new certificate authority.<br />
The certificate authority will provision and manage certificates for your Data Nodes more easily.
</p>
<StyledAlert bsStyle="info" title="Reusing certificates">
If your existing cluster uses certificates, by default these will get replaced with the Graylog CA
and automatically generated certificates during provisioning of the data nodes in the next step.
If you want to include your own CA, you can upload an existing certificate.
Please see <DocumentationLink page="graylog-data-node" text="Graylog Data Node - Getting Started" /> for more information.
</StyledAlert>
<Tabs defaultActiveKey={TAB_KEYS[0]} id="ca-configurations">
<Tab eventKey={TAB_KEYS[0]} title="Create new CA">
<CACreateForm />
</Tab>
<Tab eventKey={TAB_KEYS[1]} title="Upload CA">
<CAUpload />
</Tab>
</Tabs>
</>
);

const UploadCA = 'Upload CA';

const CAConfiguration = () => {
const sendTelemetry = useSendTelemetry();

const handleTabSwitch = (e) => {
sendTelemetry((e?.target?.innerText === UploadCA)
? TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_UPLOAD_TAB_CLICKED
: TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_CREATE_TAB_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});
};

return (
<>
<h2>Configure Certificate Authority</h2>
<p>
In this step you can either upload or create a new certificate authority.<br />
The certificate authority will provision and manage certificates for your Data Nodes more easily.
</p>
<StyledAlert bsStyle="info" title="Reusing certificates">
If your existing cluster uses certificates, by default these will get replaced with the Graylog CA
and automatically generated certificates during provisioning of the data nodes in the next step.
If you want to include your own CA, you can upload an existing certificate.
Please see <DocumentationLink page="graylog-data-node" text="Graylog Data Node - Getting Started" /> for more information.
</StyledAlert>
<Tabs defaultActiveKey={TAB_KEYS[0]} id="ca-configurations" onClick={handleTabSwitch}>
<Tab eventKey={TAB_KEYS[0]} title="Create new CA">
<CACreateForm />
</Tab>
<Tab eventKey={TAB_KEYS[1]} title={UploadCA}>
<CAUpload />
</Tab>
</Tabs>
</>
);
};

export default CAConfiguration;
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { FormikInput } from 'components/common';
import { Button } from 'components/bootstrap';
import { QUERY_KEY as DATA_NODES_CA_QUERY_KEY } from 'components/datanode/hooks/useDataNodesCA';
import { MIGRATION_STATE_QUERY_KEY } from 'components/datanode/hooks/useMigrationState';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

type FormValues = {
organization: string
Expand All @@ -39,6 +41,7 @@ const createCA = (caData: FormValues) => fetch(

const CaCreateForm = () => {
const queryClient = useQueryClient();
const sendTelemetry = useSendTelemetry();

const { mutateAsync: onCreateCA } = useMutation(createCA, {
onSuccess: () => {
Expand All @@ -50,7 +53,15 @@ const CaCreateForm = () => {
UserNotification.error(`CA creation failed with error: ${error}`);
},
});
const onSubmit = (formValues: FormValues) => onCreateCA(formValues).catch(() => {});

const onSubmit = (formValues: FormValues) => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_CREATE_CA_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

return onCreateCA(formValues).catch(() => {});
};

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { qualifyUrl } from 'util/URLUtils';
import { QUERY_KEY as DATA_NODES_CA_QUERY_KEY } from 'components/datanode/hooks/useDataNodesCA';
import UnsecureConnectionAlert from 'preflight/components/ConfigurationWizard/UnsecureConnectionAlert';
import { MIGRATION_STATE_QUERY_KEY } from 'components/datanode/hooks/useMigrationState';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

type FormValues = {
files?: Array<File>,
Expand Down Expand Up @@ -95,6 +97,7 @@ const Explanation = styled.p`

const CAUpload = () => {
const queryClient = useQueryClient();
const sendTelemetry = useSendTelemetry();
const onRejectUpload = useCallback(() => {
UserNotification.error('CA upload failed');
}, []);
Expand All @@ -110,7 +113,14 @@ const CAUpload = () => {
},
});

const onSubmit = useCallback((formValues: FormValues) => onProcessUpload(formValues).catch(() => {}), [onProcessUpload]);
const onSubmit = useCallback((formValues: FormValues) => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_UPLOAD_CA_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

return onProcessUpload(formValues).catch(() => {});
}, [onProcessUpload, sendTelemetry]);

return (
<Formik<FormValues> initialValues={{}} onSubmit={onSubmit} validate={validate}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import { ConfigurationType } from 'components/configurations/ConfigurationTypes'
import { IfPermitted, TimeUnitInput, Spinner } from 'components/common';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import Select from 'components/common/Select';
import useLocation from 'routing/useLocation';
import { getPathnameWithoutId } from 'util/URLUtils';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';
import { MIGRATION_STATE_QUERY_KEY } from 'components/datanode/hooks/useMigrationState';

Expand Down Expand Up @@ -121,7 +119,6 @@ const CertificateRenewalPolicyConfig = ({ className }: Props) => {
const { data: currentConfig, isLoading } = useQuery(queryKey, fetchCurrentConfig);

const sendTelemetry = useSendTelemetry();
const { pathname } = useLocation();
const queryClient = useQueryClient();

const { mutateAsync: updateConfig } = useMutation(handleSaveConfig, {
Expand Down Expand Up @@ -166,10 +163,9 @@ const CertificateRenewalPolicyConfig = ({ className }: Props) => {
};

const saveConfig = (values: FormConfig) => {
sendTelemetry(TELEMETRY_EVENT_TYPE.CONFIGURATIONS.CERTIFICATE_RENEWAL_POLICY_UPDATED, {
app_pathname: getPathnameWithoutId(pathname),
app_section: 'certificate-renewal-policy',
app_action_value: 'configuration-save',
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CR_UPDATE_CONFIGURATION_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

const newConfig = {
Expand Down Expand Up @@ -202,6 +198,11 @@ const CertificateRenewalPolicyConfig = ({ className }: Props) => {
<Button bsStyle="primary"
bsSize="small"
onClick={() => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CR_EDIT_CONFIGURATION_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

setShowModal(true);
}}>Edit configuration
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,26 @@ import {
} from 'components/datanode/Constants';
import RemoteReindexingMigration from 'components/datanode/migrations/RemoteReindexingMigration';
import MigrationError from 'components/datanode/migrations/common/MigrationError';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

const ManualMigrationStep = () => {
const { currentStep } = useMigrationState();
const { onTriggerNextState } = useTriggerMigrationState();
const sendTelemetry = useSendTelemetry();

const onMigrationStepChange = async (step: MigrationActions, args?: StepArgs = {}) => onTriggerNextState({ step, args });

const handleSelectMigrationType = async (step: MigrationActions, args?: StepArgs = {}) => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.MIGRATION_TYPE_SELECTED, {
app_pathname: 'datanode',
app_section: 'migration',
event_details: { migration_type: (step === 'SELECT_ROLLING_UPGRADE_MIGRATION') ? 'IN-PLACE' : 'REMOTE REINDEX' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, constant for the step would be nice.

});

return onTriggerNextState({ step, args });
};

const migrationTypeOptions = [
{ label: 'In-Place migration', value: 'SELECT_ROLLING_UPGRADE_MIGRATION' },
{ label: 'Remote Re-indexing Migration', value: 'SELECT_REMOTE_REINDEX_MIGRATION' },
Expand All @@ -59,7 +72,7 @@ const ManualMigrationStep = () => {
inputId="datanode-migration-type-select"
options={migrationTypeOptions}
matchProp="label"
onChange={onMigrationStepChange}
onChange={handleSelectMigrationType}
value={null} />
</Input>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
import React from 'react';
import styled, { css } from 'styled-components';

import type { MigrationActions, OnTriggerStepFunction, StepArgs } from 'components/datanode/Types';
import type { MigrationActions, MigrationStateItem, OnTriggerStepFunction, StepArgs } from 'components/datanode/Types';
import { Button, ButtonToolbar } from 'components/bootstrap';
import { MIGRATION_ACTIONS } from 'components/datanode/Constants';
import useMigrationState from 'components/datanode/hooks/useMigrationState';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';
import type { TelemetryEventType } from 'logic/telemetry/TelemetryContext';

const StyledButtonToolbar = styled(ButtonToolbar)(({ theme }) => css`
margin-top: ${theme.spacings.md};
Expand All @@ -45,14 +49,73 @@ type Props = {
children?: React.ReactNode,
}

const getTelemetryEvent = (state: MigrationStateItem, step: MigrationActions): TelemetryEventType => {
switch (state) {
case 'MIGRATION_WELCOME_PAGE':
grotlue marked this conversation as resolved.
Show resolved Hide resolved
if (step === 'SHOW_MIGRATION_SELECTION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.WELCOME_GO_TO_MIGRATION_STEPS_CLICKED;
if (step === 'SHOW_RENEWAL_POLICY_CREATION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.WELCOME_CONFIGURE_CERTIFICATE_RENEWAL_POLICY_CLICKED;

return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.WELCOME_NEXT_CLICKED;
case 'CA_CREATION_PAGE':
if (step === 'SHOW_MIGRATION_SELECTION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_GO_TO_MIGRATION_STEPS_CLICKED;
if (step === 'SHOW_RENEWAL_POLICY_CREATION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_CONFIGURE_CERTIFICATE_RENEWAL_POLICY_CLICKED;

return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CA_NEXT_CLICKED;
case 'RENEWAL_POLICY_CREATION_PAGE':
if (step === 'SHOW_MIGRATION_SELECTION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CR_GO_TO_MIGRATION_STEPS_CLICKED;

return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.CR_NEXT_CLICKED;
case 'ROLLING_UPGRADE_MIGRATION_WELCOME_PAGE':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.INPLACE_RUN_DIRECTORY_COMPATIBILITY_CHECK_CLICKED;
case 'DIRECTORY_COMPATIBILITY_CHECK_PAGE':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.INPLACE_DIRECTORY_COMPATIBILITY_CHECK_NEXT_CLICKED;
case 'JOURNAL_SIZE_DOWNTIME_WARNING':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.INPLACE_JOURNAL_SIZE_DOWNTIME_WARNING_NEXT_CLICKED;
case 'MESSAGE_PROCESSING_STOP':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.INPLACE_STOP_MESSAGE_PROCESSING_NEXT_CLICKED;
case 'RESTART_GRAYLOG':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.INPLACE_RESTART_GRAYLOG_NEXT_CLICKED;
case 'REMOTE_REINDEX_WELCOME_PAGE':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_WELCOME_NEXT_CLICKED;
case 'EXISTING_DATA_MIGRATION_QUESTION_PAGE':
if (step === 'SKIP_EXISTING_DATA_MIGRATION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_MIGRATE_EXISTING_DATA_QUESTION_SKIP_CLICKED;

return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_MIGRATE_EXISTING_DATA_QUESTION_NEXT_CLICKED;
case 'MIGRATE_EXISTING_DATA':
if (step === 'START_REMOTE_REINDEX_MIGRATION') return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_MIGRATE_EXISTING_DATA_START_CLICKED;

return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_MIGRATE_EXISTING_DATA_CHECK_CONNECTION_CLICKED;
case 'ASK_TO_SHUTDOWN_OLD_CLUSTER':
return TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.REMOTEREINDEX_SHUTDOWN_OLD_CLUSTER_NEXT_CLICKED;
default:
return null;
}
};

const MigrationStepTriggerButtonToolbar = ({ nextSteps, disabled, onTriggerStep, args, hidden, children }: Props) => {
const sendTelemetry = useSendTelemetry();
const { currentStep } = useMigrationState();

if (hidden) {
return null;
}

const handleButtonClick = (step: MigrationActions) => {
const eventType = getTelemetryEvent(currentStep?.state, step);

if (eventType) {
sendTelemetry(eventType, {
app_pathname: 'datanode',
app_section: 'migration',
});
}

onTriggerStep(step, args);
};

return (
<StyledButtonToolbar>
{getSortedNextSteps(nextSteps).map((step, index) => <Button key={step} bsStyle={index ? 'default' : 'success'} bsSize="small" disabled={disabled} onClick={() => onTriggerStep(step, args)}>{MIGRATION_ACTIONS[step]?.label || 'Next'}</Button>)}
{getSortedNextSteps(nextSteps).map((step, index) => <Button key={step} bsStyle={index ? 'default' : 'success'} bsSize="small" disabled={disabled} onClick={() => handleButtonClick(step)}>{MIGRATION_ACTIONS[step]?.label || 'Next'}</Button>)}
{children}
</StyledButtonToolbar>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ import { qualifyUrl } from 'util/URLUtils';
import UserNotification from 'util/UserNotification';
import { QUERY_KEY as DATA_NODES_CA_QUERY_KEY } from 'preflight/hooks/useDataNodesCA';
import { MIGRATION_STATE_QUERY_KEY } from 'components/datanode/hooks/useMigrationState';
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';

const resetMigration = async () => fetch('DELETE', qualifyUrl('/migration/state'));

const ResetMigrationButton = () => {
const queryClient = useQueryClient();
const [showDialog, setShowDialog] = useState(false);
const sendTelemetry = useSendTelemetry();

const { mutateAsync: onResetMigration } = useMutation(resetMigration, {
onSuccess: () => {
Expand All @@ -43,18 +46,34 @@ const ResetMigrationButton = () => {
},
});

const handleResetClick = () => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.RESET_MIGRATION_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

setShowDialog(true);
};

const handleConfirmClick = async () => {
sendTelemetry(TELEMETRY_EVENT_TYPE.DATANODE_MIGRATION.RESET_MIGRATION_CONFIRM_CLICKED, {
app_pathname: 'datanode',
app_section: 'migration',
});

await onResetMigration();
setShowDialog(false);
};

return (
<>
<Button bsStyle="primary" bsSize="small" onClick={() => setShowDialog(true)}>
<Button bsStyle="primary" bsSize="small" onClick={handleResetClick}>
Reset Migration
</Button>
{showDialog && (
<ConfirmDialog title="Reset Migration"
show
onConfirm={async () => {
await onResetMigration();
setShowDialog(false);
}}
onConfirm={handleConfirmClick}
onCancel={() => setShowDialog(false)}>
Are you sure you want to reset the migration?
</ConfirmDialog>
Expand Down
Loading
Loading