openstack

package
v1.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 83 Imported by: 0

README

pkg/providers/internal/openstack

Intention

pkg/providers/internal/openstack is the real cloud provider implementation for OpenStack-backed regions.

It does not merely wrap the OpenStack SDK. It is the package that translates the region service's storage model, tenancy model, metadata conventions, and lifecycle rules into concrete OpenStack operations across identity, compute, image, network, security group, load balancer, quota, and block storage surfaces.

The most important philosophy in this package is the trust and scoping model:

  • region-level manager authority is used only to provision and manage users and projects inside a managed Keystone domain
  • once that scaffolding exists, most OpenStack operations deliberately context switch into the specific project provisioned for one Unikorn identity
  • that OpenStack project then becomes the practical mapping, isolation, and accounting boundary between Unikorn resources and real cloud resources

That model is what makes it realistic for multiple regions or deployments to share one underlying cloud while still limiting blast radius. It is also what makes backchannel accounting and billing integration plausible, because the project scope becomes the place where cloud resource usage can be tied back to a specific Unikorn identity and its descendants.

The most important architectural rule is that this package prefers deterministic lookup against OpenStack over maintaining broad mirrored OpenStack state in Kubernetes. In older designs, dedicated Openstack* CRDs were used to persist more provider-side state locally. That drifted from reality and introduced race conditions. The current direction is:

  • service-native CRDs remain the primary control objects
  • OpenStack remains the source of truth for cloud-side resources where they can be re-found deterministically
  • only the provider state that still cannot be reconstructed safely or sufficiently remains persisted locally, most notably OpenstackIdentity

This package therefore owns the mapping between:

  • region-native CRDs and OpenStack resources
  • service labels, tags, and metadata conventions
  • per-identity delegated cloud credentials
  • compensating local mechanisms where OpenStack is not sufficient on its own, such as image caching and provider-network VLAN allocation

pkg/apis/unikorn/v1alpha1 defines the service-native resources and the remaining persisted provider-state records this package consumes. pkg/providers/types defines the provider-neutral contract this package implements. pkg/providers/allocation/vlan covers the local VLAN allocator used when provider networks need segmentation IDs. ADMIN.md keeps the human operator setup guidance for preparing an OpenStack region.

OpenStack Region Registration

An OpenStack-backed region should be registered with hack/openstack/configure and hack/openstack/register-region, rather than by hand-creating the Keystone domain, project, user, Kubernetes Secret, and Region manifest. That flow keeps operator setup aligned with this package's scoping model: region-level provider credentials are used to manage provider-domain scaffolding and discover region inventory, while workload operations still context-switch into per-identity OpenStack projects.

The full operator procedure lives in ./ADMIN.md.

