Skip to content

Enh/fallback allowable values simpleupdate#846

Open
davidgrun wants to merge 3 commits into
mainfrom
enh/fallback_allowableValues_simpleupdate
Open

Enh/fallback allowable values simpleupdate#846
davidgrun wants to merge 3 commits into
mainfrom
enh/fallback_allowableValues_simpleupdate

Conversation

@davidgrun
Copy link
Copy Markdown
Contributor

@davidgrun davidgrun commented Apr 28, 2026

Fallback transfer protocol for firmware updates

Some BMC implementations only support a subset of transfer protocols for the Redfish SimpleUpdate action. When a firmware image URI is configured with a protocol the BMC doesn't support, the update would silently fail or error with no clear recovery path.

This PR adds a fallback mechanism to ImageSpec: alongside the existing transferProtocol and uri fields, operators can now configure fallbackTransferProtocol and fallbackURI. Before issuing a firmware update, the controller queries the BMC's UpdateService for its TransferProtocol@Redfish.AllowableValues list and selects which protocol to use. The BMC then fetches the firmware image directly from the resolved URI.

Protocol resolution:

  • If the BMC reports no restrictions, the primary protocol is used as-is.
  • If the primary protocol is supported, it is used.
  • If not, and a fallback is configured and supported, the fallback is used.
  • If neither is supported, reconciliation fails with a clear error.

This applies to both BMCVersion and BIOSVersion controllers. CEL validation rules enforce that fallbackTransferProtocol and fallbackURI are always set together and non-empty, and that a fallback requires a non-empty primary protocol to be set.

Example

apiVersion: metal.ironcore.dev/v1alpha1
kind: BMCVersion
metadata:
  name: my-bmc-update
spec:
  bmcRef:
    name: my-bmc
  bmcVersionTemplate:
    version: "1.2.3"
    image:
      uri: "https://firmware.example.com/bmc-v1.2.3.bin"
      transferProtocol: "HTTPS"
      fallbackTransferProtocol: "TFTP"
      fallbackURI: "tftp://firmware-internal.example.com/bmc-v1.2.3.bin"

The fallback fields are optional — if omitted, the controller behaves exactly as before.

@davidgrun davidgrun requested review from atd9876 and xkonni April 28, 2026 17:45
@davidgrun davidgrun requested a review from a team as a code owner April 28, 2026 17:46
@github-actions github-actions Bot added api-change documentation Improvements or additions to documentation enhancement New feature or request size/XL labels Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR introduces fallback transfer protocol support for firmware updates. It adds optional FallbackTransferProtocol and FallbackURI fields to ImageSpec, extends the BMC interface with protocol discovery via GetSupportedTransferProtocols, implements protocol validation and fallback resolution logic in controllers, and updates all version CRDs with validation constraints enforcing consistent field configuration.

Changes

