diff --git a/cloud/services/loadbalancers/loadbalancers_test.go b/cloud/services/loadbalancers/loadbalancers_test.go index 4cdbd595e6b..1e887bb52f9 100644 --- a/cloud/services/loadbalancers/loadbalancers_test.go +++ b/cloud/services/loadbalancers/loadbalancers_test.go @@ -22,20 +22,24 @@ import ( "testing" "github.com/Azure/go-autorest/autorest/to" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/subnets/mock_subnets" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/virtualnetworks/mock_virtualnetworks" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" "k8s.io/klog/klogr" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" "github.com/Azure/go-autorest/autorest" "github.com/golang/mock/gomock" . "github.com/onsi/gomega" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/loadbalancers/mock_loadbalancers" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips/mock_publicips" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" ) @@ -125,7 +129,7 @@ func TestReconcileLoadBalancer(t *testing.T) { s.AdditionalTags().AnyTimes().Return(infrav1.Tags{}) gomock.InOrder( mPublicIP.Get(context.TODO(), "my-rg", "my-publicip").Return(network.PublicIPAddress{Name: to.StringPtr("my-publicip")}, nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-publiclb", matchers.DiffEq(network.LoadBalancer{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-publiclb", gomockinternal.DiffEq(network.LoadBalancer{ Tags: map[string]*string{ "sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": to.StringPtr("owned"), "sigs.k8s.io_cluster-api-provider-azure_role": to.StringPtr(infrav1.APIServerRole), @@ -221,7 +225,7 @@ func TestReconcileLoadBalancer(t *testing.T) { s.AdditionalTags().AnyTimes().Return(infrav1.Tags{}) gomock.InOrder( mPublicIP.Get(context.TODO(), "my-rg", "outbound-publicip").Return(network.PublicIPAddress{Name: to.StringPtr("outbound-publicip")}, nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "cluster-name", matchers.DiffEq(network.LoadBalancer{ + m.CreateOrUpdate(context.TODO(), "my-rg", "cluster-name", gomockinternal.DiffEq(network.LoadBalancer{ Tags: map[string]*string{ "sigs.k8s.io_cluster-api-provider-azure_cluster_cluster-name": to.StringPtr("owned"), "sigs.k8s.io_cluster-api-provider-azure_role": to.StringPtr(infrav1.NodeOutboundRole), @@ -355,7 +359,7 @@ func TestReconcileLoadBalancer(t *testing.T) { }, }}}, nil) mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil) - m.CreateOrUpdate(context.TODO(), "my-rg", "my-lb", matchers.DiffEq(network.LoadBalancer{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-lb", gomockinternal.DiffEq(network.LoadBalancer{ Sku: &network.LoadBalancerSku{Name: network.LoadBalancerSkuNameStandard}, Location: to.StringPtr("testlocation"), Tags: map[string]*string{ diff --git a/cloud/services/networkinterfaces/networkinterfaces_test.go b/cloud/services/networkinterfaces/networkinterfaces_test.go index 420604ebb52..9ceb3985f53 100644 --- a/cloud/services/networkinterfaces/networkinterfaces_test.go +++ b/cloud/services/networkinterfaces/networkinterfaces_test.go @@ -24,13 +24,14 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" azure "sigs.k8s.io/cluster-api-provider-azure/cloud" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" . "github.com/onsi/gomega" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/loadbalancers/mock_loadbalancers" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/networkinterfaces/mock_networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips/mock_publicips" @@ -140,7 +141,7 @@ func TestReconcileNetworkInterface(t *testing.T) { gomock.InOrder( mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), mLoadBalancer.Get(context.TODO(), "my-rg", "my-public-lb").Return(getFakeNodeOutboundLoadBalancer(), nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", matchers.DiffEq(network.Interface{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ Location: to.StringPtr("fake-location"), InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ EnableAcceleratedNetworking: to.BoolPtr(true), @@ -187,7 +188,7 @@ func TestReconcileNetworkInterface(t *testing.T) { gomock.InOrder( mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), mLoadBalancer.Get(context.TODO(), "my-rg", "my-public-lb").Return(getFakeNodeOutboundLoadBalancer(), nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", matchers.DiffEq(network.Interface{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ Location: to.StringPtr("fake-location"), InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ EnableAcceleratedNetworking: to.BoolPtr(true), @@ -261,7 +262,7 @@ func TestReconcileNetworkInterface(t *testing.T) { }, }, }}, nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", matchers.DiffEq(network.Interface{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ Location: to.StringPtr("fake-location"), InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ EnableAcceleratedNetworking: to.BoolPtr(true), @@ -452,7 +453,7 @@ func TestReconcileNetworkInterface(t *testing.T) { gomock.InOrder( mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), mLoadBalancer.Get(context.TODO(), "my-rg", "my-public-lb").Return(getFakeNodeOutboundLoadBalancer(), nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", matchers.DiffEq(network.Interface{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ Location: to.StringPtr("fake-location"), InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ EnableAcceleratedNetworking: to.BoolPtr(true), @@ -501,7 +502,7 @@ func TestReconcileNetworkInterface(t *testing.T) { gomock.InOrder( mSubnet.Get(context.TODO(), "my-rg", "my-vnet", "my-subnet").Return(network.Subnet{}, nil), mLoadBalancer.Get(context.TODO(), "my-rg", "my-public-lb").Return(getFakeNodeOutboundLoadBalancer(), nil), - m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", matchers.DiffEq(network.Interface{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ Location: to.StringPtr("fake-location"), InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ EnableAcceleratedNetworking: to.BoolPtr(false), diff --git a/cloud/services/scalesets/vmss_test.go b/cloud/services/scalesets/vmss_test.go index 45cbfbd27de..09630b00c6a 100644 --- a/cloud/services/scalesets/vmss_test.go +++ b/cloud/services/scalesets/vmss_test.go @@ -42,7 +42,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/cloud/services/resourceskus" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/scalesets/mock_scalesets" infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha3" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" ) func init() { @@ -394,7 +394,7 @@ func TestService_Reconcile(t *testing.T) { lbMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.ClusterName).Return(getFakeNodeOutboundLoadBalancer(), nil) vmssMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name).Return(compute.VirtualMachineScaleSet{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - vmssMock.EXPECT().CreateOrUpdate(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, matchers.DiffEq(vmss)).Return(nil) + vmssMock.EXPECT().CreateOrUpdate(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, gomockinternal.DiffEq(vmss)).Return(nil) return mockCtrl }, @@ -529,7 +529,7 @@ func TestService_Reconcile(t *testing.T) { lbMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.ClusterName).Return(getFakeNodeOutboundLoadBalancer(), nil) vmssMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name).Return(compute.VirtualMachineScaleSet{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - vmssMock.EXPECT().CreateOrUpdate(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, matchers.DiffEq(vmss)).Return(nil) + vmssMock.EXPECT().CreateOrUpdate(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, gomockinternal.DiffEq(vmss)).Return(nil) return mockCtrl }, @@ -715,7 +715,7 @@ func TestService_Reconcile(t *testing.T) { lbMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.ClusterName).Return(getFakeNodeOutboundLoadBalancer(), nil) vmssMock.EXPECT().Get(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name).Return(vmss, nil) - vmssMock.EXPECT().Update(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, matchers.DiffEq(update)).Return(nil) + vmssMock.EXPECT().Update(gomock.Any(), scope.AzureCluster.Spec.ResourceGroup, spec.Name, gomockinternal.DiffEq(update)).Return(nil) return mockCtrl }, diff --git a/cloud/services/securitygroups/securitygroups_test.go b/cloud/services/securitygroups/securitygroups_test.go index 6799ee84db7..a793881634b 100644 --- a/cloud/services/securitygroups/securitygroups_test.go +++ b/cloud/services/securitygroups/securitygroups_test.go @@ -18,20 +18,24 @@ package securitygroups import ( "context" + "net/http" + "testing" + "github.com/Azure/go-autorest/autorest/to" "k8s.io/klog/klogr" - "net/http" + azure "sigs.k8s.io/cluster-api-provider-azure/cloud" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" - "testing" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" . "github.com/onsi/gomega" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/securitygroups/mock_securitygroups" "github.com/Azure/go-autorest/autorest" "github.com/golang/mock/gomock" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-06-01/network" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" ) @@ -79,7 +83,7 @@ func TestReconcileSecurityGroups(t *testing.T) { s.Location().AnyTimes().Return("test-location") s.V(gomock.AssignableToTypeOf(2)).AnyTimes().Return(klogr.New()) m.Get(context.TODO(), "my-rg", "nsg-one").Return(network.SecurityGroup{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-one", matchers.DiffEq(network.SecurityGroup{ + m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-one", gomockinternal.DiffEq(network.SecurityGroup{ SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ SecurityRules: &[]network.SecurityRule{ { @@ -116,7 +120,7 @@ func TestReconcileSecurityGroups(t *testing.T) { Location: to.StringPtr("test-location"), })) m.Get(context.TODO(), "my-rg", "nsg-two").Return(network.SecurityGroup{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-two", matchers.DiffEq(network.SecurityGroup{ + m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-two", gomockinternal.DiffEq(network.SecurityGroup{ SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ SecurityRules: &[]network.SecurityRule{}, }, @@ -176,7 +180,7 @@ func TestReconcileSecurityGroups(t *testing.T) { ID: to.StringPtr("fake/nsg/id"), Name: to.StringPtr("nsg-one"), }, nil) - m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-one", matchers.DiffEq(network.SecurityGroup{ + m.CreateOrUpdate(context.TODO(), "my-rg", "nsg-one", gomockinternal.DiffEq(network.SecurityGroup{ SecurityGroupPropertiesFormat: &network.SecurityGroupPropertiesFormat{ SecurityRules: &[]network.SecurityRule{ { diff --git a/cloud/services/virtualmachines/virtualmachines_test.go b/cloud/services/virtualmachines/virtualmachines_test.go index 27ac200c5da..c808c95e83d 100644 --- a/cloud/services/virtualmachines/virtualmachines_test.go +++ b/cloud/services/virtualmachines/virtualmachines_test.go @@ -18,15 +18,18 @@ package virtualmachines import ( "context" + "net/http" + "testing" + corev1 "k8s.io/api/core/v1" "k8s.io/klog/klogr" - "net/http" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" azure "sigs.k8s.io/cluster-api-provider-azure/cloud" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" - "testing" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" . "github.com/onsi/gomega" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/networkinterfaces/mock_networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips/mock_publicips" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/virtualmachines/mock_virtualmachines" @@ -334,7 +337,7 @@ func TestReconcileVM(t *testing.T) { }, }, nil) s.GetBootstrapData(context.TODO()).Return("fake-bootstrap-data", nil) - m.CreateOrUpdate(context.TODO(), "my-rg", "my-vm", matchers.DiffEq(compute.VirtualMachine{ + m.CreateOrUpdate(context.TODO(), "my-rg", "my-vm", gomockinternal.DiffEq(compute.VirtualMachine{ VirtualMachineProperties: &compute.VirtualMachineProperties{ HardwareProfile: &compute.HardwareProfile{VMSize: "Standard_D2v3"}, StorageProfile: &compute.StorageProfile{ diff --git a/controllers/azurecluster_controller.go b/controllers/azurecluster_controller.go index 0c89315a326..de159e524f4 100644 --- a/controllers/azurecluster_controller.go +++ b/controllers/azurecluster_controller.go @@ -51,10 +51,11 @@ type AzureClusterReconciler struct { } func (r *AzureClusterReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureCluster") c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav1.AzureCluster{}). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused Build(r) if err != nil { return errors.Wrapf(err, "error creating controller") @@ -66,7 +67,7 @@ func (r *AzureClusterReconciler) SetupWithManager(mgr ctrl.Manager, options cont &handler.EnqueueRequestsFromMapFunc{ ToRequests: util.ClusterToInfrastructureMapFunc(infrav1.GroupVersion.WithKind("AzureCluster")), }, - predicates.ClusterUnpaused(r.Log), + predicates.ClusterUnpaused(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/controllers/azurecluster_controller_test.go b/controllers/azurecluster_controller_test.go index ef4dfe4b3e4..90f68e6aa5d 100644 --- a/controllers/azurecluster_controller_test.go +++ b/controllers/azurecluster_controller_test.go @@ -25,7 +25,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -97,66 +96,5 @@ var _ = Describe("AzureClusterReconciler", func() { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Or(Equal("context deadline exceeded"), Equal("rate: Wait(n=1) would exceed context deadline"))) }) - - It("should skip reconciliation if cluster is paused", func() { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - logListener := record.NewListener(testEnv.LogRecorder) - del := logListener.Listen() - defer del() - - clusterName := test.RandomName("foo", 10) - cluster := &clusterv1.Cluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: clusterName, - Namespace: "default", - }, - Spec: clusterv1.ClusterSpec{ - Paused: true, - }, - } - Expect(testEnv.Create(ctx, cluster)).To(Succeed()) - defer func() { - err := testEnv.Delete(ctx, cluster) - Expect(err).NotTo(HaveOccurred()) - }() - - azClusterName := test.RandomName("foo", 10) - azCluster := &infrav1.AzureCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: azClusterName, - Namespace: "default", - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: clusterv1.GroupVersion.String(), - Kind: "Cluster", - Name: cluster.Name, - UID: cluster.GetUID(), - }, - }, - }, - } - Expect(testEnv.Create(ctx, azCluster)).To(Succeed()) - defer func() { - err := testEnv.Delete(ctx, azCluster) - Expect(err).NotTo(HaveOccurred()) - }() - - Eventually(logListener.GetEntries).Should(ContainElement( - record.LogEntry{ - LogFunc: "Info", - Values: []interface{}{ - "namespace", - cluster.Namespace, - "AzureCluster", - azCluster.Name, - "cluster", - cluster.Name, - "msg", - "AzureCluster or linked Cluster is marked as paused. Won't reconcile", - }, - })) - }) }) }) diff --git a/controllers/azuremachine_controller.go b/controllers/azuremachine_controller.go index 58ea74be278..d6516126b25 100644 --- a/controllers/azuremachine_controller.go +++ b/controllers/azuremachine_controller.go @@ -53,8 +53,9 @@ type AzureMachineReconciler struct { } func (r *AzureMachineReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureMachine") // create mapper to transform incoming AzureClusters into AzureMachine requests - azureClusterToAzureMachinesMapper, err := AzureClusterToAzureMachinesMapper(r.Client, mgr.GetScheme(), r.Log) + azureClusterToAzureMachinesMapper, err := AzureClusterToAzureMachinesMapper(r.Client, mgr.GetScheme(), log) if err != nil { return errors.Wrapf(err, "failed to create AzureCluster to AzureMachines mapper") } @@ -62,7 +63,7 @@ func (r *AzureMachineReconciler) SetupWithManager(mgr ctrl.Manager, options cont c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav1.AzureMachine{}). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused // watch for changes in CAPI Machine resources Watches( &source.Kind{Type: &clusterv1.Machine{}}, @@ -93,7 +94,7 @@ func (r *AzureMachineReconciler) SetupWithManager(mgr ctrl.Manager, options cont &handler.EnqueueRequestsFromMapFunc{ ToRequests: azureMachineMapper, }, - predicates.ClusterUnpausedAndInfrastructureReady(r.Log), + predicates.ClusterUnpausedAndInfrastructureReady(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/controllers/azuremachine_controller_test.go b/controllers/azuremachine_controller_test.go index 40a083674db..dcfa434689f 100644 --- a/controllers/azuremachine_controller_test.go +++ b/controllers/azuremachine_controller_test.go @@ -19,12 +19,10 @@ package controllers import ( "context" "testing" - "time" "github.com/Azure/go-autorest/autorest" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -37,7 +35,6 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" "sigs.k8s.io/cluster-api-provider-azure/cloud/scope" "sigs.k8s.io/cluster-api-provider-azure/internal/test" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/record" ) var _ = Describe("AzureMachineReconciler", func() { @@ -64,132 +61,9 @@ var _ = Describe("AzureMachineReconciler", func() { Expect(err).To(BeNil()) Expect(result.RequeueAfter).To(BeZero()) }) - - It("should exit early if the cluster is paused", func() { - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - - logListener := record.NewListener(testEnv.LogRecorder) - del := logListener.Listen() - defer del() - - cluster, _, del := createPausedOwningClusterAndAzCluster(ctx) - defer del() - - azureMachineName := test.RandomName("azmachine", 10) - machine := &clusterv1.Machine{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{clusterv1.ClusterLabelName: cluster.Name}, - Name: test.RandomName("machine", 10), - Namespace: cluster.Namespace, - }, - Spec: clusterv1.MachineSpec{ - ClusterName: cluster.Name, - InfrastructureRef: corev1.ObjectReference{ - APIVersion: infrav1.GroupVersion.String(), - Kind: "AzureMachine", - Name: azureMachineName, - Namespace: cluster.Namespace, - }, - }, - } - Expect(testEnv.Create(ctx, machine)).To(Succeed()) - defer func() { - err := testEnv.Delete(ctx, machine) - Expect(err).NotTo(HaveOccurred()) - }() - - azureMachine := &infrav1.AzureMachine{ - ObjectMeta: metav1.ObjectMeta{ - Name: test.RandomName("azmachine", 10), - Namespace: cluster.Namespace, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: clusterv1.GroupVersion.String(), - Kind: "Machine", - Name: machine.Name, - UID: machine.GetUID(), - }, - }, - }, - } - Expect(testEnv.Create(ctx, azureMachine)).To(Succeed()) - defer func() { - err := testEnv.Delete(ctx, azureMachine) - Expect(err).NotTo(HaveOccurred()) - }() - - Eventually(logListener.GetEntries).Should(ContainElement( - record.LogEntry{ - LogFunc: "Info", - Values: []interface{}{ - "namespace", - cluster.Namespace, - "azureMachine", - azureMachine.Name, - "machine", - machine.Name, - "cluster", - cluster.Name, - "msg", - "AzureMachine or linked Cluster is marked as paused. Won't reconcile", - }, - })) - - }) }) }) -func createPausedOwningClusterAndAzCluster(ctx context.Context) (*clusterv1.Cluster, *infrav1.AzureCluster, func()) { - azClusterName := test.RandomName("azcluster", 10) - clusterName := test.RandomName("cluster", 10) - cluster := &clusterv1.Cluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: clusterName, - Namespace: "default", - }, - Spec: clusterv1.ClusterSpec{ - InfrastructureRef: &corev1.ObjectReference{ - APIVersion: infrav1.GroupVersion.String(), - Kind: "AzureCluster", - Name: azClusterName, - Namespace: "default", - }, - Paused: true, - }, - } - Expect(testEnv.Create(ctx, cluster)).To(Succeed()) - cleanupCluster := func() error { - return testEnv.Delete(ctx, cluster) - } - - azCluster := &infrav1.AzureCluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: azClusterName, - Namespace: "default", - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: clusterv1.GroupVersion.String(), - Kind: "Cluster", - Name: cluster.Name, - UID: cluster.GetUID(), - }, - }, - }, - } - Expect(testEnv.Create(ctx, azCluster)).To(Succeed()) - cleanupAzCluster := func() error { - return testEnv.Delete(ctx, azCluster) - } - - return cluster, azCluster, func() { - err1 := cleanupCluster() - err2 := cleanupAzCluster() - Expect(err1).ToNot(HaveOccurred()) - Expect(err2).ToNot(HaveOccurred()) - } -} - func TestConditions(t *testing.T) { g := NewWithT(t) scheme := setupScheme(g) diff --git a/controllers/common_controller_test.go b/controllers/common_controller_test.go new file mode 100644 index 00000000000..58f823b8bc5 --- /dev/null +++ b/controllers/common_controller_test.go @@ -0,0 +1,109 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" + + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" + "sigs.k8s.io/cluster-api-provider-azure/internal/test" + "sigs.k8s.io/cluster-api-provider-azure/internal/test/logentries" + "sigs.k8s.io/cluster-api-provider-azure/internal/test/record" +) + +var ( + clusterControllers = []string{ + "AzureCluster", + } + + infraControllers = []string{ + "AzureMachine", + } +) + +var _ = Describe("CommonReconcilerBehaviors", func() { + BeforeEach(func() {}) + AfterEach(func() {}) + + It("should trigger reconciliation if cluster is unpaused", func() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + logListener := record.NewListener(testEnv.LogRecorder) + del := logListener.Listen() + defer del() + + clusterName := test.RandomName("foo", 10) + azClusterName := test.RandomName("foo", 10) + cluster := &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: "default", + }, + Spec: clusterv1.ClusterSpec{ + InfrastructureRef: &corev1.ObjectReference{ + Name: azClusterName, + Namespace: "default", + Kind: "AzureCluster", + APIVersion: infrav1.GroupVersion.Identifier(), + }, + }, + } + + Expect(testEnv.Create(ctx, cluster)).To(Succeed()) + defer func() { + err := testEnv.Delete(ctx, cluster) + Expect(err).NotTo(HaveOccurred()) + }() + + cluster.Status.InfrastructureReady = true + Expect(testEnv.Status().Update(ctx, cluster)).To(Succeed()) + ec := logentries.EntryCriteria{ + ClusterName: cluster.Name, + ClusterNamespace: cluster.Namespace, + InfraControllers: infraControllers, + ClusterControllers: clusterControllers, + } + + logNotPausedEntries := logentries.GenerateCreateNotPausedLogEntries(ec) + // check to make sure the cluster has reconciled and is not in paused state + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logNotPausedEntries)) + + // we have tried to reconcile, and cluster was not paused + // now, we will pause the cluster and we should trigger a watch event + cluster.Spec.Paused = true + Expect(testEnv.Update(ctx, cluster)).To(Succeed()) + logPausedEntry := logentries.GenerateUpdatePausedClusterLogEntries(ec) + // check to make sure the cluster has reconciled and is paused + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logPausedEntry)) + + // cluster was paused with an update + // now, we will unpause the cluster and we should trigger an unpause watch event for all controllers + cluster.Spec.Paused = false + Expect(testEnv.Update(ctx, cluster)).To(Succeed()) + logUpdatePausedEntries := logentries.GenerateUpdatePausedClusterLogEntries(ec) + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logUpdatePausedEntries)) + }) + +}) diff --git a/exp/controllers/azuremachinepool_controller.go b/exp/controllers/azuremachinepool_controller.go index fe175ecc07f..4f0cc541947 100644 --- a/exp/controllers/azuremachinepool_controller.go +++ b/exp/controllers/azuremachinepool_controller.go @@ -81,8 +81,9 @@ type ( ) func (r *AzureMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureMachinePool") // create mapper to transform incoming AzureClusters into AzureMachinePool requests - azureClusterMapper, err := AzureClusterToAzureMachinePoolsMapper(r.Client, mgr.GetScheme(), r.Log) + azureClusterMapper, err := AzureClusterToAzureMachinePoolsMapper(r.Client, mgr.GetScheme(), log) if err != nil { return errors.Wrapf(err, "failed to create AzureCluster to AzureMachinePools mapper") } @@ -90,12 +91,12 @@ func (r *AzureMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, options c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(&infrav1exp.AzureMachinePool{}). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused // watch for changes in CAPI MachinePool resources Watches( &source.Kind{Type: &capiv1exp.MachinePool{}}, &handler.EnqueueRequestsFromMapFunc{ - ToRequests: machinePoolToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind("AzureMachinePool"), r.Log), + ToRequests: machinePoolToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind("AzureMachinePool"), log), }, ). // watch for changes in AzureCluster resources @@ -120,7 +121,7 @@ func (r *AzureMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, options &handler.EnqueueRequestsFromMapFunc{ ToRequests: azureMachinePoolMapper, }, - predicates.ClusterUnpausedAndInfrastructureReady(r.Log), + predicates.ClusterUnpausedAndInfrastructureReady(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/exp/controllers/azuremachinepool_controller_test.go b/exp/controllers/azuremachinepool_controller_test.go index 8a8cc776971..15995affa2b 100644 --- a/exp/controllers/azuremachinepool_controller_test.go +++ b/exp/controllers/azuremachinepool_controller_test.go @@ -19,7 +19,6 @@ package controllers import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -35,7 +34,7 @@ var _ = Describe("AzureMachinePoolReconciler", func() { Context("Reconcile an AzureMachinePool", func() { It("should not error with minimal set up", func() { reconciler := &AzureMachinePoolReconciler{ - Client: k8sClient, + Client: testEnv, Log: log.Log, } By("Calling reconcile") diff --git a/exp/controllers/azuremachinepool_controller_unit_test.go b/exp/controllers/azuremachinepool_controller_unit_test.go index 1ac78297536..81c7ed607f9 100644 --- a/exp/controllers/azuremachinepool_controller_unit_test.go +++ b/exp/controllers/azuremachinepool_controller_unit_test.go @@ -37,7 +37,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/cloud/scope" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/scalesets/mock_scalesets" infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha3" - "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers" + gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" "sigs.k8s.io/cluster-api-provider-azure/internal/test/mock_log" ) @@ -134,7 +134,7 @@ func Test_azureClusterToAzureMachinePoolsFunc(t *testing.T) { mockCtrl := gomock.NewController(t) log := mock_log.NewMockLogger(mockCtrl) kClient := fake.NewFakeClientWithScheme(newScheme(g)) - log.EXPECT().Error(matchers.ErrStrEq("expected a AzureCluster but got a *v1alpha3.MachinePool"), "failed to get AzureCluster") + log.EXPECT().Error(gomockinternal.ErrStrEq("expected a AzureCluster but got a *v1alpha3.MachinePool"), "failed to get AzureCluster") return log, mockCtrl, kClient }, diff --git a/exp/controllers/azuremanagedcluster_controller.go b/exp/controllers/azuremanagedcluster_controller.go index 1c2b28c8feb..411dd2d2934 100644 --- a/exp/controllers/azuremanagedcluster_controller.go +++ b/exp/controllers/azuremanagedcluster_controller.go @@ -50,11 +50,12 @@ type AzureManagedClusterReconciler struct { } func (r *AzureManagedClusterReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureManagedCluster") azManagedCluster := &infrav1exp.AzureManagedCluster{} c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(azManagedCluster). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused Build(r) if err != nil { return errors.Wrapf(err, "error creating controller") @@ -64,9 +65,9 @@ func (r *AzureManagedClusterReconciler) SetupWithManager(mgr ctrl.Manager, optio if err = c.Watch( &source.Kind{Type: &clusterv1.Cluster{}}, &handler.EnqueueRequestsFromMapFunc{ - ToRequests: util.ClusterToInfrastructureMapFunc(azManagedCluster.GroupVersionKind()), + ToRequests: util.ClusterToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind("AzureManagedCluster")), }, - predicates.ClusterUnpaused(r.Log), + predicates.ClusterUnpaused(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/exp/controllers/azuremanagedmachinepool_controller.go b/exp/controllers/azuremanagedmachinepool_controller.go index 41e9db523e9..723d7bd3882 100644 --- a/exp/controllers/azuremanagedmachinepool_controller.go +++ b/exp/controllers/azuremanagedmachinepool_controller.go @@ -53,9 +53,10 @@ type AzureManagedMachinePoolReconciler struct { } func (r *AzureManagedMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureManagedMachinePool") azManagedMachinePool := &infrav1exp.AzureManagedMachinePool{} // create mapper to transform incoming AzureManagedClusters into AzureManagedMachinePool requests - azureManagedClusterMapper, err := AzureManagedClusterToAzureManagedMachinePoolsMapper(r.Client, mgr.GetScheme(), r.Log) + azureManagedClusterMapper, err := AzureManagedClusterToAzureManagedMachinePoolsMapper(r.Client, mgr.GetScheme(), log) if err != nil { return errors.Wrapf(err, "failed to create AzureManagedCluster to AzureManagedMachinePools mapper") } @@ -63,7 +64,7 @@ func (r *AzureManagedMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, o c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(azManagedMachinePool). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused // watch for changes in CAPI MachinePool resources Watches( &source.Kind{Type: &clusterv1exp.MachinePool{}}, @@ -87,9 +88,9 @@ func (r *AzureManagedMachinePoolReconciler) SetupWithManager(mgr ctrl.Manager, o if err = c.Watch( &source.Kind{Type: &clusterv1.Cluster{}}, &handler.EnqueueRequestsFromMapFunc{ - ToRequests: util.ClusterToInfrastructureMapFunc(azManagedMachinePool.GroupVersionKind()), + ToRequests: util.ClusterToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind("AzureManagedMachinePool")), }, - predicates.ClusterUnpausedAndInfrastructureReady(r.Log), + predicates.ClusterUnpausedAndInfrastructureReady(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/exp/controllers/azuremangedcontrolplane_controller.go b/exp/controllers/azuremangedcontrolplane_controller.go index 65ba44bc8fd..534740bb2b6 100644 --- a/exp/controllers/azuremangedcontrolplane_controller.go +++ b/exp/controllers/azuremangedcontrolplane_controller.go @@ -52,9 +52,10 @@ type AzureManagedControlPlaneReconciler struct { } func (r *AzureManagedControlPlaneReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { + log := r.Log.WithValues("controller", "AzureManagedControlPlane") azManagedControlPlane := &infrav1exp.AzureManagedControlPlane{} // create mapper to transform incoming AzureManagedClusters into AzureManagedControlPlane requests - azureManagedClusterMapper, err := AzureManagedClusterToAzureManagedControlPlaneMapper(r.Client, r.Log) + azureManagedClusterMapper, err := AzureManagedClusterToAzureManagedControlPlaneMapper(r.Client, log) if err != nil { return errors.Wrapf(err, "failed to create AzureManagedCluster to AzureManagedControlPlane mapper") } @@ -62,7 +63,7 @@ func (r *AzureManagedControlPlaneReconciler) SetupWithManager(mgr ctrl.Manager, c, err := ctrl.NewControllerManagedBy(mgr). WithOptions(options). For(azManagedControlPlane). - WithEventFilter(predicates.ResourceNotPaused(r.Log)). // don't queue reconcile if resource is paused + WithEventFilter(predicates.ResourceNotPaused(log)). // don't queue reconcile if resource is paused // watch AzureManagedCluster resources Watches( &source.Kind{Type: &infrav1exp.AzureManagedCluster{}}, @@ -79,9 +80,9 @@ func (r *AzureManagedControlPlaneReconciler) SetupWithManager(mgr ctrl.Manager, if err = c.Watch( &source.Kind{Type: &clusterv1.Cluster{}}, &handler.EnqueueRequestsFromMapFunc{ - ToRequests: util.ClusterToInfrastructureMapFunc(azManagedControlPlane.GroupVersionKind()), + ToRequests: util.ClusterToInfrastructureMapFunc(infrav1exp.GroupVersion.WithKind("AzureManagedControlPlane")), }, - predicates.ClusterUnpausedAndInfrastructureReady(r.Log), + predicates.ClusterUnpausedAndInfrastructureReady(log), ); err != nil { return errors.Wrapf(err, "failed adding a watch for ready clusters") } diff --git a/exp/controllers/common_controller_test.go b/exp/controllers/common_controller_test.go new file mode 100644 index 00000000000..10dc26b6c9a --- /dev/null +++ b/exp/controllers/common_controller_test.go @@ -0,0 +1,109 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 controllers + +import ( + "context" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" + + infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha3" + "sigs.k8s.io/cluster-api-provider-azure/internal/test" + "sigs.k8s.io/cluster-api-provider-azure/internal/test/logentries" + "sigs.k8s.io/cluster-api-provider-azure/internal/test/record" +) + +var ( + clusterControllers = []string{ + "AzureManagedCluster", + } + + infraControllers = []string{ + "AzureMachinePool", + "AzureManagedControlPlane", + "AzureManagedMachinePool", + } +) + +var _ = Describe("CommonReconcilerBehaviors", func() { + BeforeEach(func() {}) + AfterEach(func() {}) + + It("should trigger reconciliation if cluster is unpaused", func() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + logListener := record.NewListener(testEnv.LogRecorder) + del := logListener.Listen() + defer del() + + clusterName := test.RandomName("foo", 10) + azManagedClusterName := test.RandomName("foo", 10) + cluster := &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: "default", + }, + Spec: clusterv1.ClusterSpec{ + InfrastructureRef: &corev1.ObjectReference{ + Name: azManagedClusterName, + Namespace: "default", + Kind: "AzureManagedCluster", + APIVersion: infrav1exp.GroupVersion.Identifier(), + }, + }, + } + Expect(testEnv.Create(ctx, cluster)).To(Succeed()) + defer func() { + err := testEnv.Delete(ctx, cluster) + Expect(err).NotTo(HaveOccurred()) + }() + + cluster.Status.InfrastructureReady = true + Expect(testEnv.Status().Update(ctx, cluster)).To(Succeed()) + ec := logentries.EntryCriteria{ + ClusterName: cluster.Name, + ClusterNamespace: cluster.Namespace, + InfraControllers: infraControllers, + ClusterControllers: clusterControllers, + } + logNotPausedEntries := logentries.GenerateCreateNotPausedLogEntries(ec) + // check to make sure the cluster has reconciled and is not in paused state + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logNotPausedEntries)) + + // we have tried to reconcile, and cluster was not paused + // now, we will pause the cluster and we should trigger a watch event + cluster.Spec.Paused = true + Expect(testEnv.Update(ctx, cluster)).To(Succeed()) + logPausedEntries := logentries.GenerateUpdatePausedClusterLogEntries(ec) + // check to make sure the cluster has reconciled and is paused + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logPausedEntries)) + + // cluster was paused with an update + // now, we will unpause the cluster and we should trigger an unpause watch event for all controllers + cluster.Spec.Paused = false + Expect(testEnv.Update(ctx, cluster)).To(Succeed()) + logUnpausedEntries := logentries.GenerateUpdateUnpausedClusterLogEntries(ec) + Eventually(logListener.GetEntries, test.DefaultEventualTimeout, 1*time.Second).Should(ContainElements(logUnpausedEntries)) + }) + +}) diff --git a/exp/controllers/suite_test.go b/exp/controllers/suite_test.go index 30641a6f7b6..b457e42df2c 100644 --- a/exp/controllers/suite_test.go +++ b/exp/controllers/suite_test.go @@ -17,39 +17,25 @@ limitations under the License. package controllers import ( - "path/filepath" + "context" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "k8s.io/klog" - "k8s.io/klog/klogr" - clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/envtest/printer" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" - infrav1exp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1alpha3" + "sigs.k8s.io/cluster-api-provider-azure/internal/test/env" // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func init() { - klog.InitFlags(nil) - klog.SetOutput(GinkgoWriter) - logf.SetLogger(klogr.New()) -} +var ( + testEnv *env.TestEnvironment +) func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) @@ -61,32 +47,54 @@ func TestAPIs(t *testing.T) { var _ = BeforeSuite(func(done Done) { By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "config", "crd", "bases"), - }, - } - - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - Expect(clusterv1.AddToScheme(scheme.Scheme)).To(Succeed()) - Expect(infrav1.AddToScheme(scheme.Scheme)).To(Succeed()) - Expect(infrav1exp.AddToScheme(scheme.Scheme)).To(Succeed()) + testEnv = env.NewTestEnvironment() + + Expect((&AzureManagedClusterReconciler{ + Client: testEnv, + Log: testEnv.Log, + Recorder: testEnv.GetEventRecorderFor("azuremanagedcluster-reconciler"), + }).SetupWithManager(testEnv.Manager, controller.Options{MaxConcurrentReconciles: 1})).To(Succeed()) + + Expect((&AzureManagedControlPlaneReconciler{ + Client: testEnv, + Log: testEnv.Log, + Recorder: testEnv.GetEventRecorderFor("azuremanagedcontrolplane-reconciler"), + }).SetupWithManager(testEnv.Manager, controller.Options{MaxConcurrentReconciles: 1})).To(Succeed()) + + Expect((&AzureManagedMachinePoolReconciler{ + Client: testEnv, + Log: testEnv.Log, + Recorder: testEnv.GetEventRecorderFor("azuremanagedmachinepool-reconciler"), + }).SetupWithManager(testEnv.Manager, controller.Options{MaxConcurrentReconciles: 1})).To(Succeed()) + + Expect((&AzureMachinePoolReconciler{ + Client: testEnv, + Log: testEnv.Log, + Recorder: testEnv.GetEventRecorderFor("azuremachinepool-reconciler"), + }).SetupWithManager(testEnv.Manager, controller.Options{MaxConcurrentReconciles: 1})).To(Succeed()) // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) + By("starting the manager") + go func() { + defer GinkgoRecover() + Expect(testEnv.StartManager()).To(Succeed()) + }() + + Eventually(func() bool { + nodes := &v1.NodeList{} + if err := testEnv.Client.List(context.Background(), nodes); err != nil { + return false + } + return true + }).Should(BeTrue()) close(done) }, 60) var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) + if testEnv != nil { + By("tearing down the test environment") + Expect(testEnv.Stop()).To(Succeed()) + } }) diff --git a/internal/test/logentries/pause_events.go b/internal/test/logentries/pause_events.go new file mode 100644 index 00000000000..74faa71ec63 --- /dev/null +++ b/internal/test/logentries/pause_events.go @@ -0,0 +1,165 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 logentries + +import ( + "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomega" +) + +type ( + EntryCriteria struct { + ClusterNamespace string + ClusterName string + InfraControllers []string + ClusterControllers []string + } +) + +func GenerateCreateNotPausedLogEntries(ec EntryCriteria) []gomega.LogMatcher { + infraEntries := make([]gomega.LogMatcher, len(ec.InfraControllers)) + for i, c := range ec.InfraControllers { + c := c + infraEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpausedAndInfrastructureReady", + "predicate", + "ClusterCreateNotPaused", + "eventType", + "create", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster is not paused, allowing further processing", + ).WithLevel(4).WithLogFunc("Info") + } + + clusterEntries := make([]gomega.LogMatcher, len(ec.ClusterControllers)) + for i, c := range ec.ClusterControllers { + c := c + clusterEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpaused", + "predicate", + "ClusterCreateNotPaused", + "eventType", + "create", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster is not paused, allowing further processing", + ).WithLevel(4).WithLogFunc("Info") + } + return append(clusterEntries, infraEntries...) +} + +func GenerateUpdatePausedClusterLogEntries(ec EntryCriteria) []gomega.LogMatcher { + infraEntries := make([]gomega.LogMatcher, len(ec.InfraControllers)) + for i, c := range ec.InfraControllers { + c := c + infraEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpausedAndInfrastructureReady", + "predicate", + "ClusterUpdateUnpaused", + "eventType", + "update", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster was not unpaused, blocking further processing", + ).WithLevel(4).WithLogFunc("Info") + } + + clusterEntries := make([]gomega.LogMatcher, len(ec.ClusterControllers)) + for i, c := range ec.ClusterControllers { + c := c + clusterEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpaused", + "predicate", + "ClusterUpdateUnpaused", + "eventType", + "update", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster was not unpaused, blocking further processing", + ).WithLevel(4).WithLogFunc("Info") + } + return append(clusterEntries, infraEntries...) +} + +func GenerateUpdateUnpausedClusterLogEntries(ec EntryCriteria) []gomega.LogMatcher { + infraEntries := make([]gomega.LogMatcher, len(ec.InfraControllers)) + for i, c := range ec.InfraControllers { + c := c + infraEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpausedAndInfrastructureReady", + "predicate", + "ClusterUpdateUnpaused", + "eventType", + "update", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster was unpaused, allowing further processing", + ).WithLevel(4).WithLogFunc("Info") + } + + clusterEntries := make([]gomega.LogMatcher, len(ec.ClusterControllers)) + for i, c := range ec.ClusterControllers { + c := c + clusterEntries[i] = gomega.LogContains( + "controller", + c, + "predicate", + "ClusterUnpaused", + "predicate", + "ClusterUpdateUnpaused", + "eventType", + "update", + "namespace", + ec.ClusterNamespace, + "cluster", + ec.ClusterName, + "msg", + "Cluster was unpaused, allowing further processing", + ).WithLevel(4).WithLogFunc("Info") + } + return append(clusterEntries, infraEntries...) +} diff --git a/internal/test/matchers/gomega/matchers.go b/internal/test/matchers/gomega/matchers.go new file mode 100644 index 00000000000..cbd01fcc1e0 --- /dev/null +++ b/internal/test/matchers/gomega/matchers.go @@ -0,0 +1,105 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 gomega + +import ( + "fmt" + "strings" + + "github.com/onsi/gomega/matchers" + "github.com/onsi/gomega/types" + + "sigs.k8s.io/cluster-api-provider-azure/internal/test/record" +) + +type ( + logEntryMactcher struct { + level *int + logFunc *string + values []interface{} + } + + LogMatcher interface { + types.GomegaMatcher + WithLevel(int) LogMatcher + WithLogFunc(string) LogMatcher + } +) + +func LogContains(values ...interface{}) LogMatcher { + return &logEntryMactcher{ + values: values, + } +} + +func (l *logEntryMactcher) WithLevel(level int) LogMatcher { + l.level = &level + return l +} + +func (l *logEntryMactcher) WithLogFunc(logFunc string) LogMatcher { + l.logFunc = &logFunc + return l +} + +func (l *logEntryMactcher) Match(actual interface{}) (bool, error) { + logEntry, ok := actual.(record.LogEntry) + if !ok { + return false, fmt.Errorf("LogContains matcher expects an record.LogEntry") + } + return len(l.validate(logEntry)) == 0, nil +} + +func (l *logEntryMactcher) FailureMessage(actual interface{}) string { + return failMessage(l.validate(actual)) +} + +func (l *logEntryMactcher) NegatedFailureMessage(actual interface{}) string { + return failMessage(l.validate(actual)) +} + +func (l *logEntryMactcher) validate(actual interface{}) []error { + logEntry, ok := actual.(record.LogEntry) + if !ok { + return []error{fmt.Errorf("expected record.LogEntry, but got %T", actual)} + } + + var errs []error + containsValues := matchers.ContainElementsMatcher{Elements: l.values} + ok, err := containsValues.Match(logEntry.Values) + if err != nil || !ok { + errs = append(errs, fmt.Errorf("actual log values %q didn't match expected %q", logEntry.Values, l.values)) + } + + if l.logFunc != nil && *l.logFunc != logEntry.LogFunc { + errs = append(errs, fmt.Errorf("actual log Func %q didn't match expected %q", logEntry.LogFunc, *l.logFunc)) + } + + if l.level != nil && *l.level != logEntry.Level { + errs = append(errs, fmt.Errorf("actual log level %q didn't match expected %q", logEntry.Level, *l.level)) + } + + return errs +} + +func failMessage(errs []error) string { + errMsgs := make([]string, len(errs)) + for i, err := range errs { + errMsgs[i] = err.Error() + } + return fmt.Sprintf("LogEntry errors: %s", strings.Join(errMsgs, ", ")) +} diff --git a/internal/test/matchers/gomega/matchers_test.go b/internal/test/matchers/gomega/matchers_test.go new file mode 100644 index 00000000000..7a076b829d5 --- /dev/null +++ b/internal/test/matchers/gomega/matchers_test.go @@ -0,0 +1,148 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 gomega + +import ( + "testing" + + "github.com/onsi/gomega" + + "sigs.k8s.io/cluster-api-provider-azure/internal/test/record" +) + +var ( + defaultLogEntry = record.LogEntry{ + Values: []interface{}{ + "foo", + "bin", + "bax", + }, + LogFunc: "Info", + Level: 2, + } +) + +func TestLogContains(t *testing.T) { + cases := []struct { + Name string + LogEntry record.LogEntry + Matcher LogMatcher + ShouldMatch bool + }{ + { + Name: "MatchesCompletely", + LogEntry: defaultLogEntry, + Matcher: LogContains( + "foo", + "bin", + "bax").WithLevel(2).WithLogFunc("Info"), + ShouldMatch: true, + }, + { + Name: "MatchesWithoutSpecifyingLevel", + LogEntry: defaultLogEntry, + Matcher: LogContains( + "foo", + "bin", + "bax").WithLogFunc("Info"), + ShouldMatch: true, + }, + { + Name: "MatchesWithoutSpecifyingLogFunc", + LogEntry: defaultLogEntry, + Matcher: LogContains( + "foo", + "bin", + "bax"), + ShouldMatch: true, + }, + { + Name: "MatchesWithoutSpecifyingAllValues", + LogEntry: defaultLogEntry, + Matcher: LogContains("foo", "bax"), + ShouldMatch: true, + }, + } + + for _, c := range cases { + c := c + t.Run(c.Name, func(t *testing.T) { + t.Parallel() + g := gomega.NewWithT(t) + success, err := c.Matcher.Match(c.LogEntry) + g.Expect(err).NotTo(gomega.HaveOccurred()) + g.Expect(success).To(gomega.Equal(c.ShouldMatch)) + }) + } +} + +func TestLogContainsEntries(t *testing.T) { + entries := []record.LogEntry{ + defaultLogEntry, + { + Values: []interface{}{ + "controller", + "AzureCluster", + "predicate", + "ClusterUnpaused", + "predicate", + "ClusterCreateNotPaused", + "eventType", + "create", + "namespace", + "default", + "cluster", + "foo-52824hhgdv", + "eventType", + "create", + "namespace", + "default", + "cluster", + "cluster-xxnmwzz2wz", + "eventType", + "create", + "namespace", + "default", + "cluster", + "foo-zljvddw5c2", + "msg", + "Cluster is not paused, allowing further processing", + }, + LogFunc: "Error", + Level: 4, + }, + } + + g := gomega.NewWithT(t) + g.Expect(entries).To(gomega.ContainElements([]LogMatcher{ + LogContains("bin"), + LogContains("controller", + "AzureCluster", + "predicate", + "ClusterUnpaused", + "predicate", + "ClusterCreateNotPaused", + "eventType", + "create", + "namespace", + "default", + "cluster", + "foo-zljvddw5c2", + "msg", + "Cluster is not paused, allowing further processing"), + })) +} diff --git a/internal/test/matchers/matchers.go b/internal/test/matchers/gomock/matchers.go similarity index 86% rename from internal/test/matchers/matchers.go rename to internal/test/matchers/gomock/matchers.go index a6cd946f4dd..9458b9e43a5 100644 --- a/internal/test/matchers/matchers.go +++ b/internal/test/matchers/gomock/matchers.go @@ -14,13 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -package matchers +package gomock import ( "fmt" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" + "github.com/onsi/gomega/types" ) type ( @@ -33,6 +34,19 @@ type ( expected string actual string } + + logEntryMactcher struct { + level int + logFunc string + values []interface{} + errors []error + } + + LogMatcher interface { + types.GomegaMatcher + WithLevel(int) LogMatcher + WithLogFunc(string) LogMatcher + } ) // DiffEq will verify cmp.Diff(expected, actual) == "" using github.com/google/go-cmp/cmp diff --git a/internal/test/record/logger.go b/internal/test/record/logger.go index af421f35148..bb8993041e8 100644 --- a/internal/test/record/logger.go +++ b/internal/test/record/logger.go @@ -71,6 +71,8 @@ func NewLogger(options ...Option) *Logger { return l } +var _ logr.Logger = (*Logger)(nil) + type ( // Logger defines a test friendly logr.Logger Logger struct { @@ -82,6 +84,7 @@ type ( listeners map[string]*Listener writer io.Writer root *Logger + cloneMu sync.Mutex } Listener struct { @@ -139,22 +142,17 @@ func (l *Logger) removeListener(id string) { delete(l.listeners, id) } -// Enabled tests whether this Logger is enabled. +// Enabled is always enabled func (l *Logger) Enabled() bool { - if l.threshold == nil { - return true - } - return l.level <= *l.threshold + return true } // Info logs a non-error message with the given key/value pairs as context. func (l *Logger) Info(msg string, kvs ...interface{}) { - if l.Enabled() { - values := copySlice(l.values) - values = append(values, kvs...) - values = append(values, "msg", msg) - l.write("Info", values) - } + values := copySlice(l.values) + values = append(values, kvs...) + values = append(values, "msg", msg) + l.write("Info", values) } // Error logs an error message with the given key/value pairs as context. @@ -194,7 +192,7 @@ func (l *Logger) write(logFunc string, values []interface{}) { Prefix: l.prefix, LogFunc: logFunc, Level: l.level, - Values: values, + Values: copySlice(values), } f, err := flatten(entry) if err != nil { @@ -229,6 +227,9 @@ func (l *Logger) writeToListeners(entry LogEntry) { } func (l *Logger) clone() *Logger { + l.cloneMu.Lock() + defer l.cloneMu.Unlock() + root := l.root if root == nil { root = l diff --git a/internal/test/test.go b/internal/test/test.go index d27419e6156..05b50fa761c 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -18,10 +18,15 @@ package test import ( "fmt" + "time" "k8s.io/apimachinery/pkg/util/rand" ) +const ( + DefaultEventualTimeout = 20 * time.Second +) + // RandomName will generate a random name "{prefix}-{rand(len)}" func RandomName(prefix string, len int) string { return fmt.Sprintf("%s-%s", prefix, rand.String(len))