From 14971332d43bdf976f7dca2d29d3a349a078bca1 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 2 Oct 2023 15:48:47 +0200 Subject: [PATCH 01/17] Add endpont for machine issues. --- .../internal/issues/asn-uniqueness.go | 122 +++++ .../internal/issues/bmc-info-outdated.go | 47 ++ .../internal/issues/bmc-without-ip.go | 28 + .../internal/issues/bmc-without-mac.go | 28 + cmd/metal-api/internal/issues/crash-loop.go | 38 ++ .../internal/issues/failed-machine-reclaim.go | 41 ++ cmd/metal-api/internal/issues/issues.go | 188 +++++++ cmd/metal-api/internal/issues/issues_test.go | 484 ++++++++++++++++++ .../internal/issues/last-event-error.go | 51 ++ .../internal/issues/liveliness-dead.go | 28 + .../issues/liveliness-not-available.go | 33 ++ .../internal/issues/liveliness-unknown.go | 28 + .../internal/issues/no-event-container.go | 30 ++ cmd/metal-api/internal/issues/no-partition.go | 28 + .../internal/issues/non-distinct-bmc-ip.go | 65 +++ cmd/metal-api/internal/issues/severeties.go | 48 ++ cmd/metal-api/internal/issues/types.go | 58 +++ .../internal/service/machine-service.go | 79 +++ cmd/metal-api/internal/service/v1/machine.go | 25 + spec/metal-api.json | 303 +++++++++++ 20 files changed, 1752 insertions(+) create mode 100644 cmd/metal-api/internal/issues/asn-uniqueness.go create mode 100644 cmd/metal-api/internal/issues/bmc-info-outdated.go create mode 100644 cmd/metal-api/internal/issues/bmc-without-ip.go create mode 100644 cmd/metal-api/internal/issues/bmc-without-mac.go create mode 100644 cmd/metal-api/internal/issues/crash-loop.go create mode 100644 cmd/metal-api/internal/issues/failed-machine-reclaim.go create mode 100644 cmd/metal-api/internal/issues/issues.go create mode 100644 cmd/metal-api/internal/issues/issues_test.go create mode 100644 cmd/metal-api/internal/issues/last-event-error.go create mode 100644 cmd/metal-api/internal/issues/liveliness-dead.go create mode 100644 cmd/metal-api/internal/issues/liveliness-not-available.go create mode 100644 cmd/metal-api/internal/issues/liveliness-unknown.go create mode 100644 cmd/metal-api/internal/issues/no-event-container.go create mode 100644 cmd/metal-api/internal/issues/no-partition.go create mode 100644 cmd/metal-api/internal/issues/non-distinct-bmc-ip.go create mode 100644 cmd/metal-api/internal/issues/severeties.go create mode 100644 cmd/metal-api/internal/issues/types.go diff --git a/cmd/metal-api/internal/issues/asn-uniqueness.go b/cmd/metal-api/internal/issues/asn-uniqueness.go new file mode 100644 index 000000000..e01a07756 --- /dev/null +++ b/cmd/metal-api/internal/issues/asn-uniqueness.go @@ -0,0 +1,122 @@ +package issues + +import ( + "fmt" + "sort" + "strings" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +const ( + IssueTypeASNUniqueness IssueType = "asn-not-unique" +) + +type ( + IssueASNUniqueness struct { + details string + } +) + +func (i *IssueASNUniqueness) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeASNUniqueness, + Severity: IssueSeverityMinor, + Description: "The ASN is not unique (only impact on firewalls)", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#asn-not-unique", + } +} + +func (i *IssueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + var ( + machineASNs = map[uint32]metal.Machines{} + overlaps []string + isNoFirewall = func(m metal.Machine) bool { + return m.Allocation == nil || m.Allocation.Role != metal.RoleFirewall + } + ) + + if isNoFirewall(m) { + return false + } + + for _, n := range m.Allocation.MachineNetworks { + n := n + + if n.ASN == 0 { + continue + } + + machineASNs[n.ASN] = nil + } + + for _, machineFromAll := range c.Machines { + machineFromAll := machineFromAll + + if machineFromAll.ID == m.ID { + continue + } + otherMachine := machineFromAll + + if isNoFirewall(otherMachine) { + continue + } + + for _, n := range otherMachine.Allocation.MachineNetworks { + n := n + + if n.ASN == 0 { + continue + } + + _, ok := machineASNs[n.ASN] + if !ok { + continue + } + + machineASNs[n.ASN] = append(machineASNs[n.ASN], otherMachine) + } + } + + var asnList []uint32 + for asn := range machineASNs { + asn := asn + asnList = append(asnList, asn) + } + sort.Slice(asnList, func(i, j int) bool { + return asnList[i] < asnList[j] + }) + + for _, asn := range asnList { + asn := asn + + overlappingMachines, ok := machineASNs[asn] + if !ok || len(overlappingMachines) == 0 { + continue + } + + var sharedIDs []string + for _, m := range overlappingMachines { + m := m + sharedIDs = append(sharedIDs, m.ID) + } + + overlaps = append(overlaps, fmt.Sprintf("- ASN (%d) not unique, shared with %s", asn, sharedIDs)) + } + + if len(overlaps) == 0 { + return false + } + + sort.Slice(overlaps, func(i, j int) bool { + return overlaps[i] < overlaps[j] + }) + + i.details = strings.Join(overlaps, "\n") + + return true +} + +func (i *IssueASNUniqueness) Details() string { + return i.details +} diff --git a/cmd/metal-api/internal/issues/bmc-info-outdated.go b/cmd/metal-api/internal/issues/bmc-info-outdated.go new file mode 100644 index 000000000..d7766d875 --- /dev/null +++ b/cmd/metal-api/internal/issues/bmc-info-outdated.go @@ -0,0 +1,47 @@ +package issues + +import ( + "fmt" + "time" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +const ( + IssueTypeBMCInfoOutdated IssueType = "bmc-info-outdated" +) + +type ( + IssueBMCInfoOutdated struct { + details string + } +) + +func (i *IssueBMCInfoOutdated) Details() string { + return i.details +} + +func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + if m.IPMI.LastUpdated.IsZero() { + i.details = "machine ipmi has never been set" + return true + } + + lastUpdated := time.Since(m.IPMI.LastUpdated) + + if lastUpdated > 20*time.Minute { + i.details = fmt.Sprintf("last updated %s ago", lastUpdated.String()) + return true + } + + return false +} + +func (*IssueBMCInfoOutdated) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeBMCInfoOutdated, + Severity: IssueSeverityMajor, + Description: "BMC has not been updated from either metal-hammer or metal-bmc", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-info-outdated", + } +} diff --git a/cmd/metal-api/internal/issues/bmc-without-ip.go b/cmd/metal-api/internal/issues/bmc-without-ip.go new file mode 100644 index 000000000..3552b440e --- /dev/null +++ b/cmd/metal-api/internal/issues/bmc-without-ip.go @@ -0,0 +1,28 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeBMCWithoutIP IssueType = "bmc-without-ip" +) + +type ( + IssueBMCWithoutIP struct{} +) + +func (i *IssueBMCWithoutIP) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeBMCWithoutIP, + Severity: IssueSeverityMajor, + Description: "BMC has no ip address", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-without-ip", + } +} + +func (i *IssueBMCWithoutIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return m.IPMI.Address == "" +} + +func (i *IssueBMCWithoutIP) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/bmc-without-mac.go b/cmd/metal-api/internal/issues/bmc-without-mac.go new file mode 100644 index 000000000..bb220b9da --- /dev/null +++ b/cmd/metal-api/internal/issues/bmc-without-mac.go @@ -0,0 +1,28 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeBMCWithoutMAC IssueType = "bmc-without-mac" +) + +type ( + IssueBMCWithoutMAC struct{} +) + +func (i *IssueBMCWithoutMAC) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeBMCWithoutMAC, + Severity: IssueSeverityMajor, + Description: "BMC has no mac address", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-without-mac", + } +} + +func (i *IssueBMCWithoutMAC) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return m.IPMI.MacAddress == "" +} + +func (i *IssueBMCWithoutMAC) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/crash-loop.go b/cmd/metal-api/internal/issues/crash-loop.go new file mode 100644 index 000000000..668d69e03 --- /dev/null +++ b/cmd/metal-api/internal/issues/crash-loop.go @@ -0,0 +1,38 @@ +package issues + +import ( + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + "github.com/metal-stack/metal-lib/pkg/pointer" +) + +const ( + IssueTypeCrashLoop IssueType = "crashloop" +) + +type ( + IssueCrashLoop struct{} +) + +func (i *IssueCrashLoop) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeCrashLoop, + Severity: IssueSeverityMajor, + Description: "machine is in a provisioning crash loop (⭕)", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#crashloop", + } +} + +func (i *IssueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + if ec.CrashLoop { + if pointer.FirstOrZero(ec.Events).Event == metal.ProvisioningEventWaiting { + // Machine which are waiting are not considered to have issues + } else { + return true + } + } + return false +} + +func (i *IssueCrashLoop) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/failed-machine-reclaim.go b/cmd/metal-api/internal/issues/failed-machine-reclaim.go new file mode 100644 index 000000000..02b62ebfa --- /dev/null +++ b/cmd/metal-api/internal/issues/failed-machine-reclaim.go @@ -0,0 +1,41 @@ +package issues + +import ( + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + "github.com/metal-stack/metal-lib/pkg/pointer" +) + +const ( + IssueTypeFailedMachineReclaim IssueType = "failed-machine-reclaim" +) + +type ( + IssueFailedMachineReclaim struct{} +) + +func (i *IssueFailedMachineReclaim) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeFailedMachineReclaim, + Severity: IssueSeverityCritical, + Description: "machine phones home but not allocated", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#failed-machine-reclaim", + } +} + +func (i *IssueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + if ec.FailedMachineReclaim { + return true + } + + // compatibility: before the provisioning FSM was renewed, this state could be detected the following way + // we should keep this condition + if m.Allocation == nil && pointer.FirstOrZero(ec.Events).Event == metal.ProvisioningEventPhonedHome { + return true + } + + return false +} + +func (i *IssueFailedMachineReclaim) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go new file mode 100644 index 000000000..1181f8588 --- /dev/null +++ b/cmd/metal-api/internal/issues/issues.go @@ -0,0 +1,188 @@ +package issues + +import ( + "sort" + "time" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +type ( + // IssueConfig contains configuration parameters for finding machine issues + IssueConfig struct { + Machines metal.Machines + EventContainers metal.ProvisioningEventContainers + Severity IssueSeverity + Only []IssueType + Omit []IssueType + LastErrorThreshold time.Duration + } + + // Issue formulates an issue of a machine + Issue struct { + Type IssueType + Severity IssueSeverity + Description string + RefURL string + Details string + } + + // Issues is a list of issues + Issues []Issue + + // MachineWithIssues summarizes a machine with issues + MachineWithIssues struct { + Machine *metal.Machine + Issues Issues + } + // MachineIssues is map of a machine response to a list of machine issues + MachineIssues []*MachineWithIssues + + machineIssueMap map[*metal.Machine]Issues + + issueImpl interface { + // Evaluate decides whether a given machine has the machine issue. + // the third argument contains additional information that may be required for the issue evaluation + Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool + // Spec returns the issue spec of this issue. + Spec() *issueSpec + // Details returns additional information on the issue after the evaluation. + Details() string + } + + // issueSpec defines the specification of an issue. + issueSpec struct { + Type IssueType + Severity IssueSeverity + Description string + RefURL string + } +) + +func AllIssues() Issues { + var res Issues + + for _, t := range AllIssueTypes() { + i, err := newIssueFromType(t) + if err != nil { + continue + } + + res = append(res, toIssue(i)) + } + + return res +} + +func toIssue(i issueImpl) Issue { + return Issue{ + Type: i.Spec().Type, + Severity: i.Spec().Severity, + Description: i.Spec().Description, + RefURL: i.Spec().RefURL, + Details: i.Details(), + } +} + +func FindIssues(c *IssueConfig) (MachineIssues, error) { + res := machineIssueMap{} + + ecs := c.EventContainers.ByID() + + for _, t := range AllIssueTypes() { + if !c.includeIssue(t) { + continue + } + + for _, m := range c.Machines { + m := m + + i, err := newIssueFromType(t) + if err != nil { + return nil, err + } + + ec, ok := ecs[m.ID] + if !ok { + res.add(m, toIssue(&IssueNoEventContainer{})) + continue + } + + if i.Evaluate(m, ec, c) { + res.add(m, toIssue(i)) + } + } + } + + return res.toList(), nil +} + +func (mis MachineIssues) Get(id string) *MachineWithIssues { + for _, m := range mis { + m := m + + if m.Machine == nil { + continue + } + + if m.Machine.ID == id { + return m + } + } + + return nil +} + +func (c *IssueConfig) includeIssue(t IssueType) bool { + issue, err := newIssueFromType(t) + if err != nil { + return false + } + + if issue.Spec().Severity.LowerThan(c.Severity) { + return false + } + + for _, o := range c.Omit { + if t == o { + return false + } + } + + if len(c.Only) > 0 { + for _, o := range c.Only { + if t == o { + return true + } + } + return false + } + + return true +} + +func (mim machineIssueMap) add(m metal.Machine, issue Issue) { + issues, ok := mim[&m] + if !ok { + issues = Issues{} + } + issues = append(issues, issue) + mim[&m] = issues +} + +func (mim machineIssueMap) toList() MachineIssues { + var res MachineIssues + + for m, issues := range mim { + res = append(res, &MachineWithIssues{ + Machine: m, + Issues: issues, + }) + } + + sort.Slice(res, func(i, j int) bool { + return res[i].Machine.ID < res[j].Machine.ID + }) + + return res +} diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go new file mode 100644 index 000000000..31b359ca7 --- /dev/null +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -0,0 +1,484 @@ +package issues + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + "github.com/stretchr/testify/require" +) + +func TestFindIssues(t *testing.T) { + machineTemplate := func(id string) metal.Machine { + return metal.Machine{ + Base: metal.Base{ + ID: id, + }, + PartitionID: "a", + IPMI: metal.IPMI{ + Address: "1.2.3.4", + MacAddress: "aa:bb:00", + LastUpdated: time.Now().Add(-1 * time.Minute), + }, + } + } + eventContainerTemplate := func(id string) metal.ProvisioningEventContainer { + return metal.ProvisioningEventContainer{ + Base: metal.Base{ + ID: id, + }, + Liveliness: metal.MachineLivelinessAlive, + } + } + + tests := []struct { + name string + only []IssueType + + machines func() metal.Machines + eventContainers func() metal.ProvisioningEventContainers + + want func(machines metal.Machines) MachineIssues + }{ + { + name: "good machine has no issues", + machines: func() metal.Machines { + return metal.Machines{ + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + return metal.ProvisioningEventContainers{ + eventContainerTemplate("good"), + } + }, + want: nil, + }, + { + name: "no partition", + only: []IssueType{IssueTypeNoPartition}, + machines: func() metal.Machines { + noPartitionMachine := machineTemplate("no-partition") + noPartitionMachine.PartitionID = "" + + return metal.Machines{ + noPartitionMachine, + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + return metal.ProvisioningEventContainers{ + eventContainerTemplate("no-partition"), + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueNoPartition{}), + }, + }, + } + }, + }, + { + name: "liveliness dead", + only: []IssueType{IssueTypeLivelinessDead}, + machines: func() metal.Machines { + return metal.Machines{ + machineTemplate("dead"), + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + dead := eventContainerTemplate("dead") + dead.Liveliness = metal.MachineLivelinessDead + + return metal.ProvisioningEventContainers{ + dead, + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueLivelinessDead{}), + }, + }, + } + }, + }, + { + name: "liveliness unknown", + only: []IssueType{IssueTypeLivelinessUnknown}, + machines: func() metal.Machines { + return metal.Machines{ + machineTemplate("unknown"), + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + unknown := eventContainerTemplate("unknown") + unknown.Liveliness = metal.MachineLivelinessUnknown + + return metal.ProvisioningEventContainers{ + unknown, + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueLivelinessUnknown{}), + }, + }, + } + }, + }, + { + name: "liveliness not available", + only: []IssueType{IssueTypeLivelinessNotAvailable}, + machines: func() metal.Machines { + return metal.Machines{ + machineTemplate("n/a"), + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + na := eventContainerTemplate("n/a") + na.Liveliness = metal.MachineLiveliness("") + + return metal.ProvisioningEventContainers{ + na, + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueLivelinessNotAvailable{}), + }, + }, + } + }, + }, + { + name: "failed machine reclaim", + only: []IssueType{IssueTypeFailedMachineReclaim}, + machines: func() metal.Machines { + failedOld := machineTemplate("failed-old") + + return metal.Machines{ + machineTemplate("good"), + machineTemplate("failed"), + failedOld, + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + failed := eventContainerTemplate("failed") + failed.FailedMachineReclaim = true + + failedOld := eventContainerTemplate("failed-old") + failedOld.Events = metal.ProvisioningEvents{ + { + Event: metal.ProvisioningEventPhonedHome, + }, + } + + return metal.ProvisioningEventContainers{ + failed, + eventContainerTemplate("good"), + failedOld, + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueFailedMachineReclaim{}), + }, + }, + { + Machine: &machines[2], + Issues: Issues{ + toIssue(&IssueFailedMachineReclaim{}), + }, + }, + } + }, + }, + { + name: "crashloop", + only: []IssueType{IssueTypeCrashLoop}, + machines: func() metal.Machines { + return metal.Machines{ + machineTemplate("good"), + machineTemplate("crash"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + crash := eventContainerTemplate("crash") + crash.CrashLoop = true + + return metal.ProvisioningEventContainers{ + crash, + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueCrashLoop{}), + }, + }, + } + }, + }, + // { + // name: "last event error", + // only: []IssueType{IssueTypeLastEventError}, + // machines: func() metal.Machines { + // lastEventErrorMachine := machineTemplate("last") + // lastEventErrorMachine.Events = &models.V1MachineRecentProvisioningEvents{ + // LastErrorEvent: &models.V1MachineProvisioningEvent{ + // Time: strfmt.DateTime(testTime.Add(-5 * time.Minute)), + // }, + // } + + // return metal.Machines{ + // machineTemplate("0"), + // lastEventErrorMachine, + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueLastEventError{details: "occurred 5m0s ago"}), + // }, + // }, + // } + // }, + // }, + // { + // name: "bmc without mac", + // only: []IssueType{IssueTypeBMCWithoutMAC}, + // machines: func() metal.Machines { + // bmcWithoutMacMachine := machineTemplate("no-mac") + // bmcWithoutMacMachine.Ipmi.Mac = nil + + // return metal.Machines{ + // machineTemplate("0"), + // bmcWithoutMacMachine, + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueBMCWithoutMAC{}), + // }, + // }, + // } + // }, + // }, + // { + // name: "bmc without ip", + // only: []IssueType{IssueTypeBMCWithoutIP}, + // machines: func() metal.Machines { + // bmcWithoutMacMachine := machineTemplate("no-ip") + // bmcWithoutMacMachine.Ipmi.Address = nil + + // return metal.Machines{ + // machineTemplate("0"), + // bmcWithoutMacMachine, + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueBMCWithoutIP{}), + // }, + // }, + // } + // }, + // }, + // { + // name: "bmc info outdated", + // only: []IssueType{IssueTypeBMCInfoOutdated}, + // machines: func() metal.Machines { + // bmcOutdatedMachine := machineTemplate("outdated") + // bmcOutdatedMachine.Ipmi.LastUpdated = pointer.Pointer(strfmt.DateTime(testTime.Add(-3 * 60 * time.Minute))) + + // return metal.Machines{ + // machineTemplate("0"), + // bmcOutdatedMachine, + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueBMCInfoOutdated{ + // details: "last updated 3h0m0s ago", + // }), + // }, + // }, + // } + // }, + // }, + // { + // name: "asn shared", + // only: []IssueType{IssueTypeASNUniqueness}, + // machines: func() metal.Machines { + // asnSharedMachine1 := machineTemplate("shared1") + // asnSharedMachine1.Allocation = &models.V1MachineAllocation{ + // Role: pointer.Pointer(models.V1MachineAllocationRoleFirewall), + // Networks: []*models.V1MachineNetwork{ + // { + // Asn: pointer.Pointer(int64(0)), + // }, + // { + // Asn: pointer.Pointer(int64(100)), + // }, + // { + // Asn: pointer.Pointer(int64(200)), + // }, + // }, + // } + + // asnSharedMachine2 := machineTemplate("shared2") + // asnSharedMachine2.Allocation = &models.V1MachineAllocation{ + // Role: pointer.Pointer(models.V1MachineAllocationRoleFirewall), + // Networks: []*models.V1MachineNetwork{ + // { + // Asn: pointer.Pointer(int64(1)), + // }, + // { + // Asn: pointer.Pointer(int64(100)), + // }, + // { + // Asn: pointer.Pointer(int64(200)), + // }, + // }, + // } + + // return metal.Machines{ + // asnSharedMachine1, + // asnSharedMachine2, + // machineTemplate("0"), + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[0], + // Issues: Issues{ + // toIssue(&IssueASNUniqueness{ + // details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", *machines[1].ID), + // }), + // }, + // }, + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueASNUniqueness{ + // details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", *machines[0].ID), + // }), + // }, + // }, + // } + // }, + // }, + // { + // name: "non distinct bmc ip", + // only: []IssueType{IssueTypeNonDistinctBMCIP}, + // machines: func() metal.Machines { + // nonDistinctBMCMachine1 := machineTemplate("bmc1") + // nonDistinctBMCMachine1.Ipmi.Address = pointer.Pointer("127.0.0.1") + + // nonDistinctBMCMachine2 := machineTemplate("bmc2") + // nonDistinctBMCMachine2.Ipmi.Address = pointer.Pointer("127.0.0.1") + + // return metal.Machines{ + // nonDistinctBMCMachine1, + // nonDistinctBMCMachine2, + // machineTemplate("0"), + // } + // }, + // want: func(machines metal.Machines) MachineIssues { + // return MachineIssues{ + // { + // Machine: machines[0], + // Issues: Issues{ + // toIssue(&IssueNonDistinctBMCIP{ + // details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", *machines[1].ID), + // }), + // }, + // }, + // { + // Machine: machines[1], + // Issues: Issues{ + // toIssue(&IssueNonDistinctBMCIP{ + // details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", *machines[0].ID), + // }), + // }, + // }, + // } + // }, + // }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + ms := tt.machines() + + got, err := FindIssues(&IssueConfig{ + Machines: ms, + EventContainers: tt.eventContainers(), + Only: tt.only, + LastErrorThreshold: DefaultLastErrorThreshold(), + }) + require.NoError(t, err) + + var want MachineIssues + if tt.want != nil { + want = tt.want(ms) + } + + if diff := cmp.Diff(want, got, cmp.AllowUnexported(IssueLastEventError{}, IssueASNUniqueness{}, IssueNonDistinctBMCIP{})); diff != "" { + t.Errorf("diff (+got -want):\n %s", diff) + } + }) + } +} + +func TestAllIssues(t *testing.T) { + issuesTypes := map[IssueType]bool{} + for _, i := range AllIssues() { + issuesTypes[i.Type] = true + } + + for _, ty := range AllIssueTypes() { + if _, ok := issuesTypes[ty]; !ok { + t.Errorf("issue of type %s not contained in all issues", ty) + } + } +} diff --git a/cmd/metal-api/internal/issues/last-event-error.go b/cmd/metal-api/internal/issues/last-event-error.go new file mode 100644 index 000000000..09ec35999 --- /dev/null +++ b/cmd/metal-api/internal/issues/last-event-error.go @@ -0,0 +1,51 @@ +package issues + +import ( + "fmt" + "time" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +const ( + IssueTypeLastEventError IssueType = "last-event-error" +) + +type ( + IssueLastEventError struct { + details string + } +) + +func DefaultLastErrorThreshold() time.Duration { + return 7 * 24 * time.Hour +} + +func (i *IssueLastEventError) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeLastEventError, + Severity: IssueSeverityMinor, + Description: "the machine had an error during the provisioning lifecycle", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#last-event-error", + } +} + +func (i *IssueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + if c.LastErrorThreshold == 0 { + return false + } + + if ec.LastErrorEvent != nil { + timeSince := time.Since(time.Time(ec.LastErrorEvent.Time)) + if timeSince < c.LastErrorThreshold { + i.details = fmt.Sprintf("occurred %s ago", timeSince.String()) + return true + } + } + + return false +} + +func (i *IssueLastEventError) Details() string { + return i.details +} diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go new file mode 100644 index 000000000..88b9a5a2b --- /dev/null +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -0,0 +1,28 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeLivelinessDead IssueType = "liveliness-dead" +) + +type ( + IssueLivelinessDead struct{} +) + +func (i *IssueLivelinessDead) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeLivelinessDead, + Severity: IssueSeverityMajor, + Description: "the machine is not sending events anymore", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#liveliness-dead", + } +} + +func (i *IssueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return ec.Liveliness.Is(string(metal.MachineLivelinessDead)) +} + +func (i *IssueLivelinessDead) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/liveliness-not-available.go b/cmd/metal-api/internal/issues/liveliness-not-available.go new file mode 100644 index 000000000..11d7af720 --- /dev/null +++ b/cmd/metal-api/internal/issues/liveliness-not-available.go @@ -0,0 +1,33 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeLivelinessNotAvailable IssueType = "liveliness-not-available" +) + +type ( + IssueLivelinessNotAvailable struct{} +) + +func (i *IssueLivelinessNotAvailable) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeLivelinessNotAvailable, + Severity: IssueSeverityMinor, + Description: "the machine liveliness is not available", + } +} + +func (i *IssueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + allowed := map[metal.MachineLiveliness]bool{ + metal.MachineLivelinessAlive: true, + metal.MachineLivelinessDead: true, + metal.MachineLivelinessUnknown: true, + } + + return !allowed[ec.Liveliness] +} + +func (i *IssueLivelinessNotAvailable) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go new file mode 100644 index 000000000..b306f82b8 --- /dev/null +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -0,0 +1,28 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeLivelinessUnknown IssueType = "liveliness-unknown" +) + +type ( + IssueLivelinessUnknown struct{} +) + +func (i *IssueLivelinessUnknown) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeLivelinessUnknown, + Severity: IssueSeverityMajor, + Description: "the machine is not sending LLDP alive messages anymore", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#liveliness-unknown", + } +} + +func (i *IssueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return ec.Liveliness.Is(string(metal.MachineLivelinessUnknown)) +} + +func (i *IssueLivelinessUnknown) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/no-event-container.go b/cmd/metal-api/internal/issues/no-event-container.go new file mode 100644 index 000000000..314510348 --- /dev/null +++ b/cmd/metal-api/internal/issues/no-event-container.go @@ -0,0 +1,30 @@ +package issues + +import ( + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +const ( + IssueTypeNoEventContainer IssueType = "no-event-container" +) + +type ( + IssueNoEventContainer struct{} +) + +func (i *IssueNoEventContainer) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeNoEventContainer, + Severity: IssueSeverityMajor, + Description: "machine has no event container", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#no-event-container", + } +} + +func (i *IssueNoEventContainer) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return ec.Base.ID == "" +} + +func (i *IssueNoEventContainer) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/no-partition.go b/cmd/metal-api/internal/issues/no-partition.go new file mode 100644 index 000000000..85e4e6460 --- /dev/null +++ b/cmd/metal-api/internal/issues/no-partition.go @@ -0,0 +1,28 @@ +package issues + +import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" + +const ( + IssueTypeNoPartition IssueType = "no-partition" +) + +type ( + IssueNoPartition struct{} +) + +func (i *IssueNoPartition) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeNoPartition, + Severity: IssueSeverityMajor, + Description: "machine with no partition", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#no-partition", + } +} + +func (i *IssueNoPartition) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + return m.PartitionID == "" +} + +func (i *IssueNoPartition) Details() string { + return "" +} diff --git a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go new file mode 100644 index 000000000..29a35023d --- /dev/null +++ b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go @@ -0,0 +1,65 @@ +package issues + +import ( + "fmt" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" +) + +const ( + IssueTypeNonDistinctBMCIP IssueType = "bmc-no-distinct-ip" +) + +type ( + IssueNonDistinctBMCIP struct { + details string + } +) + +func (i *IssueNonDistinctBMCIP) Spec() *issueSpec { + return &issueSpec{ + Type: IssueTypeNonDistinctBMCIP, + Description: "BMC IP address is not distinct", + RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-no-distinct-ip", + } +} + +func (i *IssueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { + if m.IPMI.Address == "" { + return false + } + + var ( + bmcIP = m.IPMI.Address + overlaps []string + ) + + for _, machineFromAll := range c.Machines { + machineFromAll := machineFromAll + + if machineFromAll.ID == m.ID { + continue + } + otherMachine := machineFromAll + + if otherMachine.IPMI.Address == "" { + continue + } + + if bmcIP == otherMachine.IPMI.Address { + overlaps = append(overlaps, otherMachine.ID) + } + } + + if len(overlaps) == 0 { + return false + } + + i.details = fmt.Sprintf("BMC IP (%s) not unique, shared with %s", bmcIP, overlaps) + + return true +} + +func (i *IssueNonDistinctBMCIP) Details() string { + return i.details +} diff --git a/cmd/metal-api/internal/issues/severeties.go b/cmd/metal-api/internal/issues/severeties.go new file mode 100644 index 000000000..624195bab --- /dev/null +++ b/cmd/metal-api/internal/issues/severeties.go @@ -0,0 +1,48 @@ +package issues + +import "fmt" + +const ( + // IssueSeverityMinor is an issue that should be checked from time to time but has no bad effects for the user. + IssueSeverityMinor IssueSeverity = "minor" + // IssueSeverityMajor is an issue where user experience is affected or provider resources are wasted. + // overall functionality is still maintained though. major issues should be resolved as soon as possible. + IssueSeverityMajor IssueSeverity = "major" + // IssueSeverityCritical is an issue that can lead to disfunction of the system and need to be handled as quickly as possible. + IssueSeverityCritical IssueSeverity = "critical" +) + +type ( + IssueSeverity string +) + +func AllSevereties() []IssueSeverity { + return []IssueSeverity{ + IssueSeverityMinor, + IssueSeverityMajor, + IssueSeverityCritical, + } +} + +func SeverityFromString(input string) (IssueSeverity, error) { + switch IssueSeverity(input) { + case IssueSeverityCritical: + return IssueSeverityCritical, nil + case IssueSeverityMajor: + return IssueSeverityMajor, nil + case IssueSeverityMinor: + return IssueSeverityMinor, nil + default: + return "", fmt.Errorf("unknown issue severity: %s", input) + } +} + +func (s IssueSeverity) LowerThan(o IssueSeverity) bool { + smap := map[IssueSeverity]int{ + IssueSeverityCritical: 10, + IssueSeverityMajor: 5, + IssueSeverityMinor: 0, + } + + return smap[s] < smap[o] +} diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go new file mode 100644 index 000000000..8b15a672f --- /dev/null +++ b/cmd/metal-api/internal/issues/types.go @@ -0,0 +1,58 @@ +package issues + +import "fmt" + +type ( + IssueType string +) + +func AllIssueTypes() []IssueType { + return []IssueType{ + IssueTypeNoPartition, + IssueTypeLivelinessDead, + IssueTypeLivelinessUnknown, + IssueTypeLivelinessNotAvailable, + IssueTypeFailedMachineReclaim, + IssueTypeCrashLoop, + IssueTypeLastEventError, + IssueTypeBMCWithoutMAC, + IssueTypeBMCWithoutIP, + IssueTypeBMCInfoOutdated, + IssueTypeASNUniqueness, + IssueTypeNonDistinctBMCIP, + IssueTypeNoEventContainer, + } +} + +func newIssueFromType(t IssueType) (issueImpl, error) { + switch t { + case IssueTypeNoPartition: + return &IssueNoPartition{}, nil + case IssueTypeLivelinessDead: + return &IssueLivelinessDead{}, nil + case IssueTypeLivelinessUnknown: + return &IssueLivelinessUnknown{}, nil + case IssueTypeLivelinessNotAvailable: + return &IssueLivelinessNotAvailable{}, nil + case IssueTypeFailedMachineReclaim: + return &IssueFailedMachineReclaim{}, nil + case IssueTypeCrashLoop: + return &IssueCrashLoop{}, nil + case IssueTypeLastEventError: + return &IssueLastEventError{}, nil + case IssueTypeBMCWithoutMAC: + return &IssueBMCWithoutMAC{}, nil + case IssueTypeBMCWithoutIP: + return &IssueBMCWithoutIP{}, nil + case IssueTypeBMCInfoOutdated: + return &IssueBMCInfoOutdated{}, nil + case IssueTypeASNUniqueness: + return &IssueASNUniqueness{}, nil + case IssueTypeNonDistinctBMCIP: + return &IssueNonDistinctBMCIP{}, nil + case IssueTypeNoEventContainer: + return &IssueNoEventContainer{}, nil + default: + return nil, fmt.Errorf("unknown issue type: %s", t) + } +} diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index bf63ff9f2..c8fd91b39 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -11,6 +11,7 @@ import ( "time" "github.com/metal-stack/metal-api/cmd/metal-api/internal/headscale" + "github.com/metal-stack/metal-api/cmd/metal-api/internal/issues" "github.com/metal-stack/metal-lib/auditing" "github.com/avast/retry-go/v4" @@ -243,6 +244,17 @@ func (r *machineResource) webService() *restful.WebService { Returns(http.StatusOK, "OK", v1.MachineResponse{}). DefaultReturns("Error", httperrors.HTTPErrorResponse{})) + ws.Route(ws.POST("/issues"). + To(viewer(r.issues)). + Operation("issues"). + Doc("returns machine issues"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Metadata(auditing.Exclude, true). + Reads(v1.MachineIssuesRequest{}). + Writes([]v1.MachineIssueResponse{}). + Returns(http.StatusOK, "OK", []v1.MachineIssueResponse{}). + DefaultReturns("Error", httperrors.HTTPErrorResponse{})) + ws.Route(ws.POST("/ipmi"). To(editor(r.ipmiReport)). Operation("ipmiReport"). @@ -483,6 +495,73 @@ func (r *machineResource) updateMachine(request *restful.Request, response *rest r.send(request, response, http.StatusOK, resp) } +func (r *machineResource) issues(request *restful.Request, response *restful.Response) { + var requestPayload v1.MachineIssuesRequest + err := request.ReadEntity(&requestPayload) + if err != nil { + r.sendError(request, response, httperrors.BadRequest(err)) + return + } + + ms := metal.Machines{} + err = r.ds.SearchMachines(&requestPayload.MachineSearchQuery, &ms) + if err != nil { + r.sendError(request, response, defaultError(err)) + return + } + + ecs, err := r.ds.ListProvisioningEventContainers() + if err != nil { + r.sendError(request, response, defaultError(err)) + return + } + + var ( + severity = issues.IssueSeverityMinor + only []issues.IssueType + omit []issues.IssueType + lastErrorThreshold = issues.DefaultLastErrorThreshold() + ) + + issues, err := issues.FindIssues(&issues.IssueConfig{ + Machines: ms, + EventContainers: ecs, + Severity: severity, + Only: only, + Omit: omit, + LastErrorThreshold: lastErrorThreshold, + }) + if err != nil { + r.sendError(request, response, defaultError(err)) + return + } + + var issueResponse []*v1.MachineIssueResponse + for _, machineWithIssues := range issues { + machineWithIssues := machineWithIssues + + entry := &v1.MachineIssueResponse{ + MachineID: machineWithIssues.Machine.ID, + } + + for _, issue := range machineWithIssues.Issues { + issue := issue + + entry.Issues = append(entry.Issues, v1.MachineIssue{ + ID: string(issue.Type), + Severity: string(issue.Severity), + Description: issue.Description, + RefURL: issue.RefURL, + Details: issue.Details, + }) + } + + issueResponse = append(issueResponse, entry) + } + + r.send(request, response, http.StatusOK, issueResponse) +} + func (r *machineResource) getMachineConsolePassword(request *restful.Request, response *restful.Response) { var requestPayload v1.MachineConsolePasswordRequest err := request.ReadEntity(&requestPayload) diff --git a/cmd/metal-api/internal/service/v1/machine.go b/cmd/metal-api/internal/service/v1/machine.go index 3a83c1801..a964a2f63 100644 --- a/cmd/metal-api/internal/service/v1/machine.go +++ b/cmd/metal-api/internal/service/v1/machine.go @@ -4,6 +4,7 @@ import ( "time" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" + "github.com/metal-stack/metal-api/cmd/metal-api/internal/issues" "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" ) @@ -227,6 +228,7 @@ type MachineConsolePasswordRequest struct { ID string `json:"id" description:"id of the machine to get the consolepassword for"` Reason string `json:"reason" description:"reason why the consolepassword is requested, typically a incident number with short description"` } + type MachineConsolePasswordResponse struct { Common ConsolePassword string `json:"console_password" description:"the console password which was generated while provisioning"` @@ -264,6 +266,16 @@ type MachineReinstallRequest struct { ImageID string `json:"imageid" description:"the image id to be installed"` } +type MachineIssuesRequest struct { + datastore.MachineSearchQuery + + Only []issues.IssueType `json:"only" description:"a list of machine issues to include"` + Omit []issues.IssueType `json:"omit" description:"a list of machine issues to omit"` + + Severity string `json:"severity" description:"filters issue for given severity"` + LastErrorThreshold time.Duration `json:"last_error_threshold" description:"defines the last error threshold"` +} + type MachineAbortReinstallRequest struct { PrimaryDiskWiped bool `json:"primary_disk_wiped" description:"indicates whether the primary disk is already wiped"` } @@ -274,6 +286,19 @@ type MachineVPN struct { Connected bool `json:"connected" description:"connected to the VPN"` } +type MachineIssueResponse struct { + MachineID string `json:"machineid" description:"the machine id that has the given issues"` + Issues []MachineIssue `json:"issues" description:"the list of issues of this machine"` +} + +type MachineIssue struct { + ID string `json:"id" description:"the id of the issue"` + Severity string `json:"severity" description:"the severity of the issue"` + Description string `json:"description" description:"a description of the issue"` + RefURL string `json:"ref_url" description:"an issue reference to the issue in metal-stack docs"` + Details string `json:"details" description:"details of the issue"` +} + func NewMetalMachineHardware(r *MachineHardware) metal.MachineHardware { nics := metal.Nics{} for i := range r.Nics { diff --git a/spec/metal-api.json b/spec/metal-api.json index dc97a467e..acbf4f4f4 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -2736,6 +2736,267 @@ } } }, + "v1.MachineIssue": { + "properties": { + "description": { + "description": "a description of the issue", + "type": "string" + }, + "details": { + "description": "details of the issue", + "type": "string" + }, + "id": { + "description": "the id of the issue", + "type": "string" + }, + "ref_url": { + "description": "an issue reference to the issue in metal-stack docs", + "type": "string" + }, + "severity": { + "description": "the severity of the issue", + "type": "string" + } + }, + "required": [ + "description", + "details", + "id", + "ref_url", + "severity" + ] + }, + "v1.MachineIssueResponse": { + "properties": { + "issues": { + "description": "the list of issues of this machine", + "items": { + "$ref": "#/definitions/v1.MachineIssue" + }, + "type": "array" + }, + "machineid": { + "description": "the machine id that has the given issues", + "type": "string" + } + }, + "required": [ + "issues", + "machineid" + ] + }, + "v1.MachineIssuesRequest": { + "properties": { + "allocation_hostname": { + "type": "string" + }, + "allocation_image_id": { + "type": "string" + }, + "allocation_name": { + "type": "string" + }, + "allocation_project": { + "type": "string" + }, + "allocation_role": { + "type": "string" + }, + "allocation_succeeded": { + "type": "boolean" + }, + "disk_names": { + "items": { + "type": "string" + }, + "type": "array" + }, + "disk_sizes": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + }, + "fru_board_mfg": { + "type": "string" + }, + "fru_board_mfg_serial": { + "type": "string" + }, + "fru_board_part_number": { + "type": "string" + }, + "fru_chassis_part_number": { + "type": "string" + }, + "fru_chassis_part_serial": { + "type": "string" + }, + "fru_product_manufacturer": { + "type": "string" + }, + "fru_product_part_number": { + "type": "string" + }, + "fru_product_serial": { + "type": "string" + }, + "hardware_cpu_cores": { + "format": "int64", + "type": "integer" + }, + "hardware_memory": { + "format": "int64", + "type": "integer" + }, + "id": { + "type": "string" + }, + "ipmi_address": { + "type": "string" + }, + "ipmi_interface": { + "type": "string" + }, + "ipmi_mac_address": { + "type": "string" + }, + "ipmi_user": { + "type": "string" + }, + "last_error_threshold": { + "description": "defines the last error threshold", + "format": "int64", + "type": "integer" + }, + "name": { + "type": "string" + }, + "network_asns": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + }, + "network_destination_prefixes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "network_ids": { + "items": { + "type": "string" + }, + "type": "array" + }, + "network_ips": { + "items": { + "type": "string" + }, + "type": "array" + }, + "network_prefixes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "network_vrfs": { + "items": { + "format": "int64", + "type": "integer" + }, + "type": "array" + }, + "nics_mac_addresses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "nics_names": { + "items": { + "type": "string" + }, + "type": "array" + }, + "nics_neighbor_mac_addresses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "nics_neighbor_names": { + "items": { + "type": "string" + }, + "type": "array" + }, + "nics_neighbor_vrfs": { + "items": { + "type": "string" + }, + "type": "array" + }, + "nics_vrfs": { + "items": { + "type": "string" + }, + "type": "array" + }, + "omit": { + "description": "a list of machine issues to omit", + "items": { + "type": "string" + }, + "type": "array" + }, + "only": { + "description": "a list of machine issues to include", + "items": { + "type": "string" + }, + "type": "array" + }, + "partition_id": { + "type": "string" + }, + "rackid": { + "type": "string" + }, + "severity": { + "description": "filters issue for given severity", + "type": "string" + }, + "sizeid": { + "type": "string" + }, + "state_value": { + "enum": [ + "", + "LOCKED", + "RESERVED" + ], + "type": "string" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "last_error_threshold", + "omit", + "only", + "severity" + ] + }, "v1.MachineNetwork": { "description": "prefixes that are reachable within this network", "properties": { @@ -6507,6 +6768,48 @@ ] } }, + "/v1/machine/issues": { + "post": { + "consumes": [ + "application/json" + ], + "operationId": "issues", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.MachineIssuesRequest" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/v1.MachineIssueResponse" + }, + "type": "array" + } + }, + "default": { + "description": "Error", + "schema": { + "$ref": "#/definitions/httperrors.HTTPErrorResponse" + } + } + }, + "summary": "returns machine issues", + "tags": [ + "machine" + ] + } + }, "/v1/machine/update-firmware/{id}": { "post": { "consumes": [ From 7e557f39797cef143e384a16034db9f2fde05a3a Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 2 Oct 2023 16:17:10 +0200 Subject: [PATCH 02/17] Add filters. --- cmd/metal-api/internal/issues/types.go | 2 +- .../internal/service/machine-service.go | 57 ++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go index 8b15a672f..6362c4352 100644 --- a/cmd/metal-api/internal/issues/types.go +++ b/cmd/metal-api/internal/issues/types.go @@ -24,7 +24,7 @@ func AllIssueTypes() []IssueType { } } -func newIssueFromType(t IssueType) (issueImpl, error) { +func NewIssueFromType(t IssueType) (issueImpl, error) { switch t { case IssueTypeNoPartition: return &IssueNoPartition{}, nil diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index c8fd91b39..578612b6d 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -503,7 +503,55 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res return } - ms := metal.Machines{} + var ( + ms = metal.Machines{} + + severity = issues.IssueSeverityMinor + only []issues.IssueType + omit []issues.IssueType + lastErrorThreshold = issues.DefaultLastErrorThreshold() + ) + + if requestPayload.Severity != "" { + severity, err = issues.SeverityFromString(requestPayload.Severity) + if err != nil { + r.sendError(request, response, httperrors.BadRequest(err)) + return + } + } + + if len(requestPayload.Omit) > 0 { + for _, o := range requestPayload.Omit { + o := o + + _, err := issues.NewIssueFromType(o) + if err != nil { + r.sendError(request, response, httperrors.BadRequest(err)) + return + } + + omit = append(omit, o) + } + } + + if len(requestPayload.Only) > 0 { + for _, o := range requestPayload.Only { + o := o + + _, err := issues.NewIssueFromType(o) + if err != nil { + r.sendError(request, response, httperrors.BadRequest(err)) + return + } + + only = append(only, o) + } + } + + if requestPayload.LastErrorThreshold > 0 { + lastErrorThreshold = requestPayload.LastErrorThreshold + } + err = r.ds.SearchMachines(&requestPayload.MachineSearchQuery, &ms) if err != nil { r.sendError(request, response, defaultError(err)) @@ -516,13 +564,6 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res return } - var ( - severity = issues.IssueSeverityMinor - only []issues.IssueType - omit []issues.IssueType - lastErrorThreshold = issues.DefaultLastErrorThreshold() - ) - issues, err := issues.FindIssues(&issues.IssueConfig{ Machines: ms, EventContainers: ecs, From a203d19bc02e0f8c46d4f42a844328c6f7c13968 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 2 Oct 2023 16:17:20 +0200 Subject: [PATCH 03/17] More tests. --- cmd/metal-api/internal/issues/issues.go | 6 +- cmd/metal-api/internal/issues/issues_test.go | 348 +++++++++++-------- 2 files changed, 200 insertions(+), 154 deletions(-) diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index 1181f8588..f7c584502 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -63,7 +63,7 @@ func AllIssues() Issues { var res Issues for _, t := range AllIssueTypes() { - i, err := newIssueFromType(t) + i, err := NewIssueFromType(t) if err != nil { continue } @@ -97,7 +97,7 @@ func FindIssues(c *IssueConfig) (MachineIssues, error) { for _, m := range c.Machines { m := m - i, err := newIssueFromType(t) + i, err := NewIssueFromType(t) if err != nil { return nil, err } @@ -134,7 +134,7 @@ func (mis MachineIssues) Get(id string) *MachineWithIssues { } func (c *IssueConfig) includeIssue(t IssueType) bool { - issue, err := newIssueFromType(t) + issue, err := NewIssueFromType(t) if err != nil { return false } diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 31b359ca7..1057669c6 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -1,6 +1,7 @@ package issues import ( + "fmt" "testing" "time" @@ -246,95 +247,126 @@ func TestFindIssues(t *testing.T) { } }, }, + // FIXME: // { // name: "last event error", // only: []IssueType{IssueTypeLastEventError}, // machines: func() metal.Machines { // lastEventErrorMachine := machineTemplate("last") - // lastEventErrorMachine.Events = &models.V1MachineRecentProvisioningEvents{ - // LastErrorEvent: &models.V1MachineProvisioningEvent{ - // Time: strfmt.DateTime(testTime.Add(-5 * time.Minute)), - // }, - // } // return metal.Machines{ - // machineTemplate("0"), + // machineTemplate("good"), // lastEventErrorMachine, // } // }, - // want: func(machines metal.Machines) MachineIssues { - // return MachineIssues{ - // { - // Machine: machines[1], - // Issues: Issues{ - // toIssue(&IssueLastEventError{details: "occurred 5m0s ago"}), - // }, - // }, + // eventContainers: func() metal.ProvisioningEventContainers { + // last := eventContainerTemplate("last") + // last.LastErrorEvent = &metal.ProvisioningEvent{ + // Time: time.Now().Add(-5 * time.Minute), // } - // }, - // }, - // { - // name: "bmc without mac", - // only: []IssueType{IssueTypeBMCWithoutMAC}, - // machines: func() metal.Machines { - // bmcWithoutMacMachine := machineTemplate("no-mac") - // bmcWithoutMacMachine.Ipmi.Mac = nil - - // return metal.Machines{ - // machineTemplate("0"), - // bmcWithoutMacMachine, + // return metal.ProvisioningEventContainers{ + // last, + // eventContainerTemplate("good"), // } // }, // want: func(machines metal.Machines) MachineIssues { // return MachineIssues{ // { - // Machine: machines[1], + // Machine: &machines[1], // Issues: Issues{ - // toIssue(&IssueBMCWithoutMAC{}), + // toIssue(&IssueLastEventError{details: "occurred 5m0s ago"}), // }, // }, // } // }, // }, - // { - // name: "bmc without ip", - // only: []IssueType{IssueTypeBMCWithoutIP}, - // machines: func() metal.Machines { - // bmcWithoutMacMachine := machineTemplate("no-ip") - // bmcWithoutMacMachine.Ipmi.Address = nil + { + name: "bmc without mac", + only: []IssueType{IssueTypeBMCWithoutMAC}, + machines: func() metal.Machines { + noMac := machineTemplate("no-mac") + noMac.IPMI.MacAddress = "" - // return metal.Machines{ - // machineTemplate("0"), - // bmcWithoutMacMachine, - // } - // }, - // want: func(machines metal.Machines) MachineIssues { - // return MachineIssues{ - // { - // Machine: machines[1], - // Issues: Issues{ - // toIssue(&IssueBMCWithoutIP{}), - // }, - // }, - // } - // }, - // }, + return metal.Machines{ + machineTemplate("good"), + noMac, + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + crash := eventContainerTemplate("crash") + crash.CrashLoop = true + + return metal.ProvisioningEventContainers{ + eventContainerTemplate("no-mac"), + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueBMCWithoutMAC{}), + }, + }, + } + }, + }, + { + name: "bmc without ip", + only: []IssueType{IssueTypeBMCWithoutIP}, + machines: func() metal.Machines { + noIP := machineTemplate("no-ip") + noIP.IPMI.Address = "" + + return metal.Machines{ + machineTemplate("good"), + noIP, + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + crash := eventContainerTemplate("crash") + crash.CrashLoop = true + + return metal.ProvisioningEventContainers{ + eventContainerTemplate("no-ip"), + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueBMCWithoutIP{}), + }, + }, + } + }, + }, + // FIXME: // { // name: "bmc info outdated", // only: []IssueType{IssueTypeBMCInfoOutdated}, // machines: func() metal.Machines { - // bmcOutdatedMachine := machineTemplate("outdated") - // bmcOutdatedMachine.Ipmi.LastUpdated = pointer.Pointer(strfmt.DateTime(testTime.Add(-3 * 60 * time.Minute))) + // outdated := machineTemplate("outdated") + // outdated.IPMI.LastUpdated = time.Now().Add(-3 * 60 * time.Minute) // return metal.Machines{ - // machineTemplate("0"), - // bmcOutdatedMachine, + // machineTemplate("good"), + // outdated, + // } + // }, + // eventContainers: func() metal.ProvisioningEventContainers { + // return metal.ProvisioningEventContainers{ + // eventContainerTemplate("outdated"), + // eventContainerTemplate("good"), // } // }, // want: func(machines metal.Machines) MachineIssues { // return MachineIssues{ // { - // Machine: machines[1], + // Machine: &machines[1], // Issues: Issues{ // toIssue(&IssueBMCInfoOutdated{ // details: "last updated 3h0m0s ago", @@ -344,106 +376,120 @@ func TestFindIssues(t *testing.T) { // } // }, // }, - // { - // name: "asn shared", - // only: []IssueType{IssueTypeASNUniqueness}, - // machines: func() metal.Machines { - // asnSharedMachine1 := machineTemplate("shared1") - // asnSharedMachine1.Allocation = &models.V1MachineAllocation{ - // Role: pointer.Pointer(models.V1MachineAllocationRoleFirewall), - // Networks: []*models.V1MachineNetwork{ - // { - // Asn: pointer.Pointer(int64(0)), - // }, - // { - // Asn: pointer.Pointer(int64(100)), - // }, - // { - // Asn: pointer.Pointer(int64(200)), - // }, - // }, - // } + { + name: "asn shared", + only: []IssueType{IssueTypeASNUniqueness}, + machines: func() metal.Machines { + shared1 := machineTemplate("shared1") + shared1.Allocation = &metal.MachineAllocation{ + Role: metal.RoleFirewall, + MachineNetworks: []*metal.MachineNetwork{ + { + ASN: 0, + }, + { + ASN: 100, + }, + { + ASN: 200, + }, + }, + } - // asnSharedMachine2 := machineTemplate("shared2") - // asnSharedMachine2.Allocation = &models.V1MachineAllocation{ - // Role: pointer.Pointer(models.V1MachineAllocationRoleFirewall), - // Networks: []*models.V1MachineNetwork{ - // { - // Asn: pointer.Pointer(int64(1)), - // }, - // { - // Asn: pointer.Pointer(int64(100)), - // }, - // { - // Asn: pointer.Pointer(int64(200)), - // }, - // }, - // } + shared2 := machineTemplate("shared2") + shared2.Allocation = &metal.MachineAllocation{ + Role: metal.RoleFirewall, + MachineNetworks: []*metal.MachineNetwork{ + { + ASN: 1, + }, + { + ASN: 100, + }, + { + ASN: 200, + }, + }, + } - // return metal.Machines{ - // asnSharedMachine1, - // asnSharedMachine2, - // machineTemplate("0"), - // } - // }, - // want: func(machines metal.Machines) MachineIssues { - // return MachineIssues{ - // { - // Machine: machines[0], - // Issues: Issues{ - // toIssue(&IssueASNUniqueness{ - // details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", *machines[1].ID), - // }), - // }, - // }, - // { - // Machine: machines[1], - // Issues: Issues{ - // toIssue(&IssueASNUniqueness{ - // details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", *machines[0].ID), - // }), - // }, - // }, - // } - // }, - // }, - // { - // name: "non distinct bmc ip", - // only: []IssueType{IssueTypeNonDistinctBMCIP}, - // machines: func() metal.Machines { - // nonDistinctBMCMachine1 := machineTemplate("bmc1") - // nonDistinctBMCMachine1.Ipmi.Address = pointer.Pointer("127.0.0.1") + return metal.Machines{ + shared1, + shared2, + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + return metal.ProvisioningEventContainers{ + eventContainerTemplate("shared1"), + eventContainerTemplate("shared2"), + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueASNUniqueness{ + details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", machines[1].ID), + }), + }, + }, + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueASNUniqueness{ + details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", machines[0].ID), + }), + }, + }, + } + }, + }, + { + name: "non distinct bmc ip", + only: []IssueType{IssueTypeNonDistinctBMCIP}, + machines: func() metal.Machines { + bmc1 := machineTemplate("bmc1") + bmc1.IPMI.Address = "127.0.0.1" - // nonDistinctBMCMachine2 := machineTemplate("bmc2") - // nonDistinctBMCMachine2.Ipmi.Address = pointer.Pointer("127.0.0.1") + bmc2 := machineTemplate("bmc2") + bmc2.IPMI.Address = "127.0.0.1" - // return metal.Machines{ - // nonDistinctBMCMachine1, - // nonDistinctBMCMachine2, - // machineTemplate("0"), - // } - // }, - // want: func(machines metal.Machines) MachineIssues { - // return MachineIssues{ - // { - // Machine: machines[0], - // Issues: Issues{ - // toIssue(&IssueNonDistinctBMCIP{ - // details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", *machines[1].ID), - // }), - // }, - // }, - // { - // Machine: machines[1], - // Issues: Issues{ - // toIssue(&IssueNonDistinctBMCIP{ - // details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", *machines[0].ID), - // }), - // }, - // }, - // } - // }, - // }, + return metal.Machines{ + bmc1, + bmc2, + machineTemplate("good"), + } + }, + eventContainers: func() metal.ProvisioningEventContainers { + return metal.ProvisioningEventContainers{ + eventContainerTemplate("bmc1"), + eventContainerTemplate("bmc2"), + eventContainerTemplate("good"), + } + }, + want: func(machines metal.Machines) MachineIssues { + return MachineIssues{ + { + Machine: &machines[0], + Issues: Issues{ + toIssue(&IssueNonDistinctBMCIP{ + details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", machines[1].ID), + }), + }, + }, + { + Machine: &machines[1], + Issues: Issues{ + toIssue(&IssueNonDistinctBMCIP{ + details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", machines[0].ID), + }), + }, + }, + } + }, + }, } for _, tt := range tests { tt := tt From 5e0b9e5cbff6187cb8908d041e949de725f61239 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 2 Oct 2023 16:21:28 +0200 Subject: [PATCH 04/17] Add list endpoint. --- .../internal/service/machine-service.go | 31 +++++++++++++++++- spec/metal-api.json | 32 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 578612b6d..e95b6ea97 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -244,7 +244,17 @@ func (r *machineResource) webService() *restful.WebService { Returns(http.StatusOK, "OK", v1.MachineResponse{}). DefaultReturns("Error", httperrors.HTTPErrorResponse{})) - ws.Route(ws.POST("/issues"). + ws.Route(ws.GET("/issues"). + To(viewer(r.issues)). + Operation("listIssues"). + Doc("returns the list of issues that exist in the API"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Metadata(auditing.Exclude, true). + Writes([]v1.MachineIssue{}). + Returns(http.StatusOK, "OK", []v1.MachineIssue{}). + DefaultReturns("Error", httperrors.HTTPErrorResponse{})) + + ws.Route(ws.POST("/issues/evaluate"). To(viewer(r.issues)). Operation("issues"). Doc("returns machine issues"). @@ -495,6 +505,25 @@ func (r *machineResource) updateMachine(request *restful.Request, response *rest r.send(request, response, http.StatusOK, resp) } +func (r *machineResource) listIssues(request *restful.Request, response *restful.Response) { + issues := issues.AllIssues() + + var issueResponse []v1.MachineIssue + for _, issue := range issues { + issue := issue + + issueResponse = append(issueResponse, v1.MachineIssue{ + ID: string(issue.Type), + Severity: string(issue.Severity), + Description: issue.Description, + RefURL: issue.RefURL, + Details: issue.Details, + }) + } + + r.send(request, response, http.StatusOK, issueResponse) +} + func (r *machineResource) issues(request *restful.Request, response *restful.Response) { var requestPayload v1.MachineIssuesRequest err := request.ReadEntity(&requestPayload) diff --git a/spec/metal-api.json b/spec/metal-api.json index acbf4f4f4..1b61ed960 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -6769,6 +6769,38 @@ } }, "/v1/machine/issues": { + "get": { + "consumes": [ + "application/json" + ], + "operationId": "listIssues", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "items": { + "$ref": "#/definitions/v1.MachineIssue" + }, + "type": "array" + } + }, + "default": { + "description": "Error", + "schema": { + "$ref": "#/definitions/httperrors.HTTPErrorResponse" + } + } + }, + "summary": "returns the list of issues that exist in the API", + "tags": [ + "machine" + ] + } + }, + "/v1/machine/issues/evaluate": { "post": { "consumes": [ "application/json" From 92b4a4fe5cfc47fd268611fa1e0f3423c832f115 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 2 Oct 2023 16:26:29 +0200 Subject: [PATCH 05/17] Fix. --- cmd/metal-api/internal/service/machine-service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index e95b6ea97..3f92935ce 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -245,7 +245,7 @@ func (r *machineResource) webService() *restful.WebService { DefaultReturns("Error", httperrors.HTTPErrorResponse{})) ws.Route(ws.GET("/issues"). - To(viewer(r.issues)). + To(viewer(r.listIssues)). Operation("listIssues"). Doc("returns the list of issues that exist in the API"). Metadata(restfulspec.KeyOpenAPITags, tags). From 500f1f5a25966bc9db482005d999ae306ad788f6 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 9 Oct 2023 14:07:58 +0200 Subject: [PATCH 06/17] Let partition capacity use issue evaluation. --- .../internal/issues/asn-uniqueness.go | 4 +- .../internal/issues/bmc-info-outdated.go | 4 +- .../internal/issues/bmc-without-ip.go | 4 +- .../internal/issues/bmc-without-mac.go | 4 +- cmd/metal-api/internal/issues/crash-loop.go | 4 +- .../internal/issues/failed-machine-reclaim.go | 4 +- cmd/metal-api/internal/issues/issues.go | 26 +-- cmd/metal-api/internal/issues/issues_test.go | 2 +- .../internal/issues/last-event-error.go | 4 +- .../internal/issues/liveliness-dead.go | 4 +- .../issues/liveliness-not-available.go | 4 +- .../internal/issues/liveliness-unknown.go | 4 +- .../internal/issues/no-event-container.go | 4 +- cmd/metal-api/internal/issues/no-partition.go | 4 +- .../internal/issues/non-distinct-bmc-ip.go | 4 +- cmd/metal-api/internal/issues/types.go | 2 +- cmd/metal-api/internal/metal/provisioning.go | 4 +- .../internal/metal/provisioning_test.go | 2 +- .../internal/service/machine-service.go | 29 +-- .../internal/service/partition-service.go | 185 +++++++++--------- .../service/partition-service_test.go | 24 ++- .../internal/service/v1/partition.go | 15 +- spec/metal-api.json | 31 --- 23 files changed, 178 insertions(+), 194 deletions(-) diff --git a/cmd/metal-api/internal/issues/asn-uniqueness.go b/cmd/metal-api/internal/issues/asn-uniqueness.go index e01a07756..d2fe39e3e 100644 --- a/cmd/metal-api/internal/issues/asn-uniqueness.go +++ b/cmd/metal-api/internal/issues/asn-uniqueness.go @@ -18,8 +18,8 @@ type ( } ) -func (i *IssueASNUniqueness) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueASNUniqueness) Spec() *spec { + return &spec{ Type: IssueTypeASNUniqueness, Severity: IssueSeverityMinor, Description: "The ASN is not unique (only impact on firewalls)", diff --git a/cmd/metal-api/internal/issues/bmc-info-outdated.go b/cmd/metal-api/internal/issues/bmc-info-outdated.go index d7766d875..7c76b1b3d 100644 --- a/cmd/metal-api/internal/issues/bmc-info-outdated.go +++ b/cmd/metal-api/internal/issues/bmc-info-outdated.go @@ -37,8 +37,8 @@ func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEv return false } -func (*IssueBMCInfoOutdated) Spec() *issueSpec { - return &issueSpec{ +func (*IssueBMCInfoOutdated) Spec() *spec { + return &spec{ Type: IssueTypeBMCInfoOutdated, Severity: IssueSeverityMajor, Description: "BMC has not been updated from either metal-hammer or metal-bmc", diff --git a/cmd/metal-api/internal/issues/bmc-without-ip.go b/cmd/metal-api/internal/issues/bmc-without-ip.go index 3552b440e..dad08b13f 100644 --- a/cmd/metal-api/internal/issues/bmc-without-ip.go +++ b/cmd/metal-api/internal/issues/bmc-without-ip.go @@ -10,8 +10,8 @@ type ( IssueBMCWithoutIP struct{} ) -func (i *IssueBMCWithoutIP) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueBMCWithoutIP) Spec() *spec { + return &spec{ Type: IssueTypeBMCWithoutIP, Severity: IssueSeverityMajor, Description: "BMC has no ip address", diff --git a/cmd/metal-api/internal/issues/bmc-without-mac.go b/cmd/metal-api/internal/issues/bmc-without-mac.go index bb220b9da..28b6e4197 100644 --- a/cmd/metal-api/internal/issues/bmc-without-mac.go +++ b/cmd/metal-api/internal/issues/bmc-without-mac.go @@ -10,8 +10,8 @@ type ( IssueBMCWithoutMAC struct{} ) -func (i *IssueBMCWithoutMAC) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueBMCWithoutMAC) Spec() *spec { + return &spec{ Type: IssueTypeBMCWithoutMAC, Severity: IssueSeverityMajor, Description: "BMC has no mac address", diff --git a/cmd/metal-api/internal/issues/crash-loop.go b/cmd/metal-api/internal/issues/crash-loop.go index 668d69e03..c50d30f84 100644 --- a/cmd/metal-api/internal/issues/crash-loop.go +++ b/cmd/metal-api/internal/issues/crash-loop.go @@ -13,8 +13,8 @@ type ( IssueCrashLoop struct{} ) -func (i *IssueCrashLoop) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueCrashLoop) Spec() *spec { + return &spec{ Type: IssueTypeCrashLoop, Severity: IssueSeverityMajor, Description: "machine is in a provisioning crash loop (⭕)", diff --git a/cmd/metal-api/internal/issues/failed-machine-reclaim.go b/cmd/metal-api/internal/issues/failed-machine-reclaim.go index 02b62ebfa..b66a2a9b3 100644 --- a/cmd/metal-api/internal/issues/failed-machine-reclaim.go +++ b/cmd/metal-api/internal/issues/failed-machine-reclaim.go @@ -13,8 +13,8 @@ type ( IssueFailedMachineReclaim struct{} ) -func (i *IssueFailedMachineReclaim) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueFailedMachineReclaim) Spec() *spec { + return &spec{ Type: IssueTypeFailedMachineReclaim, Severity: IssueSeverityCritical, Description: "machine phones home but not allocated", diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index f7c584502..19538b52e 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -38,20 +38,20 @@ type ( // MachineIssues is map of a machine response to a list of machine issues MachineIssues []*MachineWithIssues - machineIssueMap map[*metal.Machine]Issues + MachineIssuesMap map[*metal.Machine]Issues - issueImpl interface { + issue interface { // Evaluate decides whether a given machine has the machine issue. // the third argument contains additional information that may be required for the issue evaluation Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool // Spec returns the issue spec of this issue. - Spec() *issueSpec + Spec() *spec // Details returns additional information on the issue after the evaluation. Details() string } - // issueSpec defines the specification of an issue. - issueSpec struct { + // spec defines the specification of an issue. + spec struct { Type IssueType Severity IssueSeverity Description string @@ -74,7 +74,7 @@ func AllIssues() Issues { return res } -func toIssue(i issueImpl) Issue { +func toIssue(i issue) Issue { return Issue{ Type: i.Spec().Type, Severity: i.Spec().Severity, @@ -84,8 +84,12 @@ func toIssue(i issueImpl) Issue { } } -func FindIssues(c *IssueConfig) (MachineIssues, error) { - res := machineIssueMap{} +func FindIssues(c *IssueConfig) (MachineIssuesMap, error) { + if c.LastErrorThreshold == 0 { + c.LastErrorThreshold = DefaultLastErrorThreshold() + } + + res := MachineIssuesMap{} ecs := c.EventContainers.ByID() @@ -114,7 +118,7 @@ func FindIssues(c *IssueConfig) (MachineIssues, error) { } } - return res.toList(), nil + return res, nil } func (mis MachineIssues) Get(id string) *MachineWithIssues { @@ -161,7 +165,7 @@ func (c *IssueConfig) includeIssue(t IssueType) bool { return true } -func (mim machineIssueMap) add(m metal.Machine, issue Issue) { +func (mim MachineIssuesMap) add(m metal.Machine, issue Issue) { issues, ok := mim[&m] if !ok { issues = Issues{} @@ -170,7 +174,7 @@ func (mim machineIssueMap) add(m metal.Machine, issue Issue) { mim[&m] = issues } -func (mim machineIssueMap) toList() MachineIssues { +func (mim MachineIssuesMap) ToList() MachineIssues { var res MachineIssues for m, issues := range mim { diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 1057669c6..02f6fd5ac 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -509,7 +509,7 @@ func TestFindIssues(t *testing.T) { want = tt.want(ms) } - if diff := cmp.Diff(want, got, cmp.AllowUnexported(IssueLastEventError{}, IssueASNUniqueness{}, IssueNonDistinctBMCIP{})); diff != "" { + if diff := cmp.Diff(want, got.ToList(), cmp.AllowUnexported(IssueLastEventError{}, IssueASNUniqueness{}, IssueNonDistinctBMCIP{})); diff != "" { t.Errorf("diff (+got -want):\n %s", diff) } }) diff --git a/cmd/metal-api/internal/issues/last-event-error.go b/cmd/metal-api/internal/issues/last-event-error.go index 09ec35999..694bd0832 100644 --- a/cmd/metal-api/internal/issues/last-event-error.go +++ b/cmd/metal-api/internal/issues/last-event-error.go @@ -21,8 +21,8 @@ func DefaultLastErrorThreshold() time.Duration { return 7 * 24 * time.Hour } -func (i *IssueLastEventError) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueLastEventError) Spec() *spec { + return &spec{ Type: IssueTypeLastEventError, Severity: IssueSeverityMinor, Description: "the machine had an error during the provisioning lifecycle", diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go index 88b9a5a2b..e9620e3be 100644 --- a/cmd/metal-api/internal/issues/liveliness-dead.go +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -10,8 +10,8 @@ type ( IssueLivelinessDead struct{} ) -func (i *IssueLivelinessDead) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueLivelinessDead) Spec() *spec { + return &spec{ Type: IssueTypeLivelinessDead, Severity: IssueSeverityMajor, Description: "the machine is not sending events anymore", diff --git a/cmd/metal-api/internal/issues/liveliness-not-available.go b/cmd/metal-api/internal/issues/liveliness-not-available.go index 11d7af720..44f51cfab 100644 --- a/cmd/metal-api/internal/issues/liveliness-not-available.go +++ b/cmd/metal-api/internal/issues/liveliness-not-available.go @@ -10,8 +10,8 @@ type ( IssueLivelinessNotAvailable struct{} ) -func (i *IssueLivelinessNotAvailable) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueLivelinessNotAvailable) Spec() *spec { + return &spec{ Type: IssueTypeLivelinessNotAvailable, Severity: IssueSeverityMinor, Description: "the machine liveliness is not available", diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go index b306f82b8..3e072ee18 100644 --- a/cmd/metal-api/internal/issues/liveliness-unknown.go +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -10,8 +10,8 @@ type ( IssueLivelinessUnknown struct{} ) -func (i *IssueLivelinessUnknown) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueLivelinessUnknown) Spec() *spec { + return &spec{ Type: IssueTypeLivelinessUnknown, Severity: IssueSeverityMajor, Description: "the machine is not sending LLDP alive messages anymore", diff --git a/cmd/metal-api/internal/issues/no-event-container.go b/cmd/metal-api/internal/issues/no-event-container.go index 314510348..2c260c30d 100644 --- a/cmd/metal-api/internal/issues/no-event-container.go +++ b/cmd/metal-api/internal/issues/no-event-container.go @@ -12,8 +12,8 @@ type ( IssueNoEventContainer struct{} ) -func (i *IssueNoEventContainer) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueNoEventContainer) Spec() *spec { + return &spec{ Type: IssueTypeNoEventContainer, Severity: IssueSeverityMajor, Description: "machine has no event container", diff --git a/cmd/metal-api/internal/issues/no-partition.go b/cmd/metal-api/internal/issues/no-partition.go index 85e4e6460..276237c54 100644 --- a/cmd/metal-api/internal/issues/no-partition.go +++ b/cmd/metal-api/internal/issues/no-partition.go @@ -10,8 +10,8 @@ type ( IssueNoPartition struct{} ) -func (i *IssueNoPartition) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueNoPartition) Spec() *spec { + return &spec{ Type: IssueTypeNoPartition, Severity: IssueSeverityMajor, Description: "machine with no partition", diff --git a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go index 29a35023d..1fd3a68d6 100644 --- a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go +++ b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go @@ -16,8 +16,8 @@ type ( } ) -func (i *IssueNonDistinctBMCIP) Spec() *issueSpec { - return &issueSpec{ +func (i *IssueNonDistinctBMCIP) Spec() *spec { + return &spec{ Type: IssueTypeNonDistinctBMCIP, Description: "BMC IP address is not distinct", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-no-distinct-ip", diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go index 6362c4352..29f9b133d 100644 --- a/cmd/metal-api/internal/issues/types.go +++ b/cmd/metal-api/internal/issues/types.go @@ -24,7 +24,7 @@ func AllIssueTypes() []IssueType { } } -func NewIssueFromType(t IssueType) (issueImpl, error) { +func NewIssueFromType(t IssueType) (issue, error) { switch t { case IssueTypeNoPartition: return &IssueNoPartition{}, nil diff --git a/cmd/metal-api/internal/metal/provisioning.go b/cmd/metal-api/internal/metal/provisioning.go index 12503d7f5..207ba286a 100644 --- a/cmd/metal-api/internal/metal/provisioning.go +++ b/cmd/metal-api/internal/metal/provisioning.go @@ -48,8 +48,8 @@ var ( type ProvisioningEvents []ProvisioningEvent // Is return true if given event is equal to specific EventType -func (p ProvisioningEventType) Is(event string) bool { - return string(p) == event +func (p ProvisioningEventType) Is(event ProvisioningEventType) bool { + return p == event } // TrimEvents trim the events to maxCount diff --git a/cmd/metal-api/internal/metal/provisioning_test.go b/cmd/metal-api/internal/metal/provisioning_test.go index ac8b83ba6..df76495b8 100644 --- a/cmd/metal-api/internal/metal/provisioning_test.go +++ b/cmd/metal-api/internal/metal/provisioning_test.go @@ -8,7 +8,7 @@ import ( func TestProvisioningEventType_Is(t *testing.T) { tests := []struct { name string - event string + event ProvisioningEventType p ProvisioningEventType want bool }{ diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 3f92935ce..b66f0c706 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -593,7 +593,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res return } - issues, err := issues.FindIssues(&issues.IssueConfig{ + machinesWithIssues, err := issues.FindIssues(&issues.IssueConfig{ Machines: ms, EventContainers: ecs, Severity: severity, @@ -607,7 +607,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res } var issueResponse []*v1.MachineIssueResponse - for _, machineWithIssues := range issues { + for _, machineWithIssues := range machinesWithIssues.ToList() { machineWithIssues := machineWithIssues entry := &v1.MachineIssueResponse{ @@ -1939,7 +1939,7 @@ func evaluateMachineLiveliness(ds *datastore.RethinkStore, m metal.Machine) (met provisioningEvents, err := ds.FindProvisioningEventContainer(m.ID) if err != nil { // we have no provisioning events... we cannot tell - return metal.MachineLivelinessUnknown, fmt.Errorf("no provisioningEvents found for ID: %s", m.ID) + return metal.MachineLivelinessUnknown, fmt.Errorf("no provisioning event container found for machine: %s", m.ID) } old := *provisioningEvents @@ -1956,6 +1956,7 @@ func evaluateMachineLiveliness(ds *datastore.RethinkStore, m metal.Machine) (met } else { provisioningEvents.Liveliness = metal.MachineLivelinessAlive } + err = ds.UpdateProvisioningEventContainer(&old, provisioningEvents) if err != nil { return provisioningEvents.Liveliness, err @@ -2013,7 +2014,6 @@ func ResurrectMachines(ctx context.Context, ds *datastore.RethinkStore, publishe } continue } - } logger.Info("finished machine resurrection") @@ -2238,27 +2238,6 @@ func publishMachineCmd(logger *zap.SugaredLogger, m *metal.Machine, publisher bu return nil } -func machineHasIssues(m *v1.MachineResponse) bool { - if m.Partition == nil { - return true - } - if !metal.MachineLivelinessAlive.Is(m.Liveliness) { - return true - } - if m.Allocation == nil && len(m.RecentProvisioningEvents.Events) > 0 && metal.ProvisioningEventPhonedHome.Is(m.RecentProvisioningEvents.Events[0].Event) { - // not allocated, but phones home - return true - } - if m.RecentProvisioningEvents.CrashLoop || m.RecentProvisioningEvents.FailedMachineReclaim { - // Machines in crash loop but in "Waiting" state are considered available - if len(m.RecentProvisioningEvents.Events) > 0 && !metal.ProvisioningEventWaiting.Is(m.RecentProvisioningEvents.Events[0].Event) { - return true - } - } - - return false -} - func makeMachineResponse(m *metal.Machine, ds *datastore.RethinkStore) (*v1.MachineResponse, error) { s, p, i, ec, err := findMachineReferencedEntities(m, ds) if err != nil { diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index 4075a119e..25936fdc9 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -2,11 +2,14 @@ package service import ( "errors" + "fmt" "net/http" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" + "github.com/metal-stack/metal-api/cmd/metal-api/internal/issues" "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" "github.com/metal-stack/metal-lib/auditing" + "github.com/metal-stack/metal-lib/pkg/pointer" "go.uber.org/zap" v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1" @@ -97,18 +100,6 @@ func (r *partitionResource) webService() *restful.WebService { Returns(http.StatusConflict, "Conflict", httperrors.HTTPErrorResponse{}). DefaultReturns("Error", httperrors.HTTPErrorResponse{})) - // Deprecated, can be removed in the future - ws.Route(ws.GET("/capacity"). - To(r.partitionCapacityCompat). - Operation("partitionCapacityCompat"). - Doc("get partition capacity"). - Metadata(restfulspec.KeyOpenAPITags, tags). - Metadata(auditing.Exclude, true). - Writes([]v1.PartitionCapacity{}). - Returns(http.StatusOK, "OK", []v1.PartitionCapacity{}). - DefaultReturns("Error", httperrors.HTTPErrorResponse{}). - Deprecate()) - ws.Route(ws.POST("/capacity"). To(r.partitionCapacity). Operation("partitionCapacity"). @@ -314,16 +305,6 @@ func (r *partitionResource) updatePartition(request *restful.Request, response * r.send(request, response, http.StatusOK, v1.NewPartitionResponse(&newPartition)) } -func (r *partitionResource) partitionCapacityCompat(request *restful.Request, response *restful.Response) { - partitionCapacities, err := r.calcPartitionCapacity(nil) - if err != nil { - r.sendError(request, response, httperrors.BadRequest(err)) - return - } - - r.send(request, response, http.StatusOK, partitionCapacities) -} - func (r *partitionResource) partitionCapacity(request *restful.Request, response *restful.Response) { var requestPayload v1.PartitionCapacityRequest err := request.ReadEntity(&requestPayload) @@ -342,15 +323,13 @@ func (r *partitionResource) partitionCapacity(request *restful.Request, response } func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityRequest) ([]v1.PartitionCapacity, error) { - // FIXME bad workaround to be able to run make spec - if r.ds == nil { - return nil, nil - } - var ( - ps metal.Partitions - ms metal.Machines - err error + ps metal.Partitions + ms metal.Machines + + pcs = map[string]*v1.PartitionCapacity{} + + machineQuery = datastore.MachineSearchQuery{} ) if pcr != nil && pcr.ID != nil { @@ -359,96 +338,118 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque return nil, err } ps = metal.Partitions{*p} + + machineQuery.PartitionID = pcr.ID } else { + var err error ps, err = r.ds.ListPartitions() if err != nil { return nil, err } } - msq := datastore.MachineSearchQuery{} if pcr != nil && pcr.Size != nil { - msq.SizeID = pcr.Size + machineQuery.SizeID = pcr.Size } - err = r.ds.SearchMachines(&msq, &ms) + err := r.ds.SearchMachines(&machineQuery, &ms) if err != nil { return nil, err } - machines, err := makeMachineResponseList(ms, r.ds) + + ecs, err := r.ds.ListProvisioningEventContainers() if err != nil { - return nil, err + return nil, fmt.Errorf("unable to fetch provisioning event containers: %w", err) + } + + machinesWithIssues, err := issues.FindIssues(&issues.IssueConfig{ + Machines: ms, + EventContainers: ecs, + Only: []issues.IssueType{ + issues.IssueTypeLivelinessDead, + issues.IssueTypeLivelinessUnknown, + issues.IssueTypeLivelinessNotAvailable, + issues.IssueTypeFailedMachineReclaim, + issues.IssueTypeCrashLoop, + }, + }) + if err != nil { + return nil, fmt.Errorf("unable to calculate machine issues: %w", err) } - partitionCapacities := []v1.PartitionCapacity{} - for _, p := range ps { - p := p - capacities := make(map[string]*v1.ServerCapacity) - for _, m := range machines { - m := m - if m.Partition == nil { - continue - } - if m.Partition.ID != p.ID { - continue - } + partitionsByID := ps.ByID() + ecsByID := ecs.ByID() - size := metal.UnknownSize.ID - if m.Size != nil { - size = m.Size.ID - } + for _, m := range ms { + m := m - available := false - if m.State.Value == string(metal.AvailableState) && len(m.RecentProvisioningEvents.Events) > 0 { - events := m.RecentProvisioningEvents.Events - if metal.ProvisioningEventWaiting.Is(events[0].Event) && metal.ProvisioningEventAlive.Is(m.Liveliness) { - available = true - } - } + ec, ok := ecsByID[m.ID] + if !ok { + continue + } - cap, ok := capacities[size] - if !ok { - cap = &v1.ServerCapacity{Size: size} - capacities[size] = cap - } + p, ok := partitionsByID[m.PartitionID] + if !ok { + continue + } - if m.Allocation != nil { - cap.Allocated++ - } else if machineHasIssues(m) { - cap.Faulty++ - cap.FaultyMachines = append(cap.FaultyMachines, m.ID) - } else if available { - cap.Free++ - } else { - cap.Other++ - cap.OtherMachines = append(cap.OtherMachines, m.ID) + pc, ok := pcs[m.PartitionID] + if !ok { + pc = &v1.PartitionCapacity{ + Common: v1.Common{ + Identifiable: v1.Identifiable{ + ID: p.ID, + }, + Describable: v1.Describable{ + Name: &p.Name, + Description: &p.Description, + }, + }, + ServerCapacities: v1.ServerCapacities{}, } + } + pcs[m.PartitionID] = pc - cap.Total++ + size := metal.UnknownSize.ID + if m.SizeID != "" { + size = m.SizeID } - sc := []v1.ServerCapacity{} - for i := range capacities { - if capacities[i] == nil { - continue + + cap := pc.ServerCapacities.FindBySize(size) + if cap == nil { + cap = &v1.ServerCapacity{ + Size: size, } - sc = append(sc, *capacities[i]) + pc.ServerCapacities = append(pc.ServerCapacities, cap) } - pc := v1.PartitionCapacity{ - Common: v1.Common{ - Identifiable: v1.Identifiable{ - ID: p.ID, - }, - Describable: v1.Describable{ - Name: &p.Name, - Description: &p.Description, - }, - }, - ServerCapacities: sc, + cap.Total++ + + if m.Allocation != nil { + cap.Allocated++ + continue + } + + if _, ok := machinesWithIssues[&m]; ok { + cap.Faulty++ + cap.FaultyMachines = append(cap.FaultyMachines, m.ID) + continue + } + + if m.State.Value == metal.AvailableState && metal.ProvisioningEventWaiting.Is(pointer.FirstOrZero(ec.Events).Event) { + cap.Free++ + continue } - partitionCapacities = append(partitionCapacities, pc) + cap.Other++ + cap.OtherMachines = append(cap.OtherMachines, m.ID) + } + + res := []v1.PartitionCapacity{} + for _, pc := range pcs { + pc := pc + res = append(res, *pc) } - return partitionCapacities, err + return res, nil } diff --git a/cmd/metal-api/internal/service/partition-service_test.go b/cmd/metal-api/internal/service/partition-service_test.go index 503c5dce7..0606015ab 100644 --- a/cmd/metal-api/internal/service/partition-service_test.go +++ b/cmd/metal-api/internal/service/partition-service_test.go @@ -10,9 +10,11 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/zap/zaptest" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" restful "github.com/emicklei/go-restful/v3" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" + "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1" "github.com/metal-stack/metal-api/cmd/metal-api/internal/testdata" "github.com/metal-stack/metal-lib/httperrors" @@ -244,13 +246,28 @@ func TestUpdatePartition(t *testing.T) { func TestPartitionCapacity(t *testing.T) { ds, mock := datastore.InitMockDB(t) + + ecs := []metal.ProvisioningEventContainer{} + for _, m := range testdata.TestMachines { + m := m + ecs = append(ecs, metal.ProvisioningEventContainer{ + Base: m.Base, + }) + } + mock.On(r.DB("mockdb").Table("event")).Return(ecs, nil) + testdata.InitMockDBData(mock) log := zaptest.NewLogger(t).Sugar() service := NewPartition(log, ds, &nopTopicCreater{}) container := restful.NewContainer().Add(service) - req := httptest.NewRequest("GET", "/v1/partition/capacity", nil) + pcRequest := &v1.PartitionCapacityRequest{} + js, err := json.Marshal(pcRequest) + require.NoError(t, err) + body := bytes.NewBuffer(js) + + req := httptest.NewRequest("POST", "/v1/partition/capacity", body) req.Header.Add("Content-Type", "application/json") container = injectAdmin(log, container, req) w := httptest.NewRecorder() @@ -260,12 +277,13 @@ func TestPartitionCapacity(t *testing.T) { defer resp.Body.Close() require.Equal(t, http.StatusOK, resp.StatusCode, w.Body.String()) var result []v1.PartitionCapacity - err := json.NewDecoder(resp.Body).Decode(&result) + err = json.NewDecoder(resp.Body).Decode(&result) require.NoError(t, err) + require.Len(t, result, 1) require.Equal(t, testdata.Partition1.ID, result[0].ID) require.NotNil(t, result[0].ServerCapacities) - require.Equal(t, 1, len(result[0].ServerCapacities)) + require.Len(t, result[0].ServerCapacities, 1) c := result[0].ServerCapacities[0] require.Equal(t, "1", c.Size) require.Equal(t, 5, c.Total) diff --git a/cmd/metal-api/internal/service/v1/partition.go b/cmd/metal-api/internal/service/v1/partition.go index 49f1bbff5..522075f31 100644 --- a/cmd/metal-api/internal/service/v1/partition.go +++ b/cmd/metal-api/internal/service/v1/partition.go @@ -39,9 +39,11 @@ type PartitionCapacityRequest struct { Size *string `json:"sizeid" description:"the size to filter for" optional:"true"` } +type ServerCapacities []*ServerCapacity + type PartitionCapacity struct { Common - ServerCapacities []ServerCapacity `json:"servers" description:"servers available in this partition"` + ServerCapacities ServerCapacities `json:"servers" description:"servers available in this partition"` } type ServerCapacity struct { @@ -85,3 +87,14 @@ func NewPartitionResponse(p *metal.Partition) *PartitionResponse { }, } } + +func (s ServerCapacities) FindBySize(size string) *ServerCapacity { + for _, sc := range s { + sc := sc + if sc.Size == size { + return sc + } + } + + return nil +} diff --git a/spec/metal-api.json b/spec/metal-api.json index 1b61ed960..a33c1f3ff 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -8048,37 +8048,6 @@ } }, "/v1/partition/capacity": { - "get": { - "consumes": [ - "application/json" - ], - "deprecated": true, - "operationId": "partitionCapacityCompat", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "items": { - "$ref": "#/definitions/v1.PartitionCapacity" - }, - "type": "array" - } - }, - "default": { - "description": "Error", - "schema": { - "$ref": "#/definitions/httperrors.HTTPErrorResponse" - } - } - }, - "summary": "get partition capacity", - "tags": [ - "Partition" - ] - }, "post": { "consumes": [ "application/json" From aca10dba5d15ef2899d2f5a6e3f0dbdd232fcbc5 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 9 Oct 2023 14:14:56 +0200 Subject: [PATCH 07/17] Naming. --- .../internal/issues/asn-uniqueness.go | 8 +-- .../internal/issues/bmc-info-outdated.go | 8 +-- .../internal/issues/bmc-without-ip.go | 8 +-- .../internal/issues/bmc-without-mac.go | 8 +-- cmd/metal-api/internal/issues/crash-loop.go | 8 +-- .../internal/issues/failed-machine-reclaim.go | 8 +-- cmd/metal-api/internal/issues/issues.go | 10 ++-- cmd/metal-api/internal/issues/issues_test.go | 22 ++++---- .../internal/issues/last-event-error.go | 8 +-- .../internal/issues/liveliness-dead.go | 8 +-- .../issues/liveliness-not-available.go | 8 +-- .../internal/issues/liveliness-unknown.go | 8 +-- .../internal/issues/no-event-container.go | 8 +-- cmd/metal-api/internal/issues/no-partition.go | 8 +-- .../internal/issues/non-distinct-bmc-ip.go | 6 +-- cmd/metal-api/internal/issues/severeties.go | 36 ++++++------- cmd/metal-api/internal/issues/types.go | 52 +++++++++---------- .../internal/service/machine-service.go | 4 +- .../internal/service/partition-service.go | 12 ++--- 19 files changed, 119 insertions(+), 119 deletions(-) diff --git a/cmd/metal-api/internal/issues/asn-uniqueness.go b/cmd/metal-api/internal/issues/asn-uniqueness.go index d2fe39e3e..32f47f0ba 100644 --- a/cmd/metal-api/internal/issues/asn-uniqueness.go +++ b/cmd/metal-api/internal/issues/asn-uniqueness.go @@ -9,7 +9,7 @@ import ( ) const ( - IssueTypeASNUniqueness IssueType = "asn-not-unique" + TypeASNUniqueness IssueType = "asn-not-unique" ) type ( @@ -20,14 +20,14 @@ type ( func (i *IssueASNUniqueness) Spec() *spec { return &spec{ - Type: IssueTypeASNUniqueness, - Severity: IssueSeverityMinor, + Type: TypeASNUniqueness, + Severity: SeverityMinor, Description: "The ASN is not unique (only impact on firewalls)", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#asn-not-unique", } } -func (i *IssueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { var ( machineASNs = map[uint32]metal.Machines{} overlaps []string diff --git a/cmd/metal-api/internal/issues/bmc-info-outdated.go b/cmd/metal-api/internal/issues/bmc-info-outdated.go index 7c76b1b3d..3e06a4f50 100644 --- a/cmd/metal-api/internal/issues/bmc-info-outdated.go +++ b/cmd/metal-api/internal/issues/bmc-info-outdated.go @@ -8,7 +8,7 @@ import ( ) const ( - IssueTypeBMCInfoOutdated IssueType = "bmc-info-outdated" + TypeBMCInfoOutdated IssueType = "bmc-info-outdated" ) type ( @@ -21,7 +21,7 @@ func (i *IssueBMCInfoOutdated) Details() string { return i.details } -func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if m.IPMI.LastUpdated.IsZero() { i.details = "machine ipmi has never been set" return true @@ -39,8 +39,8 @@ func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEv func (*IssueBMCInfoOutdated) Spec() *spec { return &spec{ - Type: IssueTypeBMCInfoOutdated, - Severity: IssueSeverityMajor, + Type: TypeBMCInfoOutdated, + Severity: SeverityMajor, Description: "BMC has not been updated from either metal-hammer or metal-bmc", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-info-outdated", } diff --git a/cmd/metal-api/internal/issues/bmc-without-ip.go b/cmd/metal-api/internal/issues/bmc-without-ip.go index dad08b13f..8a6e2addc 100644 --- a/cmd/metal-api/internal/issues/bmc-without-ip.go +++ b/cmd/metal-api/internal/issues/bmc-without-ip.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeBMCWithoutIP IssueType = "bmc-without-ip" + TypeBMCWithoutIP IssueType = "bmc-without-ip" ) type ( @@ -12,14 +12,14 @@ type ( func (i *IssueBMCWithoutIP) Spec() *spec { return &spec{ - Type: IssueTypeBMCWithoutIP, - Severity: IssueSeverityMajor, + Type: TypeBMCWithoutIP, + Severity: SeverityMajor, Description: "BMC has no ip address", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-without-ip", } } -func (i *IssueBMCWithoutIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueBMCWithoutIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.IPMI.Address == "" } diff --git a/cmd/metal-api/internal/issues/bmc-without-mac.go b/cmd/metal-api/internal/issues/bmc-without-mac.go index 28b6e4197..959ae429f 100644 --- a/cmd/metal-api/internal/issues/bmc-without-mac.go +++ b/cmd/metal-api/internal/issues/bmc-without-mac.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeBMCWithoutMAC IssueType = "bmc-without-mac" + TypeBMCWithoutMAC IssueType = "bmc-without-mac" ) type ( @@ -12,14 +12,14 @@ type ( func (i *IssueBMCWithoutMAC) Spec() *spec { return &spec{ - Type: IssueTypeBMCWithoutMAC, - Severity: IssueSeverityMajor, + Type: TypeBMCWithoutMAC, + Severity: SeverityMajor, Description: "BMC has no mac address", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-without-mac", } } -func (i *IssueBMCWithoutMAC) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueBMCWithoutMAC) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.IPMI.MacAddress == "" } diff --git a/cmd/metal-api/internal/issues/crash-loop.go b/cmd/metal-api/internal/issues/crash-loop.go index c50d30f84..58744db9a 100644 --- a/cmd/metal-api/internal/issues/crash-loop.go +++ b/cmd/metal-api/internal/issues/crash-loop.go @@ -6,7 +6,7 @@ import ( ) const ( - IssueTypeCrashLoop IssueType = "crashloop" + TypeCrashLoop IssueType = "crashloop" ) type ( @@ -15,14 +15,14 @@ type ( func (i *IssueCrashLoop) Spec() *spec { return &spec{ - Type: IssueTypeCrashLoop, - Severity: IssueSeverityMajor, + Type: TypeCrashLoop, + Severity: SeverityMajor, Description: "machine is in a provisioning crash loop (⭕)", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#crashloop", } } -func (i *IssueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if ec.CrashLoop { if pointer.FirstOrZero(ec.Events).Event == metal.ProvisioningEventWaiting { // Machine which are waiting are not considered to have issues diff --git a/cmd/metal-api/internal/issues/failed-machine-reclaim.go b/cmd/metal-api/internal/issues/failed-machine-reclaim.go index b66a2a9b3..484bb7396 100644 --- a/cmd/metal-api/internal/issues/failed-machine-reclaim.go +++ b/cmd/metal-api/internal/issues/failed-machine-reclaim.go @@ -6,7 +6,7 @@ import ( ) const ( - IssueTypeFailedMachineReclaim IssueType = "failed-machine-reclaim" + TypeFailedMachineReclaim IssueType = "failed-machine-reclaim" ) type ( @@ -15,14 +15,14 @@ type ( func (i *IssueFailedMachineReclaim) Spec() *spec { return &spec{ - Type: IssueTypeFailedMachineReclaim, - Severity: IssueSeverityCritical, + Type: TypeFailedMachineReclaim, + Severity: SeverityCritical, Description: "machine phones home but not allocated", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#failed-machine-reclaim", } } -func (i *IssueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if ec.FailedMachineReclaim { return true } diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index 19538b52e..fe1c0e424 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -8,8 +8,8 @@ import ( ) type ( - // IssueConfig contains configuration parameters for finding machine issues - IssueConfig struct { + // Config contains configuration parameters for finding machine issues + Config struct { Machines metal.Machines EventContainers metal.ProvisioningEventContainers Severity IssueSeverity @@ -43,7 +43,7 @@ type ( issue interface { // Evaluate decides whether a given machine has the machine issue. // the third argument contains additional information that may be required for the issue evaluation - Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool + Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool // Spec returns the issue spec of this issue. Spec() *spec // Details returns additional information on the issue after the evaluation. @@ -84,7 +84,7 @@ func toIssue(i issue) Issue { } } -func FindIssues(c *IssueConfig) (MachineIssuesMap, error) { +func FindIssues(c *Config) (MachineIssuesMap, error) { if c.LastErrorThreshold == 0 { c.LastErrorThreshold = DefaultLastErrorThreshold() } @@ -137,7 +137,7 @@ func (mis MachineIssues) Get(id string) *MachineWithIssues { return nil } -func (c *IssueConfig) includeIssue(t IssueType) bool { +func (c *Config) includeIssue(t IssueType) bool { issue, err := NewIssueFromType(t) if err != nil { return false diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 02f6fd5ac..4a90a38df 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -58,7 +58,7 @@ func TestFindIssues(t *testing.T) { }, { name: "no partition", - only: []IssueType{IssueTypeNoPartition}, + only: []IssueType{TypeNoPartition}, machines: func() metal.Machines { noPartitionMachine := machineTemplate("no-partition") noPartitionMachine.PartitionID = "" @@ -87,7 +87,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness dead", - only: []IssueType{IssueTypeLivelinessDead}, + only: []IssueType{TypeLivelinessDead}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("dead"), @@ -116,7 +116,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness unknown", - only: []IssueType{IssueTypeLivelinessUnknown}, + only: []IssueType{TypeLivelinessUnknown}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("unknown"), @@ -145,7 +145,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness not available", - only: []IssueType{IssueTypeLivelinessNotAvailable}, + only: []IssueType{TypeLivelinessNotAvailable}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("n/a"), @@ -174,7 +174,7 @@ func TestFindIssues(t *testing.T) { }, { name: "failed machine reclaim", - only: []IssueType{IssueTypeFailedMachineReclaim}, + only: []IssueType{TypeFailedMachineReclaim}, machines: func() metal.Machines { failedOld := machineTemplate("failed-old") @@ -220,7 +220,7 @@ func TestFindIssues(t *testing.T) { }, { name: "crashloop", - only: []IssueType{IssueTypeCrashLoop}, + only: []IssueType{TypeCrashLoop}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("good"), @@ -282,7 +282,7 @@ func TestFindIssues(t *testing.T) { // }, { name: "bmc without mac", - only: []IssueType{IssueTypeBMCWithoutMAC}, + only: []IssueType{TypeBMCWithoutMAC}, machines: func() metal.Machines { noMac := machineTemplate("no-mac") noMac.IPMI.MacAddress = "" @@ -314,7 +314,7 @@ func TestFindIssues(t *testing.T) { }, { name: "bmc without ip", - only: []IssueType{IssueTypeBMCWithoutIP}, + only: []IssueType{TypeBMCWithoutIP}, machines: func() metal.Machines { noIP := machineTemplate("no-ip") noIP.IPMI.Address = "" @@ -378,7 +378,7 @@ func TestFindIssues(t *testing.T) { // }, { name: "asn shared", - only: []IssueType{IssueTypeASNUniqueness}, + only: []IssueType{TypeASNUniqueness}, machines: func() metal.Machines { shared1 := machineTemplate("shared1") shared1.Allocation = &metal.MachineAllocation{ @@ -448,7 +448,7 @@ func TestFindIssues(t *testing.T) { }, { name: "non distinct bmc ip", - only: []IssueType{IssueTypeNonDistinctBMCIP}, + only: []IssueType{TypeNonDistinctBMCIP}, machines: func() metal.Machines { bmc1 := machineTemplate("bmc1") bmc1.IPMI.Address = "127.0.0.1" @@ -496,7 +496,7 @@ func TestFindIssues(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ms := tt.machines() - got, err := FindIssues(&IssueConfig{ + got, err := FindIssues(&Config{ Machines: ms, EventContainers: tt.eventContainers(), Only: tt.only, diff --git a/cmd/metal-api/internal/issues/last-event-error.go b/cmd/metal-api/internal/issues/last-event-error.go index 694bd0832..c41f43024 100644 --- a/cmd/metal-api/internal/issues/last-event-error.go +++ b/cmd/metal-api/internal/issues/last-event-error.go @@ -8,7 +8,7 @@ import ( ) const ( - IssueTypeLastEventError IssueType = "last-event-error" + TypeLastEventError IssueType = "last-event-error" ) type ( @@ -23,14 +23,14 @@ func DefaultLastErrorThreshold() time.Duration { func (i *IssueLastEventError) Spec() *spec { return &spec{ - Type: IssueTypeLastEventError, - Severity: IssueSeverityMinor, + Type: TypeLastEventError, + Severity: SeverityMinor, Description: "the machine had an error during the provisioning lifecycle", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#last-event-error", } } -func (i *IssueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if c.LastErrorThreshold == 0 { return false } diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go index e9620e3be..8e09eb8b8 100644 --- a/cmd/metal-api/internal/issues/liveliness-dead.go +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeLivelinessDead IssueType = "liveliness-dead" + TypeLivelinessDead IssueType = "liveliness-dead" ) type ( @@ -12,14 +12,14 @@ type ( func (i *IssueLivelinessDead) Spec() *spec { return &spec{ - Type: IssueTypeLivelinessDead, - Severity: IssueSeverityMajor, + Type: TypeLivelinessDead, + Severity: SeverityMajor, Description: "the machine is not sending events anymore", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#liveliness-dead", } } -func (i *IssueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Liveliness.Is(string(metal.MachineLivelinessDead)) } diff --git a/cmd/metal-api/internal/issues/liveliness-not-available.go b/cmd/metal-api/internal/issues/liveliness-not-available.go index 44f51cfab..9ba58a575 100644 --- a/cmd/metal-api/internal/issues/liveliness-not-available.go +++ b/cmd/metal-api/internal/issues/liveliness-not-available.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeLivelinessNotAvailable IssueType = "liveliness-not-available" + TypeLivelinessNotAvailable IssueType = "liveliness-not-available" ) type ( @@ -12,13 +12,13 @@ type ( func (i *IssueLivelinessNotAvailable) Spec() *spec { return &spec{ - Type: IssueTypeLivelinessNotAvailable, - Severity: IssueSeverityMinor, + Type: TypeLivelinessNotAvailable, + Severity: SeverityMinor, Description: "the machine liveliness is not available", } } -func (i *IssueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { allowed := map[metal.MachineLiveliness]bool{ metal.MachineLivelinessAlive: true, metal.MachineLivelinessDead: true, diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go index 3e072ee18..f17615db1 100644 --- a/cmd/metal-api/internal/issues/liveliness-unknown.go +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeLivelinessUnknown IssueType = "liveliness-unknown" + TypeLivelinessUnknown IssueType = "liveliness-unknown" ) type ( @@ -12,14 +12,14 @@ type ( func (i *IssueLivelinessUnknown) Spec() *spec { return &spec{ - Type: IssueTypeLivelinessUnknown, - Severity: IssueSeverityMajor, + Type: TypeLivelinessUnknown, + Severity: SeverityMajor, Description: "the machine is not sending LLDP alive messages anymore", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#liveliness-unknown", } } -func (i *IssueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Liveliness.Is(string(metal.MachineLivelinessUnknown)) } diff --git a/cmd/metal-api/internal/issues/no-event-container.go b/cmd/metal-api/internal/issues/no-event-container.go index 2c260c30d..7abc2e2c4 100644 --- a/cmd/metal-api/internal/issues/no-event-container.go +++ b/cmd/metal-api/internal/issues/no-event-container.go @@ -5,7 +5,7 @@ import ( ) const ( - IssueTypeNoEventContainer IssueType = "no-event-container" + TypeNoEventContainer IssueType = "no-event-container" ) type ( @@ -14,14 +14,14 @@ type ( func (i *IssueNoEventContainer) Spec() *spec { return &spec{ - Type: IssueTypeNoEventContainer, - Severity: IssueSeverityMajor, + Type: TypeNoEventContainer, + Severity: SeverityMajor, Description: "machine has no event container", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#no-event-container", } } -func (i *IssueNoEventContainer) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueNoEventContainer) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Base.ID == "" } diff --git a/cmd/metal-api/internal/issues/no-partition.go b/cmd/metal-api/internal/issues/no-partition.go index 276237c54..990f490b9 100644 --- a/cmd/metal-api/internal/issues/no-partition.go +++ b/cmd/metal-api/internal/issues/no-partition.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - IssueTypeNoPartition IssueType = "no-partition" + TypeNoPartition IssueType = "no-partition" ) type ( @@ -12,14 +12,14 @@ type ( func (i *IssueNoPartition) Spec() *spec { return &spec{ - Type: IssueTypeNoPartition, - Severity: IssueSeverityMajor, + Type: TypeNoPartition, + Severity: SeverityMajor, Description: "machine with no partition", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#no-partition", } } -func (i *IssueNoPartition) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueNoPartition) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.PartitionID == "" } diff --git a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go index 1fd3a68d6..a06c127d0 100644 --- a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go +++ b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go @@ -7,7 +7,7 @@ import ( ) const ( - IssueTypeNonDistinctBMCIP IssueType = "bmc-no-distinct-ip" + TypeNonDistinctBMCIP IssueType = "bmc-no-distinct-ip" ) type ( @@ -18,13 +18,13 @@ type ( func (i *IssueNonDistinctBMCIP) Spec() *spec { return &spec{ - Type: IssueTypeNonDistinctBMCIP, + Type: TypeNonDistinctBMCIP, Description: "BMC IP address is not distinct", RefURL: "https://docs.metal-stack.io/stable/installation/troubleshoot/#bmc-no-distinct-ip", } } -func (i *IssueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *IssueConfig) bool { +func (i *IssueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if m.IPMI.Address == "" { return false } diff --git a/cmd/metal-api/internal/issues/severeties.go b/cmd/metal-api/internal/issues/severeties.go index 624195bab..418f1babe 100644 --- a/cmd/metal-api/internal/issues/severeties.go +++ b/cmd/metal-api/internal/issues/severeties.go @@ -3,13 +3,13 @@ package issues import "fmt" const ( - // IssueSeverityMinor is an issue that should be checked from time to time but has no bad effects for the user. - IssueSeverityMinor IssueSeverity = "minor" - // IssueSeverityMajor is an issue where user experience is affected or provider resources are wasted. + // SeverityMinor is an issue that should be checked from time to time but has no bad effects for the user. + SeverityMinor IssueSeverity = "minor" + // SeverityMajor is an issue where user experience is affected or provider resources are wasted. // overall functionality is still maintained though. major issues should be resolved as soon as possible. - IssueSeverityMajor IssueSeverity = "major" - // IssueSeverityCritical is an issue that can lead to disfunction of the system and need to be handled as quickly as possible. - IssueSeverityCritical IssueSeverity = "critical" + SeverityMajor IssueSeverity = "major" + // SeverityCritical is an issue that can lead to disfunction of the system and need to be handled as quickly as possible. + SeverityCritical IssueSeverity = "critical" ) type ( @@ -18,20 +18,20 @@ type ( func AllSevereties() []IssueSeverity { return []IssueSeverity{ - IssueSeverityMinor, - IssueSeverityMajor, - IssueSeverityCritical, + SeverityMinor, + SeverityMajor, + SeverityCritical, } } func SeverityFromString(input string) (IssueSeverity, error) { switch IssueSeverity(input) { - case IssueSeverityCritical: - return IssueSeverityCritical, nil - case IssueSeverityMajor: - return IssueSeverityMajor, nil - case IssueSeverityMinor: - return IssueSeverityMinor, nil + case SeverityCritical: + return SeverityCritical, nil + case SeverityMajor: + return SeverityMajor, nil + case SeverityMinor: + return SeverityMinor, nil default: return "", fmt.Errorf("unknown issue severity: %s", input) } @@ -39,9 +39,9 @@ func SeverityFromString(input string) (IssueSeverity, error) { func (s IssueSeverity) LowerThan(o IssueSeverity) bool { smap := map[IssueSeverity]int{ - IssueSeverityCritical: 10, - IssueSeverityMajor: 5, - IssueSeverityMinor: 0, + SeverityCritical: 10, + SeverityMajor: 5, + SeverityMinor: 0, } return smap[s] < smap[o] diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go index 29f9b133d..ba7e5e98e 100644 --- a/cmd/metal-api/internal/issues/types.go +++ b/cmd/metal-api/internal/issues/types.go @@ -8,49 +8,49 @@ type ( func AllIssueTypes() []IssueType { return []IssueType{ - IssueTypeNoPartition, - IssueTypeLivelinessDead, - IssueTypeLivelinessUnknown, - IssueTypeLivelinessNotAvailable, - IssueTypeFailedMachineReclaim, - IssueTypeCrashLoop, - IssueTypeLastEventError, - IssueTypeBMCWithoutMAC, - IssueTypeBMCWithoutIP, - IssueTypeBMCInfoOutdated, - IssueTypeASNUniqueness, - IssueTypeNonDistinctBMCIP, - IssueTypeNoEventContainer, + TypeNoPartition, + TypeLivelinessDead, + TypeLivelinessUnknown, + TypeLivelinessNotAvailable, + TypeFailedMachineReclaim, + TypeCrashLoop, + TypeLastEventError, + TypeBMCWithoutMAC, + TypeBMCWithoutIP, + TypeBMCInfoOutdated, + TypeASNUniqueness, + TypeNonDistinctBMCIP, + TypeNoEventContainer, } } func NewIssueFromType(t IssueType) (issue, error) { switch t { - case IssueTypeNoPartition: + case TypeNoPartition: return &IssueNoPartition{}, nil - case IssueTypeLivelinessDead: + case TypeLivelinessDead: return &IssueLivelinessDead{}, nil - case IssueTypeLivelinessUnknown: + case TypeLivelinessUnknown: return &IssueLivelinessUnknown{}, nil - case IssueTypeLivelinessNotAvailable: + case TypeLivelinessNotAvailable: return &IssueLivelinessNotAvailable{}, nil - case IssueTypeFailedMachineReclaim: + case TypeFailedMachineReclaim: return &IssueFailedMachineReclaim{}, nil - case IssueTypeCrashLoop: + case TypeCrashLoop: return &IssueCrashLoop{}, nil - case IssueTypeLastEventError: + case TypeLastEventError: return &IssueLastEventError{}, nil - case IssueTypeBMCWithoutMAC: + case TypeBMCWithoutMAC: return &IssueBMCWithoutMAC{}, nil - case IssueTypeBMCWithoutIP: + case TypeBMCWithoutIP: return &IssueBMCWithoutIP{}, nil - case IssueTypeBMCInfoOutdated: + case TypeBMCInfoOutdated: return &IssueBMCInfoOutdated{}, nil - case IssueTypeASNUniqueness: + case TypeASNUniqueness: return &IssueASNUniqueness{}, nil - case IssueTypeNonDistinctBMCIP: + case TypeNonDistinctBMCIP: return &IssueNonDistinctBMCIP{}, nil - case IssueTypeNoEventContainer: + case TypeNoEventContainer: return &IssueNoEventContainer{}, nil default: return nil, fmt.Errorf("unknown issue type: %s", t) diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index b66f0c706..1be575dc9 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -535,7 +535,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res var ( ms = metal.Machines{} - severity = issues.IssueSeverityMinor + severity = issues.SeverityMinor only []issues.IssueType omit []issues.IssueType lastErrorThreshold = issues.DefaultLastErrorThreshold() @@ -593,7 +593,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res return } - machinesWithIssues, err := issues.FindIssues(&issues.IssueConfig{ + machinesWithIssues, err := issues.FindIssues(&issues.Config{ Machines: ms, EventContainers: ecs, Severity: severity, diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index 25936fdc9..bff122d90 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -362,15 +362,15 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque return nil, fmt.Errorf("unable to fetch provisioning event containers: %w", err) } - machinesWithIssues, err := issues.FindIssues(&issues.IssueConfig{ + machinesWithIssues, err := issues.FindIssues(&issues.Config{ Machines: ms, EventContainers: ecs, Only: []issues.IssueType{ - issues.IssueTypeLivelinessDead, - issues.IssueTypeLivelinessUnknown, - issues.IssueTypeLivelinessNotAvailable, - issues.IssueTypeFailedMachineReclaim, - issues.IssueTypeCrashLoop, + issues.TypeLivelinessDead, + issues.TypeLivelinessUnknown, + issues.TypeLivelinessNotAvailable, + issues.TypeFailedMachineReclaim, + issues.TypeCrashLoop, }, }) if err != nil { From 81db52e446f742e374e759acca2239b0b2ce3e18 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 9 Oct 2023 14:21:43 +0200 Subject: [PATCH 08/17] More naming. --- .../internal/issues/asn-uniqueness.go | 2 +- .../internal/issues/bmc-info-outdated.go | 2 +- .../internal/issues/bmc-without-ip.go | 2 +- .../internal/issues/bmc-without-mac.go | 2 +- cmd/metal-api/internal/issues/crash-loop.go | 2 +- .../internal/issues/failed-machine-reclaim.go | 2 +- cmd/metal-api/internal/issues/issues.go | 10 ++++---- cmd/metal-api/internal/issues/issues_test.go | 24 +++++++++---------- .../internal/issues/last-event-error.go | 2 +- .../internal/issues/liveliness-dead.go | 2 +- .../issues/liveliness-not-available.go | 2 +- .../internal/issues/liveliness-unknown.go | 2 +- .../internal/issues/no-event-container.go | 2 +- cmd/metal-api/internal/issues/no-partition.go | 2 +- .../internal/issues/non-distinct-bmc-ip.go | 2 +- cmd/metal-api/internal/issues/types.go | 20 ++++++++++++---- .../internal/service/machine-service.go | 4 ++-- .../internal/service/partition-service.go | 8 +------ cmd/metal-api/internal/service/v1/machine.go | 4 ++-- 19 files changed, 51 insertions(+), 45 deletions(-) diff --git a/cmd/metal-api/internal/issues/asn-uniqueness.go b/cmd/metal-api/internal/issues/asn-uniqueness.go index 32f47f0ba..51b58db70 100644 --- a/cmd/metal-api/internal/issues/asn-uniqueness.go +++ b/cmd/metal-api/internal/issues/asn-uniqueness.go @@ -9,7 +9,7 @@ import ( ) const ( - TypeASNUniqueness IssueType = "asn-not-unique" + TypeASNUniqueness Type = "asn-not-unique" ) type ( diff --git a/cmd/metal-api/internal/issues/bmc-info-outdated.go b/cmd/metal-api/internal/issues/bmc-info-outdated.go index 3e06a4f50..a6e435a7f 100644 --- a/cmd/metal-api/internal/issues/bmc-info-outdated.go +++ b/cmd/metal-api/internal/issues/bmc-info-outdated.go @@ -8,7 +8,7 @@ import ( ) const ( - TypeBMCInfoOutdated IssueType = "bmc-info-outdated" + TypeBMCInfoOutdated Type = "bmc-info-outdated" ) type ( diff --git a/cmd/metal-api/internal/issues/bmc-without-ip.go b/cmd/metal-api/internal/issues/bmc-without-ip.go index 8a6e2addc..953558851 100644 --- a/cmd/metal-api/internal/issues/bmc-without-ip.go +++ b/cmd/metal-api/internal/issues/bmc-without-ip.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeBMCWithoutIP IssueType = "bmc-without-ip" + TypeBMCWithoutIP Type = "bmc-without-ip" ) type ( diff --git a/cmd/metal-api/internal/issues/bmc-without-mac.go b/cmd/metal-api/internal/issues/bmc-without-mac.go index 959ae429f..2b7f604a8 100644 --- a/cmd/metal-api/internal/issues/bmc-without-mac.go +++ b/cmd/metal-api/internal/issues/bmc-without-mac.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeBMCWithoutMAC IssueType = "bmc-without-mac" + TypeBMCWithoutMAC Type = "bmc-without-mac" ) type ( diff --git a/cmd/metal-api/internal/issues/crash-loop.go b/cmd/metal-api/internal/issues/crash-loop.go index 58744db9a..3e69a63ff 100644 --- a/cmd/metal-api/internal/issues/crash-loop.go +++ b/cmd/metal-api/internal/issues/crash-loop.go @@ -6,7 +6,7 @@ import ( ) const ( - TypeCrashLoop IssueType = "crashloop" + TypeCrashLoop Type = "crashloop" ) type ( diff --git a/cmd/metal-api/internal/issues/failed-machine-reclaim.go b/cmd/metal-api/internal/issues/failed-machine-reclaim.go index 484bb7396..3b1a31a3a 100644 --- a/cmd/metal-api/internal/issues/failed-machine-reclaim.go +++ b/cmd/metal-api/internal/issues/failed-machine-reclaim.go @@ -6,7 +6,7 @@ import ( ) const ( - TypeFailedMachineReclaim IssueType = "failed-machine-reclaim" + TypeFailedMachineReclaim Type = "failed-machine-reclaim" ) type ( diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index fe1c0e424..c8e98ecbb 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -13,14 +13,14 @@ type ( Machines metal.Machines EventContainers metal.ProvisioningEventContainers Severity IssueSeverity - Only []IssueType - Omit []IssueType + Only []Type + Omit []Type LastErrorThreshold time.Duration } // Issue formulates an issue of a machine Issue struct { - Type IssueType + Type Type Severity IssueSeverity Description string RefURL string @@ -52,7 +52,7 @@ type ( // spec defines the specification of an issue. spec struct { - Type IssueType + Type Type Severity IssueSeverity Description string RefURL string @@ -137,7 +137,7 @@ func (mis MachineIssues) Get(id string) *MachineWithIssues { return nil } -func (c *Config) includeIssue(t IssueType) bool { +func (c *Config) includeIssue(t Type) bool { issue, err := NewIssueFromType(t) if err != nil { return false diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 4a90a38df..503188840 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -35,7 +35,7 @@ func TestFindIssues(t *testing.T) { tests := []struct { name string - only []IssueType + only []Type machines func() metal.Machines eventContainers func() metal.ProvisioningEventContainers @@ -58,7 +58,7 @@ func TestFindIssues(t *testing.T) { }, { name: "no partition", - only: []IssueType{TypeNoPartition}, + only: []Type{TypeNoPartition}, machines: func() metal.Machines { noPartitionMachine := machineTemplate("no-partition") noPartitionMachine.PartitionID = "" @@ -87,7 +87,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness dead", - only: []IssueType{TypeLivelinessDead}, + only: []Type{TypeLivelinessDead}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("dead"), @@ -116,7 +116,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness unknown", - only: []IssueType{TypeLivelinessUnknown}, + only: []Type{TypeLivelinessUnknown}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("unknown"), @@ -145,7 +145,7 @@ func TestFindIssues(t *testing.T) { }, { name: "liveliness not available", - only: []IssueType{TypeLivelinessNotAvailable}, + only: []Type{TypeLivelinessNotAvailable}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("n/a"), @@ -174,7 +174,7 @@ func TestFindIssues(t *testing.T) { }, { name: "failed machine reclaim", - only: []IssueType{TypeFailedMachineReclaim}, + only: []Type{TypeFailedMachineReclaim}, machines: func() metal.Machines { failedOld := machineTemplate("failed-old") @@ -220,7 +220,7 @@ func TestFindIssues(t *testing.T) { }, { name: "crashloop", - only: []IssueType{TypeCrashLoop}, + only: []Type{TypeCrashLoop}, machines: func() metal.Machines { return metal.Machines{ machineTemplate("good"), @@ -282,7 +282,7 @@ func TestFindIssues(t *testing.T) { // }, { name: "bmc without mac", - only: []IssueType{TypeBMCWithoutMAC}, + only: []Type{TypeBMCWithoutMAC}, machines: func() metal.Machines { noMac := machineTemplate("no-mac") noMac.IPMI.MacAddress = "" @@ -314,7 +314,7 @@ func TestFindIssues(t *testing.T) { }, { name: "bmc without ip", - only: []IssueType{TypeBMCWithoutIP}, + only: []Type{TypeBMCWithoutIP}, machines: func() metal.Machines { noIP := machineTemplate("no-ip") noIP.IPMI.Address = "" @@ -378,7 +378,7 @@ func TestFindIssues(t *testing.T) { // }, { name: "asn shared", - only: []IssueType{TypeASNUniqueness}, + only: []Type{TypeASNUniqueness}, machines: func() metal.Machines { shared1 := machineTemplate("shared1") shared1.Allocation = &metal.MachineAllocation{ @@ -448,7 +448,7 @@ func TestFindIssues(t *testing.T) { }, { name: "non distinct bmc ip", - only: []IssueType{TypeNonDistinctBMCIP}, + only: []Type{TypeNonDistinctBMCIP}, machines: func() metal.Machines { bmc1 := machineTemplate("bmc1") bmc1.IPMI.Address = "127.0.0.1" @@ -517,7 +517,7 @@ func TestFindIssues(t *testing.T) { } func TestAllIssues(t *testing.T) { - issuesTypes := map[IssueType]bool{} + issuesTypes := map[Type]bool{} for _, i := range AllIssues() { issuesTypes[i.Type] = true } diff --git a/cmd/metal-api/internal/issues/last-event-error.go b/cmd/metal-api/internal/issues/last-event-error.go index c41f43024..997e840e5 100644 --- a/cmd/metal-api/internal/issues/last-event-error.go +++ b/cmd/metal-api/internal/issues/last-event-error.go @@ -8,7 +8,7 @@ import ( ) const ( - TypeLastEventError IssueType = "last-event-error" + TypeLastEventError Type = "last-event-error" ) type ( diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go index 8e09eb8b8..35d1a0f00 100644 --- a/cmd/metal-api/internal/issues/liveliness-dead.go +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeLivelinessDead IssueType = "liveliness-dead" + TypeLivelinessDead Type = "liveliness-dead" ) type ( diff --git a/cmd/metal-api/internal/issues/liveliness-not-available.go b/cmd/metal-api/internal/issues/liveliness-not-available.go index 9ba58a575..db6cc7d3a 100644 --- a/cmd/metal-api/internal/issues/liveliness-not-available.go +++ b/cmd/metal-api/internal/issues/liveliness-not-available.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeLivelinessNotAvailable IssueType = "liveliness-not-available" + TypeLivelinessNotAvailable Type = "liveliness-not-available" ) type ( diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go index f17615db1..a41e83bc9 100644 --- a/cmd/metal-api/internal/issues/liveliness-unknown.go +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeLivelinessUnknown IssueType = "liveliness-unknown" + TypeLivelinessUnknown Type = "liveliness-unknown" ) type ( diff --git a/cmd/metal-api/internal/issues/no-event-container.go b/cmd/metal-api/internal/issues/no-event-container.go index 7abc2e2c4..d6f1f6399 100644 --- a/cmd/metal-api/internal/issues/no-event-container.go +++ b/cmd/metal-api/internal/issues/no-event-container.go @@ -5,7 +5,7 @@ import ( ) const ( - TypeNoEventContainer IssueType = "no-event-container" + TypeNoEventContainer Type = "no-event-container" ) type ( diff --git a/cmd/metal-api/internal/issues/no-partition.go b/cmd/metal-api/internal/issues/no-partition.go index 990f490b9..7d082b7bd 100644 --- a/cmd/metal-api/internal/issues/no-partition.go +++ b/cmd/metal-api/internal/issues/no-partition.go @@ -3,7 +3,7 @@ package issues import "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" const ( - TypeNoPartition IssueType = "no-partition" + TypeNoPartition Type = "no-partition" ) type ( diff --git a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go index a06c127d0..7bcf1e77a 100644 --- a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go +++ b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go @@ -7,7 +7,7 @@ import ( ) const ( - TypeNonDistinctBMCIP IssueType = "bmc-no-distinct-ip" + TypeNonDistinctBMCIP Type = "bmc-no-distinct-ip" ) type ( diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go index ba7e5e98e..7ee92bdcd 100644 --- a/cmd/metal-api/internal/issues/types.go +++ b/cmd/metal-api/internal/issues/types.go @@ -3,11 +3,11 @@ package issues import "fmt" type ( - IssueType string + Type string ) -func AllIssueTypes() []IssueType { - return []IssueType{ +func AllIssueTypes() []Type { + return []Type{ TypeNoPartition, TypeLivelinessDead, TypeLivelinessUnknown, @@ -24,7 +24,19 @@ func AllIssueTypes() []IssueType { } } -func NewIssueFromType(t IssueType) (issue, error) { +func NotAllocatableIssueTypes() []Type { + return []Type{ + TypeNoPartition, + TypeLivelinessDead, + TypeLivelinessUnknown, + TypeLivelinessNotAvailable, + TypeFailedMachineReclaim, + TypeCrashLoop, + TypeNoEventContainer, + } +} + +func NewIssueFromType(t Type) (issue, error) { switch t { case TypeNoPartition: return &IssueNoPartition{}, nil diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 1be575dc9..6e74970ad 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -536,8 +536,8 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res ms = metal.Machines{} severity = issues.SeverityMinor - only []issues.IssueType - omit []issues.IssueType + only []issues.Type + omit []issues.Type lastErrorThreshold = issues.DefaultLastErrorThreshold() ) diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index bff122d90..910cf8257 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -365,13 +365,7 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque machinesWithIssues, err := issues.FindIssues(&issues.Config{ Machines: ms, EventContainers: ecs, - Only: []issues.IssueType{ - issues.TypeLivelinessDead, - issues.TypeLivelinessUnknown, - issues.TypeLivelinessNotAvailable, - issues.TypeFailedMachineReclaim, - issues.TypeCrashLoop, - }, + Only: issues.NotAllocatableIssueTypes(), }) if err != nil { return nil, fmt.Errorf("unable to calculate machine issues: %w", err) diff --git a/cmd/metal-api/internal/service/v1/machine.go b/cmd/metal-api/internal/service/v1/machine.go index a964a2f63..e699cb531 100644 --- a/cmd/metal-api/internal/service/v1/machine.go +++ b/cmd/metal-api/internal/service/v1/machine.go @@ -269,8 +269,8 @@ type MachineReinstallRequest struct { type MachineIssuesRequest struct { datastore.MachineSearchQuery - Only []issues.IssueType `json:"only" description:"a list of machine issues to include"` - Omit []issues.IssueType `json:"omit" description:"a list of machine issues to omit"` + Only []issues.Type `json:"only" description:"a list of machine issues to include"` + Omit []issues.Type `json:"omit" description:"a list of machine issues to omit"` Severity string `json:"severity" description:"filters issue for given severity"` LastErrorThreshold time.Duration `json:"last_error_threshold" description:"defines the last error threshold"` From 8f88066db15ff12f4cb33bec2c35bc74ea9af8c7 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 9 Oct 2023 14:25:36 +0200 Subject: [PATCH 09/17] Remove `Is()` as it is not very intuitive. --- .../internal/issues/liveliness-dead.go | 2 +- .../internal/issues/liveliness-unknown.go | 2 +- cmd/metal-api/internal/metal/machine.go | 5 --- cmd/metal-api/internal/metal/provisioning.go | 5 --- .../internal/metal/provisioning_test.go | 36 ------------------- .../internal/service/machine-service.go | 2 +- .../internal/service/partition-service.go | 2 +- 7 files changed, 4 insertions(+), 50 deletions(-) diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go index 35d1a0f00..80b772bb1 100644 --- a/cmd/metal-api/internal/issues/liveliness-dead.go +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -20,7 +20,7 @@ func (i *IssueLivelinessDead) Spec() *spec { } func (i *IssueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { - return ec.Liveliness.Is(string(metal.MachineLivelinessDead)) + return ec.Liveliness == metal.MachineLivelinessDead } func (i *IssueLivelinessDead) Details() string { diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go index a41e83bc9..cc730127d 100644 --- a/cmd/metal-api/internal/issues/liveliness-unknown.go +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -20,7 +20,7 @@ func (i *IssueLivelinessUnknown) Spec() *spec { } func (i *IssueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { - return ec.Liveliness.Is(string(metal.MachineLivelinessUnknown)) + return ec.Liveliness == metal.MachineLivelinessUnknown } func (i *IssueLivelinessUnknown) Details() string { diff --git a/cmd/metal-api/internal/metal/machine.go b/cmd/metal-api/internal/metal/machine.go index 3a45ab765..0195ec646 100644 --- a/cmd/metal-api/internal/metal/machine.go +++ b/cmd/metal-api/internal/metal/machine.go @@ -304,11 +304,6 @@ const ( MachineResurrectAfter time.Duration = time.Hour ) -// Is return true if given liveliness is equal to specific Liveliness -func (l MachineLiveliness) Is(liveliness string) bool { - return string(l) == liveliness -} - // DiskCapacity calculates the capacity of all disks. func (hw *MachineHardware) DiskCapacity() uint64 { var c uint64 diff --git a/cmd/metal-api/internal/metal/provisioning.go b/cmd/metal-api/internal/metal/provisioning.go index 207ba286a..618bf4c08 100644 --- a/cmd/metal-api/internal/metal/provisioning.go +++ b/cmd/metal-api/internal/metal/provisioning.go @@ -47,11 +47,6 @@ var ( // ProvisioningEvents is just a list of ProvisioningEvents type ProvisioningEvents []ProvisioningEvent -// Is return true if given event is equal to specific EventType -func (p ProvisioningEventType) Is(event ProvisioningEventType) bool { - return p == event -} - // TrimEvents trim the events to maxCount func (p *ProvisioningEventContainer) TrimEvents(maxCount int) { if len(p.Events) > maxCount { diff --git a/cmd/metal-api/internal/metal/provisioning_test.go b/cmd/metal-api/internal/metal/provisioning_test.go index df76495b8..1ce20cfe6 100644 --- a/cmd/metal-api/internal/metal/provisioning_test.go +++ b/cmd/metal-api/internal/metal/provisioning_test.go @@ -5,42 +5,6 @@ import ( "time" ) -func TestProvisioningEventType_Is(t *testing.T) { - tests := []struct { - name string - event ProvisioningEventType - p ProvisioningEventType - want bool - }{ - { - name: "simple", - event: "Waiting", - p: ProvisioningEventWaiting, - want: true, - }, - { - name: "simple", - event: "Waiting", - p: ProvisioningEventInstalling, - want: false, - }, - { - name: "simple", - event: "Alive", - p: ProvisioningEventAlive, - want: true, - }, - } - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - if got := tt.p.Is(tt.event); got != tt.want { - t.Errorf("ProvisioningEventType.Is() = %v, want %v", got, tt.want) - } - }) - } -} - func TestProvisioningEventContainer_Validate(t *testing.T) { now := time.Now() tests := []struct { diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 6e74970ad..6be2e3874 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -1731,7 +1731,7 @@ func (r *machineResource) deleteMachine(request *restful.Request, response *rest r.sendError(request, response, defaultError(err)) return } - if err == nil && !ec.Liveliness.Is(string(metal.MachineLivelinessDead)) { + if err == nil && ec.Liveliness != metal.MachineLivelinessDead { r.sendError(request, response, defaultError(errors.New("can only delete dead machines"))) return } diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index 910cf8257..3894d1e13 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -430,7 +430,7 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque continue } - if m.State.Value == metal.AvailableState && metal.ProvisioningEventWaiting.Is(pointer.FirstOrZero(ec.Events).Event) { + if m.State.Value == metal.AvailableState && metal.ProvisioningEventWaiting == pointer.FirstOrZero(ec.Events).Event { cap.Free++ continue } From 6098a9bb044162f0844a83d929637b943597bc79 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 9 Oct 2023 14:31:38 +0200 Subject: [PATCH 10/17] Naminh. --- cmd/metal-api/internal/issues/issues.go | 6 +++--- cmd/metal-api/internal/issues/severeties.go | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index c8e98ecbb..e05b5436e 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -12,7 +12,7 @@ type ( Config struct { Machines metal.Machines EventContainers metal.ProvisioningEventContainers - Severity IssueSeverity + Severity Severity Only []Type Omit []Type LastErrorThreshold time.Duration @@ -21,7 +21,7 @@ type ( // Issue formulates an issue of a machine Issue struct { Type Type - Severity IssueSeverity + Severity Severity Description string RefURL string Details string @@ -53,7 +53,7 @@ type ( // spec defines the specification of an issue. spec struct { Type Type - Severity IssueSeverity + Severity Severity Description string RefURL string } diff --git a/cmd/metal-api/internal/issues/severeties.go b/cmd/metal-api/internal/issues/severeties.go index 418f1babe..1cba9c32b 100644 --- a/cmd/metal-api/internal/issues/severeties.go +++ b/cmd/metal-api/internal/issues/severeties.go @@ -4,28 +4,28 @@ import "fmt" const ( // SeverityMinor is an issue that should be checked from time to time but has no bad effects for the user. - SeverityMinor IssueSeverity = "minor" + SeverityMinor Severity = "minor" // SeverityMajor is an issue where user experience is affected or provider resources are wasted. // overall functionality is still maintained though. major issues should be resolved as soon as possible. - SeverityMajor IssueSeverity = "major" + SeverityMajor Severity = "major" // SeverityCritical is an issue that can lead to disfunction of the system and need to be handled as quickly as possible. - SeverityCritical IssueSeverity = "critical" + SeverityCritical Severity = "critical" ) type ( - IssueSeverity string + Severity string ) -func AllSevereties() []IssueSeverity { - return []IssueSeverity{ +func AllSevereties() []Severity { + return []Severity{ SeverityMinor, SeverityMajor, SeverityCritical, } } -func SeverityFromString(input string) (IssueSeverity, error) { - switch IssueSeverity(input) { +func SeverityFromString(input string) (Severity, error) { + switch Severity(input) { case SeverityCritical: return SeverityCritical, nil case SeverityMajor: @@ -37,8 +37,8 @@ func SeverityFromString(input string) (IssueSeverity, error) { } } -func (s IssueSeverity) LowerThan(o IssueSeverity) bool { - smap := map[IssueSeverity]int{ +func (s Severity) LowerThan(o Severity) bool { + smap := map[Severity]int{ SeverityCritical: 10, SeverityMajor: 5, SeverityMinor: 0, From b0a2489621a5a147cdfa86383877d696537f3f69 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Mon, 9 Oct 2023 15:50:06 +0200 Subject: [PATCH 11/17] Don't use pointer as map key. --- cmd/metal-api/internal/issues/issues.go | 18 ++++++++++-------- .../internal/service/partition-service.go | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index e05b5436e..e4ce7addd 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -38,7 +38,7 @@ type ( // MachineIssues is map of a machine response to a list of machine issues MachineIssues []*MachineWithIssues - MachineIssuesMap map[*metal.Machine]Issues + MachineIssuesMap map[string]*MachineWithIssues issue interface { // Evaluate decides whether a given machine has the machine issue. @@ -166,21 +166,23 @@ func (c *Config) includeIssue(t Type) bool { } func (mim MachineIssuesMap) add(m metal.Machine, issue Issue) { - issues, ok := mim[&m] + machineWithIssues, ok := mim[m.ID] if !ok { - issues = Issues{} + machineWithIssues = &MachineWithIssues{ + Machine: &m, + } } - issues = append(issues, issue) - mim[&m] = issues + machineWithIssues.Issues = append(machineWithIssues.Issues, issue) + mim[m.ID] = machineWithIssues } func (mim MachineIssuesMap) ToList() MachineIssues { var res MachineIssues - for m, issues := range mim { + for _, machineWithIssues := range mim { res = append(res, &MachineWithIssues{ - Machine: m, - Issues: issues, + Machine: machineWithIssues.Machine, + Issues: machineWithIssues.Issues, }) } diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index 3894d1e13..d82746961 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -424,7 +424,7 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque continue } - if _, ok := machinesWithIssues[&m]; ok { + if _, ok := machinesWithIssues[m.ID]; ok { cap.Faulty++ cap.FaultyMachines = append(cap.FaultyMachines, m.ID) continue From b7feec166406ce26ce7382fab5c942719be91ec5 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 10 Oct 2023 11:05:55 +0200 Subject: [PATCH 12/17] Fix machine issue no event container repeated. --- cmd/metal-api/internal/issues/issues.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index e4ce7addd..0a4a876f6 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -93,25 +93,25 @@ func FindIssues(c *Config) (MachineIssuesMap, error) { ecs := c.EventContainers.ByID() - for _, t := range AllIssueTypes() { - if !c.includeIssue(t) { + for _, m := range c.Machines { + m := m + + ec, ok := ecs[m.ID] + if !ok { + res.add(m, toIssue(&IssueNoEventContainer{})) continue } - for _, m := range c.Machines { - m := m + for _, t := range AllIssueTypes() { + if !c.includeIssue(t) { + continue + } i, err := NewIssueFromType(t) if err != nil { return nil, err } - ec, ok := ecs[m.ID] - if !ok { - res.add(m, toIssue(&IssueNoEventContainer{})) - continue - } - if i.Evaluate(m, ec, c) { res.add(m, toIssue(i)) } From e1bf2de4b0fa6dda843510f06465dd95731e0957 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 10 Oct 2023 11:06:13 +0200 Subject: [PATCH 13/17] Only send back the issue ids on eval. --- cmd/metal-api/internal/service/machine-service.go | 8 +------- cmd/metal-api/internal/service/v1/machine.go | 4 ++-- spec/metal-api.json | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 6be2e3874..f3171abbe 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -617,13 +617,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res for _, issue := range machineWithIssues.Issues { issue := issue - entry.Issues = append(entry.Issues, v1.MachineIssue{ - ID: string(issue.Type), - Severity: string(issue.Severity), - Description: issue.Description, - RefURL: issue.RefURL, - Details: issue.Details, - }) + entry.Issues = append(entry.Issues, string(issue.Type)) } issueResponse = append(issueResponse, entry) diff --git a/cmd/metal-api/internal/service/v1/machine.go b/cmd/metal-api/internal/service/v1/machine.go index e699cb531..d1cfbc9d4 100644 --- a/cmd/metal-api/internal/service/v1/machine.go +++ b/cmd/metal-api/internal/service/v1/machine.go @@ -287,8 +287,8 @@ type MachineVPN struct { } type MachineIssueResponse struct { - MachineID string `json:"machineid" description:"the machine id that has the given issues"` - Issues []MachineIssue `json:"issues" description:"the list of issues of this machine"` + MachineID string `json:"machineid" description:"the machine id that has the given issues"` + Issues []string `json:"issues" description:"the list of issues (only issue ids) of this machine"` } type MachineIssue struct { diff --git a/spec/metal-api.json b/spec/metal-api.json index a33c1f3ff..624aec38a 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -2770,9 +2770,9 @@ "v1.MachineIssueResponse": { "properties": { "issues": { - "description": "the list of issues of this machine", + "description": "the list of issues (only issue ids) of this machine", "items": { - "$ref": "#/definitions/v1.MachineIssue" + "type": "string" }, "type": "array" }, From 5d615271c3da93389e446ce3c3e2fb6fd42813a7 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 10 Oct 2023 14:50:27 +0200 Subject: [PATCH 14/17] Update metal-lib dep. --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 5b57d8dbe..5806e9671 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/looplab/fsm v0.3.0 github.com/metal-stack/go-ipam v1.8.5 github.com/metal-stack/masterdata-api v0.10.0 - github.com/metal-stack/metal-lib v0.13.2 + github.com/metal-stack/metal-lib v0.13.4 github.com/metal-stack/security v0.6.7 github.com/metal-stack/v v1.0.3 github.com/nsqio/go-nsq v1.1.0 @@ -45,6 +45,7 @@ replace ( ) require ( + connectrpc.com/connect v1.11.1 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -56,7 +57,6 @@ require ( github.com/avast/retry-go v3.0.0+incompatible // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bufbuild/connect-go v1.10.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/containerd v1.7.3 // indirect diff --git a/go.sum b/go.sum index d62756884..9d9856c76 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +connectrpc.com/connect v1.11.1 h1:dqRwblixqkVh+OFBOOL1yIf1jS/yP0MSJLijRj29bFg= +connectrpc.com/connect v1.11.1/go.mod h1:3AGaO6RRGMx5IKFfqbe3hvK1NqLosFNP2BxDYTPmNPo= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= @@ -124,8 +126,6 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/bmizerany/perks v0.0.0-20230307044200-03f9df79da1e h1:mWOqoK5jV13ChKf/aF3plwQ96laasTJgZi4f1aSOu+M= github.com/bmizerany/perks v0.0.0-20230307044200-03f9df79da1e/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg= -github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= @@ -637,8 +637,8 @@ github.com/metal-stack/go-ipam v1.8.5 h1:XE1XfaU6Ck1Ucc7svTO25dlT7kEcE1oxOM3lBrW github.com/metal-stack/go-ipam v1.8.5/go.mod h1:JgsddJabu8A7lWD+4MJKqbQhmSA/zhBbO+Bp8pLhRZM= github.com/metal-stack/masterdata-api v0.10.0 h1:xcB8kd1FK5etmRbcTlAPk2bQXY6i9tTTvAeDsTjmh6E= github.com/metal-stack/masterdata-api v0.10.0/go.mod h1:xwaMDC9hhNjXep3ppD+Iqeg2OEFM6hwq2zFyBDnlXGc= -github.com/metal-stack/metal-lib v0.13.2 h1:gpsnKUxahT4r3N55QY1MTRBZy10CySTQCQKb9XFCDrA= -github.com/metal-stack/metal-lib v0.13.2/go.mod h1:l18VEuS1YkxnVE35iF8AMP6QRxoYjRZ9e2NE3aGxVY0= +github.com/metal-stack/metal-lib v0.13.4 h1:epuHE9EC3mgPyo/gs55VpibtaLhnbNldxJTfI9A3keM= +github.com/metal-stack/metal-lib v0.13.4/go.mod h1:BAR7fjdoV7DDg8i9GpJQBDaNSFirOcBs0vLYTBnhHQU= github.com/metal-stack/security v0.6.7 h1:8wstGy0pdUmphVclAlT+9RKQmx9lF+cIGklJZAB5cIc= github.com/metal-stack/security v0.6.7/go.mod h1:dXyrQ8PYZuUiodWFQ/NwSROxu6tajwRBc5yR/PoK5uE= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= From 5dce260bf2588c5040c2b9e2112ddeb14a0f9a96 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 10 Oct 2023 15:21:30 +0200 Subject: [PATCH 15/17] Bump metal-lib again. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5806e9671..ac20d0105 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/looplab/fsm v0.3.0 github.com/metal-stack/go-ipam v1.8.5 github.com/metal-stack/masterdata-api v0.10.0 - github.com/metal-stack/metal-lib v0.13.4 + github.com/metal-stack/metal-lib v0.13.5 github.com/metal-stack/security v0.6.7 github.com/metal-stack/v v1.0.3 github.com/nsqio/go-nsq v1.1.0 diff --git a/go.sum b/go.sum index 9d9856c76..548b9416c 100644 --- a/go.sum +++ b/go.sum @@ -637,8 +637,8 @@ github.com/metal-stack/go-ipam v1.8.5 h1:XE1XfaU6Ck1Ucc7svTO25dlT7kEcE1oxOM3lBrW github.com/metal-stack/go-ipam v1.8.5/go.mod h1:JgsddJabu8A7lWD+4MJKqbQhmSA/zhBbO+Bp8pLhRZM= github.com/metal-stack/masterdata-api v0.10.0 h1:xcB8kd1FK5etmRbcTlAPk2bQXY6i9tTTvAeDsTjmh6E= github.com/metal-stack/masterdata-api v0.10.0/go.mod h1:xwaMDC9hhNjXep3ppD+Iqeg2OEFM6hwq2zFyBDnlXGc= -github.com/metal-stack/metal-lib v0.13.4 h1:epuHE9EC3mgPyo/gs55VpibtaLhnbNldxJTfI9A3keM= -github.com/metal-stack/metal-lib v0.13.4/go.mod h1:BAR7fjdoV7DDg8i9GpJQBDaNSFirOcBs0vLYTBnhHQU= +github.com/metal-stack/metal-lib v0.13.5 h1:OX94H+Pw31MOE9xSr460kFBv6CNJ2Nhjf4GY5IcuCxM= +github.com/metal-stack/metal-lib v0.13.5/go.mod h1:BAR7fjdoV7DDg8i9GpJQBDaNSFirOcBs0vLYTBnhHQU= github.com/metal-stack/security v0.6.7 h1:8wstGy0pdUmphVclAlT+9RKQmx9lF+cIGklJZAB5cIc= github.com/metal-stack/security v0.6.7/go.mod h1:dXyrQ8PYZuUiodWFQ/NwSROxu6tajwRBc5yR/PoK5uE= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= From b15b80329c323a6e4eb113b2a8826173fcaf2526 Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Tue, 10 Oct 2023 15:50:03 +0200 Subject: [PATCH 16/17] Issue types can be private as well. --- .../internal/issues/asn-uniqueness.go | 8 +++--- .../internal/issues/bmc-info-outdated.go | 8 +++--- .../internal/issues/bmc-without-ip.go | 8 +++--- .../internal/issues/bmc-without-mac.go | 8 +++--- cmd/metal-api/internal/issues/crash-loop.go | 8 +++--- .../internal/issues/failed-machine-reclaim.go | 8 +++--- cmd/metal-api/internal/issues/issues.go | 2 +- cmd/metal-api/internal/issues/issues_test.go | 28 +++++++++---------- .../internal/issues/last-event-error.go | 8 +++--- .../internal/issues/liveliness-dead.go | 8 +++--- .../issues/liveliness-not-available.go | 8 +++--- .../internal/issues/liveliness-unknown.go | 8 +++--- .../internal/issues/no-event-container.go | 8 +++--- cmd/metal-api/internal/issues/no-partition.go | 8 +++--- .../internal/issues/non-distinct-bmc-ip.go | 8 +++--- cmd/metal-api/internal/issues/types.go | 26 ++++++++--------- .../internal/service/machine-service.go | 2 +- 17 files changed, 81 insertions(+), 81 deletions(-) diff --git a/cmd/metal-api/internal/issues/asn-uniqueness.go b/cmd/metal-api/internal/issues/asn-uniqueness.go index 51b58db70..9c8befe62 100644 --- a/cmd/metal-api/internal/issues/asn-uniqueness.go +++ b/cmd/metal-api/internal/issues/asn-uniqueness.go @@ -13,12 +13,12 @@ const ( ) type ( - IssueASNUniqueness struct { + issueASNUniqueness struct { details string } ) -func (i *IssueASNUniqueness) Spec() *spec { +func (i *issueASNUniqueness) Spec() *spec { return &spec{ Type: TypeASNUniqueness, Severity: SeverityMinor, @@ -27,7 +27,7 @@ func (i *IssueASNUniqueness) Spec() *spec { } } -func (i *IssueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { var ( machineASNs = map[uint32]metal.Machines{} overlaps []string @@ -117,6 +117,6 @@ func (i *IssueASNUniqueness) Evaluate(m metal.Machine, ec metal.ProvisioningEven return true } -func (i *IssueASNUniqueness) Details() string { +func (i *issueASNUniqueness) Details() string { return i.details } diff --git a/cmd/metal-api/internal/issues/bmc-info-outdated.go b/cmd/metal-api/internal/issues/bmc-info-outdated.go index a6e435a7f..1a03ced9f 100644 --- a/cmd/metal-api/internal/issues/bmc-info-outdated.go +++ b/cmd/metal-api/internal/issues/bmc-info-outdated.go @@ -12,16 +12,16 @@ const ( ) type ( - IssueBMCInfoOutdated struct { + issueBMCInfoOutdated struct { details string } ) -func (i *IssueBMCInfoOutdated) Details() string { +func (i *issueBMCInfoOutdated) Details() string { return i.details } -func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if m.IPMI.LastUpdated.IsZero() { i.details = "machine ipmi has never been set" return true @@ -37,7 +37,7 @@ func (i *IssueBMCInfoOutdated) Evaluate(m metal.Machine, ec metal.ProvisioningEv return false } -func (*IssueBMCInfoOutdated) Spec() *spec { +func (*issueBMCInfoOutdated) Spec() *spec { return &spec{ Type: TypeBMCInfoOutdated, Severity: SeverityMajor, diff --git a/cmd/metal-api/internal/issues/bmc-without-ip.go b/cmd/metal-api/internal/issues/bmc-without-ip.go index 953558851..5b1766307 100644 --- a/cmd/metal-api/internal/issues/bmc-without-ip.go +++ b/cmd/metal-api/internal/issues/bmc-without-ip.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueBMCWithoutIP struct{} + issueBMCWithoutIP struct{} ) -func (i *IssueBMCWithoutIP) Spec() *spec { +func (i *issueBMCWithoutIP) Spec() *spec { return &spec{ Type: TypeBMCWithoutIP, Severity: SeverityMajor, @@ -19,10 +19,10 @@ func (i *IssueBMCWithoutIP) Spec() *spec { } } -func (i *IssueBMCWithoutIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueBMCWithoutIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.IPMI.Address == "" } -func (i *IssueBMCWithoutIP) Details() string { +func (i *issueBMCWithoutIP) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/bmc-without-mac.go b/cmd/metal-api/internal/issues/bmc-without-mac.go index 2b7f604a8..7d92209c2 100644 --- a/cmd/metal-api/internal/issues/bmc-without-mac.go +++ b/cmd/metal-api/internal/issues/bmc-without-mac.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueBMCWithoutMAC struct{} + issueBMCWithoutMAC struct{} ) -func (i *IssueBMCWithoutMAC) Spec() *spec { +func (i *issueBMCWithoutMAC) Spec() *spec { return &spec{ Type: TypeBMCWithoutMAC, Severity: SeverityMajor, @@ -19,10 +19,10 @@ func (i *IssueBMCWithoutMAC) Spec() *spec { } } -func (i *IssueBMCWithoutMAC) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueBMCWithoutMAC) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.IPMI.MacAddress == "" } -func (i *IssueBMCWithoutMAC) Details() string { +func (i *issueBMCWithoutMAC) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/crash-loop.go b/cmd/metal-api/internal/issues/crash-loop.go index 3e69a63ff..cf3a6c866 100644 --- a/cmd/metal-api/internal/issues/crash-loop.go +++ b/cmd/metal-api/internal/issues/crash-loop.go @@ -10,10 +10,10 @@ const ( ) type ( - IssueCrashLoop struct{} + issueCrashLoop struct{} ) -func (i *IssueCrashLoop) Spec() *spec { +func (i *issueCrashLoop) Spec() *spec { return &spec{ Type: TypeCrashLoop, Severity: SeverityMajor, @@ -22,7 +22,7 @@ func (i *IssueCrashLoop) Spec() *spec { } } -func (i *IssueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if ec.CrashLoop { if pointer.FirstOrZero(ec.Events).Event == metal.ProvisioningEventWaiting { // Machine which are waiting are not considered to have issues @@ -33,6 +33,6 @@ func (i *IssueCrashLoop) Evaluate(m metal.Machine, ec metal.ProvisioningEventCon return false } -func (i *IssueCrashLoop) Details() string { +func (i *issueCrashLoop) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/failed-machine-reclaim.go b/cmd/metal-api/internal/issues/failed-machine-reclaim.go index 3b1a31a3a..5f3e84861 100644 --- a/cmd/metal-api/internal/issues/failed-machine-reclaim.go +++ b/cmd/metal-api/internal/issues/failed-machine-reclaim.go @@ -10,10 +10,10 @@ const ( ) type ( - IssueFailedMachineReclaim struct{} + issueFailedMachineReclaim struct{} ) -func (i *IssueFailedMachineReclaim) Spec() *spec { +func (i *issueFailedMachineReclaim) Spec() *spec { return &spec{ Type: TypeFailedMachineReclaim, Severity: SeverityCritical, @@ -22,7 +22,7 @@ func (i *IssueFailedMachineReclaim) Spec() *spec { } } -func (i *IssueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if ec.FailedMachineReclaim { return true } @@ -36,6 +36,6 @@ func (i *IssueFailedMachineReclaim) Evaluate(m metal.Machine, ec metal.Provision return false } -func (i *IssueFailedMachineReclaim) Details() string { +func (i *issueFailedMachineReclaim) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index 0a4a876f6..22bc57f27 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -98,7 +98,7 @@ func FindIssues(c *Config) (MachineIssuesMap, error) { ec, ok := ecs[m.ID] if !ok { - res.add(m, toIssue(&IssueNoEventContainer{})) + res.add(m, toIssue(&issueNoEventContainer{})) continue } diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 503188840..4e9263925 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -79,7 +79,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueNoPartition{}), + toIssue(&issueNoPartition{}), }, }, } @@ -108,7 +108,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueLivelinessDead{}), + toIssue(&issueLivelinessDead{}), }, }, } @@ -137,7 +137,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueLivelinessUnknown{}), + toIssue(&issueLivelinessUnknown{}), }, }, } @@ -166,7 +166,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueLivelinessNotAvailable{}), + toIssue(&issueLivelinessNotAvailable{}), }, }, } @@ -206,13 +206,13 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueFailedMachineReclaim{}), + toIssue(&issueFailedMachineReclaim{}), }, }, { Machine: &machines[2], Issues: Issues{ - toIssue(&IssueFailedMachineReclaim{}), + toIssue(&issueFailedMachineReclaim{}), }, }, } @@ -241,7 +241,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueCrashLoop{}), + toIssue(&issueCrashLoop{}), }, }, } @@ -306,7 +306,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueBMCWithoutMAC{}), + toIssue(&issueBMCWithoutMAC{}), }, }, } @@ -338,7 +338,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueBMCWithoutIP{}), + toIssue(&issueBMCWithoutIP{}), }, }, } @@ -430,7 +430,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueASNUniqueness{ + toIssue(&issueASNUniqueness{ details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", machines[1].ID), }), }, @@ -438,7 +438,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueASNUniqueness{ + toIssue(&issueASNUniqueness{ details: fmt.Sprintf("- ASN (100) not unique, shared with [%[1]s]\n- ASN (200) not unique, shared with [%[1]s]", machines[0].ID), }), }, @@ -474,7 +474,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[0], Issues: Issues{ - toIssue(&IssueNonDistinctBMCIP{ + toIssue(&issueNonDistinctBMCIP{ details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", machines[1].ID), }), }, @@ -482,7 +482,7 @@ func TestFindIssues(t *testing.T) { { Machine: &machines[1], Issues: Issues{ - toIssue(&IssueNonDistinctBMCIP{ + toIssue(&issueNonDistinctBMCIP{ details: fmt.Sprintf("BMC IP (127.0.0.1) not unique, shared with [%[1]s]", machines[0].ID), }), }, @@ -509,7 +509,7 @@ func TestFindIssues(t *testing.T) { want = tt.want(ms) } - if diff := cmp.Diff(want, got.ToList(), cmp.AllowUnexported(IssueLastEventError{}, IssueASNUniqueness{}, IssueNonDistinctBMCIP{})); diff != "" { + if diff := cmp.Diff(want, got.ToList(), cmp.AllowUnexported(issueLastEventError{}, issueASNUniqueness{}, issueNonDistinctBMCIP{})); diff != "" { t.Errorf("diff (+got -want):\n %s", diff) } }) diff --git a/cmd/metal-api/internal/issues/last-event-error.go b/cmd/metal-api/internal/issues/last-event-error.go index 997e840e5..99952d561 100644 --- a/cmd/metal-api/internal/issues/last-event-error.go +++ b/cmd/metal-api/internal/issues/last-event-error.go @@ -12,7 +12,7 @@ const ( ) type ( - IssueLastEventError struct { + issueLastEventError struct { details string } ) @@ -21,7 +21,7 @@ func DefaultLastErrorThreshold() time.Duration { return 7 * 24 * time.Hour } -func (i *IssueLastEventError) Spec() *spec { +func (i *issueLastEventError) Spec() *spec { return &spec{ Type: TypeLastEventError, Severity: SeverityMinor, @@ -30,7 +30,7 @@ func (i *IssueLastEventError) Spec() *spec { } } -func (i *IssueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if c.LastErrorThreshold == 0 { return false } @@ -46,6 +46,6 @@ func (i *IssueLastEventError) Evaluate(m metal.Machine, ec metal.ProvisioningEve return false } -func (i *IssueLastEventError) Details() string { +func (i *issueLastEventError) Details() string { return i.details } diff --git a/cmd/metal-api/internal/issues/liveliness-dead.go b/cmd/metal-api/internal/issues/liveliness-dead.go index 80b772bb1..c7c8d1407 100644 --- a/cmd/metal-api/internal/issues/liveliness-dead.go +++ b/cmd/metal-api/internal/issues/liveliness-dead.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueLivelinessDead struct{} + issueLivelinessDead struct{} ) -func (i *IssueLivelinessDead) Spec() *spec { +func (i *issueLivelinessDead) Spec() *spec { return &spec{ Type: TypeLivelinessDead, Severity: SeverityMajor, @@ -19,10 +19,10 @@ func (i *IssueLivelinessDead) Spec() *spec { } } -func (i *IssueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueLivelinessDead) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Liveliness == metal.MachineLivelinessDead } -func (i *IssueLivelinessDead) Details() string { +func (i *issueLivelinessDead) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/liveliness-not-available.go b/cmd/metal-api/internal/issues/liveliness-not-available.go index db6cc7d3a..647eb468b 100644 --- a/cmd/metal-api/internal/issues/liveliness-not-available.go +++ b/cmd/metal-api/internal/issues/liveliness-not-available.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueLivelinessNotAvailable struct{} + issueLivelinessNotAvailable struct{} ) -func (i *IssueLivelinessNotAvailable) Spec() *spec { +func (i *issueLivelinessNotAvailable) Spec() *spec { return &spec{ Type: TypeLivelinessNotAvailable, Severity: SeverityMinor, @@ -18,7 +18,7 @@ func (i *IssueLivelinessNotAvailable) Spec() *spec { } } -func (i *IssueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { allowed := map[metal.MachineLiveliness]bool{ metal.MachineLivelinessAlive: true, metal.MachineLivelinessDead: true, @@ -28,6 +28,6 @@ func (i *IssueLivelinessNotAvailable) Evaluate(m metal.Machine, ec metal.Provisi return !allowed[ec.Liveliness] } -func (i *IssueLivelinessNotAvailable) Details() string { +func (i *issueLivelinessNotAvailable) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/liveliness-unknown.go b/cmd/metal-api/internal/issues/liveliness-unknown.go index cc730127d..73cf3bd18 100644 --- a/cmd/metal-api/internal/issues/liveliness-unknown.go +++ b/cmd/metal-api/internal/issues/liveliness-unknown.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueLivelinessUnknown struct{} + issueLivelinessUnknown struct{} ) -func (i *IssueLivelinessUnknown) Spec() *spec { +func (i *issueLivelinessUnknown) Spec() *spec { return &spec{ Type: TypeLivelinessUnknown, Severity: SeverityMajor, @@ -19,10 +19,10 @@ func (i *IssueLivelinessUnknown) Spec() *spec { } } -func (i *IssueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueLivelinessUnknown) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Liveliness == metal.MachineLivelinessUnknown } -func (i *IssueLivelinessUnknown) Details() string { +func (i *issueLivelinessUnknown) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/no-event-container.go b/cmd/metal-api/internal/issues/no-event-container.go index d6f1f6399..a5da32aca 100644 --- a/cmd/metal-api/internal/issues/no-event-container.go +++ b/cmd/metal-api/internal/issues/no-event-container.go @@ -9,10 +9,10 @@ const ( ) type ( - IssueNoEventContainer struct{} + issueNoEventContainer struct{} ) -func (i *IssueNoEventContainer) Spec() *spec { +func (i *issueNoEventContainer) Spec() *spec { return &spec{ Type: TypeNoEventContainer, Severity: SeverityMajor, @@ -21,10 +21,10 @@ func (i *IssueNoEventContainer) Spec() *spec { } } -func (i *IssueNoEventContainer) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueNoEventContainer) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return ec.Base.ID == "" } -func (i *IssueNoEventContainer) Details() string { +func (i *issueNoEventContainer) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/no-partition.go b/cmd/metal-api/internal/issues/no-partition.go index 7d082b7bd..b4e79710d 100644 --- a/cmd/metal-api/internal/issues/no-partition.go +++ b/cmd/metal-api/internal/issues/no-partition.go @@ -7,10 +7,10 @@ const ( ) type ( - IssueNoPartition struct{} + issueNoPartition struct{} ) -func (i *IssueNoPartition) Spec() *spec { +func (i *issueNoPartition) Spec() *spec { return &spec{ Type: TypeNoPartition, Severity: SeverityMajor, @@ -19,10 +19,10 @@ func (i *IssueNoPartition) Spec() *spec { } } -func (i *IssueNoPartition) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueNoPartition) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { return m.PartitionID == "" } -func (i *IssueNoPartition) Details() string { +func (i *issueNoPartition) Details() string { return "" } diff --git a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go index 7bcf1e77a..15ebc6077 100644 --- a/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go +++ b/cmd/metal-api/internal/issues/non-distinct-bmc-ip.go @@ -11,12 +11,12 @@ const ( ) type ( - IssueNonDistinctBMCIP struct { + issueNonDistinctBMCIP struct { details string } ) -func (i *IssueNonDistinctBMCIP) Spec() *spec { +func (i *issueNonDistinctBMCIP) Spec() *spec { return &spec{ Type: TypeNonDistinctBMCIP, Description: "BMC IP address is not distinct", @@ -24,7 +24,7 @@ func (i *IssueNonDistinctBMCIP) Spec() *spec { } } -func (i *IssueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { +func (i *issueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningEventContainer, c *Config) bool { if m.IPMI.Address == "" { return false } @@ -60,6 +60,6 @@ func (i *IssueNonDistinctBMCIP) Evaluate(m metal.Machine, ec metal.ProvisioningE return true } -func (i *IssueNonDistinctBMCIP) Details() string { +func (i *issueNonDistinctBMCIP) Details() string { return i.details } diff --git a/cmd/metal-api/internal/issues/types.go b/cmd/metal-api/internal/issues/types.go index 7ee92bdcd..db05f05b1 100644 --- a/cmd/metal-api/internal/issues/types.go +++ b/cmd/metal-api/internal/issues/types.go @@ -39,31 +39,31 @@ func NotAllocatableIssueTypes() []Type { func NewIssueFromType(t Type) (issue, error) { switch t { case TypeNoPartition: - return &IssueNoPartition{}, nil + return &issueNoPartition{}, nil case TypeLivelinessDead: - return &IssueLivelinessDead{}, nil + return &issueLivelinessDead{}, nil case TypeLivelinessUnknown: - return &IssueLivelinessUnknown{}, nil + return &issueLivelinessUnknown{}, nil case TypeLivelinessNotAvailable: - return &IssueLivelinessNotAvailable{}, nil + return &issueLivelinessNotAvailable{}, nil case TypeFailedMachineReclaim: - return &IssueFailedMachineReclaim{}, nil + return &issueFailedMachineReclaim{}, nil case TypeCrashLoop: - return &IssueCrashLoop{}, nil + return &issueCrashLoop{}, nil case TypeLastEventError: - return &IssueLastEventError{}, nil + return &issueLastEventError{}, nil case TypeBMCWithoutMAC: - return &IssueBMCWithoutMAC{}, nil + return &issueBMCWithoutMAC{}, nil case TypeBMCWithoutIP: - return &IssueBMCWithoutIP{}, nil + return &issueBMCWithoutIP{}, nil case TypeBMCInfoOutdated: - return &IssueBMCInfoOutdated{}, nil + return &issueBMCInfoOutdated{}, nil case TypeASNUniqueness: - return &IssueASNUniqueness{}, nil + return &issueASNUniqueness{}, nil case TypeNonDistinctBMCIP: - return &IssueNonDistinctBMCIP{}, nil + return &issueNonDistinctBMCIP{}, nil case TypeNoEventContainer: - return &IssueNoEventContainer{}, nil + return &issueNoEventContainer{}, nil default: return nil, fmt.Errorf("unknown issue type: %s", t) } diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index f3171abbe..095e78f8c 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -259,7 +259,7 @@ func (r *machineResource) webService() *restful.WebService { Operation("issues"). Doc("returns machine issues"). Metadata(restfulspec.KeyOpenAPITags, tags). - Metadata(auditing.Exclude, true). + // is an expensive call so we audit this as well even if it does not change anything Reads(v1.MachineIssuesRequest{}). Writes([]v1.MachineIssueResponse{}). Returns(http.StatusOK, "OK", []v1.MachineIssueResponse{}). From a070f2518b4a2fa8a77b682762f23398542f95fd Mon Sep 17 00:00:00 2001 From: Gerrit91 Date: Wed, 11 Oct 2023 10:04:47 +0200 Subject: [PATCH 17/17] Cosmetics. --- cmd/metal-api/internal/issues/issues.go | 35 +++++++++++++------ cmd/metal-api/internal/issues/issues_test.go | 4 +-- .../internal/service/machine-service.go | 4 +-- .../internal/service/partition-service.go | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/cmd/metal-api/internal/issues/issues.go b/cmd/metal-api/internal/issues/issues.go index 22bc57f27..0d72e27b6 100644 --- a/cmd/metal-api/internal/issues/issues.go +++ b/cmd/metal-api/internal/issues/issues.go @@ -10,21 +10,33 @@ import ( type ( // Config contains configuration parameters for finding machine issues Config struct { - Machines metal.Machines - EventContainers metal.ProvisioningEventContainers - Severity Severity - Only []Type - Omit []Type + // Machines are the machines to evaluate issues for + Machines metal.Machines + // EventContainers are the event containers of the machines to evaluate issues for + // if not provided the machines will have a no-event-container issue + EventContainers metal.ProvisioningEventContainers + // Severity filters issues for the given severity + Severity Severity + // Only includes only the given issue types + Only []Type + // Omit omits the given issue types, this has precedence over only + Omit []Type + // LastErrorThreshold specifies for how long in the past the last event error is counted as an error LastErrorThreshold time.Duration } // Issue formulates an issue of a machine Issue struct { - Type Type - Severity Severity + // Type specifies the issue type (id) + Type Type + // Severity specifies the severity of an issue + Severity Severity + // Description provides an issue description Description string - RefURL string - Details string + // RefURL provides a link to a more detailed issue description in the metal-stack documentation + RefURL string + // Details may contain additional details on an evaluated issue + Details string } // Issues is a list of issues @@ -38,6 +50,7 @@ type ( // MachineIssues is map of a machine response to a list of machine issues MachineIssues []*MachineWithIssues + // MachineIssuesMap is a map of machine issues with the machine id as a map key MachineIssuesMap map[string]*MachineWithIssues issue interface { @@ -59,7 +72,7 @@ type ( } ) -func AllIssues() Issues { +func All() Issues { var res Issues for _, t := range AllIssueTypes() { @@ -84,7 +97,7 @@ func toIssue(i issue) Issue { } } -func FindIssues(c *Config) (MachineIssuesMap, error) { +func Find(c *Config) (MachineIssuesMap, error) { if c.LastErrorThreshold == 0 { c.LastErrorThreshold = DefaultLastErrorThreshold() } diff --git a/cmd/metal-api/internal/issues/issues_test.go b/cmd/metal-api/internal/issues/issues_test.go index 4e9263925..934f26add 100644 --- a/cmd/metal-api/internal/issues/issues_test.go +++ b/cmd/metal-api/internal/issues/issues_test.go @@ -496,7 +496,7 @@ func TestFindIssues(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ms := tt.machines() - got, err := FindIssues(&Config{ + got, err := Find(&Config{ Machines: ms, EventContainers: tt.eventContainers(), Only: tt.only, @@ -518,7 +518,7 @@ func TestFindIssues(t *testing.T) { func TestAllIssues(t *testing.T) { issuesTypes := map[Type]bool{} - for _, i := range AllIssues() { + for _, i := range All() { issuesTypes[i.Type] = true } diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index 095e78f8c..54e9b4821 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -506,7 +506,7 @@ func (r *machineResource) updateMachine(request *restful.Request, response *rest } func (r *machineResource) listIssues(request *restful.Request, response *restful.Response) { - issues := issues.AllIssues() + issues := issues.All() var issueResponse []v1.MachineIssue for _, issue := range issues { @@ -593,7 +593,7 @@ func (r *machineResource) issues(request *restful.Request, response *restful.Res return } - machinesWithIssues, err := issues.FindIssues(&issues.Config{ + machinesWithIssues, err := issues.Find(&issues.Config{ Machines: ms, EventContainers: ecs, Severity: severity, diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index d82746961..ee1a90173 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -362,7 +362,7 @@ func (r *partitionResource) calcPartitionCapacity(pcr *v1.PartitionCapacityReque return nil, fmt.Errorf("unable to fetch provisioning event containers: %w", err) } - machinesWithIssues, err := issues.FindIssues(&issues.Config{ + machinesWithIssues, err := issues.Find(&issues.Config{ Machines: ms, EventContainers: ecs, Only: issues.NotAllocatableIssueTypes(),