Cohort / File(s) Summary
API Type Definitions
api/v1alpha1/biosversion_types.go
Added FallbackTransferProtocol and FallbackURI fields to ImageSpec with kubebuilder XValidation constraints requiring transferProtocol to be non-empty when fallback is set, and both fallback fields to be present or absent together.
BMC Interface & Contracts
bmc/bmc.go
Extended BMC interface with new GetSupportedTransferProtocols(ctx context.Context) ([]string, error) method contract.
BMC Implementations
bmc/redfish.go, bmc/redfish_kube.go, bmc/mockup.go
Implemented GetSupportedTransferProtocols across Redfish (unmarshals UpdateService SimpleUpdate AllowableValues), Kube (returns mock protocol list), and Mockup (added SupportedTransferProtocols field with HTTPS/NFS/CIFS/TFTP defaults) implementations.
BMC Local Mock
bmc/redfish_local.go
Restructured BIOS/BMC upgrade flow to use mock-driven simulation with timed task progress, added GetSupportedTransferProtocols method, refactored SetBMCAttributesImmediately and added ResetManager for deferred settings application.
Protocol Validation
bmc/oem_helpers.go
Added conditional transfer protocol validation against BMC's allowable values with case-insensitive matching; logs informational message and returns fatal error with supported protocol list on mismatch.
CRD Schemas
config/crd/bases/metal.ironcore.dev_biosversions.yaml, config/crd/bases/metal.ironcore.dev_biosversionsets.yaml, config/crd/bases/metal.ironcore.dev_bmcversions.yaml, config/crd/bases/metal.ironcore.dev_bmcversionsets.yaml
Added fallbackTransferProtocol and fallbackURI fields to spec.image across all version CRDs with x-kubernetes-validations enforcing that fallback protocol requires non-empty transferProtocol and both fallback fields must be present or absent together.
Controller Logic
internal/controller/biosversion_controller.go, internal/controller/bmcversion_controller.go
Updated upgrade issuance flow to retrieve supported protocols, resolve/validate transfer protocol against supported list with fallback resolution, fail upgrade with ProtocolValidationFailed reason on validation error, and use resolved protocol/URI in update parameters.
Protocol Resolution Helper
internal/controller/helper.go
Added resolveTransferProtocol function implementing validation and fallback logic; updated retry propagation to use new condition constants RetryOfFailedResourceConditionIssued (removing legacy backward-compatibility fallback).
Test Fixtures & Assertions
internal/controller/biosversion_controller_test.go, internal/controller/bmcversion_controller_test.go, internal/controller/bmcsettings_controller_test.go, internal/controller/biossettingsset_controller_test.go
Updated test fixtures to use full ImageSpec with primary/fallback protocols; added end-to-end tests validating fallback protocol resolution when primary is unsupported; updated BMC settings assertions to use mock unit-test backing structure; fixed import formatting.
API Documentation
docs/api-reference/api.md
Added fallbackTransferProtocol and fallbackURI field documentation to ImageSpec schema table.

Sequence Diagram

sequenceDiagram
    participant Controller as Version Controller
    participant BMC as BMC Client
    participant Redfish as Redfish UpdateService
    participant Resolve as Protocol Resolver

    Controller->>BMC: GetSupportedTransferProtocols()
    BMC->>Redfish: Fetch SimpleUpdate.AllowableValues
    Redfish-->>BMC: [HTTPS, NFS, CIFS, TFTP]
    BMC-->>Controller: supported protocols

    Controller->>Resolve: resolveTransferProtocol(imageSpec, supported)
    alt Primary protocol supported
        Resolve-->>Controller: (primary_protocol, primary_uri, nil)
    else Primary unsupported, fallback configured
        Resolve-->>Controller: (fallback_protocol, fallback_uri, nil)
        Controller->>Controller: log fallback usage
    else Neither supported
        Resolve-->>Controller: (empty, empty, validation_error)
        Controller->>Controller: patch status Failed, return error
    end

    alt Validation success
        Controller->>BMC: UpgradeVersion(resolved_protocol, resolved_uri)
        BMC-->>Controller: success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

area/metal-automation, kind/feature

Suggested reviewers

  • xkonni
  • atd9876
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description is incomplete and does not match the provided template structure. The template requires 'Proposed Changes' with bullet points and 'Fixes #' sections, but the author provided custom narrative content instead. Restructure the PR description to follow the template: add a 'Proposed Changes' section with bullet points summarizing key changes, and include a 'Fixes #' reference if applicable. The current narrative can be incorporated as supporting context.
Title check ❓ Inconclusive The title 'Enh/fallback allowable values simpleupdate' is vague and uses abbreviated terminology that doesn't clearly convey the main change to someone scanning history. Revise title to be more descriptive and specific, e.g., 'Add transfer protocol fallback support for firmware updates' to clearly indicate the primary feature being added.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enh/fallback_allowableValues_simpleupdate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (1)
internal/controller/biosversion_controller_test.go (1)

451-480: Strengthen fallback test so it proves fallback was actually used.

