Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ type ClusterSelector struct {
Type string `json:"type,omitempty"`
}

// IPPoolSelector defines the selection criteria for an IP pool to be imported.
// +kubebuilder:validation:XValidation:rule="has(self.namePrefix) != has(self.nameOverride)", message="either namePrefix or nameOverride must be set, but not both"
type IPPoolSelector struct {
// +kubebuilder:validation:Optional
NamePrefix string `json:"namePrefix,omitempty"`
// +kubebuilder:validation:Optional
NameOverride string `json:"nameOverride,omitempty"`
// +kubebuilder:validation:Optional
Region string `json:"region,omitempty"`
// +kubebuilder:validation:Optional
Role string `json:"role,omitempty"`
Expand Down
8 changes: 8 additions & 0 deletions config/crd/bases/argora.cloud.sap_ippoolimports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,28 @@ spec:
properties:
ippools:
items:
description: IPPoolSelector defines the selection criteria for an
IP pool to be imported.
properties:
excludeMask:
type: integer
excludedAddresses:
items:
type: string
type: array
nameOverride:
type: string
namePrefix:
type: string
region:
type: string
role:
type: string
type: object
x-kubernetes-validations:
- message: either namePrefix or nameOverride must be set, but not
both
rule: has(self.namePrefix) != has(self.nameOverride)
type: array
type: object
status:
Expand Down
11 changes: 7 additions & 4 deletions internal/controller/ippoolimport_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (r *IPPoolImportReconciler) reconcileIPPool(ctx context.Context, ipPoolSele
logger.Info("reconciling IPPool", "prefix", prefix.Prefix, "ID", prefix.ID)

ippool := &ipamv1alpha2.GlobalInClusterIPPool{}
ippoolName, err := generateIPPoolName(ipPoolSelector.NamePrefix, prefix)
ippoolName, err := generateIPPoolName(ipPoolSelector, prefix)
if err != nil {
return fmt.Errorf("unable to generate ippool name for prefix %s: %w", prefix.Prefix, err)
}
Expand Down Expand Up @@ -219,7 +219,10 @@ func (r *IPPoolImportReconciler) reconcileIPPool(ctx context.Context, ipPoolSele
}

// generateIPPoolName generates the name of the IPPool based on the given name prefix and prefix information.
func generateIPPoolName(namePrefix string, prefix *models.Prefix) (string, error) {
func generateIPPoolName(ipPoolSelector *argorav1alpha1.IPPoolSelector, prefix *models.Prefix) (string, error) {
if ipPoolSelector.NameOverride != "" {
return ipPoolSelector.NameOverride, nil
}
if strings.Contains(prefix.Role.Slug, ComputeTransitPrefixRoleName) {
azLetter := strings.TrimPrefix(prefix.Site.Slug, prefix.Site.Region.Slug)

Expand All @@ -230,10 +233,10 @@ func generateIPPoolName(namePrefix string, prefix *models.Prefix) (string, error
if err != nil {
return "", err
}
return fmt.Sprintf("%s-%s%d-%s", namePrefix, azLetter, computeNum-1, prefix.Site.Region.Slug), nil
return fmt.Sprintf("%s-%s%d-%s", ipPoolSelector.NamePrefix, azLetter, computeNum-1, prefix.Site.Region.Slug), nil
}
}
return fmt.Sprintf("%s-%s", namePrefix, prefix.Site.Slug), nil
return fmt.Sprintf("%s-%s", ipPoolSelector.NamePrefix, prefix.Site.Slug), nil
}

// generateNetGatewayIP generates the network address and gateway IP from the given prefix.
Expand Down
36 changes: 36 additions & 0 deletions internal/controller/ippoolimport_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,42 @@ var _ = Describe("IPPoolImport Controller", func() {
expectStatus(argorav1alpha1.Ready, typeNamespacedIPPoolImportName, "")
})

It("should successfully create a GlobalInClusterIPPool CR with Name Override", func() {
// given
netBoxMock := prepareNetboxMock()

By("update IPPoolImport CR to add Name Override")
err := k8sClient.Get(ctx, typeNamespacedIPPoolImportName, ipPoolImport)
Expect(err).ToNot(HaveOccurred())

iPPoolName := "ippool-override-name"
ipPoolImport.Spec.IPPools[0].NameOverride = iPPoolName
ipPoolImport.Spec.IPPools[0].NamePrefix = ""
Expect(k8sClient.Update(ctx, ipPoolImport)).To(Succeed())

controllerReconciler := createIPPoolImportReconciler(netBoxMock, fileReaderMock)

// when
By("reconciling IPPoolImport CR")
res, err := controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName: typeNamespacedIPPoolImportName})

// then
Expect(err).ToNot(HaveOccurred())
Expect(res.RequeueAfter).To(Equal(reconcileInterval))

Expect(netBoxMock.IPAMMock.(*mock.IPAMMock).GetPrefixesByRegionRoleCalls).To(Equal(1))

pool := &ipamv1alpha2.GlobalInClusterIPPool{}
err = k8sClient.Get(ctx, types.NamespacedName{
Name: iPPoolName,
Namespace: resourceNamespace,
}, pool)
Expect(err).ToNot(HaveOccurred())

expectIPPool(pool, iPPoolName, iPPoolPrefix1, iPPoolPrefixMask1, nil)
expectStatus(argorav1alpha1.Ready, typeNamespacedIPPoolImportName, "")
})

It("should successfully create a GlobalInClusterIPPool CR with Excluded Mask field", func() {
// given
netBoxMock := prepareNetboxMock()
Expand Down