Invariants And Guard Rails

  • This package implements the full types.Provider contract for OpenStack regions.

  • Provider construction has an explicit bootstrap/runtime split:

    • bootstrap uses uncached Kubernetes reads to assemble OpenStack service clients before controller-manager caches exist
    • runtime operation switches back to the normal Kubernetes client and refreshes derived OpenStack client state when region configuration or credentials change
  • OpenStack access is intentionally scoped through different credential modes:

    • region-level service credentials bootstrap privileged service clients and managed-domain scaffolding
    • per-identity credentials are used for most project-scoped operations
    • some operations deliberately bind privileged credentials to a service principal's project when manager-level powers are required
    • pinned server creation is one of those privileged project-scoped operations: it still targets the identity project, but uses region-level credentials so Nova policy can authorise the requested destination
    • pinned server creation can also enable a transient openstack.compute.placementPreflight check. When enabled, the provider asks OpenStack Placement whether the pinned resource provider has available inventory for the flavor's positive custom resources:* extra spec and any trait:* extra specs, unioned with configured requiredTraits. Required flavor traits are sent as positive Placement required entries; forbidden flavor traits are sent as !TRAIT entries. Empty trait inputs mean no trait filter. A miss yields and lets the controller retry.
  • SSH injection is a create-time server decision. OpenStack receives the identity key name only for the resolved identityKeypair mode; ca and none omit Nova key_name. Image rebuild omits both key_name and user_data, so Nova preserves the stored keypair and create-time user data (including the managed SSH-CA cloud-init baked in at create) and rebuilt guests stay create-equivalent. Updated user data therefore takes effect on server replacement, not rebuild — Nova accepts user_data on rebuild from microversion 2.57, deferred until gophercloud's RebuildOpts carries the field.

  • A desired server image change is reconciled with Nova rebuild only once the server has booted at least once, decided from Nova's launched_at (OS-SRV-USG:launched_at) read fresh on the same GetServer — never from the monitor-stamped status.launchedAt/status.provisionedAt latches, which are observations and must not authorize the gate (observation is stimulus, never authorization; keying off them silently dropped image changes whenever the monitor had not yet recorded the first ACTIVE, and the clean-completing reconcile then let the API misreport the change as settled). Before first boot the desired image is a create parameter, not a rebuild target, so a pending image change on a server Nova reports with a zero launched_at defers: the reconcile yields, leaving the resource visibly provisioning and re-checking every 10s until first boot, then subsequent passes arm and submit the rebuild. A never-booted server Nova reports in ERROR is likewise deferred here — the reconcile pass yields silently without writing a health stamp (the monitor owns observed state) — and absorbed by the bounded provider-create delete-and-retry flow, which recreates it from the already-updated spec image. That retry adoption keys off the Healthy=Errored stamp the monitor's poll writes, so it takes effect at worst one poll later. This assumes the cloud exposes OS-SRV-USG:launched_at — the same signal the health monitor mirrors and the create-retry guard keys off. Deliberately not a goal: recreating a never-booted server rather than waiting for it to boot (e.g. a queued baremetal deploy, where recreate would skip a wasted provision) is a future optimization, rejected for now to avoid a third delete/recreate site and the known name-collision race.

  • Rebuild intent is write-ahead. The marker is Status.Rebuild, a two-field struct: TargetImageID (the image this intent converges toward) and State (a forward-only enum Initiated < Rebuilding < Succeeded == Failed, the terminals being peers that never retreat and never flip terminal-to-terminal — first observation wins). Arming (recording the target at Initiated) and submitting to Nova happen in separate reconcile passes, with the yield between them acting as the durable commit point, so the marker is persisted — and read back on a later pass — before Nova can be asked to destroy the root disk. The marker records the one fact fresh observation cannot reconstruct: whether Region recently asked for a destructive rebuild.

    Ownership is split. Only the reconciler creates, replaces, or clears the marker, and only the reconciler parks; the monitor's poll only rank-advances State from observed evidence, never creating, clearing, or retargeting it (observation is stimulus, never authorization — the reconciler's settlement pass always re-decides from its own fresh GetServer). The forward-only rank check (advanceRebuildState) makes every advance monotone, so a late or duplicate observation, or two writers racing the same edge, can never retreat a state or flip a terminal. The one exception is the reconciler's park, which assigns Failed directly and may overwrite a Succeeded stamped moments before an ERROR arrived — a stale success left standing would otherwise re-fire the settlement wake forever on a parked server.

    Attribution ties a Nova observation to this rebuild by the image ref, not by spec match. Nova flips the ref to the target atomically with task_state at accept, and this protocol never submits when the fresh ref already equals the target, so a standing marker observed with ref == target always means an accepted rebuild toward it (ours, or a fail-closed-equivalent foreign same-image one). Activity is read from OS-EXT-STS:task_state, which is non-empty for the whole rebuild window and empty at rest (the REBUILD status is folded in defensively, since Nova projects it only from an active rebuild task). Convergence is therefore the conjunction ref == target ∧ stable non-error status ∧ task_state empty: the ref flips at accept but task_state stays non-empty until the rebuild settles, so that conjunction is never observable while a rebuild is in flight, and it uniquely characterises completion. Both the monitor's Succeeded stamp and the reconciler's marker clear gate on it. This is what closes the accept-to- settle lag window: a stopped or errored server can display a stable SHUTOFF/ERROR throughout its rebuild, so a converged-looking status alone is not evidence of completion — task_state is the authoritative activity signal, and settlement is state-based rather than health-reason-based (SHUTOFF settles like any other stable status).

    The monitor (advanceServerRebuildState) advances: with ref == target, ERRORFailed, an active task → Rebuilding, otherwise quiescent → Succeeded; with a readable off-target ref and the marker already durably >= Rebuilding, an ERROR or a quiesced task → Failed (supersession — an accepted rebuild whose ref has moved off the target can no longer converge); with an unreadable ref, only durable acceptance plus ERRORFailed. An Initiated marker observed with ref != target advances nothing: an unattributed advance would falsely satisfy the submission gate and either wedge the rebuild or drive a second Nova accept.

    The reconciler's pass (reconcileServerImage) follows a fixed order. It replaces a marker whose target differs from the desired image (the re-arm recovery, allowed even over a parked Failed — a different image is the designed recovery), then classifies a park, then yields on an unreadable ref, then converges or submits. Park classification runs before the unreadable-ref yield so an attributable ERROR with an unverifiable ref parks rather than yielding forever. On a converged read with the marker present and the task empty, the pass clears the marker and yields so the requeued pass confirms the clear by read-back (marker absent → settled; marker still present → the write dropped → clear and yield again). A converged read with an active task and a non-terminal marker records the acceptance (Rebuilding) and completes the pass, because a future monitor rank-advance to a terminal is still guaranteed to wake the settlement pass; but a terminal marker can never rank-advance again, so a pass observing one it cannot yet settle yields rather than clean-completing, carrying liveness through its own requeue.

    The park is two-phase (reconcileServerRebuildPark). A deciding pass stamps Failed (by direct assignment) and yields; the terminal UserActionRequired return — which core turns into Available=Errored, the "parked" signal — is issued only by a pass that reads Failed back durable. The common case satisfies this on the first pass, because the monitor's own Failed stamp is the usual wake. A dropped pre-park stamp is recovered by read-back: the requeued pass re-enters the same branch (the evidence persists) and stamps again until a pass reads it back and parks. The park records the fresh-read health and retains the marker; the only re-arm is a different desired image or server replacement. Failure recovery is never data restoration.

    The submission (submitServerRebuild) is the single destructive step, gated on an Initiated marker read back durable plus a quiescent server, and it submits at most one accepted action per standing target image. On Nova's 2xx it advances to Rebuilding and writes a fixed accepted stamp (Phase=Building, Healthy=False/Provisioning, matching the monitor's REBUILD mapping so the two writers agree) — it never derives the stamp from the rebuild HTTP response body, whose server representation can still read ACTIVE for the pre-destruction server and would falsely stamp the just-accepted destructive rebuild as running. A Nova 409 Conflict is pre-acceptance and yields silently, leaving the marker at Initiated.

    Pre-acceptance situations yield silently — a log line and the yield, nothing else — because no action was taken: the arming pass; a fresh read whose image ref is missing or unparseable (every server this provider creates is image-booted, so convergence cannot be checked and the pass must not report success over a dropped image change); a foreign or blocking op holding the task busy while the marker is still Initiated; and the Nova 409 at submission. A generic Nova ERROR under an unmoved-ref Initiated marker is intent without acceptance — unrelated, never a rebuild park (the remediation submit owns it) — and a marker-less ERROR never becomes a rebuild request. The reconciler writes Phase/Healthy only when it acts (a rebuild is accepted) or decides (a park); while it waits, the health monitor owns the observed state and the core-owned provisioning status on yield keeps the pending change user-visible. Because the reconciler no longer watches the rebuild converge tick by tick, a post-success ambiguity window — during which an unrelated Nova ERROR is indistinguishable from a failed rebuild and is treated as one — widens from a single reconcile requeue to at most one or two monitor poll cycles. This fails closed: recovery is selecting an image again or replacing the server, never data restoration.

  • Nova rebuild retains the server UUID, network ports and IP relationships, attached data volumes, flavor, metadata, and placement, but recreates the root disk. It stays on the same compute host; evacuation is a separate operator workflow.

  • OpenstackIdentity is the remaining persisted provider-state anchor. It currently stores the secret-bearing user/project/application-credential and bootstrap state needed to operate on behalf of a region Identity.

  • The package relies heavily on deterministic naming and metadata conventions to re-find cloud-side resources. This is a convention-heavy contract, not magic:

    • identity-scoped resources use fixed generated names
    • network lookups rely on deterministic names
    • Cinder volumes use volume-{Region Volume UUID} inside the service principal's project; create, delete, attach, and detach rediscover that exact name
    • server metadata is written deliberately as both a control-plane lookup aid and an in-guest linkage surface exposed through the metadata service
    • legacy camelCase server metadata keys remain frozen for backwards compatibility while newer namespaced keys provide the upgrade path
  • Cinder Volume create/delete is a project-scoped lifecycle slice:

    • the Region Volume controller resolves the full cloud provider and drives its Volume capability after the service-principal Identity is ready
    • the native Region Volume CRD supplies the requested size and VolumeClassID, which becomes the Cinder volume type
    • create lists by the stable generated name and exact-matches the result before submitting a create, so controller retries adopt an existing volume rather than duplicating it
    • an accepted Cinder create is partial progress rather than success: the provider yields after submission and on subsequent rediscovery until the volume reports available; all unrecognized non-error states also yield so an unfamiliar provider state cannot be mistaken for convergence
    • a Cinder status beginning with error returns a typed terminal Available=False, Reason=Errored result with the user-safe message provider volume entered an error state; provider IDs, scheduler failures, host details, and other raw Cinder diagnostics remain operator-only
    • user tags are translated with the package's normal namespaced metadata convention; namespaced system linkage keys are written last so user input cannot override identity, organization, project, region, network, or Volume IDs; Volume metadata does not emit the legacy camelCase compatibility keys retained by older resource types
    • delete uses the same rediscovery path, treats a missing Cinder volume as success, yields after Cinder accepts an asynchronous delete until a later rediscovery confirms absence, and treats an absent or not-yet-project-backed OpenstackIdentity as proof that no provider volume could have been created; this lets controller deletion delegate unconditionally before releasing any Identity allocation
    • general observed size/status mapping and VolumeClass inventory are outside this lifecycle slice; the narrow status classification above exists only to determine create convergence, while Nova attach/detach is the separate server-owned provider slice described below
  • Flavor export is a hybrid model: OpenStack discovers the flavor inventory, but region configuration can enrich or override user-facing flavor metadata such as architecture, baremetal status, and GPU semantics. Architecture resolves from per-flavor cpu.architecture, then openstack.defaultArchitecture, then the legacy x86_64 fallback for objects that bypass CRD defaulting. The baremetal flag is also operationally meaningful for live lifecycle (Active condition) reporting: a Nova BUILD server with a baremetal flavor is disambiguated through Ironic so the API can distinguish Queued (waiting on hardware) from Building (provider actively deploying).

  • VolumeClass configuration follows the same inventory pattern for block storage. Region configuration under openstack.blockStorage.volumeClasses.selector.ids is a strict allowlist: only Cinder volume type IDs explicitly listed there are eligible for export. Missing volumeClasses configuration, a missing selector, or nil/empty IDs exports no VolumeClasses. Selected classes can be enriched with user-facing metadata such as optional minimum/maximum capacity bounds, a supportedFlavors selector, media, maximum performance caps, and encryption signals. Omitted selectors and omitted or empty selector IDs mean unrestricted compatibility; the provider resolves this operator-authored selector to the neutral Flavor ID allowlist and does not infer it from Cinder volume types. Capacity bounds are operator-authored positive whole GiB values; either may be omitted, and when both are present the maximum must be at least the minimum. The provider discovers Cinder volume types and combines them with this Region-authored metadata into provider-neutral VolumeClass values. It does not discover capacity bounds from Cinder. Maximum performance metadata records caps rather than guaranteed reservations. VolumeClass is Region-scoped inventory configuration, not a project-owned resource or lifecycle object. The block-storage service client is cached with the other OpenStack service clients so Cinder volume-type inventory cache survives repeated provider calls and is refreshed only when Region configuration or credentials change. Production Region CRs must contain their curated IDs before this fail-closed behavior is rolled out.

  • Existing-volume server attachments are a separate, project-scoped write path from Region-scoped VolumeClass inventory. The provider creates ephemeral compute and block-storage clients from the service principal, rediscovers the Nova server and detailed Cinder volume, and uses Cinder's attachment rows as the normal-path observation before calling Nova's volume-attachment create/delete API. Cinder volume lifecycle observation is not part of this slice. The Cinder name filter is treated as fuzzy and is post-filtered against the exact stable name volume-{Volume.Name}; duplicate exact matches fail closed with ErrConsistency. Only the optional guest device name crosses the provider-neutral boundary; Nova and Cinder IDs and Gophercloud objects remain internal.

    Attachment behavior is deliberately asymmetric:

    • attach requires both the server and volume; either missing resource maps to ErrResourceNotFound
    • a Cinder attachment already present on the requested server is successful and returns its observed device without a Nova read
    • an attachment to any other server maps to ErrConflict; Region does not support multi-attach even when the Cinder volume is multiattach-capable
    • when Cinder reports no attachment, attach calls Nova create directly; a create 409 Conflict is followed by one Nova attachment read so concurrent creation of the same desired attachment becomes success, while an unresolved conflict maps to ErrConflict
    • detach calls Nova delete only when Cinder reports an attachment to the requested server; a missing server, volume, requested-server attachment, or Nova delete 404 is success because detached state already holds, including when the volume remains attached only to another server
    • a Nova delete 409 Conflict maps to ErrConflict; other provider failures are preserved
    • detach also no-ops when the backing OpenStack identity was never realized, matching the provider's other teardown contracts

    Attachment intent and observed rows remain on Server.Spec.Volumes and Server.Status.Volumes; this provider slice does not mirror attachments into Volume.Status, claim volumes, or reconcile server controllers.

  • Image handling is a first-class contract surface here:

    • OpenStack image properties are validated against a schema
    • public images can additionally be signature-verified
    • image properties are translated into provider-neutral OS, package, GPU, ownership, virtualization, and tag metadata
    • an explicit Glance architecture property wins; otherwise image conversion uses openstack.defaultArchitecture, with the same defensive legacy x86_64 fallback as flavor conversion
    • an optional refresh-ahead cache exists because raw image API latency is too expensive to expose directly to every caller
  • Quota and role behaviour are not purely discovered from OpenStack defaults. The package assumes and applies a managed-role model, including default role names such as manager, member, and load-balancer_member, unless region configuration overrides parts of that behaviour.

  • Network, security group, server, and Volume resources are re-found in OpenStack by deterministic lookup rather than relying on mirrored OpenstackNetwork, OpenstackSecurityGroup, or OpenstackServer CRDs as authoritative state.

  • Baremetal server progress uses Ironic as an additional provider truth source only while Nova reports BUILD for a flavor marked baremetal in region configuration. The result feeds setServerActive, which sets the Active condition to Queued (pre-deploy Ironic states: not yet picked up, cleaning, inspecting, etc.) or Building (Ironic actively deploying — including the post-deploy Error state and the transient *Fail states, on the principle that the node is still in the build pipeline as far as the platform is concerned and the failure signal belongs on the Healthy condition rather than on the Active condition. The node lifecycle eventually terminates via delete; splitting "in the pipeline" from "in the pipeline but unhappy" across both the Active and Healthy conditions would just duplicate one concept across two axes). Provisioning status itself is a separate axis (the Available condition), provisioner-owned and the monitor never writes it; setServerActive does, however, latch the monitor-owned status.provisionedAt field from Nova launched_at the first time a server is seen booted (write-once, never cleared, independent of live power state), which the controller's bounded provider-create delete-and-retry guard relies on (so a server that has booted is never destroyed and recreated). The image-rebuild gate does not read this latch: it authorizes from Nova launched_at read fresh each pass. Alongside it, setServerMACAddress records the other monitor-owned field, status.macAddress, from the Nova response once the server is ACTIVE (the port MAC rides inline in addresses, reused from the same GetServer — no extra call). ACTIVE is required because baremetal Ironic rebinds the port to the real NIC MAC asynchronously; the value is only ever written, never cleared. The lookup is filtered by instance_uuid. Because Ironic node ownership and visibility are provider infrastructure concerns rather than tenant workload operations, this lookup uses the Region top-level provider credentials scoped to the service principal's project, matching the package's other privileged client patterns. Deployments must grant those credentials enough Ironic policy visibility to list/detail nodes by instance UUID, for example through a narrow bm-mapper-style role or equivalent admin, service, or system-reader policy that permits baremetal:node:list_all/node-detail visibility. If the privileged client cannot be created or Ironic rejects or fails the lookup, the monitor logs the failure and falls back to the VM default Building Active state so API responses still see a coherent live signal rather than failing the monitor path.

  • Some OpenStack list APIs are not safe to treat as exact lookup, notably server, network, Cinder volume, and Octavia load-balancer name filters:

    • name filters behave like prefix or regular-expression matches rather than strict equality
    • this package therefore re-checks exact names after listing to avoid aliasing and false matches
  • Provider networks that require VLAN segmentation use the local VLAN allocator because OpenStack does not allocate those IDs for us.

