Skip to content

Plugin SDK: provide the scoped control-plane URL and rest.Config so plugins don't reimplement transport #225

Description

@scotwells

Problem

Every milo-os service that ships a datumctl plugin has to hand-write the same transport layer before it can make a single API call: derive the scoped control-plane URL from the injected org/project, mint a token, build a client-go rest.Config, and fall back to a kubeconfig for local/dev use. datumctl itself, the compute plugin, and now the milo-os IPAM plugin (milo-ipam) each carry their own copy of this. It's boilerplate that's easy to get subtly wrong, and mistakes don't fail loudly — for example, a scheme-less API host silently routes to an HTML endpoint and surfaces as a confusing "serializer for text/html" error.

What we'd like

The SDK should hand a plugin a ready-to-use client path, so authors write their commands, not their transport. Two pieces:

  1. The scoped control-plane URL, derived from the injected context. The SDK already exposes org / project / api-host; it should also turn them into the fully-qualified control-plane URL (and guarantee a scheme). That construction is identical across every plugin and is the single most error-prone step. It's pure string logic with no new dependencies, so it fits the core plugin package.

  2. A ready rest.Config, plus a kubeconfig fallback. An opt-in helper that builds a client-go rest.Config from the injected context — fresh token, scoped host, user-agent — plus the standard selection: use the injected context in production, fall back to KUBECONFIG / in-cluster for dev and e2e. Because this pulls in client-go, it belongs in a separate sub-package so the core SDK stays dependency-light.

With these, a plugin's transport collapses from ~150 lines to a couple of calls, and every milo-os plugin reaches its API the same way.

Why now

The plugin-marketplace enhancement (datum-cloud/enhancements#783) names standardizing "the credentials a host passes to a plugin — across every milo-os platform" as the natural next step beyond the binary-name convention, so "build once, run on any milo-os platform" holds end-to-end rather than just for the binary name. This is that step for transport. The IPAM plugin is the first external consumer and a concrete reference for what the SDK should expose.

Engineer notes

  • Concrete surface that would have replaced our code: PluginContext.ControlPlaneURL() string in package plugin (core, no new deps), and a plugin/kube (or similar) sub-package exposing something like RESTConfig(...) (*rest.Config, namespace string, err error) that performs the mode selection (an explicit --kubeconfig forces kubeconfig; otherwise the injected context when present; otherwise kubeconfig / in-cluster).
  • Scope addressing we implement today: project → <host>/apis/resourcemanager.miloapis.com/v1alpha1/projects/<project>/control-plane; org → .../organizations/<org>/control-plane; neither → bare host (platform/operator scope). Same construction datumctl and the compute plugin use.
  • The scheme footgun to fold in: DATUM_API_HOST arrives without a scheme; client-go needs an absolute URL or it misroutes.
  • Reference implementation to lift from: cmd/milo-ipam/transport.go in milo-os/ipam (PR fix(deps): update module go.miloapis.com/milo to v0.16.0 #36).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions