Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 79 additions & 100 deletions internal/controller/metalstackcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ package controller

import (
"context"
"net/http"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
"github.com/stretchr/testify/mock"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

corev1 "k8s.io/api/core/v1"
Expand All @@ -36,6 +38,7 @@ import (
metalnetwork "github.com/metal-stack/metal-go/api/client/network"
"github.com/metal-stack/metal-go/api/models"
metalgoclient "github.com/metal-stack/metal-go/test/client"
"github.com/metal-stack/metal-lib/httperrors"
"github.com/metal-stack/metal-lib/pkg/pointer"
"github.com/metal-stack/metal-lib/pkg/testcommon"

Expand Down Expand Up @@ -270,6 +273,17 @@ var _ = Describe("MetalStackCluster Controller", func() {
"Type": Equal(v1alpha1.ClusterControlPlaneEndpointEnsured),
"Status": Equal(corev1.ConditionTrue),
})))

By("ssh keypair generation")
sshSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: owner.Name + "-ssh-keypair",
Namespace: resource.Namespace,
},
}
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(sshSecret), sshSecret)).NotTo(HaveOccurred())
Expect(sshSecret.Data).To(HaveKey("id_rsa"))
Expect(sshSecret.Data).To(HaveKey("id_rsa.pub"))
})
})
Context("reconciliation when external resources are provided", func() {
Expand Down Expand Up @@ -377,111 +391,76 @@ var _ = Describe("MetalStackCluster Controller", func() {

When("referenced resources do not exist", func() {
It("should fail reconciling", func() {
Expect(k8sClient.Create(ctx, resource)).To(Succeed())

})
})
})

Context("When reconciling a MetalStackCluster resource", func() {
// const resourceName = "test-resource"

// var (
// ctx = context.Background()

// resource *infrastructurev1alpha1.MetalStackCluster
// typeNamespacedName = types.NamespacedName{
// Name: resourceName,
// Namespace: "default",
// }
// metalstackcluster = &infrastructurev1alpha1.MetalStackCluster{}
// )

// BeforeEach(func() {
// resource = &infrastructurev1alpha1.MetalStackCluster{
// ObjectMeta: metav1.ObjectMeta{
// Name: resourceName,
// Namespace: "default",
// },
// Spec: infrastructurev1alpha1.MetalStackClusterSpec{
// ControlPlaneEndpoint: infrastructurev1alpha1.APIEndpoint{},
// ProjectID: "test-project",
// NodeNetworkID: nil,
// ControlPlaneIP: nil,
// Partition: "test-partition",
// Firewall: &infrastructurev1alpha1.Firewall{},
// },
// }

// By("creating the custom resource for the Kind MetalStackCluster")
// err := k8sClient.Get(ctx, typeNamespacedName, metalstackcluster)
// if err != nil && errors.IsNotFound(err) {
// Expect(k8sClient.Create(ctx, resource)).To(Succeed())
// }
// })

// AfterEach(func() {
// resource := &infrastructurev1alpha1.MetalStackCluster{}
// err := k8sClient.Get(ctx, typeNamespacedName, resource)
// Expect(err).NotTo(HaveOccurred())

// By("Cleanup the specific resource instance MetalStackCluster")
// Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
// })

// it should not do anything when the resource has no ownership yet

// it should reconcile successfully when the resource gets an ownership

// it automatically allocates dependent resources

// it should generate an SSH secret
// it should allocate a node network
// it should allocate a control plane ip
// it should ensure a firewall deployment

// it is idempotent!
// status conditions should be properly evaluated

// it is possible to optionally provide control plane ip, node network id and firewall

// it should delete the resource

// it should delete all managed resources

// it should not delete optionally provided resources

// Context("should successfully reconcile the resource", Ordered, func() {
// It("should not do anything when the resource has no ownership yet", func() {

// })

// By("setting an ownership ownership yet")

// It("it should reconcile successfully", func() {
By("creating the cluster resource and setting the owner reference")
owner := &clusterv1beta1.Cluster{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "owner-",
Namespace: "default",
},
}
Expect(k8sClient.Create(ctx, owner)).To(Succeed())

// })
resource.OwnerReferences = []metav1.OwnerReference{
*metav1.NewControllerRef(owner, clusterv1beta1.GroupVersion.WithKind("Cluster")),
}
Expect(k8sClient.Update(ctx, resource)).To(Succeed())

// It("it should automatically allocate dependent resources", func() {
// // it should generate an SSH secret
// // it should allocate a node network
// // it should allocate a control plane ip
// // it should ensure a firewall deployment
// })
// })
// controllerReconciler := &MetalStackClusterReconciler{
// Client: k8sClient,
// Scheme: k8sClient.Scheme(),
// }
By("reconciling the resource")

// _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
// NamespacedName: typeNamespacedName,
// })
// Expect(err).NotTo(HaveOccurred())
typeNamespacedName := types.NamespacedName{
Name: resource.Name,
Namespace: "default",
}

// resource := &infrastructurev1alpha1.MetalStackCluster{}
// err = k8sClient.Get(ctx, typeNamespacedName, resource)
// Expect(err).NotTo(HaveOccurred())
controllerReconciler.MetalClient, _ = metalgoclient.NewMetalMockClient(testingT, &metalgoclient.MetalMockFns{
IP: func(m *mock.Mock) {
m.On("FindIP", testcommon.MatchIgnoreContext(testingT, metalip.NewFindIPParams().WithID(controlPlaneIP)), nil).
Return(nil, &metalip.FindIPDefault{
Payload: &httperrors.HTTPErrorResponse{
StatusCode: http.StatusNotFound,
Message: "ip not found",
},
})
},
Network: func(m *mock.Mock) {
m.On("FindNetwork", testcommon.MatchIgnoreContext(testingT, metalnetwork.NewFindNetworkParams().WithID(nodeNetworkID)), nil).
Return(nil, &metalnetwork.FindNetworkDefault{
Payload: &httperrors.HTTPErrorResponse{
StatusCode: http.StatusNotFound,
Message: "network not found",
},
})
},
})

// Expect(resource.Status.Conditions).To(ContainElement("bla"))
Eventually(func() error {
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
return err
}).Should(MatchError(ContainSubstring("not found")))

Expect(k8sClient.Get(ctx, typeNamespacedName, resource)).ToNot(HaveOccurred())

Expect(resource.Status.Conditions).To(ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(v1alpha1.ClusterNodeNetworkEnsured),
"Status": Equal(corev1.ConditionFalse),
"Reason": Equal("InternalError"),
"Message": ContainSubstring("network not found"),
})))
Expect(resource.Status.Conditions).To(ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(v1alpha1.ClusterFirewallDeploymentReady),
"Status": Equal(corev1.ConditionTrue),
})))
Expect(resource.Status.Conditions).To(ContainElement(MatchFields(IgnoreExtras, Fields{
"Type": Equal(v1alpha1.ClusterControlPlaneEndpointEnsured),
"Status": Equal(corev1.ConditionFalse),
"Reason": Equal("InternalError"),
"Message": ContainSubstring("ip not found"),
})))
})
})
})
})
Loading