Skip to content

Commit

Permalink
Add new CRD for Firmware Components Update
Browse files Browse the repository at this point in the history
Add a new CRD for the HostFirmwareComponents described
in the Firmware Interface proposal - metal3-io/metal3-docs#364.

Signed-off-by: Iury Gregory Melo Ferreira <imelofer@redhat.com>
  • Loading branch information
iurygregory committed Jan 15, 2024
1 parent c9467a0 commit d89e8ad
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 0 deletions.
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,12 @@ resources:
kind: HardwareData
path: github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
domain: metal3.io
group: metal3.io
kind: HostFirmwareComponents
path: github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha
version: v1alpha1
version: "3"
118 changes: 118 additions & 0 deletions apis/metal3.io/v1alpha1/hostfirmwarecomponents_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// FirmwareUpdate defines a firmware update specification.
type FirmwareUpdate struct {
Component string `json:"name"`
URL string `json:"url"`
}

// FirmwareComponentStatus defines the status of a firmware component.
type FirmwareComponentStatus struct {
Component string `json:"component"`
InitialVersion string `json:"initialVersion"`
CurrentVersion string `json:"currentVersion"`
LastVersionFlashed string `json:"lastVersionFlashed"`
UpdatedAt metav1.Time `json:"updatedAt"`
}

type UpdatesConditionType string

const (
// Indicates that the updates in the Spec are different than Status
HostFirmwareComponentsChangeDetected UpdatesConditionType = "ChangeDetected"

// Indicates if the updates are valid and can be configured on the host
HostFirmwareComponentsValid UpdatesConditionType = "Valid"
)

// HostFirmwareComponentsSpec defines the desired state of HostFirmwareComponents
type HostFirmwareComponentsSpec struct {
Updates []FirmwareUpdate `json:"updates"`
}

// HostFirmwareComponentsStatus defines the observed state of HostFirmwareComponents
type HostFirmwareComponentsStatus struct {
// Updates is the list of all firmware components that should be updated
// they are specified via name and url fields.
Updates []FirmwareUpdate `json:"updates"`

// Components is the list of all available firmware components and their information.
Components []FirmwareComponentStatus `json:"components"`

// Time that the status was last updated
// +optional
LastUpdated *metav1.Time `json:"lastUpdated,omitempty"`

// Track whether updates stored in the spec are valid based on the schema
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

//+kubebuilder:object:root=true
//+kubebuilder:resource:shortName=hfc
//+kubebuilder:subresource:status

// HostFirmwareComponents is the Schema for the hostfirmwarecomponents API
type HostFirmwareComponents struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec HostFirmwareComponentsSpec `json:"spec,omitempty"`
Status HostFirmwareComponentsStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// HostFirmwareComponentsList contains a list of HostFirmwareComponents
type HostFirmwareComponentsList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HostFirmwareComponents `json:"items"`
}

// Check whether the updates's names are valid
func (host *HostFirmwareComponents) ValidateHostFirmwareComponents() error {

allowedNames := map[string]struct{}{"bmc": {}, "bios": {}}
for _, update := range host.Spec.Updates {
componentName := update.Component
if _, ok := allowedNames[componentName]; !ok {
return fmt.Errorf("Component %s is invalid, only 'bmc' or 'bios' are allowed as update names", update.Component)
}

}

return nil
}

func init() {
SchemeBuilder.Register(&HostFirmwareComponents{}, &HostFirmwareComponentsList{})
}
95 changes: 95 additions & 0 deletions apis/metal3.io/v1alpha1/hostfirmwarecomponents_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package v1alpha1

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestValidateHostFirmwareComponents(t *testing.T) {
for _, tc := range []struct {
Scenario string
Components []FirmwareComponentStatus
Updates []FirmwareUpdate
LastUpdated *metav1.Time
Conditions []metav1.Condition
ExpectedError string
}{
{
Scenario: "ValidHostFirmwareComponents",
Components: []FirmwareComponentStatus{
{
Component: "bios",
InitialVersion: "1.0",
CurrentVersion: "1.0",
UpdatedAt: metav1.NewTime(time.Now()),
},
{
Component: "bmc",
InitialVersion: "1.0",
CurrentVersion: "2.0",
LastVersionFlashed: "2.0",
UpdatedAt: metav1.NewTime(time.Now()),
},
},
Updates: []FirmwareUpdate{
{
Component: "bmc",
URL: "https://example.com/bmcupdate",
},
},
Conditions: []metav1.Condition{
{
Type: string(HostFirmwareComponentsChangeDetected),
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.NewTime(time.Now()),
},
},
ExpectedError: "",
},
{
Scenario: "InvalidHostFirmwareComponents",
Components: []FirmwareComponentStatus{},
Updates: []FirmwareUpdate{
{
Component: "nic",
URL: "https://example.com/bmcupdate",
},
},
Conditions: []metav1.Condition{
{
Type: string(HostFirmwareComponentsValid),
Status: metav1.ConditionFalse,
LastTransitionTime: metav1.NewTime(time.Now()),
},
},
ExpectedError: "Component nic is invalid, only 'bmc' or 'bios' are allowed as update names",
},
} {
t.Run(tc.Scenario, func(t *testing.T) {
hostFirmwareComponents := &HostFirmwareComponents{
ObjectMeta: metav1.ObjectMeta{
Name: "myhostfirmware",
Namespace: "myns",
},
Spec: HostFirmwareComponentsSpec{
Updates: tc.Updates,
},
Status: HostFirmwareComponentsStatus{
Components: tc.Components,
LastUpdated: tc.LastUpdated,
Conditions: tc.Conditions,
},
}

err := hostFirmwareComponents.ValidateHostFirmwareComponents()
if err == nil {
assert.Equal(t, tc.ExpectedError, "")
} else {
assert.Equal(t, tc.ExpectedError, err.Error())
}
})
}
}
148 changes: 148 additions & 0 deletions apis/metal3.io/v1alpha1/zz_generated.deepcopy.go

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

Loading

0 comments on commit d89e8ad

Please sign in to comment.