Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auto-scaling available machine pool size #356

Open
wants to merge 56 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
709fba2
compare number of waiting machines to partition pool size and react a…
iljarotar Nov 8, 2022
72197ff
poolsize validation, better db queries, pass function to fsm
iljarotar Nov 22, 2022
4e72c2a
pool scaler package
iljarotar Nov 24, 2022
b7fceb3
test adjust number of waiting machines function
iljarotar Nov 24, 2022
6105eb7
check if pool scaling is disabled
iljarotar Nov 29, 2022
489f657
min and max waiting pool size; validation; tests
iljarotar Dec 2, 2022
50b5771
linter
iljarotar Dec 2, 2022
ec55300
spec
iljarotar Dec 2, 2022
086aea2
Update cmd/metal-api/internal/scaler/poolscaler.go
iljarotar Dec 6, 2022
df0da91
Update cmd/metal-api/internal/scaler/poolscaler.go
iljarotar Dec 6, 2022
a8fbaed
Update cmd/metal-api/internal/scaler/poolscaler.go
iljarotar Dec 6, 2022
beefa8d
scaler range type for partition and noop state for fsm
iljarotar Dec 6, 2022
4d400cf
linter
iljarotar Dec 6, 2022
28473cd
fix pool size validation
iljarotar Dec 6, 2022
f3f3e1f
fix calculatePoolSizeExcess function
iljarotar Dec 6, 2022
79d82ce
spec
iljarotar Dec 6, 2022
9faf66c
remove scaler range from partition; constructor for scaler range
iljarotar Dec 8, 2022
d1145fa
fix nil pointer
iljarotar Dec 8, 2022
845a814
spec
iljarotar Dec 8, 2022
ad55fa0
OnTransition -> OnEnter
iljarotar Dec 8, 2022
7951390
scaler range in partitionbase
iljarotar Dec 8, 2022
4efb66e
scaler range in NewPartitionResponse
iljarotar Dec 8, 2022
3b8cd78
partitionid and sizeid added to manager
iljarotar Dec 8, 2022
06f269e
fix state value query filter
iljarotar Dec 8, 2022
7f6a8c2
simplify scaler range
iljarotar Dec 9, 2022
e0dea0c
log error from pool scaler instead of returning it
iljarotar Dec 13, 2022
031b2be
assert expectations in poolscaler test
iljarotar Dec 13, 2022
5fd6fb4
Merge remote-tracking branch 'origin/master' into machine-pool-auto-s…
Gerrit91 Jan 10, 2023
e24bf71
log if no scaling required
iljarotar Jan 13, 2023
c018293
Merge branch 'machine-pool-auto-scaling' of github.com:metal-stack/me…
iljarotar Jan 13, 2023
ada1bbc
named log
iljarotar Jan 13, 2023
e8e6513
no scaling on alive events
iljarotar Jan 19, 2023
f0841a2
set machine state to available if shut down machine is powered on again
iljarotar Jan 19, 2023
26d97a8
Merge branch 'master' of github.com:metal-stack/metal-api into machin…
iljarotar Jan 19, 2023
fea48f0
do not check machine liveliness, if machine is in shutdown state
iljarotar Jan 20, 2023
65e2cf3
add sleeping flag to MachineState and remove SHUTDOWN MState
iljarotar Jan 31, 2023
5fbd639
Merge branch 'master' of github.com:metal-stack/metal-api into machin…
iljarotar Feb 21, 2023
2b607d8
remove rand.Seed
iljarotar Feb 21, 2023
01e00d2
Merge branch 'master' of github.com:metal-stack/metal-api into machin…
iljarotar Feb 24, 2023
7d6afb5
Merge branch 'master' of github.com:metal-stack/metal-api into machin…
iljarotar Mar 2, 2023
aa035a3
hibernation struct
iljarotar Mar 3, 2023
a4d4507
Save liveliness properly and fix linting issue. (#427)
Gerrit91 Mar 7, 2023
b0ba295
Fix integration test (#429)
Gerrit91 Mar 7, 2023
a944273
Merge branch 'master' of github.com:metal-stack/metal-api into machin…
iljarotar Mar 7, 2023
049abbf
Do not use pool scaler in early machine lifecycle. (#430)
Gerrit91 Mar 7, 2023
0c633ef
liveliness typo
iljarotar Mar 7, 2023
1d72977
add waiting and preallocated fields to waiting machine query
iljarotar Mar 9, 2023
cfd3e94
do not update machine liveliness while machine is shutting down
iljarotar Mar 9, 2023
f8e4515
fix nil pointer
iljarotar Mar 9, 2023
9e42317
Add Dockerfile.dev to fasten up dev cycles. (#431)
Gerrit91 Mar 9, 2023
5a55a78
Merge remote-tracking branch 'origin/master' into machine-pool-auto-s…
Gerrit91 Aug 1, 2023
4809b4b
Merge master.
Gerrit91 Aug 1, 2023
6dfbda1
Merge branch 'master' into machine-pool-auto-scaling
iljarotar Aug 4, 2023
4673673
Merge branch 'master' into machine-pool-auto-scaling
iljarotar Jan 25, 2024
fe95940
remove Is functions
iljarotar Jan 25, 2024
634d513
Merge branch 'master' into machine-pool-auto-scaling
Gerrit91 Mar 6, 2024
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ visualize-fsm:
cd cmd/metal-api/internal/tools/visualize_fsm
go run main.go
dot -Tsvg fsm.dot > fsm.svg

.PHONY: mocks
mocks:
docker run --user $$(id -u):$$(id -g) --rm -w /work -v ${PWD}:/work vektra/mockery:v2.21.1 --name MachineManager --dir /work/cmd/metal-api/internal/scaler --output /work/cmd/metal-api/internal/scaler --filename pool_scaler_mock_test.go --testonly --inpackage
47 changes: 42 additions & 5 deletions cmd/metal-api/internal/datastore/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package datastore

import (
"github.com/metal-stack/metal-api/cmd/metal-api/internal/fsm"
"github.com/metal-stack/metal-api/cmd/metal-api/internal/fsm/states"
"github.com/metal-stack/metal-api/cmd/metal-api/internal/metal"
s "github.com/metal-stack/metal-api/cmd/metal-api/internal/scaler"
"github.com/metal-stack/metal-lib/bus"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -38,22 +41,53 @@ func (rs *RethinkStore) UpsertProvisioningEventContainer(ec *metal.ProvisioningE
return rs.upsertEntity(rs.eventTable(), ec)
}

func (rs *RethinkStore) ProvisioningEventForMachine(log *zap.SugaredLogger, event *metal.ProvisioningEvent, machineID string) (*metal.ProvisioningEventContainer, error) {
ec, err := rs.FindProvisioningEventContainer(machineID)
func (rs *RethinkStore) ProvisioningEventForMachine(log *zap.SugaredLogger, publisher bus.Publisher, event *metal.ProvisioningEvent, machine *metal.Machine) (*metal.ProvisioningEventContainer, error) {
ec, err := rs.FindProvisioningEventContainer(machine.ID)
if err != nil && !metal.IsNotFound(err) {
return nil, err
}

if ec == nil {
ec = &metal.ProvisioningEventContainer{
Base: metal.Base{
ID: machineID,
ID: machine.ID,
},
Liveliness: metal.MachineLivelinessAlive,
}
}

newEC, err := fsm.HandleProvisioningEvent(log, ec, event)
var scaler *s.PoolScaler
if machine.PartitionID != "" && machine.SizeID != "" {
// in the early lifecycle, when the pxe booting event is submitted
// a machine does not have a partition or size , so the pool scaler
// can not work at this stage

p, err := rs.FindPartition(machine.PartitionID)
if err != nil {
return nil, err
}

scaler = s.NewPoolScaler(&s.PoolScalerConfig{
Log: log.Named("pool-scaler"),
Manager: &manager{
rs: rs,
publisher: publisher,
partitionid: p.ID,
sizeid: machine.SizeID,
},
Partition: *p,
})
}

config := states.StateConfig{
Log: log.Named("fsm"),
Container: ec,
Event: event,
Scaler: scaler,
Machine: machine,
}

newEC, err := fsm.HandleProvisioningEvent(&config)
if err != nil {
return nil, err
}
Expand All @@ -63,7 +97,10 @@ func (rs *RethinkStore) ProvisioningEventForMachine(log *zap.SugaredLogger, even
}

newEC.TrimEvents(100)

err = rs.UpsertProvisioningEventContainer(newEC)
if err != nil {
return nil, err
}

return newEC, err
}
34 changes: 33 additions & 1 deletion cmd/metal-api/internal/datastore/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type MachineSearchQuery struct {
Tags []string `json:"tags" optional:"true"`

// allocation
NotAllocated *bool `json:"not_allocated" optional:"true"`
iljarotar marked this conversation as resolved.
Show resolved Hide resolved
AllocationName *string `json:"allocation_name" optional:"true"`
AllocationProject *string `json:"allocation_project" optional:"true"`
AllocationImageID *string `json:"allocation_image_id" optional:"true"`
Expand Down Expand Up @@ -53,7 +54,10 @@ type MachineSearchQuery struct {
DiskSizes []int64 `json:"disk_sizes" optional:"true"`

// state
StateValue *string `json:"state_value" optional:"true" enum:"|RESERVED|LOCKED"`
HibernationEnabled *bool `json:"hibernation_enabled" optional:"true"`
StateValue *string `json:"state_value" optional:"true" enum:"|RESERVED|LOCKED"`
PreAllocated *bool `json:"preallocated" optional:"true"`
Waiting *bool `json:"waiting" optional:"true"`

// ipmi
IpmiAddress *string `json:"ipmi_address" optional:"true"`
Expand Down Expand Up @@ -113,6 +117,16 @@ func (p *MachineSearchQuery) generateTerm(rs *RethinkStore) *r.Term {
})
}

if p.NotAllocated != nil {
q = q.Filter(func(row r.Term) r.Term {
if *p.NotAllocated {
return row.Field("allocation").Eq(nil)
} else {
return row.Field("allocation").Ne(nil)
}
})
}

if p.AllocationName != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("allocation").Field("name").Eq(*p.AllocationName)
Expand Down Expand Up @@ -293,12 +307,30 @@ func (p *MachineSearchQuery) generateTerm(rs *RethinkStore) *r.Term {
})
}

if p.HibernationEnabled != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("state").Field("hibernation").Field("enabled").Eq(*p.HibernationEnabled)
})
}

if p.StateValue != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("state").Field("value").Eq(*p.StateValue)
})
}