Octavia Load Balancers

OpenStack load balancers are reconciled through Octavia in the service principal's project. The region LoadBalancer CRD is still the desired-state root, while Octavia remains the cloud-side source of truth for the realized topology.

The provider reconciles the full topology:

  • the Octavia load balancer and VIP
  • listeners, pools, members, and optional health monitors
  • the optional public floating IP attached to the Octavia-owned VIP port

Cloud-side lookup uses deterministic names:

  • load balancer: lb-{loadBalancer}
  • listener: lb-{loadBalancer}-{listener}-listener
  • pool: lb-{loadBalancer}-{listener}-pool
  • health monitor: lb-{loadBalancer}-{listener}-monitor

Those names are not just cosmetic. They are the linkage contract that lets the provider re-find and converge existing Octavia resources without mirrored provider-state CRDs. Octavia list filters are fuzzy in the same way as other OpenStack name filters, so the client always post-filters returned resources by exact name and treats duplicate exact matches as consistency errors.

Octavia provisioning status controls the reconcile outcome:

  • ACTIVE allows the provider to continue reconciling the next part of the topology
  • PENDING_CREATE, PENDING_UPDATE, and PENDING_DELETE yield the controller so the next pass can observe settled state
  • any other state is treated as a consistency error because the provider cannot safely infer a valid next action

Mutable topology is converged in place where Octavia permits it:

  • listener allowed CIDRs
  • listener default-pool linkage
  • TCP listener idle timeouts
  • pool members
  • health-monitor thresholds
  • orphaned listeners, pools, and monitors whose deterministic names are no longer implied by the current spec

Other fields are intentionally blocked before they reach this provider. The handler keeps existing listener protocol and port immutable, and it blocks proxyProtocolV2 drift for an existing listener name because that changes the derived Octavia pool protocol, which Octavia does not allow to be updated in place.

There are a few Octavia-specific constraints worth preserving:

  • UDP listeners do not support idle timeouts or Proxy Protocol v2.
  • UDP health checks use Octavia's UDP connect monitor type.
  • TCP pools use Octavia PROXYV2 only when proxyProtocolV2 is enabled; the load-balancer client pins microversion 2.22 so that protocol is available.
  • Floating IP cleanup runs before cascade-deleting the load balancer because the cascade removes the VIP port that otherwise anchors the floating IP lookup.

Caveats

  • This package is the convergence point of a large amount of platform policy, provider behaviour, and historical baggage. Its size reflects real behaviour, not just poor code hygiene.
  • Deterministic lookup is the preferred direction, but the package still lives in a mixed world:
    • some cloud-side state is derived live from OpenStack
    • some transitional compatibility fields still exist in repo-native CRDs
    • OpenstackIdentity still persists state that the service would ideally stop owning over time
  • Deterministic lookup is cleaner than mirrored CRDs, but it is still sensitive to convention drift. Renaming generated resources, changing metadata keys, or casually altering project-scoping assumptions can break the linkage between Unikorn resources, what OpenStack stores, and what users can see from inside provisioned servers.
  • OpenstackIdentity should not be treated as permanently special. Its current survival is largely driven by implicit side effects and secret-bearing service-owned state that the architecture should work to remove:
    • ephemeral SSH key generation and download
    • implicit server-group creation
    • persisted service-principal/user/project/application-credential data
  • Exposing application credentials to higher layers is current operational reality, not the desired end state. The package's scoping model helps contain blast radius today, while the wider platform works toward removing that exposure entirely.
  • If the wider API moves toward explicit SSH certificate authority use, explicit server-group resources, and less implicit provider-side identity scaffolding, deleting OpenstackIdentity becomes more realistic.
  • Image metadata translation is powerful but fragile. This package currently depends on OpenStack image properties carrying a large amount of semantic information correctly.
  • Image query, get, create, delete, and snapshot flows are tightly coupled to the image cache path. When caching is disabled, large parts of the higher image contract are effectively unavailable rather than merely slower.
  • The image query layer still contains its own comment admitting that some logic now operates on generic types and probably should not live here long term.
  • Some older assumptions still leak through in status fields and helper paths, especially where compatibility with older API or storage shapes is still being carried.
  • Rebuild settlement rests on two environmental facts about the target cloud, both worth an integration assertion rather than assumption. First, that Nova flips the image ref to the target atomically with setting task_state at accept: if a cloud made the ref visible before task_state, a poll could see the converged ref with an empty task inside the rebuild window and stamp a premature Succeeded — the accept-to-settle lag window this design closes by gating settlement on task_state emptiness would silently reopen. Second, that OS-EXT-STS:task_state is actually visible to the region service principal (its exposure is policy-gated, and an unexposed field decodes indistinguishably from "at rest"); without that visibility the same premature clear returns. A kind/devstack assertion that a just-accepted rebuild's GET shows a non-empty task_state covers both.

