Skip to content

Ask for the Collector ingest endpoint inside the onboarding wizard - #27252

Open
kroepke wants to merge 7 commits into
masterfrom
feature/collectors-onboarding-ingest-endpoint
Open

Ask for the Collector ingest endpoint inside the onboarding wizard#27252
kroepke wants to merge 7 commits into
masterfrom
feature/collectors-onboarding-ingest-endpoint

Conversation

@kroepke

@kroepke kroepke commented Sep 2, 2026

Copy link
Copy Markdown
Member

Motivation

On a cluster without a collectors config, every Collectors page redirected to the Settings page and forced the full settings form on the user before anything else. That was confusing: the user came to enroll a collector, not to configure lifecycle thresholds. Design exploration (clickable prototype) settled on asking for the ingest endpoint inside the onboarding wizard, right before the install command that depends on it.

Approach

  • FirstOnboarding: platform → fleet → IngestEndpointStrip (only when unconfigured) → install command. The enrollment token is minted after the endpoint is confirmed, because the token is signed with the key that first config save creates. Configured clusters skip the strip.
  • IngestEndpointStrip: slim confirm strip (hostname : port, Confirm). Once the config exists it becomes a status line mirroring the Settings page facts: input running / input exists but not running / no input (with "Create input" when permitted). Changing the port moves the existing ingest input via the inputs API (updateCollectorInputPort).
  • Permissions checked before the user picks anything. No config permission, or no ingest input and no permission to create one (the built-in Collectors Manager role lacks inputs:create), shows a notice instead of a wizard that cannot finish.
  • Server: CollectorsConfigResource#put calls CollectorIngestInputService#ensureCanCreateInput before persisting, so a request with create_input can no longer save the config and then 403 on the input.
  • Deployment, Fleets, Fleet detail and Instances pages redirect to the Overview (which owns the unconfigured state) instead of Settings.
  • useCollectorPermissions gains canCreateIngestInput / canEditIngestInput.

Server fix bundled here

InputEventListener#inputUpdated only relaunched an updated input if it was RUNNING or SETUP (#3824 kept deliberately stopped inputs stopped). A FAILED input fell into the same bucket: editing it (e.g. to fix a port conflict) left it TERMINATED until a manual start. Found while testing the port change in the strip; FAILED now relaunches like RUNNING. Same fix applies to the regular Inputs page.

Notes

Screenshots

  1. Administrator in onboarding flow, connection settings haven't been confirmed yet image
  2. Administator confirmed the settings image
  3. Administrator edits the previously confirmed settings: image
  4. Administrator configures in-use port and sees an error image
  5. Unconfigured ingest input with a Collectors Manager role, unable to create/edit inputs image

Instead of redirecting an unconfigured cluster to the Settings page, the
onboarding wizard now asks for the ingest endpoint itself: platform and
fleet first, then a slim "Confirm how Collectors reach this cluster"
strip, and only after that the install command. Confirming performs the
one-time collectors bootstrap; the enrollment token is minted afterwards
because it is signed with the key that first save creates.

Once the config exists the strip turns into a status line that mirrors
the Settings page facts (input running, input not running, no input),
and changing the port moves the existing ingest input along.

Permissions are checked before the user picks anything: a user who
cannot edit the config, or who cannot create the ingest input (the
built-in Collectors Manager role lacks the input permissions), sees a
notice instead of a wizard that cannot finish. On the server the PUT
checks the input permission before persisting so a request can no
longer half-succeed.

The Deployment, Fleets and Instances pages redirect to the Overview
instead of Settings while the cluster is unconfigured.
@kroepke
kroepke marked this pull request as ready for review September 4, 2026 10:22
InputEventListener only relaunched an updated input if it was RUNNING or
SETUP (from #3824, so that deliberately stopped inputs stay stopped). A
FAILED input fell into the same bucket: it was removed from the registry
and never started again, so fixing e.g. a port conflict by editing the
input left it TERMINATED until a manual start.

A failed input was never stopped on purpose, and an update is typically
the fix for whatever made it fail, so treat FAILED like RUNNING here.
@kroepke
kroepke requested review from a team September 4, 2026 11:59
@linuspahl linuspahl self-assigned this Sep 4, 2026

@linuspahl linuspahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I couldn’t find any issues with the behavior, just added a few ideas for improvements.

import { mockCollectorsMutations } from '../../testing/mockMutations';
import { mockCollectorPermissions } from '../../testing/mockPermissions';

jest.mock('../../hooks');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Usually we don't mock a complete directly and the specific hooks instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I saw the same pattern in other tests in this PR, for example for

jest.mock('components/collectors/hooks', () => ({
  useCollectorsConfig: jest.fn(),
}));

type: 'org.graylog.collectors.input.CollectorIngestHttpInput',
content_pack: '',
static_fields: {},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have a similar mock for unconfigured and mockInput in other tests already. let's create a shared fixture for it and only adjust the relevant attributes in this file?


await waitFor(() => {
expect(screen.getByText(/waiting for connection/i)).toBeInTheDocument();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Usually we use await screen.findBy instead of expect(...).toBeInTheDocument(),which is async by default. See graylog2-web-interface/CONTRIBUTING.md.

jest.mock('../../hooks');
jest.mock('hooks/useInputsStates');
jest.mock('components/collectors/hooks/useSendCollectorsTelemetry');
jest.mock('components/inputs/InputStateBadge', () => () => <span>state badge</span>);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Usually we don't mock the badge component and just check if the related text is displayed correctly. We usually only test it's existence when it is accessible.

Confirm endpoint
</button>
</div>
));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We try to avoid the component mocks, in this case we would mock the config API and make sure it returns the "confirmed" status.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I saw the same pattern in other tests in this PR. If possible we try to avoid something like

jest.mock('components/collectors/common', () => ({ CollectorsPageNavigation: () => <div>collectors nav</div> }));

If there is a runtime error in CollectorsPageNavigation we would not catch it.

An administrator must set up the Collector ingest endpoint before Collectors can be enrolled.
</Alert>
</BodyContainer>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One small thing that surprised me. I read the "Collectors Manager" role description. Grants full control of collectors and fleets (built-in)" and expected they are able to set up the Collector ingest endpoint.

<Text>{`Ask an administrator to create a Collector Ingest (HTTP) input on port ${config.http.port}.`}</Text>
)}
</Stack>
</Alert>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I was a bit overwhelmed by this alert.

Image

I started reading the headline, saw the "Change" button and assumed I am changing the fact that "No ingest input exists", until I saw the "Create input" below.

What confused me as well, when I click "Change" I see the following section:

Image

When I click "Confirm", I am actually creating an input - I thought I am just changing the address because there was already a path to create the input in the previous alert.

Using more descriptive names for "Change" and "Confirm" would already help, but imo we could just combine both screens in one. For example:

No ingest input exists

Collectors can enroll, but they will not be able to send data until an input is created.

Image

Collectors send their logs to this address. It must be reachable from every Collector host: a load balancer, or this server.

if (!canCompleteSetup) {
return (
<BodyContainer>
<Alert bsStyle="info" title="Collectors are not set up yet">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Imo the issue here is not that "Collectors are not set up yet", it's that no Collector ingest endpoint has been configured yet.

@linuspahl linuspahl Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When I read the headline for the first time I thought users with the role "Collectors manager" role no longer have the permission to create collectors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants