Skip to content

Commit 20f7de9

Browse files
committed
Improve tests for the bootserver
1 parent 901b4e7 commit 20f7de9

File tree

5 files changed

+182
-8
lines changed

5 files changed

+182
-8
lines changed

api/v1alpha1/constants.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
package v1alpha1
55

66
const (
7-
DefaultIgnitionKey = "ignition" // Key for accessing Ignition configuration data within a Kubernetes Secret object.
8-
DefaultIPXEScriptKey = "ipxe-script" // Key for accessing iPXE script data within the iPXE-specific Secret object.
9-
SystemUUIDIndexKey = "spec.systemUUID" // Field to index resources by their system UUID.
10-
SystemIPIndexKey = "spec.systemIPs" // Field to index resources by their system IP addresses.
11-
DefaultFormatKey = "format" // Key for determining the format of the data stored in a Secret, such as fcos or plain-ignition.
12-
FCOSFormat = "fcos" // Specifies the format value used for Fedora CoreOS specific configurations.
7+
DefaultIgnitionKey = "ignition" // Key for accessing Ignition configuration data within a Kubernetes Secret object.
8+
DefaultIPXEScriptKey = "ipxe-script" // Key for accessing iPXE script data within the iPXE-specific Secret object.
9+
SystemUUIDIndexKey = "spec.systemUUID" // Field to index resources by their system UUID.
10+
SystemIPIndexKey = "spec.systemIPs" // Field to index resources by their system IP addresses.
11+
NetworkIdentifierIndexKey = "spec.networkIdentifiers" // Field to index resources by their network identifiers (IP addresses and MAC addresses).
12+
DefaultFormatKey = "format" // Key for determining the format of the data stored in a Secret, such as fcos or plain-ignition.
13+
FCOSFormat = "fcos" // Specifies the format value used for Fedora CoreOS specific configurations.
1314
)

cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func IndexHTTPBootConfigByNetworkIDs(ctx context.Context, mgr ctrl.Manager) erro
354354
return mgr.GetFieldIndexer().IndexField(
355355
ctx,
356356
&bootv1alpha1.HTTPBootConfig{},
357-
bootv1alpha1.SystemIPIndexKey,
357+
bootv1alpha1.NetworkIdentifierIndexKey,
358358
func(Obj client.Object) []string {
359359
HTTPBootConfig := Obj.(*bootv1alpha1.HTTPBootConfig)
360360
return HTTPBootConfig.Spec.NetworkIdentifiers

server/bootserver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ func handleHTTPBoot(w http.ResponseWriter, r *http.Request, k8sClient client.Cli
393393

394394
var httpBootConfigs bootv1alpha1.HTTPBootConfigList
395395
for _, ip := range clientIPs {
396-
if err := k8sClient.List(ctx, &httpBootConfigs, client.MatchingFields{bootv1alpha1.SystemIPIndexKey: ip}); err != nil {
396+
if err := k8sClient.List(ctx, &httpBootConfigs, client.MatchingFields{bootv1alpha1.NetworkIdentifierIndexKey: ip}); err != nil {
397397
log.Info("Failed to list HTTPBootConfig for IP", "IP", ip, "error", err)
398398
continue
399399
}

server/bootserver_suit_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package server
5+
6+
import (
7+
"net/http"
8+
"testing"
9+
10+
"github.com/go-logr/logr"
11+
bootv1alpha1 "github.com/ironcore-dev/boot-operator/api/v1alpha1"
12+
. "github.com/onsi/ginkgo/v2"
13+
. "github.com/onsi/gomega"
14+
corev1 "k8s.io/api/core/v1"
15+
"k8s.io/apimachinery/pkg/runtime"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
18+
)
19+
20+
var (
21+
testServerAddr = ":30003"
22+
testServerURL = "http://localhost:30003"
23+
24+
defaultUKIURL = "https://example.com/default.efi"
25+
ipxeServiceURL = "http://localhost:30004"
26+
27+
k8sClient client.Client
28+
)
29+
30+
func TestBootServer(t *testing.T) {
31+
RegisterFailHandler(Fail)
32+
RunSpecs(t, "Boot Server Suite")
33+
}
34+
35+
var _ = BeforeSuite(func() {
36+
scheme := runtime.NewScheme()
37+
Expect(corev1.AddToScheme(scheme)).To(Succeed())
38+
Expect(bootv1alpha1.AddToScheme(scheme)).To(Succeed())
39+
40+
k8sClient = fake.NewClientBuilder().
41+
WithScheme(scheme).
42+
Build()
43+
44+
go func() {
45+
defer GinkgoRecover()
46+
RunBootServer(testServerAddr, ipxeServiceURL, k8sClient, logr.Discard(), defaultUKIURL)
47+
}()
48+
49+
Eventually(func() error {
50+
_, err := http.Get(testServerURL + "/httpboot")
51+
return err
52+
}, "5s", "200ms").Should(Succeed())
53+
})

server/bootserver_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package server
5+
6+
import (
7+
"context"
8+
"encoding/json"
9+
"github.com/go-logr/logr"
10+
"net/http"
11+
12+
bootv1alpha1 "github.com/ironcore-dev/boot-operator/api/v1alpha1"
13+
14+
. "github.com/onsi/ginkgo/v2"
15+
. "github.com/onsi/gomega"
16+
corev1 "k8s.io/api/core/v1"
17+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
)
19+
20+
type httpBootResponse struct {
21+
ClientIPs string `json:"ClientIPs"`
22+
UKIURL string `json:"UKIURL"`
23+
SystemUUID string `json:"SystemUUID,omitempty"`
24+
}
25+
26+
var _ = Describe("BootServer", func() {
27+
Context("/httpboot endpoint", func() {
28+
It("delivers default httpboot data when no HTTPBootConfig matches the client IP", func() {
29+
resp, err := http.Get(testServerURL + "/httpboot")
30+
Expect(err).NotTo(HaveOccurred())
31+
defer func() {
32+
_ = resp.Body.Close()
33+
}()
34+
35+
Expect(resp.StatusCode).To(Equal(http.StatusOK))
36+
Expect(resp.Header.Get("Content-Type")).To(Equal("application/json"))
37+
38+
var body httpBootResponse
39+
Expect(json.NewDecoder(resp.Body).Decode(&body)).To(Succeed())
40+
41+
By("returning the default UKI URL")
42+
Expect(body.UKIURL).To(Equal(defaultUKIURL))
43+
44+
By("including the recorded client IPs")
45+
Expect(body.ClientIPs).NotTo(BeEmpty())
46+
47+
By("not setting a SystemUUID in the default case")
48+
Expect(body.SystemUUID).To(SatisfyAny(BeEmpty(), Equal("")))
49+
})
50+
})
51+
52+
It("converts valid Butane YAML to JSON", func() {
53+
butaneYAML := []byte(`
54+
variant: fcos
55+
version: 1.5.0
56+
systemd:
57+
units:
58+
- name: test.service
59+
enabled: true
60+
`)
61+
62+
jsonData, err := renderIgnition(butaneYAML)
63+
Expect(err).ToNot(HaveOccurred())
64+
Expect(jsonData).ToNot(BeEmpty())
65+
Expect(string(jsonData)).To(ContainSubstring(`"systemd"`))
66+
})
67+
68+
It("returns an error for invalid YAML", func() {
69+
bad := []byte("this ::: is not yaml")
70+
_, err := renderIgnition(bad)
71+
Expect(err).To(HaveOccurred())
72+
})
73+
74+
Context("Verify the SetStatusCondition method", func() {
75+
76+
var testLog = logr.Discard()
77+
78+
It("returns an error for unknown condition type", func() {
79+
cfg := &bootv1alpha1.IPXEBootConfig{
80+
ObjectMeta: v1.ObjectMeta{
81+
Name: "unknown-cond",
82+
Namespace: "default",
83+
},
84+
}
85+
Expect(k8sClient.Create(context.Background(), cfg)).To(Succeed())
86+
87+
err := SetStatusCondition(
88+
context.Background(),
89+
k8sClient,
90+
testLog,
91+
cfg,
92+
"DoesNotExist",
93+
)
94+
95+
Expect(err).To(HaveOccurred())
96+
Expect(err.Error()).To(ContainSubstring("condition type DoesNotExist not found"))
97+
})
98+
99+
It("returns an error for unsupported resource types", func() {
100+
secret := &corev1.Secret{
101+
ObjectMeta: v1.ObjectMeta{
102+
Name: "bad-type",
103+
Namespace: "default",
104+
},
105+
}
106+
_ = k8sClient.Create(context.Background(), secret)
107+
108+
err := SetStatusCondition(
109+
context.Background(),
110+
k8sClient,
111+
testLog,
112+
secret,
113+
"IgnitionDataFetched",
114+
)
115+
116+
Expect(err).To(HaveOccurred())
117+
Expect(err.Error()).To(ContainSubstring("unsupported resource type"))
118+
})
119+
})
120+
})

0 commit comments

Comments
 (0)