Skip to content

feat(grpc): add list workloads/volumes#18

Open
casey-brooks wants to merge 2 commits intomainfrom
noa/issue-17
Open

feat(grpc): add list workloads/volumes#18
casey-brooks wants to merge 2 commits intomainfrom
noa/issue-17

Conversation

@casey-brooks
Copy link
Copy Markdown
Contributor

Summary

  • add gRPC handlers for ListWorkloads/ListVolumes and update service definition paths
  • apply label.* additionalProperties to workload labels and pre-create labeled volumes
  • add container/volume listing helpers plus label mapping test coverage

Testing

  • pnpm lint
  • pnpm test

Closes #17

@casey-brooks
Copy link
Copy Markdown
Contributor Author

Test & Lint Summary

  • pnpm lint (no errors)
  • pnpm test (13 passed, 0 failed, 0 skipped)

@casey-brooks
Copy link
Copy Markdown
Contributor Author

Update Summary

  • restrict proto generation to runner API and clean generated output before buf runs

Test & Lint Summary

  • pnpm proto:generate
  • pnpm build
  • pnpm test (13 passed, 0 failed, 0 skipped)
  • pnpm lint (no errors)

Copy link
Copy Markdown

@noa-lucent noa-lucent left a comment

Choose a reason for hiding this comment

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

The PR implements ListWorkloads/ListVolumes gRPC handlers and label propagation as specified in #17. The overall structure follows existing codebase patterns well. There is one major issue and several minor items to address:

Major:

  • extractRequestLabels is computed twice per startWorkload call (once in server.ts, once inside startWorkloadRequestToContainerOpts), creating redundancy and a divergence risk.

Minor:

  • Missing test coverage for the new gRPC handlers, ensureWorkloadVolumes, resolveVolumeName, and label merge precedence.
  • Duplicated container sorting comparator should be extracted.
  • ensureVolume silently skips label application on existing volumes (Docker limitation) — worth documenting/logging.
  • The skip-when-no-labels logic in ensureWorkloadVolumes is a subtle behavioral decision that should be documented.

return callback(toServiceError(status.INVALID_ARGUMENT, 'main_container_required'));
}
try {
const requestLabels = extractRequestLabels(call.request);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[major] extractRequestLabels(call.request) is computed here, and then startWorkloadRequestToContainerOpts(call.request) at line 413 calls extractRequestLabels(request) again internally (line 306 in workload.grpc.ts). This means:

  1. The label extraction runs twice per startWorkload call — redundant work.
  2. Worse, if either call site is changed independently, the two label sets could diverge silently, leading to ensureWorkloadVolumes using different labels than the container itself.

Consider having startWorkloadRequestToContainerOpts accept the pre-computed labels as a parameter, or return the extracted labels alongside opts, so the extraction happens in a single place.

const volumeName = resolveVolumeName(spec);
if (!volumeName || ensured.has(volumeName)) continue;
const volumeLabels = { ...baseLabels, ...(spec.labels ?? {}) };
if (Object.keys(volumeLabels).length === 0) continue;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] This skips ensureVolume when volumeLabels is empty. That means named volumes without any orchestrator-provided label.* properties won't be pre-created here — Docker will create them implicitly (without labels) when the container starts.

This is likely intentional (no labels → nothing useful to apply), but it's a subtle contract: a named volume that the orchestrator doesn't label will never appear in ListVolumes results. Worth a brief comment documenting the intent.

all: options?.all ?? true,
filters: { label: [labelKey] },
});
const sorted = [...list].sort((a: Docker.ContainerInfo, b: Docker.ContainerInfo) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] This sorting comparator is duplicated verbatim from findContainersByLabels (line 1004). Extract it into a named helper (e.g., sortContainersByCreatedThenId) to eliminate the duplication and make future changes to the ordering policy apply consistently.

};

const rebuilt = startWorkloadRequestToContainerOpts(request);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] Good to see coverage for label.* extraction. However, this PR introduces several new behaviors that lack test coverage:

  • extractRequestLabels merging precedence: what happens when request.labels and label.* in additionalProperties have overlapping keys?
  • labels_json + label.* merge: the existing labels_json path now merges with request-level labels — the precedence should be tested.
  • resolveVolumeName: edge cases (empty name, slashes, persistentName vs name fallback).
  • ensureWorkloadVolumes: deduplication, NAMED-only filtering, skip-when-no-labels.
  • listWorkloads / listVolumes handlers: at minimum, unit-level tests with mocked ContainerService to verify sidecar filtering and label key extraction.

Consider adding tests for these to avoid regressions in the reconciliation logic.

await this.docker.createVolume({ Name: name, Labels: labels ?? {} });
} catch (e: unknown) {
const sc = typeof e === 'object' && e && 'statusCode' in e ? (e as { statusCode?: number }).statusCode : undefined;
if (sc === 409) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[minor] Note that if the volume already exists (409), labels are silently not applied since Docker doesn't support updating volume labels post-creation. This means if a volume was created earlier (e.g., by a previous workload without labels or with different labels), ListVolumes may not return the expected volume_key. Consider adding a log at debug level when hitting the 409 path to note that labels were not applied, so operators can diagnose reconciliation mismatches.

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.

Implement RunnerService ListWorkloads/ListVolumes + propagate orchestrator label.* properties

2 participants