Bridge API

Status: Implemented Issue: https://gitlab.com/gitlab-org/cloud-native/gitlab-operator/-/work_items/2328 Parent: none

Goal

A frontend configures GitLab instances through an HTTP API instead of writing custom resources. The API grants a caller nothing beyond their own Kubernetes permissions.

Requirements

  • Bridge is served only by the kubectl bridge plugin. The Operator serves no Bridge endpoint, and no Operator image carries Bridge or its SPA.
  • The API lists, reads, creates, updates, and deletes GitLabCore resources, in one namespace or across every namespace the caller may list.
  • Every /api request acts as the kubeconfig identity of the plugin, never as the Operator.
  • A 401 or 403 from the API server, such as for an expired credential or a request that identity may not perform, reaches the caller unchanged.
  • A value the resource definition would reject receives 422, which names the field.
  • The OpenAPI document carries the field constraints of the definition. A cross-field rule of the definition comes back as the 422 of the API server.
  • Only Secret references travel over the API, never a license key or a password.
  • Each instance has at most one Siphon through the API. The API finds it by its reference to the instance, whatever its name.
  • A Siphon created through the API is named <instance>-siphon. A name longer than 31 characters receives 422.
  • A read of the Siphon of an instance reports none when the cluster does not serve the kind.
  • The Orbit of an instance follows Orbit add-on.
  • Every read of a GitLabCore, Siphon, or Orbit returns the generation of its specification. A replace that carries another one receives 409 and changes nothing, and so does a replace of a Siphon or Orbit that carries one when the resource no longer exists. A create ignores it.
  • A replace of a GitLabCore without a generation receives 428 and changes nothing. A replace of one that does not exist receives 404 and creates nothing.
  • A PUT of a Siphon or Orbit without a generation creates the add-on when the instance has none. When it has one, the PUT receives 409 and changes nothing, whatever the state of the Siphon of the instance.
  • A change of the status or the finalizers of a resource alone does not refuse a replace.
  • Every read of a GitLabCore returns the chart versions it can upgrade to, as the Operator reports them in status.upgradeVersions. The plugin reads no chart repository.
  • The OpenAPI document declares no security scheme.
  • When the caller may list namespaces, the namespaces endpoint lists every namespace, sorted by name, without the hidden ones and without those being deleted. It also returns the rules for hidden names.
  • A caller who may not list namespaces receives an empty list that says so, and the rules.
  • Hidden names start with kube- or openshift-, or are openshift or one of the add-on namespaces cert-manager, clickhouse-operator-system, cnpg-system, envoy-gateway-system, ingress-nginx, nats, seaweedfs-system, and traefik.
  • The API creates a namespace by name. A hidden name, one that is not a DNS label, or a namespace being deleted receives 422, and another existing one 409.
  • Creating a GitLabCore in a namespace that does not exist receives 404, which names the namespace.

Out of scope

  • Authentication by Bridge itself.
  • The v1beta1 GitLab resource.
  • Running Bridge in the cluster.
  • Listing chart versions apart from an instance, such as for the create form.

FAQ

  • Why does Bridge not use the Operator ServiceAccount? It holds cluster-wide permissions, and every caller would inherit them. For more information, see ADR 34.
  • Why does Bridge not run in the cluster? It added no capability the plugin lacks, and it added a token path to the process that holds the Operator credentials. For more information, see ADR 23.
  • Which chart repository do the versions to upgrade to come from? The one the Operator pulls from, or only its bundled charts when it does not pull. The Operator reports them in the status of each instance, so Bridge needs neither the settings of the Operator nor network access to the repository. For more information, see ADR 32.
  • Why does a replace compare the generation, not the resource version? The reconcilers write the status and the finalizers often, and each write changes the resource version. The generation changes only with the specification, which is what a replace would revert. For more information, see ADR 39.
  • Why does a replace of a GitLabCore without a generation receive 428, not 409? A PUT of an instance never creates one, so a request without a generation lacks the precondition that guards against a lost update. It conflicts with nothing. 428 Precondition Required (RFC 6585) states exactly that, and a client can tell it apart from a stale save, which only a new read resolves.
  • Why does a PUT of an add-on without a generation answer 409 when one exists? The PUT of an add-on creates it when the instance has none, and the form adds an add-on that way. Without a generation, the PUT is a create. A create over an existing resource is a conflict, which is also what the API answers for a name that is taken.
  • Why do the paths say gitlabs when the resource is GitLabCore? /api/v1 versions the Bridge API, not the custom resource.
  • Why does Bridge repeat the field validation of the definition? So the OpenAPI document and the generated client carry it, and a bad value fails in Bridge with the field named.
  • Why does the namespaces endpoint hide some namespaces? They belong to the cluster or to the add-ons an instance depends on, not to a GitLab instance. The add-on names are those the repository’s guides and tests install. A GitLabCore created in one of them through the gitlabs endpoints is still accepted.
  • Why does a caller who may not list namespaces get an empty list instead of 403? The form then takes a typed name, which the API server still authorizes. The rules come back either way.
  • Does Bridge label or delete the namespaces it creates? No.