if p.PreAllocated != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("preallocated").Eq(*p.PreAllocated)
})
}

if p.Waiting != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("waiting").Eq(*p.Waiting)
})
}

if p.IpmiAddress != nil {
q = q.Filter(func(row r.Term) r.Term {
return row.Field("ipmi").Field("address").Eq(*p.IpmiAddress)
Expand Down
135 changes: 135 additions & 0 deletions cmd/metal-api/internal/datastore/machine_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,54 @@ func TestRethinkStore_SearchMachines(t *testing.T) {
},
wantErr: nil,
},
{
name: "search by not_allocated true",
q: &MachineSearchQuery{
NotAllocated: pointer.Pointer(true),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "2"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "3"}, Allocation: nil},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "3"}, Allocation: nil}),
},
wantErr: nil,
},
{
name: "search by not_allocated false",
q: &MachineSearchQuery{
NotAllocated: pointer.Pointer(false),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "2"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "3"}, Allocation: nil},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, Allocation: &metal.MachineAllocation{}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, Allocation: &metal.MachineAllocation{}}),
},
wantErr: nil,
},
{
name: "search by not_allocated nil",
q: &MachineSearchQuery{
NotAllocated: nil,
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "2"}, Allocation: &metal.MachineAllocation{}},
{Base: metal.Base{ID: "3"}, Allocation: nil},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, Allocation: &metal.MachineAllocation{}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, Allocation: &metal.MachineAllocation{}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "3"}, Allocation: nil}),
},
wantErr: nil,
},
{
name: "search by network ids",
q: &MachineSearchQuery{
Expand Down Expand Up @@ -635,6 +683,93 @@ func TestRethinkStore_SearchMachines(t *testing.T) {
},
wantErr: nil,
},
{
name: "search by waiting true",
q: &MachineSearchQuery{
Waiting: pointer.Pointer(true),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, Waiting: true},
{Base: metal.Base{ID: "2"}, Waiting: true},
{Base: metal.Base{ID: "3"}, Waiting: false},
{Base: metal.Base{ID: "4"}, Waiting: false},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, Waiting: true}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, Waiting: true}),
},
wantErr: nil,
},
{
name: "search by preallocated true",
q: &MachineSearchQuery{
PreAllocated: pointer.Pointer(true),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, PreAllocated: true},
{Base: metal.Base{ID: "2"}, PreAllocated: true},
{Base: metal.Base{ID: "3"}, PreAllocated: false},
{Base: metal.Base{ID: "4"}, PreAllocated: false},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, PreAllocated: true}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, PreAllocated: true}),
},
wantErr: nil,
},
{
name: "search by hibernation_enabled true",
q: &MachineSearchQuery{
HibernationEnabled: pointer.Pointer(true),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "2"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "3"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
{Base: metal.Base{ID: "4"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}}),
},
wantErr: nil,
},
{
name: "search by hibernation_enabled false",
q: &MachineSearchQuery{
HibernationEnabled: pointer.Pointer(false),
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "2"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "3"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
{Base: metal.Base{ID: "4"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "3"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "4"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}}),
},
wantErr: nil,
},
{
name: "search by hibernation_enabled nil",
q: &MachineSearchQuery{
HibernationEnabled: nil,
},
mock: []*metal.Machine{
{Base: metal.Base{ID: "1"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "2"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}},
{Base: metal.Base{ID: "3"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
{Base: metal.Base{ID: "4"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}},
},
want: []*metal.Machine{
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "1"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "2"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: true}}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "3"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}}),
tt.defaultBody(&metal.Machine{Base: metal.Base{ID: "4"}, State: metal.MachineState{Hibernation: metal.MachineHibernation{Enabled: false}}}),
},
wantErr: nil,
},
{
name: "search by ipmi address",
q: &MachineSearchQuery{
Expand Down
Loading
Loading