Embedding in a platform¶
kubepkg is meant to become part of someone else's platform: a Kubernetes distribution, a managed Kubernetes service, an internal developer platform. It needs nothing but its CRDs and its operator, and a platform adapts it at three levels, from configuration to its own binaries. No fork is needed at any level.
Configuration¶
Operator flags, set through the chart, all off by default:
| Flag | Chart value | Does |
|---|---|---|
--api-group |
apiGroup |
serves the types under the platform's group, such as packages.example.org instead of kubepkg.dev |
--values-secret ns/name |
valuesSecret |
layers the Secret's values.yaml under every component's values |
--namespace-label k=v |
namespaceLabels |
labels every namespace that packages create, for example to put them under the platform's policies |
--backend |
backend |
helm, werf, flux or argo |
--argo-namespace, --argo-project |
argo.* |
where Argo CD Applications go |
--nelm-binary |
the nelm executable for the werf backend |
Releases are labelled kubepkg.dev/package: <name> (under the platform's group when it sets one), and components marked privileged also get <group>/privileged. Admission policies and cost reports can select on these labels.
Your own API group¶
make crds-for-group GROUP=packages.example.org OUT=dist/crd
helm install kubepkg oci://ghcr.io/kuberoot-dev/charts/kubepkg -n platform-system \
--set apiGroup=packages.example.org
kubepkg --api-group packages.example.org list
Users then see packages.example.org/v1 Packages, which fits the platform's own API. crds-for-group rewrites the CRDs, and the operator and CLI take the group as a flag.
Plugins¶
Build plugins extend how packages are made, in any language, without Go code. A platform typically adds:
- a source plugin for its internal artifact store or git server;
- a step plugin that moves images to its registry, adds its labels, or injects its sidecars.
See Building packages.
Go libraries¶
Everything the binaries do lives in importable packages. A platform builds its own operator and CLI, with its own name, defaults, trust rules and backends.
| Package | Gives you |
|---|---|
pkg/operator |
DefaultOptions, the standard flags, a registry of named backends, and Run |
pkg/cli |
NewRootCommand, with your name and API group; add your own commands |
pkg/backend |
the Backend interface the operator installs through |
pkg/controller |
the reconcilers, and the Preparer, APIs and CRDOwner interfaces |
pkg/build, pkg/repo, pkg/resolve, pkg/source |
building, indexing and trust, resolving, fetching, each usable alone |
A platform's operator that serves its own group and admits only charts from its own registry:
package main
import (
"context"
"flag"
"fmt"
"strings"
ctrl "sigs.k8s.io/controller-runtime"
"github.com/kuberoot-dev/kubepkg/pkg/operator"
"github.com/kuberoot-dev/kubepkg/pkg/repo"
)
// allowedRegistries admits only versions whose charts come from the
// platform's own registry.
type allowedRegistries struct{ prefix string }
func (allowedRegistries) AdmitIndex(context.Context, string, []byte, *repo.Index) error { return nil }
func (p allowedRegistries) AdmitVersion(_ context.Context, _, pkg string, v repo.Version) error {
for _, variant := range v.Spec.Variants {
for _, c := range variant.Components {
if c.Chart != nil && !strings.HasPrefix(c.Chart.Repository, p.prefix) {
return fmt.Errorf("%s %s: chart %s is not from %s", pkg, v.Version, c.Chart.Repository, p.prefix)
}
}
}
return nil
}
func main() {
opts := operator.DefaultOptions()
opts.Profile.Group = "packages.example.org"
opts.Policy = allowedRegistries{prefix: "oci://registry.example.org/"}
opts.BindFlags(flag.CommandLine)
flag.Parse()
if err := operator.Run(ctrl.SetupSignalHandler(), ctrl.GetConfigOrDie(), opts); err != nil {
panic(err)
}
}
The matching CLI:
o := cli.DefaultOptions()
o.Name, o.Short, o.APIGroup = "exctl", "Example Platform packages", "packages.example.org"
o.Policy = allowedRegistries{prefix: "oci://registry.example.org/"}
root := cli.NewRootCommand(o)
root.AddCommand(myPlatformCommand())
if err := root.Execute(); err != nil {
os.Exit(1)
}
Both examples are compiled with the tests (pkg/operator/example_test.go), so they keep working.
Other extension points in operator.Options:
Backends: add your own backend under a name, then select it with--backend;IndexFetchers: fetch indexes over your own URL schemes, such ass3://or an internal API;AddToScheme: register the types your backend writes.
What a platform should not change¶
The package format, the repository index and the signature formats are the same everywhere. A package built for kubepkg installs on any platform that embeds it, and a platform's repository can be read by a plain kubepkg. Keep it that way, and your users can take their packages with them.