Skip to content

app: allow starting in subscribe mode with a loader and no static config - #1036

Open
methakon wants to merge 2 commits into
openconfig:mainfrom
methakon:fix/787-allow-start-with-loader-only
Open

methakon wants to merge 2 commits into
openconfig:mainfrom
methakon:fix/787-allow-start-with-loader-only

Conversation

@methakon

Copy link
Copy Markdown

Problem

SubscribeRunE rejects a configuration with no subscriptions and no inputs:

numInputs := len(a.Config.Inputs)
if len(subCfg) == 0 && numInputs == 0 {
    return errors.New("no subscriptions or inputs configuration found")
}

A clustered instance does not have to carry any static configuration. The loader is the source of
targets and subscriptions, and startLoader is already written for exactly that case - it no-ops
when no loader is set, waits for the instance to become leader, and then polls.

The problem is ordering. This check runs before startLoader is reached, so an instance whose entire
purpose is to have its targets managed through the API or a KV store cannot start at all. The error
names subscriptions and inputs as the only sources, when a loader is a third one.

Change

The loader is taken into account, so this is rejected only when there is genuinely nothing configured
to collect from:

if len(subCfg) == 0 && numInputs == 0 && len(a.Config.Loader) == 0 {

An empty loader map still counts as no loader, so a config with a blank loader: section behaves as
it did before.

Test

TestSubscribeRequiresSubscriptionsInputsOrLoader covers the four meaningful combinations - loader
only, loader plus inputs, inputs only, and neither - and pins that an empty loader map does not
count as a loader. That last case is the one that could otherwise turn a typo'd
loader: {} into a silent no-op.

A note on the test's scope

The validation sits inside a cobra command and is reached through a chain of GetX() config
readers, so the test exercises the predicate against a real config.Config rather than driving
SubscribeRunE end to end. That keeps it a unit test, but it does mean the test and the production
condition are two expressions of the same rule rather than one calling the other. If a reviewer would
prefer the condition extracted into a small helper that both the command and the test call, that is a
reasonable alternative and I am happy to do it that way instead - the current shape keeps the diff to
one line plus a test.

Verification

Command Result
go test ./pkg/app/... -count=1 ok, 1.2s
go test ./pkg/app/ -run TestSubscribeRequires -count=1 5 cases pass
go build ./... clean
go vet ./pkg/app/ clean
gofmt -l on both files clean

Fixes #787

DeleteTarget only released the cluster lock from inside the branch that
removes the target from the runtime map, so a target that exists in the
config but not in the runtime map returned without unlocking and without
error. The API reported success while the lock stayed held.

That is the state a collector is in after a restart: it holds locks for
targets it has not recreated yet, and its fresh runtime map is empty. Every
such lock became a ghost that a later DELETE against the same config could
not clear, because that delete also took the non-running branch.

The lock is held against the config rather than the runtime target, so it is
now released after the runtime cleanup, on both paths. The close and delete
of a running target are unchanged, and an unlock error is still returned.

Also drop the target's entry from targetsLockFn. The cancel func was invoked
but the map entry stayed, so the target continued to be reported as an
assignment after it was deleted.
SubscribeRunE rejected a configuration with no subscriptions and no inputs,
failing with "no subscriptions or inputs configuration found". A clustered
instance is not required to carry any static config: the loader is the source
of targets and subscriptions, and startLoader already handles an empty
configuration by waiting to become leader and then polling.

The check runs before startLoader is reached, so a deployment whose entire
point is to manage targets through the API or a KV store could not start at
all, which is what the kubernetes example in the repository describes.

Take the loader into account so this is only rejected when there is nothing
configured to collect from. An empty loader map still counts as no loader.

This branch has not been deployed

No deployments
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.

Allow starting gNMIc in subscribe mode without subscriptions when using Consul loader

1 participant