TODO

  • Delete the remaining mirror-state OpenStack CRD usage paths entirely: OpenstackNetwork, OpenstackSecurityGroup, and OpenstackServer should not survive as authoritative provider-state patterns.
  • Continue shrinking the reasons OpenstackIdentity must exist:
    • remove service-handled private SSH key material in favour of explicit SSH certificate trust
    • stop relying on implicit server-group provisioning
    • move toward explicit API shapes where reconstructable state does not need to be persisted here
  • Revisit image-query and image-metadata logic that now operates on provider-neutral types but still lives in this package because of historical coupling.
  • Remove remaining compatibility writes and reads that depend on transitional CRD status shapes as those fields disappear from the wider system.

Cross-Package Context

  • ../../types defines the neutral provider contract and intermediate types this package must satisfy
  • ../../../apis/unikorn/v1alpha1 defines the service-native control objects and the remaining persisted provider-state records this package consumes
  • ../../../handler and specific handler packages depend on this package to make region API operations real against OpenStack
  • ../allocation/vlan exists because this package needs a compensating local allocator for provider-network VLAN IDs

Documentation

Index

Constants

View Source
const (
	// Projects are randomly named to avoid clashes, so we need to add some tags
	// in order to be able to reason about who they really belong to.  It is also
	// useful to have these in place so we can spot orphaned resources and garbage
	// collect them.
	OrganizationTag = "organization"
	ProjectTag      = "project"
)

Variables

View Source
var (
	// ErrPEMDecode is raised when the PEM decode failed for some reason.
	ErrPEMDecode = errors.New("PEM decode error")

	// ErrPEMType is raised when the encounter the wrong PEM type, e.g. PKCS#1.
	ErrPEMType = errors.New("PEM type unsupported")

	// ErrKeyType is raised when we encounter an unsupported key type.
	ErrKeyType = errors.New("key type unsupported")
)
View Source
var ErrMultipleIronicNodes = errors.New("multiple ironic nodes found")

Functions

func ImageSchema

func ImageSchema() (*jsonschema.Schema, error)

func ImageSchemaValid

func ImageSchemaValid(image *images.Image, schema *jsonschema.Schema) bool

func ImageSignatureValid

func ImageSignatureValid(image *images.Image, signingKeyRaw []byte) bool

Types

type ApplicationCredentialProvider

type ApplicationCredentialProvider struct {
	// contains filtered or unexported fields
}

ApplicationCredentialProvider allows use of an application credential.

func NewApplicationCredentialProvider

func NewApplicationCredentialProvider(endpoint, id, secret string) *ApplicationCredentialProvider

NewApplicationCredentialProvider creates a client that comsumes application credentials for authentication.

func (*ApplicationCredentialProvider) Client

Client implements the Provider interface.

type BaremetalClient added in v1.17.3

type BaremetalClient struct {
	// contains filtered or unexported fields
}

func NewBaremetalClient added in v1.17.3

func NewBaremetalClient(ctx context.Context, provider CredentialProvider) (*BaremetalClient, error)

func (*BaremetalClient) GetNodeByInstanceUUID added in v1.17.3

func (c *BaremetalClient) GetNodeByInstanceUUID(ctx context.Context, instanceUUID string) (*nodes.Node, error)

type BaremetalInterface added in v1.17.3

type BaremetalInterface interface {
	GetNodeByInstanceUUID(ctx context.Context, instanceUUID string) (*nodes.Node, error)
}

BaremetalInterface lets the live monitor look up the Ironic node bound to a Nova instance so it can refine Phase for baremetal servers in BUILD.

type BlockStorageClient

type BlockStorageClient struct {
	// contains filtered or unexported fields
}

BlockStorageClient wraps the generic client because gophercloud is unsafe.

func NewBlockStorageClient

func NewBlockStorageClient(ctx context.Context, provider CredentialProvider, options *unikornv1.RegionOpenstackBlockStorageSpec) (*BlockStorageClient, error)

NewBlockStorageClient provides a simple one-liner to start computing.

func (*BlockStorageClient) AvailabilityZones

AvailabilityZones retrieves block storage availability zones.

func (*BlockStorageClient) CreateVolume added in v1.21.0

func (c *BlockStorageClient) CreateVolume(ctx context.Context, volume *unikornv1.Volume, metadata map[string]string) (*volumes.Volume, error)

func (*BlockStorageClient) DeleteVolume added in v1.21.0

func (c *BlockStorageClient) DeleteVolume(ctx context.Context, id string) error

func (*BlockStorageClient) GetVolume added in v1.21.0

func (c *BlockStorageClient) GetVolume(ctx context.Context, volume *unikornv1.Volume) (*volumes.Volume, error)

func (*BlockStorageClient) GetVolumeTypes added in v1.20.0

func (c *BlockStorageClient) GetVolumeTypes(ctx context.Context) ([]volumetypes.VolumeType, error)

func (*BlockStorageClient) UpdateQuotas

func (c *BlockStorageClient) UpdateQuotas(ctx context.Context, projectID string) error

type ComputeClient

type ComputeClient struct {
	// contains filtered or unexported fields
}

ComputeClient wraps the generic client because gophercloud is unsafe.

func NewComputeClient

func NewComputeClient(ctx context.Context, provider CredentialProvider, options *unikornv1.RegionOpenstackComputeSpec) (*ComputeClient, error)

NewComputeClient provides a simple one-liner to start computing.

func (*ComputeClient) CreateImageFromServer added in v1.13.0

func (c *ComputeClient) CreateImageFromServer(ctx context.Context, id string, opts *servers.CreateImageOpts) (string, error)

func (*ComputeClient) CreateKeypair

func (c *ComputeClient) CreateKeypair(ctx context.Context, name, publicKey string) error

CreateKeypair creates a new keypair. NOTE: while OpenStack can generate one for us, we have far more control doing it ourselves thus allowing us to impose stricter security, and it's more provider agnostic that way.

func (*ComputeClient) CreateRemoteConsole

func (c *ComputeClient) CreateRemoteConsole(ctx context.Context, id string) (*remoteconsoles.RemoteConsole, error)

func (*ComputeClient) CreateServer

func (c *ComputeClient) CreateServer(ctx context.Context, server *unikornv1.Server, keyName string, networks []servers.Network, serverGroupID *string, metadata map[string]string) (*servers.Server, error)

func (*ComputeClient) CreateServerGroup

func (c *ComputeClient) CreateServerGroup(ctx context.Context, name string) (*servergroups.ServerGroup, error)

CreateServerGroup creates the named server group with the given policy and returns the result.

func (*ComputeClient) CreateVolumeAttachment added in v1.21.0

func (c *ComputeClient) CreateVolumeAttachment(ctx context.Context, serverID, volumeID string) (*volumeattach.VolumeAttachment, error)

func (*ComputeClient) DeleteKeypair

func (c *ComputeClient) DeleteKeypair(ctx context.Context, name string) error

func (*ComputeClient) DeleteServer

func (c *ComputeClient) DeleteServer(ctx context.Context, id string) error

func (*ComputeClient) DeleteServerGroup

func (c *ComputeClient) DeleteServerGroup(ctx context.Context, id string) error

DeleteServerGroup removes a server group, this exists because nova does do any cleanup on project deletion and just orphans the resource.

func (*ComputeClient) DeleteVolumeAttachment added in v1.21.0

func (c *ComputeClient) DeleteVolumeAttachment(ctx context.Context, serverID, volumeID string) error

func (*ComputeClient) GetFlavors

func (c *ComputeClient) GetFlavors(ctx context.Context) ([]flavors.Flavor, error)

Flavors returns a list of flavors.

func (*ComputeClient) GetServer

func (c *ComputeClient) GetServer(ctx context.Context, server *unikornv1.Server) (*servers.Server, error)

func (*ComputeClient) GetVolumeAttachment added in v1.21.0

func (c *ComputeClient) GetVolumeAttachment(ctx context.Context, serverID, volumeID string) (*volumeattach.VolumeAttachment, error)

func (*ComputeClient) RebootServer

func (c *ComputeClient) RebootServer(ctx context.Context, id string, hard bool) error

func (*ComputeClient) RebuildServer added in v1.20.0

func (c *ComputeClient) RebuildServer(ctx context.Context, id string, options ServerRebuildOptions) (*servers.Server, error)

func (*ComputeClient) ShowConsoleOutput

