Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion apis/metal3.io/v1alpha1/baremetalhost_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,14 +648,24 @@ type Image struct {
// be live-booted and not deployed to disk.
// +kubebuilder:validation:Enum=raw;qcow2;vdi;vmdk;live-iso
DiskFormat *string `json:"format,omitempty"`

// OCIAuthSecretName optionally names a Docker-config secret containing
// registry credentials for oci:// images. Must be in the same namespace
// as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
// Only used when Image.URL has the oci:// scheme.
OCIAuthSecretName *string `json:"ociAuthSecretName,omitempty"`
Comment thread
mabulgu marked this conversation as resolved.
}

func (image *Image) IsLiveISO() bool {
return image != nil && image.DiskFormat != nil && *image.DiskFormat == "live-iso"
}

// IsOCI returns true if the image URL uses the OCI scheme (oci://).
func (image *Image) IsOCI() bool {
return image != nil && strings.HasPrefix(image.URL, "oci://")
if image == nil || image.URL == "" {
return false
}
return strings.HasPrefix(strings.ToLower(image.URL), "oci://")
}

// Custom deploy is a description of a customized deploy process.
Expand Down
5 changes: 5 additions & 0 deletions apis/metal3.io/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions config/base/crds/bases/metal3.io_baremetalhosts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down Expand Up @@ -1092,6 +1099,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down
7 changes: 7 additions & 0 deletions config/base/crds/bases/metal3.io_hostclaims.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down
21 changes: 21 additions & 0 deletions config/render/capm3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down Expand Up @@ -1092,6 +1099,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down Expand Up @@ -2041,6 +2055,13 @@ spec:
- vmdk
- live-iso
type: string
ociAuthSecretName:
description: |-
OCIAuthSecretName optionally names a Docker-config secret containing
registry credentials for oci:// images. Must be in the same namespace
as the BareMetalHost. Allowed types: kubernetes.io/dockerconfigjson|dockercfg.
Only used when Image.URL has the oci:// scheme.
type: string
url:
description: URL is a location of an image to deploy.
type: string
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/metal3-io/baremetal-operator
go 1.25.0

require (
github.com/cpuguy83/dockercfg v0.3.2
Comment thread
mabulgu marked this conversation as resolved.
github.com/go-logr/logr v1.4.3
github.com/google/safetext v0.0.0-20230106111101-7156a760e523
github.com/google/uuid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
28 changes: 28 additions & 0 deletions internal/controller/metal3.io/baremetalhost_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/cluster-api/util/conditions"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
Expand Down Expand Up @@ -66,6 +67,7 @@ type BareMetalHostReconciler struct {
Log logr.Logger
ProvisionerFactory provisioner.Factory
APIReader client.Reader
Recorder record.EventRecorder
}

// Instead of passing a zillion arguments to the action of a phase,
Expand Down Expand Up @@ -1321,13 +1323,20 @@ func (r *BareMetalHostReconciler) actionProvisioning(ctx context.Context, prov p
image = *info.host.Spec.Image.DeepCopy()
}

// Extract OCI auth secret credentials if needed
authSecret, err := r.getImageAuthSecret(ctx, info.host, &image)
if err != nil {
Comment thread
mabulgu marked this conversation as resolved.
return recordActionFailure(info, metal3api.ProvisioningError, err.Error())
}

provResult, err := prov.Provision(ctx, provisioner.ProvisionData{
Image: image,
CustomDeploy: info.host.Spec.CustomDeploy.DeepCopy(),
HostConfig: hostConf,
BootMode: info.host.Status.Provisioning.BootMode,
HardwareProfile: hwProf,
RootDeviceHints: info.host.Status.Provisioning.RootDeviceHints.DeepCopy(),
ImagePullSecret: authSecret,
}, forceReboot)
if err != nil {
return actionError{fmt.Errorf("failed to provision: %w", err)}
Expand Down Expand Up @@ -2407,6 +2416,23 @@ func (r *BareMetalHostReconciler) getBMCSecretAndSetOwner(ctx context.Context, r
return bmcCredsSecret, nil
}

// getImageAuthSecret validates and extracts the OCI registry credentials for the image.
// It returns the base64-encoded credentials in the format expected by Ironic, or an empty
// string if no auth secret is configured.
func (r *BareMetalHostReconciler) getImageAuthSecret(ctx context.Context, host *metal3api.BareMetalHost, image *metal3api.Image) (string, error) {
if image == nil || !image.IsOCI() {
Comment thread
mabulgu marked this conversation as resolved.
return "", nil
}

if image.OCIAuthSecretName == nil || *image.OCIAuthSecretName == "" {
return "", nil
}

secretManager := r.secretManager(ctx, r.Log)
validator := NewImageAuthValidator(r.Recorder)
return validator.Validate(ctx, host, secretManager)
}
Comment thread
mabulgu marked this conversation as resolved.

func credentialsFromSecret(bmcCredsSecret *corev1.Secret) *bmc.Credentials {
// We trim surrounding whitespace because those characters are
// unlikely to be part of the username or password and it is
Expand Down Expand Up @@ -2492,6 +2518,8 @@ func (r *BareMetalHostReconciler) updateEventHandler(e event.UpdateEvent) bool {

// SetupWithManager registers the reconciler to be run by the manager.
func (r *BareMetalHostReconciler) SetupWithManager(mgr ctrl.Manager, preprovImgEnable bool, maxConcurrentReconcile int) error {
r.Recorder = mgr.GetEventRecorderFor("baremetalhost-controller")

controller := ctrl.NewControllerManagedBy(mgr).
For(&metal3api.BareMetalHost{}).
WithEventFilter(
Expand Down
Loading