Line 460 and Line 463 currently use equivalent successful URI payloads, so the test can still pass even if fallback resolution regresses. Consider making the primary URI intentionally invalid for the mock upgrade path while keeping fallback URI valid.

💡 Suggested test hardening
-						URI:                      upgradeServerBiosVersion,
+						URI:                      "http://primary.invalid/not-a-valid-upgrade-image",
 						TransferProtocol:         "HTTP",  // NOT supported by mock BMC
 						FallbackTransferProtocol: "HTTPS", // Supported by mock BMC
 						FallbackURI:              upgradeServerBiosVersion,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/controller/biosversion_controller_test.go` around lines 451 - 480,
The test creates a BIOSVersion (BIOSVersionSpec -> BIOSVersionTemplate ->
ImageSpec) with TransferProtocol "HTTP" and FallbackTransferProtocol "HTTPS" but
both URI and FallbackURI point to the same successful payload so the fallback
isn't actually exercised; change the primary URI (the ImageSpec.URI used with
TransferProtocol "HTTP") to an intentionally invalid/unreachable value that will
fail the mock BMC upgrade path while keeping ImageSpec.FallbackURI pointing to
the valid upgradeServerBiosVersion; this ensures the controller must attempt the
primary, fail, then use the fallback and the Eventually checks on
BIOSVersion.Status.State (InProgress -> Completed) truly validate fallback
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/v1alpha1/biosversion_types.go`:
- Around line 68-69: The CEL XValidation rules must reject empty fallback
values, not just presence: update the rules referencing fallbackTransferProtocol
and fallbackURI so they require non-empty strings (e.g. replace
has(self.fallbackURI) and has(self.fallbackTransferProtocol) with checks that
include size(...) > 0 or equivalent), and ensure the first rule also enforces
transferProtocol is non-empty when fallbackTransferProtocol is present; apply
the same tightening to the other occurrence(s) noted (the block around the
symbols fallbackTransferProtocol, fallbackURI, and transferProtocol).

In `@bmc/redfish_local.go`:
- Around line 98-131: The UnitTestMockUps shared state (fields like
BIOSUpgradeTaskIndex, BIOSUpgradingVersion, BIOSUpgradeTaskStatus,
BIOSUpgradeTaskFailedStatus, BIOSVersion and similar BMC* fields) is accessed
concurrently in UpgradeBiosVersion and GetBiosUpgradeTask (and the other
goroutine-based upgrade handlers), causing data races; add synchronization by
embedding a mutex in the UnitTestMockUps struct (or use atomics for the numeric
indices and a mutex for maps/version fields), then wrap every read/write of
those fields inside the mutex (Lock/Unlock) or use atomic ops for
BIOSUpgradeTaskIndex/BMCUpgradeTaskIndex; update UpgradeBiosVersion,
GetBiosUpgradeTask and the equivalent BMC upgrade functions to acquire the lock
before reading/writing UnitTestMockUps fields and release it after.

In `@config/crd/bases/metal.ironcore.dev_biosversions.yaml`:
- Around line 110-117: The current CEL x-kubernetes-validations in the ImageSpec
allow empty strings because they only check presence; update the ImageSpec
XValidation rules for fallbackTransferProtocol and fallbackURI to require
non-empty values when present (e.g. replace has(...) checks with has(...) &&
size(self.fallbackTransferProtocol) > 0 and similarly size(self.fallbackURI) >
0) and update the paired-rule to require both are set and non-empty or both
unset, then regenerate the CRD with the project manifest generation (run make
manifests) so the changes propagate into the auto-generated CRD YAML.

