Add BMC authentication data to SIP machines
This change retrieves and stores BMC authentication information for each BaremetalHost object represented as a machine. This will enable SIP's Jump Host service to create a Secret with BMC information for each node in a sub-cluster. Signed-off-by: Drew Walters <andrew.walters@att.com> Change-Id: Ie11fa95f694f050b4e61866aaf81ae6410147fc1
This commit is contained in:
parent
9fcfae7107
commit
00c45a4444
@ -54,10 +54,19 @@ var _ = Describe("SIPCluster controller", func() {
|
||||
// Create vBMH test objects
|
||||
nodes := []airshipv1.VMRole{airshipv1.VMControlPlane, airshipv1.VMControlPlane, airshipv1.VMControlPlane,
|
||||
airshipv1.VMWorker, airshipv1.VMWorker, airshipv1.VMWorker, airshipv1.VMWorker}
|
||||
bmcUsername := "root"
|
||||
bmcPassword := "test"
|
||||
for node, role := range nodes {
|
||||
vBMH, networkData := testutil.CreateBMH(node, testNamespace, role, 6)
|
||||
bmcSecret := testutil.CreateBMCAuthSecret(vBMH.Name, vBMH.Namespace, bmcUsername,
|
||||
bmcPassword)
|
||||
|
||||
vBMH.Spec.BMC.CredentialsName = bmcSecret.Name
|
||||
|
||||
Expect(k8sClient.Create(context.Background(), bmcSecret)).Should(Succeed())
|
||||
Expect(k8sClient.Create(context.Background(), vBMH)).Should(Succeed())
|
||||
Expect(k8sClient.Create(context.Background(), networkData)).Should(Succeed())
|
||||
|
||||
}
|
||||
|
||||
// Create SIP cluster
|
||||
|
@ -54,3 +54,13 @@ type ErrorNetworkDataNotFound struct {
|
||||
func (e ErrorNetworkDataNotFound) Error() string {
|
||||
return fmt.Sprintf("vBMH Host %v does not define NetworkData, but is required for scheduling.", e.BMH)
|
||||
}
|
||||
|
||||
// ErrMalformedManagementCredentials occurs when a BMC credentials secret does not contain username and password fields.
|
||||
type ErrMalformedManagementCredentials struct {
|
||||
SecretName string
|
||||
}
|
||||
|
||||
func (e ErrMalformedManagementCredentials) Error() string {
|
||||
return fmt.Sprintf("secret %s contains malformed management credentials. Must contain '%s' and '%s' fields.",
|
||||
e.SecretName, keyBMCUsername, keyBMCPassword)
|
||||
}
|
||||
|
@ -78,6 +78,12 @@ const (
|
||||
SipNodeTypeLabel = BaseAirshipSelector + "/" + SipNodeTypeLabelName
|
||||
)
|
||||
|
||||
// Keys used to retrieve credentials from the BMC credentials secret
|
||||
const (
|
||||
keyBMCUsername = "username"
|
||||
keyBMCPassword = "password"
|
||||
)
|
||||
|
||||
// MAchine represents an individual BMH CR, and the appropriate
|
||||
// attributes required to manage the SIP Cluster scheduling and
|
||||
// rocesing needs about thhem
|
||||
@ -118,6 +124,8 @@ type MachineData struct {
|
||||
// Collect all IP's for the interfaces defined
|
||||
// In the list of Services
|
||||
IPOnInterface map[string]string
|
||||
BMCUsername string
|
||||
BMCPassword string
|
||||
}
|
||||
|
||||
// MachineList contains the list of Scheduled or ToBeScheduled machines
|
||||
@ -428,6 +436,26 @@ func (ml *MachineList) Extrapolate(sip airshipv1.SIPCluster, c client.Client) bo
|
||||
ml.ReadyForScheduleCount[machine.VMRole]--
|
||||
extrapolateSuccess = false
|
||||
}
|
||||
|
||||
// Retrieve BMC credentials
|
||||
mgmtCredsSecret := &corev1.Secret{}
|
||||
err = c.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: bmh.Namespace,
|
||||
Name: bmh.Spec.BMC.CredentialsName,
|
||||
}, mgmtCredsSecret)
|
||||
if err != nil {
|
||||
machine.ScheduleStatus = UnableToSchedule
|
||||
ml.ReadyForScheduleCount[machine.VMRole]--
|
||||
extrapolateSuccess = false
|
||||
}
|
||||
|
||||
// Parse BMC credentials from Secret
|
||||
err = ml.getMangementCredentials(machine, mgmtCredsSecret)
|
||||
if err != nil {
|
||||
machine.ScheduleStatus = UnableToSchedule
|
||||
ml.ReadyForScheduleCount[machine.VMRole]--
|
||||
extrapolateSuccess = false
|
||||
}
|
||||
}
|
||||
fmt.Printf("Schedule.Extrapolate extrapolateSuccess:%t\n", extrapolateSuccess)
|
||||
return extrapolateSuccess
|
||||
@ -623,6 +651,23 @@ func (ml *MachineList) getIP(machine *Machine, networkDataSecret *corev1.Secret,
|
||||
return nil
|
||||
}
|
||||
|
||||
// getManagementCredentials retrieves BMC credentials from a Kubernetes secret.
|
||||
func (ml *MachineList) getMangementCredentials(machine *Machine, secret *corev1.Secret) error {
|
||||
username, exists := secret.Data[keyBMCUsername]
|
||||
if !exists {
|
||||
return ErrMalformedManagementCredentials{SecretName: secret.Name}
|
||||
}
|
||||
machine.Data.BMCUsername = string(username)
|
||||
|
||||
password, exists := secret.Data[keyBMCPassword]
|
||||
if !exists {
|
||||
return ErrMalformedManagementCredentials{SecretName: secret.Name}
|
||||
}
|
||||
machine.Data.BMCPassword = string(password)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
ScheduleSet is a simple object to encapsulate data that
|
||||
helps our poor man scheduler
|
||||
|
@ -95,12 +95,23 @@ var _ = Describe("MachineList", func() {
|
||||
|
||||
It("Should retrieve the BMH IP from the BMH's NetworkData secret when infra services are defined", func() {
|
||||
// Create a BMH with a NetworkData secret
|
||||
bmh, secret := testutil.CreateBMH(1, "default", airshipv1.VMControlPlane, 6)
|
||||
bmh, networkData := testutil.CreateBMH(1, "default", airshipv1.VMControlPlane, 6)
|
||||
|
||||
// Create BMH and NetworkData secret
|
||||
var objsToApply []runtime.Object
|
||||
objsToApply = append(objsToApply, bmh)
|
||||
objsToApply = append(objsToApply, networkData)
|
||||
|
||||
// Create BMC credential secret
|
||||
username := "root"
|
||||
password := "test"
|
||||
bmcSecret := testutil.CreateBMCAuthSecret(bmh.Name, bmh.Namespace, username, password)
|
||||
|
||||
bmh.Spec.BMC.CredentialsName = bmcSecret.Name
|
||||
objsToApply = append(objsToApply, bmcSecret)
|
||||
|
||||
m, err := NewMachine(*bmh, airshipv1.VMControlPlane, NotScheduled)
|
||||
Expect(err).To(BeNil())
|
||||
var objs []runtime.Object
|
||||
objs = append(objs, bmh)
|
||||
objs = append(objs, secret)
|
||||
|
||||
ml := &MachineList{
|
||||
NamespacedName: types.NamespacedName{
|
||||
@ -126,21 +137,31 @@ var _ = Describe("MachineList", func() {
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := mockClient.NewFakeClient(objs...)
|
||||
k8sClient := mockClient.NewFakeClient(objsToApply...)
|
||||
Expect(ml.Extrapolate(*sipCluster, k8sClient)).To(BeTrue())
|
||||
|
||||
// NOTE(drewwalters96): Interface data is b64 encoded in the testutil convenience function.
|
||||
Expect(ml.Machines[bmh.Name].Data.IPOnInterface).To(Equal(map[string]string{"oam-ipv4": "32.68.51.139"}))
|
||||
})
|
||||
|
||||
It("Should not retrieve the BMH IP from the BMH's NetworkData secret if no infraServices are defined", func() {
|
||||
// Create a BMH with a NetworkData secret
|
||||
bmh, secret := testutil.CreateBMH(1, "default", airshipv1.VMControlPlane, 6)
|
||||
bmh, networkData := testutil.CreateBMH(1, "default", airshipv1.VMControlPlane, 6)
|
||||
|
||||
// Create BMH and NetworkData secret
|
||||
var objsToApply []runtime.Object
|
||||
objsToApply = append(objsToApply, bmh)
|
||||
objsToApply = append(objsToApply, networkData)
|
||||
|
||||
// Create BMC credential secret
|
||||
username := "root"
|
||||
password := "test"
|
||||
bmcSecret := testutil.CreateBMCAuthSecret(bmh.Name, bmh.Namespace, username, password)
|
||||
|
||||
bmh.Spec.BMC.CredentialsName = bmcSecret.Name
|
||||
objsToApply = append(objsToApply, bmcSecret)
|
||||
|
||||
m, err := NewMachine(*bmh, airshipv1.VMControlPlane, NotScheduled)
|
||||
Expect(err).To(BeNil())
|
||||
var objs []runtime.Object
|
||||
objs = append(objs, bmh)
|
||||
objs = append(objs, secret)
|
||||
|
||||
ml := &MachineList{
|
||||
NamespacedName: types.NamespacedName{
|
||||
@ -153,10 +174,69 @@ var _ = Describe("MachineList", func() {
|
||||
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
|
||||
}
|
||||
|
||||
k8sClient := mockClient.NewFakeClient(objs...)
|
||||
sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
|
||||
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
|
||||
LoadBalancer: []airshipv1.SIPClusterService{
|
||||
{
|
||||
Image: "haproxy:latest",
|
||||
NodeLabels: map[string]string{
|
||||
"test": "true",
|
||||
},
|
||||
NodePort: 30000,
|
||||
NodeInterface: "oam-ipv4",
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := mockClient.NewFakeClient(objsToApply...)
|
||||
Expect(ml.Extrapolate(*sipCluster, k8sClient)).To(BeTrue())
|
||||
Expect(len(ml.Machines[bmh.Name].Data.IPOnInterface)).To(Equal(0))
|
||||
|
||||
Expect(ml.Machines[bmh.Name].Data.BMCUsername).To(Equal(username))
|
||||
Expect(ml.Machines[bmh.Name].Data.BMCPassword).To(Equal(password))
|
||||
})
|
||||
|
||||
It("Should not process a BMH when its BMC secret is missing", func() {
|
||||
var objsToApply []runtime.Object
|
||||
|
||||
// Create BMH and NetworkData secret
|
||||
bmh, networkData := testutil.CreateBMH(1, "default", "master", 6)
|
||||
objsToApply = append(objsToApply, bmh)
|
||||
objsToApply = append(objsToApply, networkData)
|
||||
|
||||
bmh.Spec.BMC.CredentialsName = "foo-does-not-exist"
|
||||
|
||||
m, err := NewMachine(*bmh, airshipv1.VMControlPlane, NotScheduled)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
ml := &MachineList{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "vbmh",
|
||||
Namespace: "default",
|
||||
},
|
||||
Machines: map[string]*Machine{
|
||||
bmh.Name: m,
|
||||
},
|
||||
ReadyForScheduleCount: map[airshipv1.VMRole]int{
|
||||
airshipv1.VMControlPlane: 1,
|
||||
airshipv1.VMWorker: 0,
|
||||
},
|
||||
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
|
||||
}
|
||||
|
||||
sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
|
||||
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
|
||||
LoadBalancer: []airshipv1.SIPClusterService{
|
||||
{
|
||||
Image: "haproxy:latest",
|
||||
NodeLabels: map[string]string{
|
||||
"test": "true",
|
||||
},
|
||||
NodePort: 30000,
|
||||
NodeInterface: "oam-ipv4",
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := mockClient.NewFakeClient(objsToApply...)
|
||||
Expect(ml.Extrapolate(*sipCluster, k8sClient)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("Should not retrieve the BMH IP if it has been previously extrapolated", func() {
|
||||
|
@ -256,3 +256,22 @@ func CreateSIPCluster(name string, namespace string, controlPlanes int, workers
|
||||
Status: airshipv1.SIPClusterStatus{},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateBMCAuthSecret creates a K8s Secret that matches the Metal3.io BaremetalHost credential format for use in test
|
||||
// cases.
|
||||
func CreateBMCAuthSecret(nodeName string, namespace string, username string, password string) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: corev1.SchemeGroupVersion.String(),
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-bmc-credentials", nodeName),
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"username": []byte(username),
|
||||
"password": []byte(password),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user