func (c *ComputeClient) ShowConsoleOutput(ctx context.Context, id string, length *int) (string, error)

func (*ComputeClient) StartServer

func (c *ComputeClient) StartServer(ctx context.Context, id string) error

func (*ComputeClient) StopServer

func (c *ComputeClient) StopServer(ctx context.Context, id string) error

func (*ComputeClient) UpdateQuotas

func (c *ComputeClient) UpdateQuotas(ctx context.Context, projectID string) error

type ComputeQuotaInterface

type ComputeQuotaInterface interface {
	UpdateQuotas(ctx context.Context, projectID string) error
}

type CreateTokenOptions

type CreateTokenOptions interface {
	// Options returns a valid set of authentication options.
	Options() *tokens.AuthOptions
}

CreateTokenOptions abstracts away how schizophrenic Openstack is with its million options and million ways to fuck it up.

type CreateTokenOptionsScopedToken

type CreateTokenOptionsScopedToken struct {
	// contains filtered or unexported fields
}

CreateTokenOptionsScopedToken is typically used to upgrade from an unscoped password passed login to a project scoped one once you have determined a valid project.

func NewCreateTokenOptionsScopedToken

func NewCreateTokenOptionsScopedToken(token, projectID string) *CreateTokenOptionsScopedToken

NewCreateTokenOptionsScopedToken returns a new instance of project scoped token options.

func (*CreateTokenOptionsScopedToken) Options

Options implements the CreateTokenOptions interface.

type CreateTokenOptionsUnscopedPassword

type CreateTokenOptionsUnscopedPassword struct {
	// contains filtered or unexported fields
}

CreateTokenOptionsUnscopedPassword is typically used when logging on to a UI when you don't know anything other than username/password.

func NewCreateTokenOptionsUnscopedPassword

func NewCreateTokenOptionsUnscopedPassword(domain, username, password string) *CreateTokenOptionsUnscopedPassword

NewCreateTokenOptionsUnscopedPassword returns a new instance of unscoped username/password options.

func (*CreateTokenOptionsUnscopedPassword) Options

Options implements the CreateTokenOptions interface.

type CredentialProvider

type CredentialProvider interface {
	// Client returns a new provider client.
	Client(ctx context.Context) (*gophercloud.ProviderClient, error)
}

CredentialProvider abstracts authentication methods.

type DomainScopedPasswordProvider

type DomainScopedPasswordProvider struct {
	// contains filtered or unexported fields
}

DomainScopedPasswordProvider allows use of an application credential.

func NewDomainScopedPasswordProvider

func NewDomainScopedPasswordProvider(endpoint, userID, password, domainID string) *DomainScopedPasswordProvider

NewDomainScopedPasswordProvider creates a client that consumes passwords for authentication.

func (*DomainScopedPasswordProvider) Client

Client implements the Provider interface.

type ExternalNetworkInterface added in v1.13.0

type ExternalNetworkInterface interface {
	ExternalNetworks(ctx context.Context) ([]networks.Network, error)
}

type FlavorInterface

type FlavorInterface interface {
	GetFlavors(ctx context.Context) ([]flavors.Flavor, error)
}

type FloatingIPInterface

type FloatingIPInterface interface {
	GetFloatingIP(ctx context.Context, portID string) (*floatingips.FloatingIP, error)
	CreateFloatingIP(ctx context.Context, portID string) (*floatingips.FloatingIP, error)
	DeleteFloatingIP(ctx context.Context, id string) error
}

type IdentityClient

type IdentityClient struct {
	// contains filtered or unexported fields
}

IdentityClient wraps up gophercloud identity management.

func NewIdentityClient

func NewIdentityClient(ctx context.Context, provider CredentialProvider) (*IdentityClient, error)

NewIdentityClient returns a new identity client.

func (*IdentityClient) CreateApplicationCredential

func (c *IdentityClient) CreateApplicationCredential(ctx context.Context, userID, name, description string, roles []string) (*applicationcredentials.ApplicationCredential, error)

CreateApplicationCredential creates an application credential for the user.

func (*IdentityClient) CreateProject

func (c *IdentityClient) CreateProject(ctx context.Context, domainID, name string, tags []string) (*projects.Project, error)

CreateProject creates the named project.

func (*IdentityClient) CreateRoleAssignment

func (c *IdentityClient) CreateRoleAssignment(ctx context.Context, userID, projectID, roleID string) error

CreateRoleAssignment creates a role between a user and a project.

func (*IdentityClient) CreateToken

func (c *IdentityClient) CreateToken(ctx context.Context, options CreateTokenOptions) (*tokens.Token, *tokens.User, error)

CreateToken issues a new token.

func (*IdentityClient) CreateUser

func (c *IdentityClient) CreateUser(ctx context.Context, domainID, name, password string) (*users.User, error)

CreateUser creates a new user.

func (*IdentityClient) DeleteApplicationCredential

func (c *IdentityClient) DeleteApplicationCredential(ctx context.Context, userID, id string) error

DeleteApplicationCredential deletes an application credential for the user.

func (*IdentityClient) DeleteProject

func (c *IdentityClient) DeleteProject(ctx context.Context, projectID string) error

func (*IdentityClient) DeleteUser

func (c *IdentityClient) DeleteUser(ctx context.Context, userID string) error

DeleteUser removes an existing user.

func (*IdentityClient) ListApplicationCredentials

func (c *IdentityClient) ListApplicationCredentials(ctx context.Context, userID string) ([]applicationcredentials.ApplicationCredential, error)

ListApplicationCredentials lists application credentials for the scoped user.

func (*IdentityClient) ListAvailableProjects

func (c *IdentityClient) ListAvailableProjects(ctx context.Context) ([]projects.Project, error)

ListAvailableProjects lists projects that an authenticated (but unscoped) user can scope to.

func (*IdentityClient) ListRoles

func (c *IdentityClient) ListRoles(ctx context.Context) ([]roles.Role, error)

ListRoles grabs a set of roles that are on the provider.

type ImageClient

type ImageClient struct {
	// contains filtered or unexported fields
}

ImageClient wraps the generic client because gophercloud is unsafe.

func NewImageClient

func NewImageClient(ctx context.Context, provider CredentialProvider, options *unikornv1.RegionOpenstackImageSpec) (*ImageClient, error)

NewImageClient provides a simple one-liner to start computing.

func (*ImageClient) CreateImage added in v1.12.0

func (c *ImageClient) CreateImage(ctx context.Context, opts *images.CreateOpts) (*images.Image, error)

CreateImage creates a new image.

func (*ImageClient) DeleteImage added in v1.12.0

func (c *ImageClient) DeleteImage(ctx context.Context, id string) error

func (*ImageClient) GetImage

func (c *ImageClient) GetImage(ctx context.Context, id string) (*images.Image, error)

GetImage retrieves a specific image by its ID.

func (*ImageClient) Import added in v1.13.0

func (c *ImageClient) Import(ctx context.Context, id, uri string) error

func (*ImageClient) ListImages

func (c *ImageClient) ListImages(ctx context.Context) ([]images.Image, error)

ListImages returns a list of images.

func (*ImageClient) UpdateImage added in v1.12.0

func (c *ImageClient) UpdateImage(ctx context.Context, id string, opts images.UpdateOpts) (*images.Image, error)

type KeypairInterface

type KeypairInterface interface {
	CreateKeypair(ctx context.Context, name, publicKey string) error
	DeleteKeypair(ctx context.Context, name string) error
}

type LoadBalancerClient added in v1.16.5

type LoadBalancerClient struct {
	// contains filtered or unexported fields
}

LoadBalancerClient wraps the generic client because gophercloud is unsafe.

func NewLoadBalancerClient added in v1.16.5

func NewLoadBalancerClient(ctx context.Context, provider CredentialProvider) (*LoadBalancerClient, error)

NewLoadBalancerClient creates an Octavia v2 client.

func (*LoadBalancerClient) BatchUpdateMembers added in v1.16.5

func (c *LoadBalancerClient) BatchUpdateMembers(ctx context.Context, poolID string, opts []pools.BatchUpdateMemberOpts) error

func (*LoadBalancerClient) CreateListener added in v1.16.5

func (*LoadBalancerClient) CreateLoadBalancer added in v1.16.5

func (*LoadBalancerClient) CreateMonitor added in v1.16.5

func (*LoadBalancerClient) CreatePool added in v1.16.5

func (*LoadBalancerClient) DeleteListener added in v1.16.5

func (c *LoadBalancerClient) DeleteListener(ctx context.Context, id string) error

func (*LoadBalancerClient) DeleteLoadBalancer added in v1.16.5

