Skip to content

Commit

Permalink
Remove bastion security group when disabling the bastion
Browse files Browse the repository at this point in the history
We reconcile the security groups before the bastion, because the bastion
needs its security group to be created first when managed security groups are enabled.
When the bastion is disabled, we will try to delete the security group if it exists.
In the first attempt, the security group will still be in-use by the bastion instance
but then the bastion instance will be deleted in the next reconcile loop.
We do that here because we don't want to manage the bastion security group from
elsewhere, that could cause infinite loops between ReconCileSecurityGroups and ReconcileBastion.
Therefore we try to delete the bastion security group as a best effort here
and also when the cluster is deleted so we're sure it will be deleted at some point.

Also, we're trying to remove it when the cluster is deleted in case it
wasn't done before. This doesn't trigger an error if the security group
didn't exist.

Adding e2e tests to cover the scenarios:
* Disabling the bastion should reduce the total number of managed SGs to
  2.
* Re-enabling it should make it to 3 SGs.
  • Loading branch information
EmilienM committed Jun 6, 2024
1 parent a2acc63 commit 489961a
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
25 changes: 20 additions & 5 deletions pkg/cloud/services/networking/securitygroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,24 @@ func (s *Service) ReconcileSecurityGroups(openStackCluster *infrav1.OpenStackClu
workerSuffix: secWorkerGroupName,
}

secBastionGroupName := getSecBastionGroupName(clusterResourceName)
if bastionEnabled {
secBastionGroupName := getSecBastionGroupName(clusterResourceName)
suffixToNameMap[bastionSuffix] = secBastionGroupName
} else {
// We reconcile the security groups before the bastion, because the bastion
// needs its security group to be created first when managed security groups are enabled.
// When the bastion is disabled, we will try to delete the security group if it exists.
// In the first attempt, the security group will still be in-use by the bastion instance
// but then the bastion instance will be deleted in the next reconcile loop.
// We do that here because we don't want to manage the bastion security group from
// elsewhere, that could cause infinite loops between ReconCileSecurityGroups and ReconcileBastion.
// Therefore we try to delete the bastion security group as a best effort here
// and also when the cluster is deleted so we're sure it will be deleted at some point.
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/2113
if err := s.deleteSecurityGroup(openStackCluster, secBastionGroupName); err != nil {
s.scope.Logger().Info("Non-fatal error when deleting the bastion security group", "name", secBastionGroupName, "error", err)
return nil
}
}

// create security groups first, because desired rules use group ids.
Expand Down Expand Up @@ -351,10 +366,10 @@ func (s *Service) DeleteSecurityGroups(openStackCluster *infrav1.OpenStackCluste
secGroupNames := []string{
getSecControlPlaneGroupName(clusterResourceName),
getSecWorkerGroupName(clusterResourceName),
}

if openStackCluster.Spec.Bastion.IsEnabled() {
secGroupNames = append(secGroupNames, getSecBastionGroupName(clusterResourceName))
// Even if the bastion might be disabled, we still try to delete the security group in case
// we had a bastion before and for some reason we didn't delete its security group.
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/2113
getSecBastionGroupName(clusterResourceName),
}

for _, secGroupName := range secGroupNames {
Expand Down
1 change: 1 addition & 0 deletions pkg/cloud/services/networking/securitygroups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ func TestService_ReconcileSecurityGroups(t *testing.T) {
Return([]groups.SecGroup{{ID: "0", Name: controlPlaneSGName}}, nil)
m.ListSecGroup(groups.ListOpts{Name: workerSGName}).
Return([]groups.SecGroup{{ID: "1", Name: workerSGName}}, nil)
m.ListSecGroup(groups.ListOpts{Name: bastionSGName}).Return(nil, nil)

// We expect a total of 12 rules to be created.
// Nothing actually looks at the generated
Expand Down
6 changes: 6 additions & 0 deletions test/e2e/suites/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ var _ = Describe("e2e tests [PR-Blocking]", func() {
return false, errors.New("Bastion was not removed in OpenStackCluster.Status")
}, e2eCtx.E2EConfig.GetIntervals(specName, "wait-bastion")...,
).Should(BeTrue())
securityGroupsList, err = shared.DumpOpenStackSecurityGroups(e2eCtx, groups.ListOpts{Tags: clusterName})
Expect(err).NotTo(HaveOccurred())
Expect(securityGroupsList).To(HaveLen(2))

shared.Logf("Delete the bastion")
openStackCluster, err = shared.ClusterForSpec(ctx, e2eCtx, namespace)
Expand Down Expand Up @@ -242,6 +245,9 @@ var _ = Describe("e2e tests [PR-Blocking]", func() {
Expect(err).NotTo(HaveOccurred())
Expect(openStackCluster.Spec.Bastion).To(Equal(openStackClusterWithNewBastionFlavor.Spec.Bastion))
Expect(openStackCluster.Status.Bastion).NotTo(BeNil(), "OpenStackCluster.Status.Bastion with new flavor has not been populated")
securityGroupsList, err = shared.DumpOpenStackSecurityGroups(e2eCtx, groups.ListOpts{Tags: clusterName})
Expect(err).NotTo(HaveOccurred())
Expect(securityGroupsList).To(HaveLen(3))
})
})

Expand Down

0 comments on commit 489961a

Please sign in to comment.