Files
warewulf/internal/pkg/warewulfd/api/profile.go
Jonathon Anderson 66450a7dcd Simpler names for overlay methods
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
2025-08-27 22:33:18 -06:00

200 lines
6.6 KiB
Go

package api
import (
"context"
"fmt"
"dario.cat/mergo"
"github.com/swaggest/usecase"
"github.com/swaggest/usecase/status"
"github.com/warewulf/warewulf/internal/pkg/image"
"github.com/warewulf/warewulf/internal/pkg/node"
"github.com/warewulf/warewulf/internal/pkg/overlay"
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
func getProfiles() usecase.Interactor {
u := usecase.NewInteractor(func(ctx context.Context, _ struct{}, output *map[string]*node.Profile) error {
wwlog.Debug("api.getProfiles()")
if registry, err := node.New(); err != nil {
return err
} else {
*output = registry.NodeProfiles
return nil
}
})
u.SetTitle("Get profiles")
u.SetDescription("Get all node profiles.")
u.SetTags("Profile")
return u
}
func getProfileByID() usecase.Interactor {
type getProfileByIDInput struct {
ID string `path:"id" required:"true" description:"ID of profile to get"`
}
u := usecase.NewInteractor(func(ctx context.Context, input getProfileByIDInput, output *node.Profile) error {
wwlog.Debug("api.getProfileByID(ID:%v)", input.ID)
if registry, err := node.New(); err != nil {
return err
} else {
if profile, err := registry.GetProfile(input.ID); err != nil {
return status.Wrap(fmt.Errorf("profile not found: %v (%v)", input.ID, err), status.NotFound)
} else {
*output = profile
return nil
}
}
})
u.SetTitle("Get a profile")
u.SetDescription("Get a node profile by its ID.")
u.SetTags("Profile")
u.SetExpectedErrors(status.NotFound)
return u
}
func addProfile() usecase.Interactor {
type addProfileInput struct {
ID string `path:"id" required:"true" description:"ID of profile to add"`
Profile node.Profile `json:"profile" required:"true" description:"Field values in JSON format for added profile"`
IfNoneMatch string `header:"If-None-Match" description:"Set to '*' to indicate that the profile should only be created if it does not already exist"`
}
u := usecase.NewInteractor(func(ctx context.Context, input addProfileInput, output *node.Profile) error {
wwlog.Debug("api.addProfile(ID:%v, Profile:%+v)", input.ID, input.Profile)
if registry, err := node.New(); err != nil {
return err
} else {
if input.IfNoneMatch == "*" {
if _, ok := registry.NodeProfiles[input.ID]; ok {
return status.Wrap(fmt.Errorf("profile '%s' already exists", input.ID), status.InvalidArgument)
}
}
for _, profile := range input.Profile.Profiles {
if _, ok := registry.NodeProfiles[profile]; !ok {
return status.Wrap(fmt.Errorf("profile '%s' does not exist", profile), status.InvalidArgument)
}
}
if input.Profile.ImageName != "" && !image.ValidSource(input.Profile.ImageName) {
return status.Wrap(fmt.Errorf("image '%s' does not exist", input.Profile.ImageName), status.InvalidArgument)
}
for _, overlay_ := range input.Profile.SystemOverlay {
if _, err = overlay.Get(overlay_); err != nil {
return status.Wrap(fmt.Errorf("overlay '%s' does not exist", overlay_), status.InvalidArgument)
}
}
for _, overlay_ := range input.Profile.RuntimeOverlay {
if _, err = overlay.Get(overlay_); err != nil {
return status.Wrap(fmt.Errorf("overlay '%s' does not exist", overlay_), status.InvalidArgument)
}
}
registry.NodeProfiles[input.ID] = &input.Profile
if err := registry.Persist(); err != nil {
return err
}
warewulfd.Reload()
*output = *(registry.NodeProfiles[input.ID])
return nil
}
})
u.SetTitle("Add a profile")
u.SetDescription("Add a new node profile.")
u.SetTags("Profile")
return u
}
func updateProfile() usecase.Interactor {
type updateProfileInput struct {
ID string `path:"id" required:"true" description:"ID of profile to update"`
Profile node.Profile `json:"profile" required:"true" description:"Field values in JSON format to update on profile"`
}
u := usecase.NewInteractor(func(ctx context.Context, input updateProfileInput, output *node.Profile) error {
wwlog.Debug("api.updateProfile(ID:%v, Profile:%+v)", input.ID, input.Profile)
if registry, err := node.New(); err != nil {
return err
} else {
for _, profile := range input.Profile.Profiles {
if _, ok := registry.NodeProfiles[profile]; !ok {
return status.Wrap(fmt.Errorf("profile '%s' does not exist", profile), status.InvalidArgument)
}
}
if input.Profile.ImageName != "" && !image.ValidSource(input.Profile.ImageName) {
return status.Wrap(fmt.Errorf("image '%s' does not exist", input.Profile.ImageName), status.InvalidArgument)
}
for _, overlay_ := range input.Profile.SystemOverlay {
if _, err = overlay.Get(overlay_); err != nil {
return status.Wrap(fmt.Errorf("overlay '%s' does not exist", overlay_), status.InvalidArgument)
}
}
for _, overlay_ := range input.Profile.RuntimeOverlay {
if _, err = overlay.Get(overlay_); err != nil {
return status.Wrap(fmt.Errorf("overlay '%s' does not exist", overlay_), status.InvalidArgument)
}
}
if profilePtr, err := registry.GetProfilePtr(input.ID); err != nil {
return status.Wrap(err, status.NotFound)
} else {
if err := mergo.MergeWithOverwrite(profilePtr, &input.Profile); err != nil {
return err
}
if err := registry.Persist(); err != nil {
return err
}
warewulfd.Reload()
*output = *profilePtr
return nil
}
}
})
u.SetTitle("Update a profile")
u.SetDescription("Update an existing node profile.")
u.SetTags("Profile")
return u
}
func deleteProfile() usecase.Interactor {
type deleteProfileInput struct {
ID string `path:"id" required:"true" description:"ID of profile to delete"`
}
u := usecase.NewInteractor(func(ctx context.Context, input deleteProfileInput, output *node.Profile) error {
wwlog.Debug("api.deleteProfile(ID:%v)", input.ID)
if registry, err := node.New(); err != nil {
return err
} else {
if profile, ok := registry.NodeProfiles[input.ID]; ok {
*output = *profile
}
nodesCount := len(registry.ListNodesUsingProfile(input.ID))
profilesCount := len(registry.ListProfilesUsingProfile(input.ID))
if nodesCount > 0 || profilesCount > 0 {
return status.Wrap(fmt.Errorf(
"profile '%s' is in use by %v nodes and %v profiles", input.ID, nodesCount, profilesCount),
status.InvalidArgument)
}
if err := registry.DelProfile(input.ID); err != nil {
return err
}
if err := registry.Persist(); err != nil {
return err
}
warewulfd.Reload()
return nil
}
})
u.SetTitle("Delete a profile")
u.SetDescription("Delete an existing node profile.")
u.SetTags("Profile")
return u
}