func (c *LoadBalancerClient) DeleteLoadBalancer(ctx context.Context, id string, cascade bool) error

func (*LoadBalancerClient) DeleteMonitor added in v1.16.5

func (c *LoadBalancerClient) DeleteMonitor(ctx context.Context, id string) error

func (*LoadBalancerClient) DeletePool added in v1.16.5

func (c *LoadBalancerClient) DeletePool(ctx context.Context, id string) error

func (*LoadBalancerClient) GetListener added in v1.16.5

func (c *LoadBalancerClient) GetListener(ctx context.Context, loadBalancerID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*listeners.Listener, error)

func (*LoadBalancerClient) GetLoadBalancer added in v1.16.5

func (c *LoadBalancerClient) GetLoadBalancer(ctx context.Context, loadBalancer *unikornv1.LoadBalancer) (*loadbalancers.LoadBalancer, error)

func (*LoadBalancerClient) GetMonitor added in v1.16.5

func (c *LoadBalancerClient) GetMonitor(ctx context.Context, poolID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*monitors.Monitor, error)

func (*LoadBalancerClient) GetPool added in v1.16.5

func (c *LoadBalancerClient) GetPool(ctx context.Context, loadBalancerID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*pools.Pool, error)

func (*LoadBalancerClient) ListListeners added in v1.16.5

func (c *LoadBalancerClient) ListListeners(ctx context.Context, loadBalancerID, name string) ([]listeners.Listener, error)

func (*LoadBalancerClient) ListLoadBalancers added in v1.16.5

func (c *LoadBalancerClient) ListLoadBalancers(ctx context.Context, name string) ([]loadbalancers.LoadBalancer, error)

func (*LoadBalancerClient) ListMembers added in v1.16.5

func (c *LoadBalancerClient) ListMembers(ctx context.Context, poolID string) ([]pools.Member, error)

func (*LoadBalancerClient) ListMonitors added in v1.16.5

func (c *LoadBalancerClient) ListMonitors(ctx context.Context, poolID, name string) ([]monitors.Monitor, error)

func (*LoadBalancerClient) ListPools added in v1.16.5

func (c *LoadBalancerClient) ListPools(ctx context.Context, loadBalancerID, name string) ([]pools.Pool, error)

func (*LoadBalancerClient) UpdateListener added in v1.16.5

func (*LoadBalancerClient) UpdateLoadBalancer added in v1.16.5

func (*LoadBalancerClient) UpdateMonitor added in v1.16.5

func (*LoadBalancerClient) UpdatePool added in v1.16.5

func (c *LoadBalancerClient) UpdatePool(ctx context.Context, id string, opts pools.UpdateOptsBuilder) (*pools.Pool, error)

type LoadBalancerInterface added in v1.16.5

type LoadBalancerInterface interface {
	ListLoadBalancers(ctx context.Context, name string) ([]loadbalancers.LoadBalancer, error)
	GetLoadBalancer(ctx context.Context, loadBalancer *unikornv1.LoadBalancer) (*loadbalancers.LoadBalancer, error)
	CreateLoadBalancer(ctx context.Context, opts loadbalancers.CreateOptsBuilder) (*loadbalancers.LoadBalancer, error)
	UpdateLoadBalancer(ctx context.Context, id string, opts loadbalancers.UpdateOptsBuilder) (*loadbalancers.LoadBalancer, error)
	DeleteLoadBalancer(ctx context.Context, id string, cascade bool) error
}

type LoadBalancerListenerInterface added in v1.16.5

type LoadBalancerListenerInterface interface {
	ListListeners(ctx context.Context, loadBalancerID, name string) ([]listeners.Listener, error)
	GetListener(ctx context.Context, loadBalancerID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*listeners.Listener, error)
	CreateListener(ctx context.Context, opts listeners.CreateOptsBuilder) (*listeners.Listener, error)
	UpdateListener(ctx context.Context, id string, opts listeners.UpdateOptsBuilder) (*listeners.Listener, error)
	DeleteListener(ctx context.Context, id string) error
}

type LoadBalancerMemberInterface added in v1.16.5

type LoadBalancerMemberInterface interface {
	ListMembers(ctx context.Context, poolID string) ([]pools.Member, error)
	BatchUpdateMembers(ctx context.Context, poolID string, opts []pools.BatchUpdateMemberOpts) error
}

type LoadBalancerMonitorInterface added in v1.16.5

type LoadBalancerMonitorInterface interface {
	ListMonitors(ctx context.Context, poolID, name string) ([]monitors.Monitor, error)
	GetMonitor(ctx context.Context, poolID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*monitors.Monitor, error)
	CreateMonitor(ctx context.Context, opts monitors.CreateOptsBuilder) (*monitors.Monitor, error)
	UpdateMonitor(ctx context.Context, id string, opts monitors.UpdateOptsBuilder) (*monitors.Monitor, error)
	DeleteMonitor(ctx context.Context, id string) error
}

type LoadBalancerPoolInterface added in v1.16.5

type LoadBalancerPoolInterface interface {
	ListPools(ctx context.Context, loadBalancerID, name string) ([]pools.Pool, error)
	GetPool(ctx context.Context, loadBalancerID string, loadBalancer *unikornv1.LoadBalancer, listener *unikornv1.LoadBalancerListener) (*pools.Pool, error)
	CreatePool(ctx context.Context, opts pools.CreateOptsBuilder) (*pools.Pool, error)
	UpdatePool(ctx context.Context, id string, opts pools.UpdateOptsBuilder) (*pools.Pool, error)
	DeletePool(ctx context.Context, id string) error
}

type NetworkClient

type NetworkClient struct {
	// contains filtered or unexported fields
}

NetworkClient wraps the generic client because gophercloud is unsafe.

func NewNetworkClient

func NewNetworkClient(ctx context.Context, provider CredentialProvider, options *unikornv1.RegionOpenstackNetworkSpec) (*NetworkClient, error)

NewNetworkClient provides a simple one-liner to start networking.

func NewTestNetworkClient

func NewTestNetworkClient(options *unikornv1.RegionOpenstackNetworkSpec) *NetworkClient

func (*NetworkClient) AddRouterInterface

func (c *NetworkClient) AddRouterInterface(ctx context.Context, routerID, subnetID string) error

func (*NetworkClient) CreateFloatingIP

func (c *NetworkClient) CreateFloatingIP(ctx context.Context, portID string) (*floatingips.FloatingIP, error)

CreateFloatingIP creates a floating IP.

func (*NetworkClient) CreateNetwork

func (c *NetworkClient) CreateNetwork(ctx context.Context, network *unikornv1.Network, vlanID *int) (*NetworkExt, error)

CreateNetwork creates a virtual or VLAN provider network for a project. This requires https://github.com/unikorn-cloud/python-unikorn-openstack-policy to be installed, see the README for further details on how this has to work.

func (*NetworkClient) CreateRouter

func (c *NetworkClient) CreateRouter(ctx context.Context, network *unikornv1.Network) (*routers.Router, error)

func (*NetworkClient) CreateSecurityGroup

func (c *NetworkClient) CreateSecurityGroup(ctx context.Context, securityGroup *unikornv1.SecurityGroup) (*groups.SecGroup, error)

CreateSecurityGroup creates a new security group.

func (*NetworkClient) CreateSecurityGroupRule

func (c *NetworkClient) CreateSecurityGroupRule(ctx context.Context, securityGroupID string, direction rules.RuleDirection, protocol rules.RuleProtocol, portStart, portEnd int, prefix string) (*rules.SecGroupRule, error)

CreateSecurityGroupRule adds a security group rule to a security group.

func (*NetworkClient) CreateServerPort

func (c *NetworkClient) CreateServerPort(ctx context.Context, server *unikornv1.Server, networkID string, securityGroupIDs []string, allowedAddressPairs []ports.AddressPair) (*ports.Port, error)

func (*NetworkClient) CreateSubnet

func (c *NetworkClient) CreateSubnet(ctx context.Context, network *unikornv1.Network, networkID, prefix, gatewayIP string, dnsNameservers []string, routes []subnets.HostRoute, allocationPools []subnets.AllocationPool) (*subnets.Subnet, error)

func (*NetworkClient) DeleteFloatingIP

func (c *NetworkClient) DeleteFloatingIP(ctx context.Context, id string) error

DeleteFloatingIP deletes a floating IP.

func (*NetworkClient) DeleteNetwork

func (c *NetworkClient) DeleteNetwork(ctx context.Context, id string) error

func (*NetworkClient) DeletePort

func (c *NetworkClient) DeletePort(ctx context.Context, portID string) error

func (*NetworkClient) DeleteRouter

