Skip to content

Commit 45de2a8

Browse files
committed
[Starlark] Add Starlark-based provisioner backend
- New provisioner implementation that delegates every `pkg/provisioner.Provisioner` method to a named function in a user-supplied Starlark script, enabled via `--starlark-provisioner-script` CLI arg. - The reference script `efi-vmedia.star` that targets Ironic redfish-virtualmedia UEFI (custom-deploy or qcow2) and rejects unsupported specs. Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
1 parent 5f7da9c commit 45de2a8

20 files changed

Lines changed: 4643 additions & 7 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/prometheus/client_golang v1.23.2
1616
github.com/stretchr/testify v1.11.1
1717
go.etcd.io/etcd/client/pkg/v3 v3.6.10
18+
go.starlark.net v0.0.0-20260326113308-fadfc96def35
1819
go.uber.org/zap v1.27.1
1920
k8s.io/api v0.35.4
2021
k8s.io/apimachinery v0.35.4

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09
185185
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
186186
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
187187
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
188+
go.starlark.net v0.0.0-20260326113308-fadfc96def35 h1:VYAqieSOJNxBDX8KJneTAwvdf4J4zRDE2u+UFXtt9h4=
189+
go.starlark.net v0.0.0-20260326113308-fadfc96def35/go.mod h1:Iue6g6iirlfLoVi/DYCi5/x0h/bAOuWF3dULTKpt2Vo=
188190
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
189191
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
190192
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=

internal/controller/metal3.io/baremetalhost_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ func (r *BareMetalHostReconciler) registerHost(ctx context.Context, prov provisi
833833
dirty = true
834834
}
835835

836-
preprovImgFormats, err := prov.PreprovisioningImageFormats()
836+
preprovImgFormats, err := prov.PreprovisioningImageFormats(ctx)
837837
if err != nil {
838838
return actionError{err}
839839
}

internal/controller/metal3.io/host_state_machine_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ func (m *mockProvisioner) Register(_ context.Context, _ provisioner.ManagementAc
13191319
return m.getNextResultByMethod("ValidateManagementAccess"), "", err
13201320
}
13211321