In `@config/crd/bases/metal.ironcore.dev_bmcversions.yaml`:
- Around line 125-132: The current CRD validation allows empty fallback values;
update the API marker validations on the BMC version Go type (add size(...) > 0
checks for both fallbackTransferProtocol and fallbackURI) so the generated
x-kubernetes-validations require non-empty strings when those fields are present
(e.g., change the second rule to check size(self.fallbackURI) > 0 and the
pairing rule to include size(...) > 0 for both fields), then run make manifests
to regenerate the config/crd/bases/*.yaml rather than editing the generated YAML
directly.

In `@internal/controller/bmcversion_controller.go`:
- Around line 298-313: The code currently calls
handleUpgradeInProgressState(...) first which can issue the upgrade task and
only afterward calls resetBMC(..., ConditionResetIssued), allowing a pre-upgrade
reset (GracefulRestartBMC) to be scheduled after the upgrade start; move the
pre-upgrade reset ahead of issuance by invoking resetBMC(ctx, bmcVersion,
bmcObj, ConditionResetIssued) and ensuring it succeeds (returning errors/result
as before) before calling handleUpgradeInProgressState; alternatively, if you
need early protocol validation keep protocol checks separate from the actual
issuance inside handleUpgradeInProgressState and only call the function that
performs the SimpleUpdate after resetBMC succeeds.

In `@internal/controller/helper.go`:
- Around line 637-650: The function currently compares image.TransferProtocol
and image.FallbackTransferProtocol to supportedProtocols case-insensitively but
returns the user's original casing (image.TransferProtocol /
image.FallbackTransferProtocol); change the returns to return the matching
element from supportedProtocols (the local variable used in the loop) so the
canonical/advertised protocol casing is returned (e.g., return supported,
image.URI, nil and return supported, image.FallbackURI, nil); locate the logic
around image.TransferProtocol, image.FallbackTransferProtocol, and
supportedProtocols in the helper.go function to update both the primary and
fallback return paths.
- Around line 36-37: The literals RetryOfFailedResourceConditionIssued and
RetryOfFailedResourceReasonIssued are old pre-migration names causing mismatch
with the canonical condition/reason (used by BMCVersionReconciler and
handleRetryAnnotationPropagation()), so replace these literal constants with the
canonical constant names (use the project’s canonical
ConditionRetryOfFailedResourceIssued and ReasonRetryOfFailedResourceIssued
equivalents) wherever they are declared/used (including the occurrences around
lines 36 and the block at 382–389) so the reconciler and
handleRetryAnnotationPropagation() operate on the same canonical names.

---

Nitpick comments:
In `@internal/controller/biosversion_controller_test.go`:
- Around line 451-480: The test creates a BIOSVersion (BIOSVersionSpec ->
BIOSVersionTemplate -> ImageSpec) with TransferProtocol "HTTP" and
FallbackTransferProtocol "HTTPS" but both URI and FallbackURI point to the same
successful payload so the fallback isn't actually exercised; change the primary
URI (the ImageSpec.URI used with TransferProtocol "HTTP") to an intentionally
invalid/unreachable value that will fail the mock BMC upgrade path while keeping
ImageSpec.FallbackURI pointing to the valid upgradeServerBiosVersion; this
ensures the controller must attempt the primary, fail, then use the fallback and
the Eventually checks on BIOSVersion.Status.State (InProgress -> Completed)
truly validate fallback behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80892058-5652-4fbc-bb55-529e4c46efe1

📥 Commits

Reviewing files that changed from the base of the PR and between ed27370 and 36ea10d.

📒 Files selected for processing (20)
  • api/v1alpha1/biosversion_types.go
  • bmc/bmc.go
  • bmc/mockup.go
  • bmc/oem_helpers.go
  • bmc/redfish.go
  • bmc/redfish_kube.go
  • bmc/redfish_local.go
  • config/crd/bases/metal.ironcore.dev_biosversions.yaml
  • config/crd/bases/metal.ironcore.dev_biosversionsets.yaml
  • config/crd/bases/metal.ironcore.dev_bmcversions.yaml
  • config/crd/bases/metal.ironcore.dev_bmcversionsets.yaml
  • docs/api-reference/api.md
  • internal/controller/biossettingsset_controller_test.go
  • internal/controller/biosversion_controller.go
  • internal/controller/biosversion_controller_test.go
  • internal/controller/bmcsettings_controller_test.go
  • internal/controller/bmcsettingsset_controller_test.go
  • internal/controller/bmcversion_controller.go
  • internal/controller/bmcversion_controller_test.go
  • internal/controller/helper.go
💤 Files with no reviewable changes (1)
  • internal/controller/bmcsettingsset_controller_test.go

Comment thread api/v1alpha1/biosversion_types.go Outdated
Comment thread bmc/redfish_local.go Outdated
Comment thread config/crd/bases/metal.ironcore.dev_biosversions.yaml Outdated
Comment thread config/crd/bases/metal.ironcore.dev_bmcversions.yaml Outdated
Comment thread internal/controller/bmcversion_controller.go Outdated
Comment thread internal/controller/helper.go Outdated
Comment thread internal/controller/helper.go Outdated
@xkonni xkonni force-pushed the enh/fallback_allowableValues_simpleupdate branch from 36ea10d to d6c2fca Compare May 4, 2026 10:10
davidgrun added 2 commits May 4, 2026 12:12
  Implements transfer protocol fallback mechanism for BMC and BIOS
  firmware updates when primary protocol is not supported by BMC.

  Changes:
  - Add TransferProtocol, FallbackTransferProtocol, FallbackURI to ImageSpec
  - Add XValidation rules for fallback field consistency
  - Implement GetSupportedTransferProtocols() in BMC interface
  - Add protocol validation with case-insensitive matching
  - Add resolveTransferProtocol() helper function
  - Integrate protocol resolution in BMC/BIOS controllers
  - Extend 13 tests with protocol fields + add 2 fallback tests

  The implementation automatically falls back to alternate protocol+URI
  when BMC reports primary protocol as unsupported.

Signed-off-by: Konstantin Koslowski <konstantin.koslowski@sap.com>
Signed-off-by: Konstantin Koslowski <konstantin.koslowski@sap.com>
@xkonni xkonni force-pushed the enh/fallback_allowableValues_simpleupdate branch from d6c2fca to 21c8907 Compare May 4, 2026 10:13
- Remove duplicate RetryOfFailedResource* and ServerMaintenance* constants
  from helper.go; use canonical ones from conditions.go everywhere
- Fix bmcversion_controller.go to use ConditionRetryOfFailedResourceIssued
  and ReasonRetryOfFailedResourceIssued (correct short-form values)
- Tighten CEL XValidation rule for ImageSpec: require size() > 0 on both
  fallbackTransferProtocol and fallbackURI, not just has()
- Fix resolveTransferProtocol to return the canonical casing from the
  BMC's AllowableValues list instead of the user-supplied casing
- Revert bmcversion_controller.go upgrade state order: resetBMC must run
  before handleUpgradeInProgressState to avoid nil UpgradeTask dereference
  on next reconcile

Signed-off-by: Konstantin Koslowski <konstantin.koslowski@sap.com>
@xkonni xkonni force-pushed the enh/fallback_allowableValues_simpleupdate branch from 21c8907 to add18cd Compare May 4, 2026 10:23
@xkonni xkonni requested a review from afritzler May 4, 2026 10:33
@nagadeesh-nagaraja
Copy link
Copy Markdown
Contributor

do we happen to know the reason behind this change? Can not find issue or a description.

@nagadeesh-nagaraja
Copy link
Copy Markdown
Contributor

@coderabbitai what do you think is most k8 native way to solve the problem this PR is addressing? ( lets say 90% of the server supported all protocol, 10% support subset of it. Hence the need of fallback implemented here)

Does it make sense to implement it this way?
or should we handle it creating multiple BIOSSettingsSet CRD with appropriate labels to identify the vendor/mode/BMC which does support all protocol and then create SET CRD for each of these with respective supported protocols?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Tip

For best results, initiate chat on the files or code changes.

@nagadeesh-nagaraja Great design question. Here's a breakdown of the tradeoffs between the approaches, plus a couple of other patterns worth considering.


Approach 1 — Current PR: Fallback fields in ImageSpec

How it works: A single BIOSVersion/BMCVersion CR carries both primary and fallback protocol+URI. The controller queries GetSupportedTransferProtocols() at reconcile time and picks the right one.

✅ Simple for the operator — one CR per firmware version.
✅ Logic is contained in the controller (no extra objects to manage).
❌ The API grows linearly if you need more fallback levels (primary → fallback1 → fallback2…).
❌ The ImageSpec conflates "what I want" with "what to do if that fails" — mixing intent and retry strategy.
❌ Fallback logic is bespoke and invisible to kubectl get / kubectl describe output.


Approach 2 — Multiple Set CRDs with labels

How it works: Separate BIOSVersionSet or BMCVersionSet CRs per vendor/model/protocol-capability group, identified by label selectors that match Server objects.

✅ Very Kubernetes-native — label selectors are a first-class K8s pattern (used by Services, PodAffinity, NodeSelector, etc.).
✅ Each CR expresses a single, clear intent — no ambiguity about which protocol is preferred.
✅ Scales naturally: adding a new protocol capability group = adding a new labeled CR.
❌ Operational burden: operators must know which BMC models support which protocols upfront and maintain multiple CRs.
❌ Can create label taxonomy sprawl if there are many vendor/model combinations.


Other K8s-native patterns worth considering

3. Protocol priority list (more flexible than primary + fallback)

Instead of TransferProtocol + FallbackTransferProtocol, use an ordered slice:

image:
  transferProtocols:
    - protocol: HTTPS
      uri: https://server/fw.bin
    - protocol: HTTP
      uri: http://server/fw.bin

The controller picks the first entry whose protocol appears in GetSupportedTransferProtocols(). This is extensible without API changes and mirrors patterns like imagePullPolicy or topologySpreadConstraints.

4. Capability annotation/label on the Server or BMC object

Store BMC protocol capabilities as labels/annotations on the BMC CR (populated by the operator or a discovery controller):

# On the BMC object
labels:
  metal.ironcore.dev/supported-protocols: "HTTPS,NFS,CIFS"

Then BIOSVersionSet uses a label selector to target only the right servers. This is the most Kubernetes-native pattern overall — it mirrors how StorageClass, RuntimeClass, and NodeSelector work. The protocol resolution becomes a pure label-matching problem, not controller logic.

5. Separate FirmwareUpdateProfile CRD (similar to StorageClass/RuntimeClass)

A FirmwareUpdateProfile CR holds protocol preferences, and BIOSVersionSpec references it by name — similar to how PersistentVolumeClaim references a StorageClass. Different profiles exist for different BMC capability tiers.


Recommendation

For a small, near-term fix (the 10% subset problem), the current PR is pragmatic and low-overhead. But architecturally, a combination of approach 4 + approach 3 is the most Kubernetes-native:

  1. A lightweight discovery step annotates BMC objects with their supported protocols (either from GetSupportedTransferProtocols() stored at reconcile time, or an explicit label set by the operator).
  2. BIOSVersionSet/BMCVersionSet uses a label selector to target the right Server/BMC group.
  3. ImageSpec uses an ordered protocol list rather than a single fallback field.

This keeps intent (what firmware, which servers) and capability (what protocols are supported) cleanly separated, which is how the broader Kubernetes ecosystem handles heterogeneous infrastructure.

That said, if the goal is a minimal, incremental improvement to the existing API, the current PR is a reasonable step — the main suggestion would be to consider evolving FallbackTransferProtocol toward a prioritized list in a follow-up to avoid a deeper API refactor later.

@afritzler afritzler added this to the v0.6.0 milestone May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change area/metal-automation documentation Improvements or additions to documentation enhancement New feature or request size/XL

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants