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

     1  /*
     2  Copyright 2022 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 securitygroups
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4"
    24  	. "github.com/onsi/gomega"
    25  	"k8s.io/utils/ptr"
    26  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    27  	"sigs.k8s.io/cluster-api-provider-azure/azure/converters"
    28  )
    29  
    30  var (
    31  	sshRule = infrav1.SecurityRule{
    32  		Name:             "allow_ssh",
    33  		Description:      "Allow SSH",
    34  		Priority:         2200,
    35  		Protocol:         infrav1.SecurityGroupProtocolTCP,
    36  		Direction:        infrav1.SecurityRuleDirectionInbound,
    37  		Source:           ptr.To("*"),
    38  		SourcePorts:      ptr.To("*"),
    39  		Destination:      ptr.To("*"),
    40  		DestinationPorts: ptr.To("22"),
    41  		Action:           infrav1.SecurityRuleActionAllow,
    42  	}
    43  	otherRule = infrav1.SecurityRule{
    44  		Name:             "other_rule",
    45  		Description:      "Test Rule",
    46  		Priority:         500,
    47  		Protocol:         infrav1.SecurityGroupProtocolTCP,
    48  		Direction:        infrav1.SecurityRuleDirectionInbound,
    49  		Source:           ptr.To("*"),
    50  		SourcePorts:      ptr.To("*"),
    51  		Destination:      ptr.To("*"),
    52  		DestinationPorts: ptr.To("80"),
    53  		Action:           infrav1.SecurityRuleActionAllow,
    54  	}
    55  	customRule = infrav1.SecurityRule{
    56  		Name:             "custom_rule",
    57  		Description:      "Test Rule",
    58  		Priority:         501,
    59  		Protocol:         infrav1.SecurityGroupProtocolTCP,
    60  		Direction:        infrav1.SecurityRuleDirectionOutbound,
    61  		Source:           ptr.To("*"),
    62  		SourcePorts:      ptr.To("*"),
    63  		Destination:      ptr.To("*"),
    64  		DestinationPorts: ptr.To("80"),
    65  		Action:           infrav1.SecurityRuleActionAllow,
    66  	}
    67  	denyRule = infrav1.SecurityRule{
    68  		Name:             "deny_rule",
    69  		Description:      "Deny Rule",
    70  		Priority:         510,
    71  		Protocol:         infrav1.SecurityGroupProtocolTCP,
    72  		Direction:        infrav1.SecurityRuleDirectionOutbound,
    73  		Source:           ptr.To("*"),
    74  		SourcePorts:      ptr.To("*"),
    75  		Destination:      ptr.To("*"),
    76  		DestinationPorts: ptr.To("80"),
    77  		Action:           infrav1.SecurityRuleActionDeny,
    78  	}
    79  )
    80  
    81  func TestParameters(t *testing.T) {
    82  	testcases := []struct {
    83  		name          string
    84  		spec          *NSGSpec
    85  		existing      interface{}
    86  		expect        func(g *WithT, result interface{})
    87  		expectedError string
    88  	}{
    89  		{
    90  			name: "NSG already exists with all rules present",
    91  			spec: &NSGSpec{
    92  				Name:     "test-nsg",
    93  				Location: "test-location",
    94  				SecurityRules: infrav1.SecurityRules{
    95  					sshRule,
    96  					otherRule,
    97  				},
    98  				ResourceGroup: "test-group",
    99  				ClusterName:   "my-cluster",
   100  			},
   101  			existing: armnetwork.SecurityGroup{
   102  				Name: ptr.To("test-nsg"),
   103  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   104  					SecurityRules: []*armnetwork.SecurityRule{
   105  						converters.SecurityRuleToSDK(sshRule),
   106  						converters.SecurityRuleToSDK(otherRule),
   107  					},
   108  				},
   109  			},
   110  			expect: func(g *WithT, result interface{}) {
   111  				g.Expect(result).To(BeNil())
   112  			},
   113  		},
   114  		{
   115  			name: "NSG already exists but missing a rule",
   116  			spec: &NSGSpec{
   117  				Name:     "test-nsg",
   118  				Location: "test-location",
   119  				SecurityRules: infrav1.SecurityRules{
   120  					sshRule,
   121  					otherRule,
   122  				},
   123  				ResourceGroup: "test-group",
   124  				ClusterName:   "my-cluster",
   125  			},
   126  			existing: armnetwork.SecurityGroup{
   127  				Name:     ptr.To("test-nsg"),
   128  				Location: ptr.To("test-location"),
   129  				Etag:     ptr.To("fake-etag"),
   130  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   131  					SecurityRules: []*armnetwork.SecurityRule{
   132  						converters.SecurityRuleToSDK(sshRule),
   133  						converters.SecurityRuleToSDK(customRule),
   134  					},
   135  				},
   136  			},
   137  			expect: func(g *WithT, result interface{}) {
   138  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.SecurityGroup{}))
   139  				g.Expect(result).To(Equal(armnetwork.SecurityGroup{
   140  					Location: ptr.To("test-location"),
   141  					Etag:     ptr.To("fake-etag"),
   142  					Properties: &armnetwork.SecurityGroupPropertiesFormat{
   143  						SecurityRules: []*armnetwork.SecurityRule{
   144  							converters.SecurityRuleToSDK(otherRule),
   145  							converters.SecurityRuleToSDK(sshRule),
   146  							converters.SecurityRuleToSDK(customRule),
   147  						},
   148  					},
   149  					Tags: map[string]*string{
   150  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   151  						"Name": ptr.To("test-nsg"),
   152  					},
   153  				}))
   154  			},
   155  		},
   156  		{
   157  			name: "NSG already exists but missing a rule",
   158  			spec: &NSGSpec{
   159  				Name:     "test-nsg",
   160  				Location: "test-location",
   161  				SecurityRules: infrav1.SecurityRules{
   162  					sshRule,
   163  					otherRule,
   164  				},
   165  				ResourceGroup: "test-group",
   166  				ClusterName:   "my-cluster",
   167  			},
   168  			existing: armnetwork.SecurityGroup{
   169  				Name:     ptr.To("test-nsg"),
   170  				Location: ptr.To("test-location"),
   171  				Etag:     ptr.To("fake-etag"),
   172  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   173  					SecurityRules: []*armnetwork.SecurityRule{
   174  						converters.SecurityRuleToSDK(sshRule),
   175  						converters.SecurityRuleToSDK(denyRule),
   176  					},
   177  				},
   178  			},
   179  			expect: func(g *WithT, result interface{}) {
   180  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.SecurityGroup{}))
   181  				g.Expect(result).To(Equal(armnetwork.SecurityGroup{
   182  					Location: ptr.To("test-location"),
   183  					Etag:     ptr.To("fake-etag"),
   184  					Properties: &armnetwork.SecurityGroupPropertiesFormat{
   185  						SecurityRules: []*armnetwork.SecurityRule{
   186  							converters.SecurityRuleToSDK(otherRule),
   187  							converters.SecurityRuleToSDK(sshRule),
   188  							converters.SecurityRuleToSDK(denyRule),
   189  						},
   190  					},
   191  					Tags: map[string]*string{
   192  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   193  						"Name": ptr.To("test-nsg"),
   194  					},
   195  				}))
   196  			},
   197  		},
   198  		{
   199  			name: "NSG already exists and a rule is deleted",
   200  			spec: &NSGSpec{
   201  				Name:     "test-nsg",
   202  				Location: "test-location",
   203  				SecurityRules: infrav1.SecurityRules{
   204  					sshRule,
   205  					customRule,
   206  				},
   207  				ResourceGroup: "test-group",
   208  				ClusterName:   "my-cluster",
   209  				LastAppliedSecurityRules: map[string]interface{}{
   210  					"allow_ssh":   sshRule,
   211  					"custom_rule": customRule,
   212  					"other_rule":  otherRule,
   213  				},
   214  			},
   215  			existing: armnetwork.SecurityGroup{
   216  				Name:     ptr.To("test-nsg"),
   217  				Location: ptr.To("test-location"),
   218  				Etag:     ptr.To("fake-etag"),
   219  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   220  					SecurityRules: []*armnetwork.SecurityRule{
   221  						converters.SecurityRuleToSDK(sshRule),
   222  						converters.SecurityRuleToSDK(customRule),
   223  						converters.SecurityRuleToSDK(otherRule),
   224  					},
   225  				},
   226  			},
   227  			expect: func(g *WithT, result interface{}) {
   228  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.SecurityGroup{}))
   229  				g.Expect(result).To(Equal(armnetwork.SecurityGroup{
   230  					Location: ptr.To("test-location"),
   231  					Etag:     ptr.To("fake-etag"),
   232  					Properties: &armnetwork.SecurityGroupPropertiesFormat{
   233  						SecurityRules: []*armnetwork.SecurityRule{
   234  							converters.SecurityRuleToSDK(sshRule),
   235  							converters.SecurityRuleToSDK(customRule),
   236  						},
   237  					},
   238  					Tags: map[string]*string{
   239  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   240  						"Name": ptr.To("test-nsg"),
   241  					},
   242  				}))
   243  			},
   244  		},
   245  		{
   246  			name: "NSG already exists and a deny rule is deleted",
   247  			spec: &NSGSpec{
   248  				Name:     "test-nsg",
   249  				Location: "test-location",
   250  				SecurityRules: infrav1.SecurityRules{
   251  					sshRule,
   252  					customRule,
   253  				},
   254  				ResourceGroup: "test-group",
   255  				ClusterName:   "my-cluster",
   256  				LastAppliedSecurityRules: map[string]interface{}{
   257  					"allow_ssh":   sshRule,
   258  					"custom_rule": customRule,
   259  					"deny_rule":   denyRule,
   260  				},
   261  			},
   262  			existing: armnetwork.SecurityGroup{
   263  				Name:     ptr.To("test-nsg"),
   264  				Location: ptr.To("test-location"),
   265  				Etag:     ptr.To("fake-etag"),
   266  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   267  					SecurityRules: []*armnetwork.SecurityRule{
   268  						converters.SecurityRuleToSDK(sshRule),
   269  						converters.SecurityRuleToSDK(customRule),
   270  						converters.SecurityRuleToSDK(denyRule),
   271  					},
   272  				},
   273  			},
   274  			expect: func(g *WithT, result interface{}) {
   275  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.SecurityGroup{}))
   276  				g.Expect(result).To(Equal(armnetwork.SecurityGroup{
   277  					Location: ptr.To("test-location"),
   278  					Etag:     ptr.To("fake-etag"),
   279  					Properties: &armnetwork.SecurityGroupPropertiesFormat{
   280  						SecurityRules: []*armnetwork.SecurityRule{
   281  							converters.SecurityRuleToSDK(sshRule),
   282  							converters.SecurityRuleToSDK(customRule),
   283  						},
   284  					},
   285  					Tags: map[string]*string{
   286  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   287  						"Name": ptr.To("test-nsg"),
   288  					},
   289  				}))
   290  			},
   291  		},
   292  		{
   293  			name: "NSG already exists and a rule not owned by CAPZ is present",
   294  			spec: &NSGSpec{
   295  				Name:     "test-nsg",
   296  				Location: "test-location",
   297  				SecurityRules: infrav1.SecurityRules{
   298  					sshRule,
   299  					customRule,
   300  				},
   301  				ResourceGroup: "test-group",
   302  				ClusterName:   "my-cluster",
   303  				LastAppliedSecurityRules: map[string]interface{}{
   304  					"allow_ssh":   sshRule,
   305  					"custom_rule": customRule,
   306  				},
   307  			},
   308  			existing: armnetwork.SecurityGroup{
   309  				Name:     ptr.To("test-nsg"),
   310  				Location: ptr.To("test-location"),
   311  				Etag:     ptr.To("fake-etag"),
   312  				Properties: &armnetwork.SecurityGroupPropertiesFormat{
   313  					SecurityRules: []*armnetwork.SecurityRule{
   314  						converters.SecurityRuleToSDK(sshRule),
   315  						converters.SecurityRuleToSDK(customRule),
   316  						converters.SecurityRuleToSDK(otherRule),
   317  					},
   318  				},
   319  			},
   320  			expect: func(g *WithT, result interface{}) {
   321  				g.Expect(result).To(BeNil())
   322  			},
   323  		},
   324  		{
   325  			name: "NSG does not exist",
   326  			spec: &NSGSpec{
   327  				Name:     "test-nsg",
   328  				Location: "test-location",
   329  				SecurityRules: infrav1.SecurityRules{
   330  					sshRule,
   331  					otherRule,
   332  				},
   333  				ResourceGroup: "test-group",
   334  				ClusterName:   "my-cluster",
   335  			},
   336  			existing: nil,
   337  			expect: func(g *WithT, result interface{}) {
   338  				g.Expect(result).To(BeAssignableToTypeOf(armnetwork.SecurityGroup{}))
   339  				g.Expect(result).To(Equal(armnetwork.SecurityGroup{
   340  					Properties: &armnetwork.SecurityGroupPropertiesFormat{
   341  						SecurityRules: []*armnetwork.SecurityRule{
   342  							converters.SecurityRuleToSDK(sshRule),
   343  							converters.SecurityRuleToSDK(otherRule),
   344  						},
   345  					},
   346  					Location: ptr.To("test-location"),
   347  					Tags: map[string]*string{
   348  						"sigs.k8s.io_cluster-api-provider-azure_cluster_my-cluster": ptr.To("owned"),
   349  						"Name": ptr.To("test-nsg"),
   350  					},
   351  				}))
   352  			},
   353  		},
   354  	}
   355  
   356  	for _, tc := range testcases {
   357  		tc := tc
   358  		t.Run(tc.name, func(t *testing.T) {
   359  			g := NewWithT(t)
   360  			t.Parallel()
   361  
   362  			result, err := tc.spec.Parameters(context.TODO(), tc.existing)
   363  			if tc.expectedError != "" {
   364  				g.Expect(err).To(HaveOccurred())
   365  				g.Expect(err).To(MatchError(tc.expectedError))
   366  			} else {
   367  				g.Expect(err).NotTo(HaveOccurred())
   368  			}
   369  			tc.expect(g, result)
   370  		})
   371  	}
   372  }
   373  
   374  func TestRuleExists(t *testing.T) {
   375  	testcases := []struct {
   376  		name     string
   377  		rules    []*armnetwork.SecurityRule
   378  		rule     *armnetwork.SecurityRule
   379  		expected bool
   380  	}{
   381  		{
   382  			name:     "rule doesn't exitst",
   383  			rules:    []*armnetwork.SecurityRule{ruleA},
   384  			rule:     ruleB,
   385  			expected: false,
   386  		},
   387  		{
   388  			name:     "rule exists",
   389  			rules:    []*armnetwork.SecurityRule{ruleA, ruleB},
   390  			rule:     ruleB,
   391  			expected: true,
   392  		},
   393  		{
   394  			name:     "rule exists but has been modified",
   395  			rules:    []*armnetwork.SecurityRule{ruleA, ruleB},
   396  			rule:     ruleBModified,
   397  			expected: false,
   398  		},
   399  	}
   400  	for _, tc := range testcases {
   401  		tc := tc
   402  		t.Run(tc.name, func(t *testing.T) {
   403  			g := NewWithT(t)
   404  			t.Parallel()
   405  			result := ruleExists(tc.rules, tc.rule)
   406  			g.Expect(result).To(Equal(tc.expected))
   407  		})
   408  	}
   409  }