1322-
func (m *mockProvisioner) PreprovisioningImageFormats() ([]metal3api.ImageFormat, error) {
1322+
func (m *mockProvisioner) PreprovisioningImageFormats(_ context.Context) ([]metal3api.ImageFormat, error) {
13231323
return nil, nil
13241324
}
13251325

main.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/metal3-io/baremetal-operator/pkg/provisioner/demo"
3434
"github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture"
3535
"github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic"
36+
starlarkprov "github.com/metal3-io/baremetal-operator/pkg/provisioner/starlark"
3637
"github.com/metal3-io/baremetal-operator/pkg/secretutils"
3738
"github.com/metal3-io/baremetal-operator/pkg/version"
3839
ironicv1alpha1 "github.com/metal3-io/ironic-standalone-operator/api/v1alpha1"
@@ -133,6 +134,7 @@ func main() {
133134
var devLogging bool
134135
var runInTestMode bool
135136
var runInDemoMode bool
137+
var starlarkScript string
136138
var webhookPort int
137139
var restConfigQPS float64
138140
var restConfigBurst int
@@ -157,6 +159,8 @@ func main() {
157159
flag.BoolVar(&runInTestMode, "test-mode", false, "disable ironic communication")
158160
flag.BoolVar(&runInDemoMode, "demo-mode", false,
159161
"use the demo provisioner to set host states")
162+
flag.StringVar(&starlarkScript, "starlark-provisioner-script", os.Getenv("STARLARK_PROVISIONER_SCRIPT"),
163+
"path to a Starlark script implementing the provisioner interface")
160164
flag.StringVar(&healthAddr, "health-addr", ":9440",
161165
"The address the health endpoint binds to.")
162166
flag.IntVar(&webhookPort, "webhook-port", 9443, //nolint:mnd
@@ -201,6 +205,23 @@ func main() {
201205

202206
printVersion()
203207

208+
// At most one provisioner mode may be selected. Silently picking the
209+
// first true mode (the previous behavior) hides a misconfiguration
210+
// that would otherwise surface much later as confusing reconcile
211+
// behavior. Run this guard before any expensive setup so the operator
212+
// fails fast at startup. Enumerating the three pairwise conflicts
213+
// covers every "two or more true" combination (the all-three case
214+
// matches all three disjuncts).
215+
if (runInTestMode && runInDemoMode) ||
216+
(runInTestMode && starlarkScript != "") ||
217+
(runInDemoMode && starlarkScript != "") {
218+
setupLog.Error(nil, "only one provisioner mode may be set",
219+
"test-mode", runInTestMode,
220+
"demo-mode", runInDemoMode,
221+
"starlark-provisioner-script", starlarkScript)
222+
os.Exit(1)
223+
}
224+
204225
enableWebhook := webhookPort != 0
205226

206227
leaderElectionNamespace := os.Getenv("POD_NAMESPACE")
@@ -320,6 +341,13 @@ func main() {
320341
} else if runInDemoMode {
321342
ctrl.Log.Info("using demo provisioner")
322343
provisionerFactory = &demo.Demo{}
344+
} else if starlarkScript != "" {
345+
ctrl.Log.Info("using starlark provisioner", "script", starlarkScript)
346+
provisionerFactory, err = starlarkprov.NewProvisionerFactory(starlarkScript)
347+
if err != nil {
348+
setupLog.Error(err, "cannot start starlark provisioner")
349+
os.Exit(1)
350+
}
323351
} else {
324352
provLog := zap.New(zap.UseFlagOptions(&logOpts)).WithName("provisioner")
325353
// Check if we should use Ironic CR integration

pkg/provisioner/demo/demo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (p *demoProvisioner) Register(_ context.Context, _ provisioner.ManagementAc
111111
return
112112
}
113113

114-
func (p *demoProvisioner) PreprovisioningImageFormats() ([]metal3api.ImageFormat, error) {
114+
func (p *demoProvisioner) PreprovisioningImageFormats(_ context.Context) ([]metal3api.ImageFormat, error) {
115115
return nil, nil
116116
}
117117

pkg/provisioner/fixture/fixture.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (p *fixtureProvisioner) Register(_ context.Context, _ provisioner.Managemen
149149
return
150150
}
151151

152-
func (p *fixtureProvisioner) PreprovisioningImageFormats() ([]metal3api.ImageFormat, error) {
152+
func (p *fixtureProvisioner) PreprovisioningImageFormats(_ context.Context) ([]metal3api.ImageFormat, error) {
153153
return nil, nil
154154
}
155155

pkg/provisioner/ironic/ironic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ func (p *ironicProvisioner) configureNode(ctx context.Context, data provisioner.
382382
// PreprovisioningImageFormats returns a list of acceptable formats for a
383383
// pre-provisioning image to be built by a PreprovisioningImage object. The
384384
// list should be nil if no image build is requested.
385-
func (p *ironicProvisioner) PreprovisioningImageFormats() ([]metal3api.ImageFormat, error) {
385+
func (p *ironicProvisioner) PreprovisioningImageFormats(_ context.Context) ([]metal3api.ImageFormat, error) {
386386
if !p.config.havePreprovImgBuilder {
387387
return nil, nil
388388
}

pkg/provisioner/ironic/register_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ironic
22

33
import (
4+
"context"
45
"net/http"
56
"testing"
67

@@ -964,7 +965,7 @@ func TestPreprovisioningImageFormats(t *testing.T) {
964965
prov, _ := newProvisionerWithSettings(host, bmc.Credentials{}, nil, ironicEndpoint, auth)
965966
prov.config.havePreprovImgBuilder = tc.PreprovImgEnabled
966967

967-
fmts, err := prov.PreprovisioningImageFormats()
968+
fmts, err := prov.PreprovisioningImageFormats(context.Background())
968969

969970
require.NoError(t, err)
970971
assert.Equal(t, tc.Expected, fmts)

pkg/provisioner/provisioner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ type Provisioner interface {
147147
// PreprovisioningImageFormats returns a list of acceptable formats for a
148148
// pre-provisioning image to be built by a PreprovisioningImage object. The
149149
// list should be nil if no image build is requested.
150-
PreprovisioningImageFormats() ([]metal3api.ImageFormat, error)
150+
PreprovisioningImageFormats(ctx context.Context) ([]metal3api.ImageFormat, error)
151151

152152
// InspectHardware updates the HardwareDetails field of the host with
153153
// details of devices discovered on the hardware. It may be called

0 commit comments

Comments
 (0)