sigs.k8s.io/cluster-api-provider-azure@v1.14.3/azure/services/bastionhosts/spec_test.go (about)

     1  /*
     2  Copyright 2023 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bastionhosts
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	asonetworkv1 "github.com/Azure/azure-service-operator/v2/api/network/v1api20220701"
    25  	"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
    26  	. "github.com/onsi/gomega"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/utils/ptr"
    29  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    30  )
    31  
    32  var (
    33  	fakeSKU         = asonetworkv1.Sku_Name("fake-SKU")
    34  	fakeBastionHost = asonetworkv1.BastionHost{
    35  		Spec: asonetworkv1.BastionHost_Spec{
    36  			Location:  ptr.To(fakeAzureBastionSpec1.Location),
    37  			AzureName: fakeAzureBastionSpec1.Name,
    38  			DnsName:   ptr.To(fakeAzureBastionSpec1.Name + "-bastion"),
    39  			Sku:       &asonetworkv1.Sku{Name: &fakeSKU},
    40  			Owner: &genruntime.KnownResourceReference{
    41  				Name: fakeAzureBastionSpec1.ResourceGroup,
    42  			},
    43  			Tags:            fakeBastionHostTags,
    44  			EnableTunneling: ptr.To(false),
    45  			IpConfigurations: []asonetworkv1.BastionHostIPConfiguration{
    46  				{
    47  					Name: ptr.To(fmt.Sprintf("%s-%s", fakeAzureBastionSpec1.Name, "bastionIP")),
    48  					Subnet: &asonetworkv1.BastionHostSubResource{
    49  						Reference: &genruntime.ResourceReference{
    50  							ARMID: fakeAzureBastionSpec1.SubnetID,
    51  						},
    52  					},
    53  					PublicIPAddress: &asonetworkv1.BastionHostSubResource{
    54  						Reference: &genruntime.ResourceReference{
    55  							ARMID: fakeAzureBastionSpec1.PublicIPID,
    56  						},
    57  					},
    58  					PrivateIPAllocationMethod: ptr.To(asonetworkv1.IPAllocationMethod_Dynamic),
    59  				},
    60  			},
    61  		},
    62  	}
    63  	fakeAzureBastionSpec1 = AzureBastionSpec{
    64  		Name:            "my-bastion-host",
    65  		ClusterName:     "cluster",
    66  		Location:        "westus",
    67  		SubnetID:        "my-subnet-id",
    68  		PublicIPID:      "my-public-ip-id",
    69  		Sku:             infrav1.BastionHostSkuName("fake-SKU"),
    70  		EnableTunneling: false,
    71  	}
    72  
    73  	fakeBastionHostStatus = asonetworkv1.BastionHost_STATUS{
    74  		Name:              ptr.To(fakeAzureBastionSpec1.Name),
    75  		ProvisioningState: ptr.To(asonetworkv1.BastionHostProvisioningState_STATUS_Succeeded),
    76  	}
    77  
    78  	fakeBastionHostTags = map[string]string{
    79  		"sigs.k8s.io_cluster-api-provider-azure_cluster_cluster": "owned",
    80  		"sigs.k8s.io_cluster-api-provider-azure_role":            "Bastion",
    81  		"Name": fakeAzureBastionSpec1.Name,
    82  	}
    83  )
    84  
    85  func getASOBastionHost(changes ...func(*asonetworkv1.BastionHost)) *asonetworkv1.BastionHost {
    86  	BastionHost := fakeBastionHost.DeepCopy()
    87  	for _, change := range changes {
    88  		change(BastionHost)
    89  	}
    90  	return BastionHost
    91  }
    92  
    93  func TestAzureBastionSpec_Parameters(t *testing.T) {
    94  	testcases := []struct {
    95  		name          string
    96  		spec          *AzureBastionSpec
    97  		existing      *asonetworkv1.BastionHost
    98  		expect        func(g *WithT, result asonetworkv1.BastionHost)
    99  		expectedError string
   100  	}{
   101  		{
   102  			name:     "Creating a new BastionHost",
   103  			spec:     &fakeAzureBastionSpec1,
   104  			existing: nil,
   105  			expect: func(g *WithT, result asonetworkv1.BastionHost) {
   106  				g.Expect(result).To(Not(BeNil()))
   107  
   108  				// ObjectMeta is populated later in the codeflow
   109  				g.Expect(result.ObjectMeta).To(Equal(metav1.ObjectMeta{}))
   110  
   111  				// Spec is populated from the spec passed in
   112  				g.Expect(result.Spec).To(Equal(getASOBastionHost().Spec))
   113  			},
   114  		},
   115  		{
   116  			name: "user updates to bastion hosts DisableCopyPaste should be accepted",
   117  			spec: &fakeAzureBastionSpec1,
   118  			existing: getASOBastionHost(
   119  				// user added DisableCopyPaste
   120  				func(bastion *asonetworkv1.BastionHost) {
   121  					bastion.Spec.DisableCopyPaste = ptr.To(true)
   122  				},
   123  				// user added Status
   124  				func(bastion *asonetworkv1.BastionHost) {
   125  					bastion.Status = fakeBastionHostStatus
   126  				},
   127  			),
   128  			expect: func(g *WithT, result asonetworkv1.BastionHost) {
   129  				g.Expect(result).To(Not(BeNil()))
   130  				resultantASOBastionHost := getASOBastionHost(
   131  					func(bastion *asonetworkv1.BastionHost) {
   132  						bastion.Spec.DisableCopyPaste = ptr.To(true)
   133  					},
   134  				)
   135  
   136  				// ObjectMeta should be carried over from existing bastion host.
   137  				g.Expect(result.ObjectMeta).To(Equal(resultantASOBastionHost.ObjectMeta))
   138  
   139  				// DisableCopyPaste addition is accepted.
   140  				g.Expect(result.Spec).To(Equal(resultantASOBastionHost.Spec))
   141  
   142  				// Status should be carried over.
   143  				g.Expect(result.Status).To(Equal(fakeBastionHostStatus))
   144  			},
   145  		},
   146  		{
   147  			name: "user updates to ASO's bastion hosts resource and capz should overwrite it",
   148  			spec: &fakeAzureBastionSpec1,
   149  			existing: getASOBastionHost(
   150  				// user added DisableCopyPaste
   151  				func(bastion *asonetworkv1.BastionHost) {
   152  					bastion.Spec.DisableCopyPaste = ptr.To(true)
   153  				},
   154  
   155  				// user also added EnableTunneling which should be overwritten by capz
   156  				func(bastion *asonetworkv1.BastionHost) {
   157  					bastion.Spec.EnableTunneling = ptr.To(true)
   158  				},
   159  				// user added Status
   160  				func(bastion *asonetworkv1.BastionHost) {
   161  					bastion.Status = fakeBastionHostStatus
   162  				},
   163  			),
   164  			expect: func(g *WithT, result asonetworkv1.BastionHost) {
   165  				g.Expect(result).To(Not(BeNil()))
   166  				resultantASOBastionHost := getASOBastionHost(
   167  					func(endpoint *asonetworkv1.BastionHost) {
   168  						endpoint.Spec.DisableCopyPaste = ptr.To(true)
   169  					},
   170  				)
   171  
   172  				// user changes except DisableCopyPaste should be overwritten.
   173  				g.Expect(result.ObjectMeta).To(Equal(resultantASOBastionHost.ObjectMeta))
   174  
   175  				// DisableCopyPaste addition is accepted.
   176  				g.Expect(result.Spec).To(Equal(resultantASOBastionHost.Spec))
   177  
   178  				// Status should be carried over.
   179  				g.Expect(result.Status).To(Equal(fakeBastionHostStatus))
   180  			},
   181  		},
   182  	}
   183  
   184  	for _, tc := range testcases {
   185  		tc := tc
   186  		t.Run(tc.name, func(t *testing.T) {
   187  			g := NewWithT(t)
   188  			t.Parallel()
   189  
   190  			result, err := tc.spec.Parameters(context.TODO(), tc.existing)
   191  			if tc.expectedError != "" {
   192  				g.Expect(err).To(HaveOccurred())
   193  				g.Expect(err).To(MatchError(tc.expectedError))
   194  			} else {
   195  				g.Expect(err).NotTo(HaveOccurred())
   196  			}
   197  			tc.expect(g, *result)
   198  		})
   199  	}
   200  }