func (c *NetworkClient) DeleteRouter(ctx context.Context, id string) error

func (*NetworkClient) DeleteSecurityGroup

func (c *NetworkClient) DeleteSecurityGroup(ctx context.Context, securityGroupID string) error

DeleteSecurityGroup deletes a security group.

func (*NetworkClient) DeleteSecurityGroupRule

func (c *NetworkClient) DeleteSecurityGroupRule(ctx context.Context, securityGroupID, ruleID string) error

DeleteSecurityGroupRule deletes a security group rule from a security group.

func (*NetworkClient) DeleteSubnet

func (c *NetworkClient) DeleteSubnet(ctx context.Context, id string) error

func (*NetworkClient) ExternalNetworks

func (c *NetworkClient) ExternalNetworks(ctx context.Context) ([]networks.Network, error)

ExternalNetworks returns a list of external networks.

func (*NetworkClient) GetFloatingIP

func (c *NetworkClient) GetFloatingIP(ctx context.Context, portID string) (*floatingips.FloatingIP, error)

func (*NetworkClient) GetNetwork

func (c *NetworkClient) GetNetwork(ctx context.Context, network *unikornv1.Network) (*NetworkExt, error)

func (*NetworkClient) GetRouter

func (c *NetworkClient) GetRouter(ctx context.Context, network *unikornv1.Network) (*routers.Router, error)

func (*NetworkClient) GetSecurityGroup

func (c *NetworkClient) GetSecurityGroup(ctx context.Context, securityGroup *unikornv1.SecurityGroup) (*groups.SecGroup, error)

func (*NetworkClient) GetServerPort

func (c *NetworkClient) GetServerPort(ctx context.Context, server *unikornv1.Server) (*ports.Port, error)

func (*NetworkClient) GetSubnet

func (c *NetworkClient) GetSubnet(ctx context.Context, network *unikornv1.Network) (*subnets.Subnet, error)

func (*NetworkClient) ListRouterPorts

func (c *NetworkClient) ListRouterPorts(ctx context.Context, routerID string) ([]ports.Port, error)

func (*NetworkClient) ListSecurityGroupRules

func (c *NetworkClient) ListSecurityGroupRules(ctx context.Context, securityGroupID string) ([]rules.SecGroupRule, error)

ListSecurityGroupRules does exactly that.

func (*NetworkClient) ListServerPorts

func (c *NetworkClient) ListServerPorts(ctx context.Context, serverID string) ([]ports.Port, error)

ListServerPorts returns a list of ports for a server.

func (*NetworkClient) RemoveRouterInterface

func (c *NetworkClient) RemoveRouterInterface(ctx context.Context, routerID, subnetID string) error

func (*NetworkClient) UpdatePort

func (c *NetworkClient) UpdatePort(ctx context.Context, portID string, securityGroupIDs []string, allowedAddressPairs []ports.AddressPair) (*ports.Port, error)

func (*NetworkClient) UpdateQuotas added in v1.13.2

func (c *NetworkClient) UpdateQuotas(ctx context.Context, projectID string) error

Update quotas overrides any OpenStack default quotas for the project's networking. All networking limits are removed.

func (*NetworkClient) UpdateSubnet

func (c *NetworkClient) UpdateSubnet(ctx context.Context, subnetID string, dnsNameservers []string, routes []subnets.HostRoute) (*subnets.Subnet, error)

type NetworkExt

type NetworkExt struct {
	networks.Network
	provider.NetworkProviderExt
}

type NetworkInterface

type NetworkInterface interface {
	GetNetwork(ctx context.Context, network *unikornv1.Network) (*NetworkExt, error)
	CreateNetwork(ctx context.Context, network *unikornv1.Network, vlanID *int) (*NetworkExt, error)
	DeleteNetwork(ctx context.Context, id string) error
}

type Options added in v1.16.0

type Options struct {
	// WarmImageCache enables startup-time image cache initialization.
	WarmImageCache bool
}

type PasswordProvider

type PasswordProvider struct {
	// contains filtered or unexported fields
}

PasswordProvider allows use of an application credential.

func NewPasswordProvider

func NewPasswordProvider(endpoint, userID, password, projectID string) *PasswordProvider

NewPasswordProvider creates a client that comsumes passwords for authentication.

func (*PasswordProvider) Client

Client implements the Provider interface.

type PlacementClient added in v1.17.6

type PlacementClient struct {
	// contains filtered or unexported fields
}

PlacementClient queries OpenStack Placement for provider availability.

func NewPlacementClient added in v1.17.6

func NewPlacementClient(ctx context.Context, provider CredentialProvider) (*PlacementClient, error)

NewPlacementClient creates a Placement API client.

func (*PlacementClient) ResourceProviderAvailable added in v1.17.6

func (c *PlacementClient) ResourceProviderAvailable(ctx context.Context, query PlacementResourceProviderQuery) (bool, error)

ResourceProviderAvailable reports whether the pinned provider currently has available inventory and required traits.

type PlacementInterface added in v1.17.6

type PlacementInterface interface {
	ResourceProviderAvailable(ctx context.Context, query PlacementResourceProviderQuery) (bool, error)
}

type PlacementResourceProviderQuery added in v1.17.6

type PlacementResourceProviderQuery struct {
	InfrastructureRef string
	ResourceClass     string
	RequiredTraits    []string
}

PlacementResourceProviderQuery scopes an OpenStack Placement provider lookup.

type PortInterface

type PortInterface interface {
	ListServerPorts(ctx context.Context, serverID string) ([]ports.Port, error)
	ListRouterPorts(ctx context.Context, routerID string) ([]ports.Port, error)
	GetServerPort(ctx context.Context, server *unikornv1.Server) (*ports.Port, error)
	CreateServerPort(ctx context.Context, server *unikornv1.Server, networkID string, securityGroupIDs []string, allowedAddressPairs []ports.AddressPair) (*ports.Port, error)
	UpdatePort(ctx context.Context, portID string, securityGroupIDs []string, allowedAddressPairs []ports.AddressPair) (*ports.Port, error)
	DeletePort(ctx context.Context, portID string) error
}

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

func New

func New(ctx context.Context, initClient client.Client, runtimeClient client.Client, region *unikornv1.Region, opts Options) (*Provider, error)

New constructs an OpenStack provider.

Provider construction has two phases: 1. Bootstrap service-client state with initClient before any controller cache exists. 2. Return a provider that retains runtimeClient for all subsequent Kubernetes reads.

This makes the bootstrap/runtime boundary explicit: direct reads are used only while building the initial OpenStack client state, and normal provider operation switches back to the runtime client immediately afterwards.

func (*Provider) AttachVolume added in v1.21.0

func (p *Provider) AttachVolume(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, volume *unikornv1.Volume) (*types.ServerVolumeAttachment, error)

func (*Provider) CreateConsoleSession

func (p *Provider) CreateConsoleSession(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server) (string, error)

func (*Provider) CreateIdentity

func (p *Provider) CreateIdentity(ctx context.Context, identity *unikornv1.Identity) error

CreateIdentity creates a new identity for cloud infrastructure.

func (*Provider) CreateImage added in v1.13.0

func (p *Provider) CreateImage(ctx context.Context, image *types.Image, uri string) (*types.Image, error)

CreateImage creates a new image.

func (*Provider) CreateLoadBalancer added in v1.16.1

func (p *Provider) CreateLoadBalancer(ctx context.Context, identity *unikornv1.Identity, loadBalancer *unikornv1.LoadBalancer) error

CreateLoadBalancer reconciles the full Octavia topology — load balancer, listeners, pools, members, and health monitors — for the given spec. It yields between PENDING transitions and surfaces VIP mismatches and other terminal Octavia states as ErrConsistency.

func (*Provider) CreateNetwork

func (p *Provider) CreateNetwork(ctx context.Context, identity *unikornv1.Identity, network *unikornv1.Network) error

CreateNetwork creates a physical network for an identity.

func (*Provider) CreateSecurityGroup

func (p *Provider) CreateSecurityGroup(ctx context.Context, identity *unikornv1.Identity, securityGroup *unikornv1.SecurityGroup) error

CreateSecurityGroup creates a new security group.

func (*Provider) CreateServer

func (p *Provider) CreateServer(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, options *types.ServerCreateOptions) error

func (*Provider) CreateSnapshot added in v1.13.0

func (p *Provider) CreateSnapshot(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, image *types.Image) (*types.Image, error)

CreateSnapshot creates a new image from an existing server.

func (*Provider) CreateVolume added in v1.21.0

func (p *Provider) CreateVolume(ctx context.Context, identity *unikornv1.Identity, volume *unikornv1.Volume) error

func (*Provider) DeleteIdentity

func (p *Provider) DeleteIdentity(ctx context.Context, identity *unikornv1.Identity) error

DeleteIdentity cleans up an identity for cloud infrastructure.

func (*Provider) DeleteImage added in v1.12.0

func (p *Provider) DeleteImage(ctx context.Context, imageID regionids.ImageID) error

func (*Provider) DeleteLoadBalancer added in v1.16.1

func (p *Provider) DeleteLoadBalancer(ctx context.Context, identity *unikornv1.Identity, loadBalancer *unikornv1.LoadBalancer) error

DeleteLoadBalancer removes the Octavia topology and any attached floating IP idempotently. It yields while Octavia is in any PENDING_* state and after issuing a cascade delete so the next reconcile can confirm completion.

func (*Provider) DeleteNetwork

func (p *Provider) DeleteNetwork(ctx context.Context, identity *unikornv1.Identity, network *unikornv1.Network) error

DeleteNetwork deletes a physical network.

func (*Provider) DeleteSecurityGroup

func (p *Provider) DeleteSecurityGroup(ctx context.Context, identity *unikornv1.Identity, securityGroup *unikornv1.SecurityGroup) error

DeleteSecurityGroup deletes a security group.

func (*Provider) DeleteServer

func (p *Provider) DeleteServer(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server) error

func (*Provider) DeleteVolume added in v1.21.0

func (p *Provider) DeleteVolume(ctx context.Context, identity *unikornv1.Identity, volume *unikornv1.Volume) error

func (*Provider) DetachVolume added in v1.21.0

func (p *Provider) DetachVolume(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, volume *unikornv1.Volume) error

func (*Provider) Flavors

func (p *Provider) Flavors(ctx context.Context) (types.FlavorList, error)

Flavors list all available flavors.

func (*Provider) GetConsoleOutput

func (p *Provider) GetConsoleOutput(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, length *int) (string, error)

func (*Provider) GetImage

func (p *Provider) GetImage(ctx context.Context, organizationID identityids.OrganizationID, imageID regionids.ImageID) (*types.Image, error)

GetImage retrieves a specific image by its ID.

func (*Provider) GetOpenstackIdentity

func (p *Provider) GetOpenstackIdentity(ctx context.Context, identity *unikornv1.Identity) (*unikornv1.OpenstackIdentity, error)

func (*Provider) GetOrCreateOpenstackIdentity

func (p *Provider) GetOrCreateOpenstackIdentity(ctx context.Context, identity *unikornv1.Identity) (*unikornv1.OpenstackIdentity, bool, error)

func (*Provider) ListExternalNetworks

func (p *Provider) ListExternalNetworks(ctx context.Context) (types.ExternalNetworks, error)

ListExternalNetworks returns a list of external networks if the platform supports such a concept.

func (*Provider) QueryImages added in v1.14.0

func (p *Provider) QueryImages() (types.ImageQuery, error)

func (*Provider) RebootServer

func (p *Provider) RebootServer(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server, hard bool) error

func (*Provider) Region

func (p *Provider) Region(ctx context.Context) (*unikornv1.Region, error)

Region returns the provider's region.

func (*Provider) StartServer

func (p *Provider) StartServer(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server) error

func (*Provider) StopServer

func (p *Provider) StopServer(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server) error

func (*Provider) UpdateServerState

func (p *Provider) UpdateServerState(ctx context.Context, identity *unikornv1.Identity, server *unikornv1.Server) error

UpdateServerState checks a server's state and modifies the resource in place.

func (*Provider) VolumeClasses added in v1.20.0

func (p *Provider) VolumeClasses(ctx context.Context) (types.VolumeClassList, error)

type RouterInterface

type RouterInterface interface {
	GetRouter(ctx context.Context, network *unikornv1.Network) (*routers.Router, error)
	CreateRouter(ctx context.Context, network *unikornv1.Network) (*routers.Router, error)
	DeleteRouter(ctx context.Context, id string) error

	AddRouterInterface(ctx context.Context, routerID, subnetID string) error
	RemoveRouterInterface(ctx context.Context, routerID, subnetID string) error
}

type SecurityGroupInterface

type SecurityGroupInterface interface {
	GetSecurityGroup(ctx context.Context, securityGroup *unikornv1.SecurityGroup) (*groups.SecGroup, error)
	CreateSecurityGroup(ctx context.Context, securityGroup *unikornv1.SecurityGroup) (*groups.SecGroup, error)
	DeleteSecurityGroup(ctx context.Context, securityGroupID string) error
	ListSecurityGroupRules(ctx context.Context, securityGroupID string) ([]rules.SecGroupRule, error)
	CreateSecurityGroupRule(ctx context.Context, securityGroupID string, direction rules.RuleDirection, protocol rules.RuleProtocol, portStart, portEnd int, prefix string) (*rules.SecGroupRule, error)
	DeleteSecurityGroupRule(ctx context.Context, securityGroupID, ruleID string) error
}

type ServerGroupInterface

type ServerGroupInterface interface {
	CreateServerGroup(ctx context.Context, name string) (*servergroups.ServerGroup, error)
	DeleteServerGroup(ctx context.Context, id string) error
}

type ServerInterface

type ServerInterface interface {
	GetServer(ctx context.Context, server *unikornv1.Server) (*servers.Server, error)
	CreateServer(ctx context.Context, server *unikornv1.Server, keyName string, networks []servers.Network, serverGroupID *string, metadata map[string]string) (*servers.Server, error)
	DeleteServer(ctx context.Context, id string) error
	RebuildServer(ctx context.Context, id string, options ServerRebuildOptions) (*servers.Server, error)
	RebootServer(ctx context.Context, id string, hard bool) error
	StartServer(ctx context.Context, id string) error
	StopServer(ctx context.Context, id string) error
	CreateRemoteConsole(ctx context.Context, id string) (*remoteconsoles.RemoteConsole, error)
	ShowConsoleOutput(ctx context.Context, id string, length *int) (string, error)
	CreateImageFromServer(ctx context.Context, id string, opts *servers.CreateImageOpts) (string, error)
}

type ServerRebuildOptions added in v1.20.0

type ServerRebuildOptions struct {
	ImageID regionids.ImageID
}

type SubnetInterface

type SubnetInterface interface {
	GetSubnet(ctx context.Context, network *unikornv1.Network) (*subnets.Subnet, error)
	CreateSubnet(ctx context.Context, network *unikornv1.Network, networkID string, prefix, gatewayID string, dnsNameservers []string, routes []subnets.HostRoute, allocationPools []subnets.AllocationPool) (*subnets.Subnet, error)
	UpdateSubnet(ctx context.Context, subnetID string, dnsNameservers []string, routes []subnets.HostRoute) (*subnets.Subnet, error)
	DeleteSubnet(ctx context.Context, id string) error
}

type TokenProvider

type TokenProvider struct {
	// contains filtered or unexported fields
}

TokenProvider creates a client from an endpoint and token.

func NewTokenProvider

func NewTokenProvider(endpoint, token string) *TokenProvider

NewTokenProvider returns a new initialized provider.

func (*TokenProvider) Client

Client implements the Provider interface.

type UnauthenticatedProvider

type UnauthenticatedProvider struct {
	// contains filtered or unexported fields
}

UnauthenticatedProvider is used for token issue.

func NewUnauthenticatedProvider

func NewUnauthenticatedProvider(endpoint string) *UnauthenticatedProvider

NewUnauthenticatedProvider returns a new initialized provider.

func (*UnauthenticatedProvider) Client

Client implements the Provider interface.

type VolumeAttachmentInterface added in v1.21.0

type VolumeAttachmentInterface interface {
	GetVolumeAttachment(ctx context.Context, serverID, volumeID string) (*volumeattach.VolumeAttachment, error)
	CreateVolumeAttachment(ctx context.Context, serverID, volumeID string) (*volumeattach.VolumeAttachment, error)
	DeleteVolumeAttachment(ctx context.Context, serverID, volumeID string) error
}

type VolumeInterface added in v1.21.0

type VolumeInterface interface {
	GetVolume(ctx context.Context, volume *unikornv1.Volume) (*volumes.Volume, error)
	CreateVolume(ctx context.Context, volume *unikornv1.Volume, metadata map[string]string) (*volumes.Volume, error)
	DeleteVolume(ctx context.Context, id string) error
}

type VolumeTypeInterface added in v1.20.0

type VolumeTypeInterface interface {
	GetVolumeTypes(ctx context.Context) ([]volumetypes.VolumeType, error)
}

Directories

Path Synopsis
Code generated by MockGen.
Code generated by MockGen.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL